Initial Checkin. Coded by Oliver Ruiz Dorantes (urnen@users.sf.net)

git-svn-id: file:///srv/svn/repos/haiku/trunk/current@589 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Phil Greenway 2002-08-05 05:26:43 +00:00
parent 267cf83fb3
commit 48d741f719
49 changed files with 9371 additions and 0 deletions

53
src/prefs/sounds/Colors.h Normal file
View File

@ -0,0 +1,53 @@
//Useful until be gets around to making these sorts of things
//globals akin to be_plain_font, etc.
#ifndef _SGB_COLORS_H_
#define _SGB_COLORS_H_
//******************************************************************************************************
//**** SYSTEM HEADER FILES
//******************************************************************************************************
#include <GraphicsDefs.h>
//******************************************************************************************************
//**** CONSTANT DEFINITIONS
//******************************************************************************************************
//Be standard UI colors
const rgb_color BeBackgroundGrey = {216,216,216, 255};
const rgb_color BeInactiveControlGrey = {240,240,240, 255};
const rgb_color BeFocusBlue = {0, 0, 229, 255};
const rgb_color BeHighlight = {255,255,255, 255};
const rgb_color BeShadow = {152,152,152, 255};
const rgb_color BeDarkShadow = {108,108,108, 255};
const rgb_color BeLightShadow = {194,194,194, 255};
const rgb_color BeButtonGrey = {232,232,232, 255};
const rgb_color BeInactiveGrey = {127,127,127, 255};
const rgb_color BeListSelectGrey = {178,178,178, 255};
const rgb_color BeTitleBarYellow = {255,203,0, 255};
//Other colors
const rgb_color Black = {0, 0, 0, 255};
const rgb_color White = {255,255,255, 255};
const rgb_color Red = {255,0, 0, 255};
const rgb_color Green = {0, 167,0, 255};
const rgb_color LightGreen = {90, 240,90, 255};
const rgb_color Blue = {49, 61, 225, 255};
const rgb_color LightBlue = {64, 162,255, 255};
const rgb_color Purple = {144,64, 221, 255};
const rgb_color LightPurple = {166,74, 255, 255};
const rgb_color Lavender = {193,122,255, 255};
const rgb_color Yellow = {255,203,0, 255};
const rgb_color Orange = {255,163,0, 255};
const rgb_color Flesh = {255,231,186, 255};
const rgb_color Tan = {208,182,121, 255};
const rgb_color Brown = {154,110,45, 255};
const rgb_color Grey = {200,200,200, 255};
const rgb_color LightMetallicBlue = {143,166,240, 255};
const rgb_color MedMetallicBlue = {75, 96, 154, 255};
const rgb_color DarkMetallicBlue = {78, 89, 126, 255};
#endif

43
src/prefs/sounds/HApp.cpp Normal file
View File

@ -0,0 +1,43 @@
#include "HApp.h"
#include "HWindow.h"
#include "HAboutWindow.h"
#include "RectUtils.h"
#define APP_SIG "application/x-vnd.openSounds"
/***********************************************************
* Constructor
***********************************************************/
HApp::HApp() :BApplication(APP_SIG)
{
BRect rect;
RectUtils utils;
if(utils.LoadRectFromApp("window_rect",&rect) == false)
{
rect.Set(50,50,450,400);
}
HWindow *win = new HWindow(rect,"OpenSounds");
win->Show();
}
/***********************************************************
* Destructor
***********************************************************/
HApp::~HApp()
{
}
/***********************************************************
* AboutRequested
***********************************************************/
void
HApp::AboutRequested()
{
(new HAboutWindow("openSounds",
__DATE__,
"Created by Atsushi Takamatsu @ Sapporo,Japan.\nOpenBeOS Development by Oliver Ruiz Dorantes @ Tarragona,Spain",
"http://anas.worldonline.es/urnenfel/beos/openbeos",
"E-Mail: urnenfelder@worldonline.es"))->Show();
}

13
src/prefs/sounds/HApp.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef __HAPP_H__
#define __HAPP_H__
#include <Application.h>
class HApp :public BApplication{
public:
HApp();
virtual ~HApp();
protected:
virtual void AboutRequested();
};
#endif

View File

@ -0,0 +1,82 @@
#include "HEventItem.h"
#include "ResourceUtils.h"
#include <Bitmap.h>
#include <View.h>
#include <NodeInfo.h>
#include <Path.h>
#include <MediaFiles.h>
#include <Alert.h>
#include <Beep.h>
/***********************************************************
* Constructor
***********************************************************/
HEventItem::HEventItem(const char* name,const char* path)
:CLVEasyItem(0,false,false,20)
,fName(name)
{
//BBitmap* bitmap = new BBitmap(BRect(0,0,15,15),B_COLOR_8_BIT);
BBitmap *bitmap = ResourceUtils().GetBitmapResource('BBMP',"Sound Bitmap");
SetColumnContent(0,bitmap,false);
SetColumnContent(1,fName.String(),false,false);
SetColumnContent(2,"",false,false);
delete bitmap;
SetPath(path);
}
/***********************************************************
* Destructor
***********************************************************/
HEventItem::~HEventItem()
{
}
/***********************************************************
* Set path
***********************************************************/
void
HEventItem::SetPath(const char* in_path)
{
fPath = in_path;
BPath path(in_path);
BPath parent;
BMediaFiles mfiles;
entry_ref ref;
if(path.InitCheck() == B_OK)
{
SetColumnContent(2,path.Leaf(),false,false);
::get_ref_for_path(path.Path(),&ref);
if(mfiles.SetRefFor(BMediaFiles::B_SOUNDS,Name(),ref) != B_OK)
(new BAlert("","Error","OK"))->Go();
path.GetParent(&parent);
SetColumnContent(3,parent.Path(),false,false);// falta
}else{
SetColumnContent(2,"<none>",false,false);
//if(mfiles.RemoveRefFor(BMediaFiles::B_SOUNDS,Name(),ref) != B_OK)
// (new BAlert("","Entry not found","OK"))->Go();
mfiles.RemoveItem(BMediaFiles::B_SOUNDS,Name());
::add_system_beep_event(Name());
SetColumnContent(3,"<none>",false,false);// falta
}
}
/***********************************************************
* Remove
***********************************************************/
void
HEventItem::Remove()
{
BMediaFiles mfiles;
mfiles.RemoveItem(BMediaFiles::B_SOUNDS,Name());
}
/***********************************************************
*
***********************************************************/
const char*
HEventItem::Path() const
{
return fPath.String();
}

View File

@ -0,0 +1,26 @@
#ifndef __HEVENTITEM_H__
#define __HEVENTITEM_H__
#include "CLVEasyItem.h"
#include <String.h>
#include <Entry.h>
class HEventItem :public CLVEasyItem {
public:
HEventItem(const char* event_name
,const char* path);
virtual ~HEventItem();
const char* Name()const {return fName.String();}
const char* Path()const;
void Remove();
void SetPath(const char* path);
protected:
private:
BString fName;
BString fPath;
float fText_offset;
};
#endif

View File

@ -0,0 +1,242 @@
#include "HEventList.h"
#include "HEventItem.h"
#include "HWindow.h"
#include "IconMenuItem.h"
#include <ClassInfo.h>
#include <MediaFiles.h>
#include <Path.h>
#include <Alert.h>
#include <PopUpMenu.h>
#include <MenuField.h>
#include <MenuItem.h>
#include <stdio.h>
#include <Mime.h>
#include <Roster.h>
#include <NodeInfo.h>
/***********************************************************
* Constructor
***********************************************************/
HEventList::HEventList(BRect rect
,CLVContainerView** ContainerView
,const char* name)
:ColumnListView(rect
,ContainerView
,name
,B_FOLLOW_ALL
,B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE
,B_SINGLE_SELECTION_LIST
,false
,false
,true)
{
AddColumn(new CLVColumn(NULL,20,CLV_LOCK_AT_BEGINNING|CLV_NOT_MOVABLE|
CLV_NOT_RESIZABLE|CLV_PUSH_PASS|CLV_MERGE_WITH_RIGHT));
AddColumn(new CLVColumn("Event",100,CLV_SORT_KEYABLE));
AddColumn( new CLVColumn("Sound",130,CLV_SORT_KEYABLE));
AddColumn( new CLVColumn("Path",200,CLV_SORT_KEYABLE));
SetSortKey(1);
SetSortFunction(CLVEasyItem::CompareItems);
}
/***********************************************************
* Destructor
***********************************************************/
HEventList::~HEventList()
{
}
/***********************************************************
* Set type
***********************************************************/
void
HEventList::SetType(const char* type)
{
RemoveAll();
BMediaFiles mfiles;
mfiles.RewindRefs(type);
BString name;
entry_ref ref;
while(mfiles.GetNextRef(&name,&ref) == B_OK)
{
AddItem(new HEventItem(name.String(),BPath(&ref).Path()));
}
SortItems();
}
/***********************************************************
* Remove all items
***********************************************************/
void
HEventList::RemoveAll()
{
int32 count = CountItems();
while(count> 0)
delete fPointerList.RemoveItem(--count);
MakeEmpty();
}
/***********************************************************
* Add event item
***********************************************************/
void
HEventList::AddEvent(HEventItem *item)
{
fPointerList.AddItem(item);
AddItem(item);
}
/***********************************************************
* Remove event item
***********************************************************/
void
HEventList::RemoveEvent(HEventItem *item)
{
item->Remove();
fPointerList.RemoveItem(item);
RemoveItem(item);
}
/***********************************************************
* Selection Changed
***********************************************************/
void
HEventList::SelectionChanged()
{
int32 sel = CurrentSelection();
if(sel >= 0)
{
HEventItem *item = cast_as(ItemAt(sel),HEventItem);
if(!item)
return;
BMessage msg(M_EVENT_CHANGED);
msg.AddString("name",item->Name());
msg.AddString("path",item->Path());
Window()->PostMessage(&msg);
}
}
/***********************************************************
* MouseDown
***********************************************************/
void
HEventList::MouseDown(BPoint pos)
{
BPoint point = pos;
int32 buttons;
Window()->CurrentMessage()->FindInt32("buttons", &buttons);
this->MakeFocus(true);
// 右クリックのハンドリング
if(buttons == B_SECONDARY_MOUSE_BUTTON)
{
int32 sel = IndexOf(pos);
if(sel >= 0)
Select(sel);
else
DeselectAll();
sel = CurrentSelection();
bool enabled = (sel >= 0)?true:false;
BPopUpMenu *theMenu = new BPopUpMenu("RIGHT_CLICK",false,false);
BFont font(be_plain_font);
font.SetSize(10);
theMenu->SetFont(&font);
/* HWindow *parent = cast_as(Window(),HWindow);
BMenuField *menufield = cast_as(parent->FindView("filemenu"),BMenuField);
BMenu *old_menu = menufield->Menu();
int32 menus = old_menu->CountItems();
for(register int32 i = 0;i < menus-3;i++)
{
BMenuItem *old_item = old_menu->ItemAt(i);
if(!old_item)
continue;
BMessage *old_msg = old_item->Message();
BMessage *msg = new BMessage(*old_msg );
BMenuItem *new_item = new BMenuItem(old_item->Label(),msg);
new_item->SetEnabled(enabled);
theMenu->AddItem(new_item);
}
theMenu->AddSeparatorItem();
theMenu->AddItem(new BMenuItem("<none>",new BMessage(M_NONE_MESSAGE)));
theMenu->AddItem(new BMenuItem("Other…",new BMessage(M_OTHER_MESSAGE)));
theMenu->FindItem(M_NONE_MESSAGE)->SetEnabled(enabled);
theMenu->FindItem(M_OTHER_MESSAGE)->SetEnabled(enabled);
*/
BMenu *submenu = new BMenu("Open With…");
submenu->SetFont(&font);
BMimeType mime("audio");
if(enabled)
{
HEventItem *item = cast_as(this->ItemAt(sel),HEventItem);
const char* path = item->Path();
BFile file(path,B_READ_ONLY);
BNodeInfo ninfo(&file);
char type[B_MIME_TYPE_LENGTH+1];
ninfo.GetType(type);
mime.SetTo(type);
}
BMessage message;
mime.GetSupportingApps(&message);
/* int32 sub;
int32 supers;
message.FindInt32("be:subs", &sub);
message.FindInt32("be:supers", &supers);
*/
for (int32 index =0; ; index++)
{
const char *signature;
int32 length;
if (message.FindData("applications", 'CSTR', index, (const void **)&signature,
&length) != B_OK)
break;
entry_ref ref;
if(be_roster->FindApp(signature,&ref) != B_OK)
continue;
BMessage *msg = new BMessage(M_OPEN_WITH);
msg->AddRef("refs",&ref);
msg->AddString("sig",signature);
BBitmap *icon = new BBitmap(BRect(0,0,15,15),B_COLOR_8_BIT);
BNodeInfo::GetTrackerIcon(&ref,icon,B_MINI_ICON);
submenu->AddItem(new IconMenuItem(BPath(&ref).Leaf(),msg,0,0,icon));
// push each of the supporting apps signature uniquely
//queryIterator->PushUniqueSignature(signature);
}
theMenu->AddItem(submenu);
theMenu->AddSeparatorItem();
theMenu->AddItem(new BMenuItem("Remove Event",new BMessage(M_REMOVE_EVENT)));
//theMenu->FindItem(M_OPEN_WITH)->SetEnabled(enabled);
theMenu->FindItem(M_REMOVE_EVENT)->SetEnabled(enabled);
submenu->SetEnabled(enabled);
BRect r;
ConvertToScreen(&pos);
r.top = pos.y - 5;
r.bottom = pos.y + 5;
r.left = pos.x - 5;
r.right = pos.x + 5;
BMenuItem *theItem = theMenu->Go(pos, false,true,r);
if(theItem)
{
BMessage* aMessage = theItem->Message();
if(aMessage)
this->Window()->PostMessage(aMessage);
}
delete theMenu;
}else
ColumnListView::MouseDown(point);
}

View File

@ -0,0 +1,30 @@
#ifndef __HEVENTLIST_H__
#define __HEVENTLIST_H__
#include "ColumnListView.h"
class HEventItem;
enum{
M_EVENT_CHANGED = 'SCAG'
};
class HEventList :public ColumnListView {
public:
HEventList(BRect rect
,CLVContainerView** ContainerView
,const char* name="EventList");
virtual ~HEventList();
void RemoveAll();
void AddEvent(HEventItem *item);
void RemoveEvent(HEventItem *item);
void SetType(const char* type);
protected:
virtual void MouseDown(BPoint point);
virtual void SelectionChanged();
private:
BList fPointerList;
};
#endif

View File

@ -0,0 +1,22 @@
#ifndef __HTYPELIST_H__
#define __HTYPELIST_H__
#include <ListView.h>
enum{
M_TYPE_CHANGED = 'TCHD'
};
class HTypeList :public BListView{
public:
HTypeList(BRect rect,const char* name = "TypeList");
virtual ~HTypeList();
protected:
void AddType(const char* name);
void Init();
void RemoveAll();
virtual void SelectionChanged();
private:
BList fPointerList;
};
#endif

View File

@ -0,0 +1,578 @@
#include "HWindow.h"
#include "HEventList.h"
#include "HEventItem.h"
#include "HTypeList.h"
#include "BetterScrollView.h"
#include "RectUtils.h"
#include "TextEntryAlert.h"
#include <ClassInfo.h>
#include <MediaFiles.h>
#include <MenuBar.h>
#include <Box.h>
#include <MenuItem.h>
#include <MenuField.h>
#include <Button.h>
#include <Beep.h>
#include <Path.h>
#include <Application.h>
#include <Node.h>
#include <NodeInfo.h>
#include <Alert.h>
#include <Roster.h>
#include <fs_attr.h>
#include <stdio.h>
#include <Sound.h>
/***********************************************************
* Constructor
***********************************************************/
HWindow::HWindow(BRect rect ,const char* name)
:_inherited(rect,name,B_TITLED_WINDOW,0)
,fFilePanel(NULL)
,fPlayer(NULL)
{
InitMenu();
InitGUI();
float min_width,min_height,max_width,max_height;
GetSizeLimits(&min_width,&max_width,&min_height,&max_height);
min_width = 320;
min_height = 150;
SetSizeLimits(min_width,max_width,min_height,max_height);
}
/***********************************************************
* Destructor
***********************************************************/
HWindow::~HWindow()
{
delete fFilePanel;
delete fPlayer;
}
/***********************************************************
* Initialize GUIs.
***********************************************************/
void
HWindow::InitGUI()
{
BRect rect = Bounds();
rect.top = KeyMenuBar()->Bounds().Height()+1;
/*fTypeList = new HTypeList(rect);
BScrollView *scrollView = new BScrollView(""
,fTypeList
,B_FOLLOW_LEFT|B_FOLLOW_TOP_BOTTOM
,0
,false
,true);
AddChild(scrollView);
*/
//rect.left = rect.right + B_V_SCROLL_BAR_WIDTH+3;
rect.right -= B_V_SCROLL_BAR_WIDTH;
rect.bottom = Bounds().bottom - B_H_SCROLL_BAR_HEIGHT - 80;
BetterScrollView *betterScrollView;
fEventList = new HEventList(rect,(CLVContainerView**)&betterScrollView);
fEventList->SetType(BMediaFiles::B_SOUNDS);
AddChild(betterScrollView);
app_info info;
be_app->GetAppInfo(&info);
BEntry entry(&info.ref);
float width;
BFile appfile(&entry,B_WRITE_ONLY);
attr_info ainfo;
if(appfile.GetAttrInfo("event_width",&ainfo) == B_OK)
{
appfile.ReadAttr("event_width",B_FLOAT_TYPE,0,&width,sizeof(float));
CLVColumn *col = fEventList->ColumnAt(1);
if(col)
col->SetWidth(width);
}
if(appfile.GetAttrInfo("sound_width",&ainfo) == B_OK)
{
appfile.ReadAttr("sound_width",B_FLOAT_TYPE,0,&width,sizeof(float));
CLVColumn *col = fEventList->ColumnAt(2);
if(col)
col->SetWidth(width);
}
rect.top = rect.bottom +1;
rect.bottom = Bounds().bottom;
rect.right = Bounds().right;
BView *view = new BView(rect,"",B_FOLLOW_LEFT_RIGHT|B_FOLLOW_BOTTOM,B_WILL_DRAW|B_PULSE_NEEDED);
view->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
AddChild(view);
rect = view->Bounds();
rect.top += 10;
rect.bottom -= 10;
rect.left += 10;
rect.right -= 10;
BBox *box = new BBox(rect,"",B_FOLLOW_ALL);
view->AddChild(box);
rect = box->Bounds();
rect.top += 10;
rect.left += 10;
rect.right -= 10;
rect.bottom = rect.top + 20;
BMenu *menu = new BMenu("file");
menu->SetRadioMode(true);
menu->SetLabelFromMarked(true);
menu->AddSeparatorItem();
menu->AddItem(new BMenuItem("<none>",new BMessage(M_NONE_MESSAGE)));
menu->AddItem(new BMenuItem("Other…",new BMessage(M_OTHER_MESSAGE)));
BMenuField *menuField = new BMenuField(rect
,"filemenu"
,"Sound File:"
,menu
,B_FOLLOW_TOP|B_FOLLOW_LEFT);
menuField->SetDivider(menuField->StringWidth("Media Files:")+3);
box->AddChild(menuField);
rect.OffsetBy(0,25);
rect.left = rect.right - 80;
BButton *button = new BButton(rect
,"play"
,"Play"
,new BMessage(M_PLAY_MESSAGE)
,B_FOLLOW_RIGHT|B_FOLLOW_TOP);
box->AddChild(button);
rect.OffsetBy(-90,0);
button = new BButton(rect
,"stop"
,"Stop"
,new BMessage(M_STOP_MESSAGE)
,B_FOLLOW_RIGHT|B_FOLLOW_TOP);
box->AddChild(button);
rect.OffsetBy(-110,0);
button = new BButton(rect
,"setsystem"
,"As System event"
,NULL
,B_FOLLOW_RIGHT|B_FOLLOW_TOP);
button->ResizeToPreferred();
box->AddChild(button);
// setup file menu
SetupMenuField();
menu->FindItem("<none>")->SetMarked(true);
}
/***********************************************************
* Initalize Menus
***********************************************************/
void
HWindow::InitMenu()
{
BMenuBar *menuBar = new BMenuBar(Bounds(),"MENUBAR");
BMenu *menu;
BMenuItem *item;
menu = new BMenu("File");
item = new BMenuItem("Add New Event…",new BMessage(M_ADD_EVENT),'A');
item->SetTarget(this);
menu->AddItem(item);
item = new BMenuItem("Remove Event",new BMessage(M_REMOVE_EVENT),'R');
item->SetTarget(this);
menu->AddItem(item);
menu->AddSeparatorItem();
item = new BMenuItem("About openSounds…",new BMessage(B_ABOUT_REQUESTED),0,0);
item->SetTarget(be_app);
menu->AddItem(item);
menu->AddSeparatorItem();
item = new BMenuItem("Quit",new BMessage(B_QUIT_REQUESTED),'Q',0);
menu->AddItem(item);
menuBar->AddItem(menu);
this->AddChild(menuBar);
}
/***********************************************************
* MessageReceived
***********************************************************/
void
HWindow::MessageReceived(BMessage *message)
{
switch(message->what)
{
case M_OPEN_WITH:
{
entry_ref app_ref;
if(message->FindRef("refs",&app_ref) == B_OK)
{
BMessage msg(B_REFS_RECEIVED);
int32 sel = fEventList->CurrentSelection();
if(sel < 0)
break;
HEventItem *item = cast_as(fEventList->ItemAt(sel),HEventItem);
const char* path = item->Path();
entry_ref ref;
::get_ref_for_path(path,&ref);
msg.AddRef("refs",&ref);
be_roster->Launch(&app_ref,&msg);
}
break;
}
case M_REMOVE_EVENT:
{
RemoveEvent();
break;
}
case M_ADD_EVENT:
{
char new_Event[20];
(new BAlert("Add an event","Note that to use this event, applications should call him by the function:\n status_t system_beep(const char *eventName);\n declared on be/support/Beep.h","OK",NULL,NULL,
B_WIDTH_AS_USUAL,B_WARNING_ALERT))->Go();
if (((new TextEntryAlert("Add an event ...","Type name: ", "new event name",
"Create","Cancel",true,120,1,B_WIDTH_AS_USUAL,false,
NULL,B_FLOATING_WINDOW_LOOK)
)->Go(new_Event,20))==0){
add_system_beep_event(new_Event);
BMediaFiles mfiles;
mfiles.RewindRefs("Sound");
entry_ref ref;
mfiles.GetRefFor("Sound",new_Event,&ref);
fEventList->AddEvent(new HEventItem(new_Event,BPath(&ref).Path()));
}
break;
}
case M_OTHER_MESSAGE:
{
if(!fFilePanel)
fFilePanel = new BFilePanel();
fFilePanel->SetTarget(this);
fFilePanel->Show();
break;
}
case B_REFS_RECEIVED:
{
entry_ref ref;
int32 sel = fEventList->CurrentSelection();
if(message->FindRef("refs",&ref) == B_OK && sel >= 0)
{
BMenuField *menufield = cast_as(FindView("filemenu"),BMenuField);
BMenu *menu = menufield->Menu();
// check audio file
BNode node(&ref);
BNodeInfo ninfo(&node);
char type[B_MIME_TYPE_LENGTH+1];
ninfo.GetType(type);
BMimeType mtype(type);
BMimeType superType;
mtype.GetSupertype(&superType);
if(strcmp(superType.Type(),"audio") != 0)
{
beep();
(new BAlert("","This is not a audio file.","OK",NULL,NULL,
B_WIDTH_AS_USUAL,B_STOP_ALERT))->Go();
break;
}
// add file item
BMessage *msg = new BMessage(M_ITEM_MESSAGE);
BPath path(&ref);
msg->AddRef("refs",&ref);
if(!(menu->FindItem(path.Leaf())))
menu->AddItem(new BMenuItem(path.Leaf(),msg),0);
// refresh item
HEventItem *item = cast_as(fEventList->ItemAt(sel),HEventItem);
item->SetPath(path.Path());
fEventList->InvalidateItem(sel);
// check file menu
BMenuItem *menuitem = menu->FindItem(path.Leaf());
if(menuitem)
menuitem->SetMarked(true);
}
break;
}
case M_PLAY_MESSAGE:
{
int32 sel = fEventList->CurrentSelection();
if(sel >= 0)
{
HEventItem *item = cast_as(fEventList->ItemAt(sel),HEventItem);
//system_beep(item->Name());
const char* path = item->Path();
if(path)
{
entry_ref ref;
::get_ref_for_path(path,&ref);
/*BSound *sound = new BSound(&ref);
if(sound->InitCheck() == B_OK)
{
fSoundPlayer.Start();
fId = fSoundPlayer.StartPlaying(sound);
sound->ReleaseRef();
}*/
//fSoundHandle = ::play_sound(&ref,false,false,true);
delete fPlayer;
fPlayer = new BFileGameSound(&ref,false);
fPlayer->StartPlaying();
}
}
break;
}
case M_STOP_MESSAGE:
{
//::stop_sound(fSoundHandle);
if(!fPlayer)
break;
if(fPlayer->IsPlaying())
{
fPlayer->StopPlaying();
delete fPlayer;
fPlayer = NULL;
}
break;
}
case M_EVENT_CHANGED:
{
const char* path;
BMenuField *menufield = cast_as(FindView("filemenu"),BMenuField);
BMenu *menu = menufield->Menu();
if(message->FindString("path",&path) == B_OK)
{
BPath path(path);
if(path.InitCheck() != B_OK)
{
BMenuItem *item = menu->FindItem("<none>");
if(item)
item->SetMarked(true);
}else{
BMenuItem *item = menu->FindItem(path.Leaf());
if(item)
item->SetMarked(true);
}
}
break;
}
case M_ITEM_MESSAGE:
{
entry_ref ref;
if(message->FindRef("refs",&ref) == B_OK)
{
int32 sel = fEventList->CurrentSelection();
if(sel >= 0)
{
HEventItem *item = cast_as(fEventList->ItemAt(sel),HEventItem);
item->SetPath(BPath(&ref).Path());
fEventList->InvalidateItem(sel);
}
}
break;
}
case M_NONE_MESSAGE:
{
int32 sel = fEventList->CurrentSelection();
if(sel >= 0)
{
HEventItem *item = cast_as(fEventList->ItemAt(sel),HEventItem);
item->SetPath(NULL);
fEventList->InvalidateItem(sel);
}
break;
}
default:
_inherited::MessageReceived(message);
}
}
/***********************************************************
* Init menu
***********************************************************/
void
HWindow::SetupMenuField()
{
BMenuField *menufield = cast_as(FindView("filemenu"),BMenuField);
BMenu *menu = menufield->Menu();
int32 count = fEventList->CountItems();
for(register int32 i = 0;i < count;i++)
{
HEventItem *item = cast_as(fEventList->ItemAt(i),HEventItem);
if(!item)
continue;
BPath path(item->Path());
if(path.InitCheck() != B_OK)
continue;
if( menu->FindItem(path.Leaf()) )
continue;
BMessage *msg = new BMessage(M_ITEM_MESSAGE);
entry_ref ref;
::get_ref_for_path(path.Path(),&ref);
msg->AddRef("refs",&ref);
menu->AddItem(new BMenuItem(path.Leaf(),msg),0);
}
BPath path("/boot/beos/etc/sounds");
status_t err = B_OK;
BDirectory dir( path.Path() );
BEntry entry;
BPath item_path;
while( err == B_OK ){
err = dir.GetNextEntry( (BEntry*)&entry, TRUE );
if( entry.InitCheck() != B_NO_ERROR ){
break;
}
entry.GetPath(&item_path);
if( menu->FindItem(item_path.Leaf()) )
continue;
BMessage *msg = new BMessage(M_ITEM_MESSAGE);
entry_ref ref;
::get_ref_for_path(item_path.Path(),&ref);
msg->AddRef("refs",&ref);
menu->AddItem(new BMenuItem(item_path.Leaf(),msg),0);
}
path.SetTo("/boot/home/config/sounds");
dir.SetTo(path.Path());
err = B_OK;
while( err == B_OK ){
err = dir.GetNextEntry( (BEntry*)&entry, TRUE );
if( entry.InitCheck() != B_NO_ERROR ){
break;
}
entry.GetPath(&item_path);
if( menu->FindItem(item_path.Leaf()) )
continue;
BMessage *msg = new BMessage(M_ITEM_MESSAGE);
entry_ref ref;
::get_ref_for_path(item_path.Path(),&ref);
msg->AddRef("refs",&ref);
menu->AddItem(new BMenuItem(item_path.Leaf(),msg),0);
}
path.SetTo("/boot/home/media");
dir.SetTo(path.Path());
err = B_OK;
while( err == B_OK ){
err = dir.GetNextEntry( (BEntry*)&entry, TRUE );
if( entry.InitCheck() != B_NO_ERROR ){
break;
}
entry.GetPath(&item_path);
if( menu->FindItem(item_path.Leaf()) )
continue;
BMessage *msg = new BMessage(M_ITEM_MESSAGE);
entry_ref ref;
::get_ref_for_path(item_path.Path(),&ref);
msg->AddRef("refs",&ref);
menu->AddItem(new BMenuItem(item_path.Leaf(),msg),0);
}
}
/***********************************************************
* Remove Event
***********************************************************/
void
HWindow::RemoveEvent()
{
int32 sel = fEventList->CurrentSelection();
if(sel < 0)
return;
// check system beep event
HEventItem *item = cast_as(fEventList->ItemAt(sel),HEventItem);
if(::strcmp(item->Name(),"Beep") == 0
||::strcmp(item->Name(),"Startup")==0
||::strcmp(item->Name(),"New E-mail") == 0)
{
beep();
(new BAlert("","This is a system beep event.","OK"))->Go();
return;
}
//
int32 index = (new BAlert("","Would you like to remove this event?","OK","Cancel"))->Go();
if(index == 0)
{
if(item)
fEventList->RemoveEvent(item);
}
}
/***********************************************************
* Pulse
***********************************************************/
void
HWindow::Pulse()
{
int32 sel = fEventList->CurrentSelection();
BMenuField *menufield = cast_as(FindView("filemenu"),BMenuField);
BButton *button = cast_as(FindView("play"),BButton);
BButton *stop = cast_as(FindView("stop"),BButton);
if(!menufield)
return;
if(sel >=0)
{
menufield->SetEnabled(true);
button->SetEnabled(true);
}else{
menufield->SetEnabled(false);
button->SetEnabled(false);
}
if(fPlayer)
{
if(fPlayer->IsPlaying())
stop->SetEnabled(true);
else
stop->SetEnabled(false);
}else
stop->SetEnabled(false);
}
/***********************************************************
* DispatchMessage
***********************************************************/
void
HWindow::DispatchMessage(BMessage *message,BHandler *handler)
{
if(message->what == B_PULSE)
Pulse();
BWindow::DispatchMessage(message,handler);
}
/***********************************************************
* QuitRequested
***********************************************************/
bool
HWindow::QuitRequested()
{
RectUtils utils;
CLVColumn *col = fEventList->ColumnAt(1);
float width = col->Width();
app_info info;
be_app->GetAppInfo(&info);
BEntry entry(&info.ref);
BFile appfile(&entry,B_WRITE_ONLY);
appfile.WriteAttr("event_width",B_FLOAT_TYPE,0,&width,sizeof(float));
col = fEventList->ColumnAt(2);
width = col->Width();
appfile.WriteAttr("sound_width",B_FLOAT_TYPE,0,&width,sizeof(float));
fEventList->RemoveAll();
utils.SaveRectToApp("window_rect",this->Frame());
be_app->PostMessage(B_QUIT_REQUESTED);
return true;
}

View File

@ -0,0 +1,44 @@
#ifndef __HWINDOW_H__
#define __HWINDOW_H__
#include <Window.h>
#include <FilePanel.h>
#include <FileGameSound.h>
class HEventList;
class HTypeList;
enum{
M_PLAY_MESSAGE = 'MPLM',
M_STOP_MESSAGE = 'MSTO',
M_REMOVE_MESSAGE = 'MREM',
M_ITEM_MESSAGE = 'MITE',
M_OTHER_MESSAGE = 'MOTH',
M_NONE_MESSAGE = 'MNON',
M_ADD_EVENT = 'MADE',
M_REMOVE_EVENT = 'MREE',
M_OPEN_WITH = 'MOPW'
};
class HWindow :public BWindow {
public:
HWindow(BRect rect ,const char* name);
protected:
virtual ~HWindow();
virtual void MessageReceived(BMessage *message);
virtual bool QuitRequested();
virtual void DispatchMessage(BMessage *message
,BHandler *handler);
void InitGUI();
void InitMenu();
void SetupMenuField();
void Pulse();
void RemoveEvent();
private:
//HTypeList* fTypeList;
HEventList* fEventList;
typedef BWindow _inherited;
BFilePanel* fFilePanel;
BFileGameSound *fPlayer;
};
#endif

BIN
src/prefs/sounds/Icon.rsrc Normal file

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,213 @@
//*** LICENSE ***
//ColumnListView, its associated classes and source code, and the other components of Santa's Gift Bag are
//being made publicly available and free to use in freeware and shareware products with a price under $25
//(I believe that shareware should be cheap). For overpriced shareware (hehehe) or commercial products,
//please contact me to negotiate a fee for use. After all, I did work hard on this class and invested a lot
//of time into it. That being said, DON'T WORRY I don't want much. It totally depends on the sort of project
//you're working on and how much you expect to make off it. If someone makes money off my work, I'd like to
//get at least a little something. If any of the components of Santa's Gift Bag are is used in a shareware
//or commercial product, I get a free copy. The source is made available so that you can improve and extend
//it as you need. In general it is best to customize your ColumnListView through inheritance, so that you
//can take advantage of enhancements and bug fixes as they become available. Feel free to distribute the
//ColumnListView source, including modified versions, but keep this documentation and license with it.
//Conventions:
// Global constants (declared with const) and #defines - all uppercase letters with words separated
// by underscores.
// (E.G., #define MY_DEFINE 5).
// (E.G., const int MY_CONSTANT = 5;).
// New data types (classes, structs, typedefs, etc.) - begin with an uppercase letter followed by
// lowercase words separated by uppercase letters. Enumerated constants contain a prefix
// associating them with a particular enumerated set.
// (E.G., typedef int MyTypedef;).
// (E.G., enum MyEnumConst {MEC_ONE, MEC_TWO};)
// Global variables - begin with "g_" followed by lowercase words separated by underscores.
// (E.G., int g_my_global;).
// Argument and local variables - begin with a lowercase letter followed by
// lowercase words separated by underscores.
// (E.G., int my_local;).
// Member variables - begin with "m_" followed by lowercase words separated by underscores.
// (E.G., int m_my_member;).
// Functions (member or global) - begin with an uppercase letter followed by lowercase words
// separated by uppercase letters.
// (E.G., void MyFunction(void);).
//******************************************************************************************************
//**** PROJECT HEADER FILES
//******************************************************************************************************
#include "BetterScrollView.h"
#include "Colors.h"
//******************************************************************************************************
//**** BetterScrollView CLASS
//******************************************************************************************************
#include <stdio.h>
BetterScrollView::BetterScrollView(const char *name, BView *target, uint32 resizeMask, uint32 flags,
bool horizontal, bool vertical, bool scroll_view_corner, border_style border)
: BScrollView(name, target, resizeMask, flags, horizontal, vertical, border)
{
m_target = target;
m_data_rect.Set(-1,-1,-1,-1);
m_h_scrollbar = ScrollBar(B_HORIZONTAL);
m_v_scrollbar = ScrollBar(B_VERTICAL);
if(scroll_view_corner && horizontal && vertical)
{
m_scroll_view_corner = new ScrollViewCorner(m_v_scrollbar->Frame().left,
m_h_scrollbar->Frame().top);
AddChild(m_scroll_view_corner);
}
else
m_scroll_view_corner = NULL;
}
BetterScrollView::~BetterScrollView()
{ }
void BetterScrollView::SetDataRect(BRect data_rect, bool scrolling_allowed)
{
m_data_rect = data_rect;
UpdateScrollBars(scrolling_allowed);
}
void BetterScrollView::FrameResized(float new_width, float new_height)
{
BScrollView::FrameResized(new_width,new_height);
UpdateScrollBars(true);
}
void BetterScrollView::AttachedToWindow()
{
BScrollView::AttachedToWindow();
UpdateScrollBars(false);
}
void BetterScrollView::UpdateScrollBars(bool scrolling_allowed)
{
//Figure out the bounds and scroll if necessary
BRect view_bounds = m_target->Bounds();
float page_width, page_height, view_width, view_height;
view_width = view_bounds.right-view_bounds.left;
view_height = view_bounds.bottom-view_bounds.top;
float min,max;
if(scrolling_allowed)
{
//Figure out the width of the page rectangle
page_width = m_data_rect.right-m_data_rect.left;
page_height = m_data_rect.bottom-m_data_rect.top;
if(view_width > page_width)
page_width = view_width;
if(view_height > page_height)
page_height = view_height;
//Adjust positions
float delta_x = 0.0;
if(m_h_scrollbar)
{
if(view_bounds.left < m_data_rect.left)
delta_x = m_data_rect.left - view_bounds.left;
else if(view_bounds.right > m_data_rect.left+page_width)
delta_x = m_data_rect.left+page_width - view_bounds.right;
}
float delta_y = 0.0;
if(m_v_scrollbar)
{
if(view_bounds.top < m_data_rect.top)
delta_y = m_data_rect.top - view_bounds.top;
else if(view_bounds.bottom > m_data_rect.top+page_height)
delta_y = m_data_rect.top+page_height - view_bounds.bottom;
}
if(delta_x != 0.0 || delta_y != 0.0)
{
m_target->ScrollTo(BPoint(view_bounds.left+delta_x,view_bounds.top+delta_y));
view_bounds = Bounds();
}
}
else
{
min = m_data_rect.left;
if(view_bounds.left < min)
min = view_bounds.left;
max = m_data_rect.right;
if(view_bounds.right > max)
max = view_bounds.right;
page_width = max-min;
min = m_data_rect.top;
if(view_bounds.top < min)
min = view_bounds.top;
max = m_data_rect.bottom;
if(view_bounds.bottom > max)
max = view_bounds.bottom;
page_height = max-min;
}
//Figure out the ratio of the bounds rectangle width or height to the page rectangle width or height
float width_prop = view_width/page_width;
float height_prop = view_height/page_height;
//Set the scroll bar ranges and proportions. If the whole document is visible, inactivate the
//slider
bool active_scroller = false;
if(m_h_scrollbar)
{
if(width_prop >= 1.0)
m_h_scrollbar->SetRange(0.0,0.0);
else
{
min = m_data_rect.left;
max = m_data_rect.left + page_width - view_width;
if(view_bounds.left < min)
min = view_bounds.left;
if(view_bounds.left > max)
max = view_bounds.left;
m_h_scrollbar->SetRange(min,max);
m_h_scrollbar->SetSteps(ceil(view_width/20), view_width);
active_scroller = true;
}
m_h_scrollbar->SetProportion(width_prop);
}
if(m_v_scrollbar)
{
if(height_prop >= 1.0)
m_v_scrollbar->SetRange(0.0,0.0);
else
{
min = m_data_rect.top;
max = m_data_rect.top + page_height - view_height;
if(view_bounds.top < min)
min = view_bounds.top;
if(view_bounds.top > max)
max = view_bounds.top;
m_v_scrollbar->SetRange(min,max);
m_v_scrollbar->SetSteps(ceil(view_height/20), view_height);
active_scroller = true;
}
m_v_scrollbar->SetProportion(height_prop);
}
if(m_scroll_view_corner)
{
rgb_color cur_color = m_scroll_view_corner->ViewColor();
rgb_color new_color;
if(active_scroller)
new_color = BeBackgroundGrey;
else
new_color = BeInactiveControlGrey;
if(new_color.red != cur_color.red || new_color.green != cur_color.green ||
new_color.blue != cur_color.blue || new_color.alpha != cur_color.alpha)
{
m_scroll_view_corner->SetViewColor(new_color);
m_scroll_view_corner->Invalidate();
}
}
}

View File

@ -0,0 +1,78 @@
//*** LICENSE ***
//ColumnListView, its associated classes and source code, and the other components of Santa's Gift Bag are
//being made publicly available and free to use in freeware and shareware products with a price under $25
//(I believe that shareware should be cheap). For overpriced shareware (hehehe) or commercial products,
//please contact me to negotiate a fee for use. After all, I did work hard on this class and invested a lot
//of time into it. That being said, DON'T WORRY I don't want much. It totally depends on the sort of project
//you're working on and how much you expect to make off it. If someone makes money off my work, I'd like to
//get at least a little something. If any of the components of Santa's Gift Bag are is used in a shareware
//or commercial product, I get a free copy. The source is made available so that you can improve and extend
//it as you need. In general it is best to customize your ColumnListView through inheritance, so that you
//can take advantage of enhancements and bug fixes as they become available. Feel free to distribute the
//ColumnListView source, including modified versions, but keep this documentation and license with it.
//Conventions:
// Global constants (declared with const) and #defines - all uppercase letters with words separated
// by underscores.
// (E.G., #define MY_DEFINE 5).
// (E.G., const int MY_CONSTANT = 5;).
// New data types (classes, structs, typedefs, etc.) - begin with an uppercase letter followed by
// lowercase words separated by uppercase letters. Enumerated constants contain a prefix
// associating them with a particular enumerated set.
// (E.G., typedef int MyTypedef;).
// (E.G., enum MyEnumConst {MEC_ONE, MEC_TWO};)
// Global variables - begin with "g_" followed by lowercase words separated by underscores.
// (E.G., int g_my_global;).
// Argument and local variables - begin with a lowercase letter followed by
// lowercase words separated by underscores.
// (E.G., int my_local;).
// Member variables - begin with "m_" followed by lowercase words separated by underscores.
// (E.G., int m_my_member;).
// Functions (member or global) - begin with an uppercase letter followed by lowercase words
// separated by uppercase letters.
// (E.G., void MyFunction(void);).
#ifndef _SGB_BETTER_SCROLL_VIEW_H_
#define _SGB_BETTER_SCROLL_VIEW_H_
//******************************************************************************************************
//**** PROJECT HEADER FILES
//******************************************************************************************************
#include <ScrollView.h>
//******************************************************************************************************
//**** PROJECT HEADER FILES
//******************************************************************************************************
#include "ScrollViewCorner.h"
//******************************************************************************************************
//**** CLASS DECLARATIONS
//******************************************************************************************************
class BetterScrollView : public BScrollView
{
public:
BetterScrollView(const char *name, BView *target, uint32 resizeMask = B_FOLLOW_LEFT | B_FOLLOW_TOP,
uint32 flags = B_FRAME_EVENTS | B_WILL_DRAW, bool horizontal = true, bool vertical = true,
bool scroll_view_corner = true, border_style border = B_FANCY_BORDER);
virtual ~BetterScrollView();
virtual void SetDataRect(BRect data_rect, bool scrolling_allowed = true);
inline BRect DataRect() {return m_data_rect;}
virtual void FrameResized(float new_width, float new_height);
virtual void AttachedToWindow();
private:
void UpdateScrollBars(bool scrolling_allowed);
BRect m_data_rect;
BScrollBar* m_h_scrollbar;
BScrollBar* m_v_scrollbar;
ScrollViewCorner* m_scroll_view_corner;
BView* m_target;
};
#endif

View File

@ -0,0 +1,426 @@
//Column list header source file
//*** LICENSE ***
//ColumnListView, its associated classes and source code, and the other components of Santa's Gift Bag are
//being made publicly available and free to use in freeware and shareware products with a price under $25
//(I believe that shareware should be cheap). For overpriced shareware (hehehe) or commercial products,
//please contact me to negotiate a fee for use. After all, I did work hard on this class and invested a lot
//of time into it. That being said, DON'T WORRY I don't want much. It totally depends on the sort of project
//you're working on and how much you expect to make off it. If someone makes money off my work, I'd like to
//get at least a little something. If any of the components of Santa's Gift Bag are is used in a shareware
//or commercial product, I get a free copy. The source is made available so that you can improve and extend
//it as you need. In general it is best to customize your ColumnListView through inheritance, so that you
//can take advantage of enhancements and bug fixes as they become available. Feel free to distribute the
//ColumnListView source, including modified versions, but keep this documentation and license with it.
//******************************************************************************************************
//**** SYSTEM HEADER FILES
//******************************************************************************************************
#include <string.h>
#include <Window.h>
#include <Region.h>
//******************************************************************************************************
//**** PROJECT HEADER FILES
//******************************************************************************************************
#include "CLVColumn.h"
#include "ColumnListView.h"
#include "CLVColumnLabelView.h"
#include "NewStrings.h"
#include "Descending.h"
#include "Ascending.h"
#include "PrefilledBitmap.h"
//******************************************************************************************************
//**** CLVColumn CLASS DEFINITION
//******************************************************************************************************
CLVColumn::CLVColumn(const char* label,float width,uint32 flags,float min_width)
{
if(flags & CLV_EXPANDER)
{
label = NULL;
width = 15.0;
min_width = 15.0;
flags &= CLV_NOT_MOVABLE | CLV_LOCK_AT_BEGINNING | CLV_HIDDEN | CLV_LOCK_WITH_RIGHT;
flags |= CLV_EXPANDER | CLV_NOT_RESIZABLE | CLV_MERGE_WITH_RIGHT;
}
if(min_width < 4.0)
min_width = 4.0;
if(width < min_width)
width = min_width;
if(label)
{
fLabel = new char[strlen(label)+1];
strcpy((char*)fLabel,label);
if(CLV_HEADER_TRUNCATE)
{
int32 truncated_text_length = strlen(label)+3;
fTruncatedText = new char[truncated_text_length];
fTruncatedText[0] = 0;
fCachedRect.Set(-1,-1,-1,-1);
}
else
fTruncatedText = NULL;
}
else
{
fLabel = NULL;
fTruncatedText = NULL;
}
fWidth = width;
fMinWidth = min_width;
fFlags = flags;
fPushedByExpander = false;
fParent = NULL;
fSortMode = NoSort;
}
CLVColumn::~CLVColumn()
{
if(fParent)
fParent->RemoveColumn(this);
if(fLabel)
delete[] fLabel;
if(fTruncatedText)
delete[] fTruncatedText;
}
float CLVColumn::Width() const
{
return fWidth;
}
void CLVColumn::SetWidth(float width)
{
if(width < fMinWidth)
width = fMinWidth;
if(width != fWidth)
{
float OldWidth = fWidth;
fWidth = width;
if(IsShown() && fParent)
{
//Figure out the area after this column to scroll
BRect ColumnViewBounds = fParent->fColumnLabelView->Bounds();
BRect MainViewBounds = fParent->Bounds();
BRect SourceArea = ColumnViewBounds;
float Delta = width-OldWidth;
if(!(fFlags&CLV_RIGHT_JUSTIFIED))
SourceArea.left = fColumnEnd+1.0;
else
{
if(Delta >= 0)
SourceArea.left = fColumnBegin;
else
SourceArea.left = (fColumnBegin-Delta)+1;
}
BRect DestArea = SourceArea;
DestArea.left += Delta;
DestArea.right += Delta;
float LimitShift;
if(DestArea.right > ColumnViewBounds.right)
{
LimitShift = DestArea.right-ColumnViewBounds.right;
DestArea.right -= LimitShift;
SourceArea.right -= LimitShift;
}
if(DestArea.left < ColumnViewBounds.left)
{
LimitShift = ColumnViewBounds.left - DestArea.left;
DestArea.left += LimitShift;
SourceArea.left += LimitShift;
}
//Scroll the area that is being shifted
BWindow* ParentWindow = fParent->Window();
if(ParentWindow)
ParentWindow->UpdateIfNeeded();
fParent->fColumnLabelView->CopyBits(SourceArea,DestArea);
SourceArea.top = MainViewBounds.top;
SourceArea.bottom = MainViewBounds.bottom;
DestArea.top = MainViewBounds.top;
DestArea.bottom = MainViewBounds.bottom;
fParent->CopyBits(SourceArea,DestArea);
//Invalidate the region that got revealed
DestArea = ColumnViewBounds;
if(width > OldWidth)
{
if(!(fFlags&CLV_RIGHT_JUSTIFIED))
{
DestArea.left = fColumnEnd+1.0;
DestArea.right = fColumnEnd+Delta;
}
else
{
DestArea.left = fColumnBegin;
DestArea.right = fColumnBegin+Delta;
}
}
else
{
DestArea.left = ColumnViewBounds.right+Delta+1.0;
DestArea.right = ColumnViewBounds.right;
}
fParent->fColumnLabelView->Invalidate(DestArea);
DestArea.top = MainViewBounds.top;
DestArea.bottom = MainViewBounds.bottom;
fParent->Invalidate(DestArea);
if(fFlags & CLV_HEADER_TRUNCATE)
{
//Do truncation of label
BRect invalid_region = TruncateText(width);
if(fCachedRect != BRect(-1,-1,-1,-1))
fCachedRect.right += Delta;
if(invalid_region != BRect(-1,-1,-1,-1))
{
if(!(fFlags&CLV_RIGHT_JUSTIFIED))
GetHeaderView()->Invalidate(invalid_region);
else
GetHeaderView()->Invalidate(fCachedRect);
}
}
//Invalidate the old or new resize handle as necessary
/*DestArea = ColumnViewBounds;
if(width > OldWidth)
DestArea.left = fColumnEnd;
else
DestArea.left = fColumnEnd + Delta;
DestArea.right = DestArea.left;
fParent->fColumnLabelView->Invalidate(DestArea);
*/
fParent->fColumnLabelView->Invalidate();
//Update the column sizes, positions and group positions
fParent->UpdateColumnSizesDataRectSizeScrollBars(false);
fParent->fColumnLabelView->UpdateDragGroups();
}
if(fParent)
fParent->ColumnWidthChanged(fParent->fColumnList.IndexOf(this),fWidth);
}
}
BRect CLVColumn::TruncateText(float column_width)
//Returns whether the truncated text has changed
{
column_width -= 1+8+5+1;
//Because when I draw the text I start drawing 8 pixels to the right from the text area's left edge,
//which is in turn 1 pixel smaller than the column at each edge, and I want 5 trailing pixels.
BRect invalid(-1,-1,-1,-1);
if(fParent == NULL)
return invalid;
const char* text = GetLabel();
char new_text[256];
BFont font;
fParent->GetFont(&font);
GetTruncatedString(text,new_text,column_width,256,&font);
if(strcmp(fTruncatedText,new_text)!=0)
{
//The truncated text has changed
invalid = fCachedRect;
if(invalid != BRect(-1,-1,-1,-1))
{
//Figure out which region just got changed
int32 cmppos;
int32 cmplen = strlen(new_text);
char remember = 0;
for(cmppos = 0; cmppos <= cmplen; cmppos++)
if(new_text[cmppos] != fTruncatedText[cmppos])
{
remember = new_text[cmppos];
new_text[cmppos] = 0;
break;
}
invalid.left += 8 + be_plain_font->StringWidth(new_text);
new_text[cmppos] = remember;
}
//Remember the new truncated text
strcpy(fTruncatedText,new_text);
}
return invalid;
}
void GetTruncatedString(const char* full_string, char* truncated, float width, int32 truncate_buf_size,
const BFont* font)
{
Strtcpy(truncated,full_string,truncate_buf_size);
int32 choppos = strlen(truncated)-1;
while(choppos >= -2 && font->StringWidth(truncated) > width)
{
while(choppos > 0 && truncated[choppos-1] == ' ')
choppos--;
if(choppos > 0 || (choppos == 0 && truncated[0] == ' '))
truncated[choppos] = '.';
if(choppos > -1)
truncated[choppos+1] = '.';
if(choppos > -2)
truncated[choppos+2] = '.';
truncated[choppos+3] = 0;
choppos--;
}
}
uint32 CLVColumn::Flags() const
{
return fFlags;
}
bool CLVColumn::IsShown() const
{
if(fFlags & CLV_HIDDEN)
return false;
else
return true;
}
void CLVColumn::SetShown(bool Shown)
{
bool shown = IsShown();
if(shown != Shown)
{
if(Shown)
fFlags &= 0xFFFFFFFF^CLV_HIDDEN;
else
fFlags |= CLV_HIDDEN;
if(fParent)
{
float UpdateLeft = fColumnBegin;
fParent->UpdateColumnSizesDataRectSizeScrollBars();
fParent->fColumnLabelView->UpdateDragGroups();
if(Shown)
UpdateLeft = fColumnBegin;
BRect Area = fParent->fColumnLabelView->Bounds();
Area.left = UpdateLeft;
fParent->fColumnLabelView->Invalidate(Area);
Area = fParent->Bounds();
Area.left = UpdateLeft;
fParent->Invalidate(Area);
if(fFlags & CLV_EXPANDER)
{
if(!Shown)
fParent->fExpanderColumn = -1;
else
fParent->fExpanderColumn = fParent->IndexOfColumn(this);
}
}
}
}
CLVSortMode CLVColumn::SortMode() const
{
return fSortMode;
}
void CLVColumn::SetSortMode(CLVSortMode mode)
{
if(fParent)
fParent->SetSortMode(fParent->IndexOfColumn(this),mode);
else
fSortMode = mode;
}
const char* CLVColumn::GetLabel() const
{
return fLabel;
}
ColumnListView* CLVColumn::GetParent() const
{
return fParent;
}
BView* CLVColumn::GetHeaderView() const
{
if(fParent)
return fParent->fColumnLabelView;
else
return NULL;
}
void CLVColumn::DrawColumnHeader(BView* view, BRect header_rect, bool sort_key, bool focus,
float font_ascent)
{
char* label;
if(fFlags & CLV_HEADER_TRUNCATE)
{
if(fCachedRect == BRect(-1,-1,-1,-1))
{
//Have never drawn it before
TruncateText(header_rect.right-header_rect.left);
}
label = fTruncatedText;
}
else
label = fLabel;
if(label)
{
if(focus)
view->SetHighColor(BeFocusBlue);
else
view->SetHighColor(Black);
//Draw the label
view->SetDrawingMode(B_OP_OVER);
BPoint text_point;
if(!(fFlags&CLV_RIGHT_JUSTIFIED))
text_point.Set(header_rect.left+8.0,header_rect.top+1.0+font_ascent);
else
{
BFont label_font;
view->GetFont(&label_font);
float string_width = label_font.StringWidth(label);
text_point.Set(header_rect.right-8.0-string_width,header_rect.top+1.0+font_ascent);
}
view->DrawString(label,text_point);
view->SetDrawingMode(B_OP_COPY);
//Underline if this is a selected sort column
if(sort_key)
{
PrefilledBitmap *bitmap = NULL;
switch(fSortMode)
{
case Ascending:
bitmap = new PrefilledBitmap(BRect(0,0,8,15),B_CMAP8,kAscendingIconBits);
break;
case Descending:
bitmap = new PrefilledBitmap(BRect(0,0,8,15),B_CMAP8,kDescendingIconBits);
break;
}
float Width = view->StringWidth(label);
view->StrokeLine(BPoint(text_point.x-1,text_point.y+2.0),
BPoint(text_point.x-1+Width,text_point.y+2.0));
if(bitmap && header_rect.Width() > 10+20)
{
view->SetDrawingMode(B_OP_OVER);
BRect iconBounds = header_rect;
iconBounds.left= iconBounds.right - 8;
BRegion region;
region.Include(iconBounds);
view->ConstrainClippingRegion(&region);
view->DrawBitmap(bitmap,iconBounds);
}
delete bitmap;
}
fCachedRect = header_rect;
}
}

View File

@ -0,0 +1,161 @@
//Column list header header file
//*** LICENSE ***
//ColumnListView, its associated classes and source code, and the other components of Santa's Gift Bag are
//being made publicly available and free to use in freeware and shareware products with a price under $25
//(I believe that shareware should be cheap). For overpriced shareware (hehehe) or commercial products,
//please contact me to negotiate a fee for use. After all, I did work hard on this class and invested a lot
//of time into it. That being said, DON'T WORRY I don't want much. It totally depends on the sort of project
//you're working on and how much you expect to make off it. If someone makes money off my work, I'd like to
//get at least a little something. If any of the components of Santa's Gift Bag are is used in a shareware
//or commercial product, I get a free copy. The source is made available so that you can improve and extend
//it as you need. In general it is best to customize your ColumnListView through inheritance, so that you
//can take advantage of enhancements and bug fixes as they become available. Feel free to distribute the
//ColumnListView source, including modified versions, but keep this documentation and license with it.
#ifndef _CLV_COLUMN_H_
#define _CLV_COLUMN_H_
//******************************************************************************************************
//**** SYSTEM HEADER FILES
//******************************************************************************************************
#include <SupportDefs.h>
#include <Rect.h>
//******************************************************************************************************
//**** PROJECT HEADER FILES AND CLASS NAME DECLARATIONS
//******************************************************************************************************
class ColumnListView;
class CLVColumn;
class CLVListItem;
//******************************************************************************************************
//**** CONSTANTS
//******************************************************************************************************
//Flags
enum
{
CLV_SORT_KEYABLE = 0x00000001, //Can be used as the sorting key
CLV_NOT_MOVABLE = 0x00000002, //Column can't be moved by user
CLV_NOT_RESIZABLE = 0x00000004, //Column can't be resized by user
CLV_LOCK_AT_BEGINNING = 0x00000008, //Movable columns may not be placed or moved by the user
//into a position before this one
CLV_LOCK_AT_END = 0x00000010, //Movable columns may not be placed or moved by the user
//into a position after this one
CLV_HIDDEN = 0x00000020, //This column is hidden initially
CLV_MERGE_WITH_RIGHT = 0x00000040, //Merge this column label with the one that follows it.
CLV_LOCK_WITH_RIGHT = 0x00000080, //Lock this column to the one that follows it such that
//if the column to the right is moved by the user, this
//one will move with it and vice versa
CLV_EXPANDER = 0x00000100, //Column contains an expander. You may only use one
//expander in a ColumnListView, and an expander may not be
//added to a non-hierarchal ColumnListView. It may not
//have a label. Its width is automatically set to 20.0.
//The only flags that affect it are CLV_NOT_MOVABLE,
//CLV_LOCK_AT_BEGINNING, CLV_NOT_SHOWN and
//CLV_LOCK_WITH_RIGHT. The others are set for you:
//CLV_NOT_RESIZABLE | CLV_MERGE_WITH_RIGHT
CLV_PUSH_PASS = 0x00000200, //Causes this column, if pushed by an expander to the
//left, to pass that push on and also push the next
//column to the right.
CLV_HEADER_TRUNCATE = 0x00000400, //Causes this column label to be tructated with an ellipsis
//if the column header is narrower than the text it contains.
CLV_TELL_ITEMS_WIDTH = 0x00000800, //Causes items in this column to be informed when the column
//width is changed. This is necessary for CLVEasyItems.
CLV_RIGHT_JUSTIFIED = 0x00001000 //Causes the column, when resized, to shift its content,
//not just the content of subsequent columns. This does not
//affect the rendering of content in items in the column,
//just the area that gets scrolled.
};
enum CLVSortMode
{
Ascending,
Descending,
NoSort
};
//******************************************************************************************************
//**** FUNCTIONS
//******************************************************************************************************
void GetTruncatedString(const char* full_string, char* truncated, float width, int32 truncate_buf_size,
const BFont* font);
//******************************************************************************************************
//**** ColumnListView CLASS DECLARATION
//******************************************************************************************************
class CLVColumn
{
public:
//Constructor and destructor
CLVColumn( const char* label,
float width = 20.0,
uint32 flags = 0,
float min_width = 20.0);
virtual ~CLVColumn();
//Archival stuff
/* Not implemented yet
CLVColumn(BMessage* archive);
static CLVColumn* Instantiate(BMessage* data);
virtual status_t Archive(BMessage* data, bool deep = true) const;
*/
//Functions
float Width() const;
virtual void SetWidth(float width); //Can be overridden to detect changes to the column width
//however since you are probably overriding
//ColumnListView and dealing with an array of columns
//anyway, it is probably more useful to override
//ColumnListView::ColumnWidthChanged to detect changes to
//column widths
uint32 Flags() const;
bool IsShown() const;
virtual void SetShown(bool shown);
CLVSortMode SortMode() const;
virtual void SetSortMode(CLVSortMode mode);
const char* GetLabel() const;
ColumnListView* GetParent() const ;
BView* GetHeaderView() const;
virtual void DrawColumnHeader(BView* view, BRect header_rect, bool sort_key, bool focus,
float font_ascent);
//Can be overridden to implement your own column header drawing, for example if you want to do
//string truncation.
//- The background will already be filled with and LowColor set to BeBackgroundGrey
//- The highlight and shadow edges will already be drawn
//- The header_rect does not include the one-pixel border for the highlight and shadow edges.
//- The view font will already be set to the font specified when the ColumnListView was
// constructed, and should not be changed
//- If text is being rendered, it should be rendered at
// BPoint text_point(header_rect.left+8.0,header_rect.top+1.0+font_ascent)
//- If sort_key is true, the text should be underlined, with the underline being drawn from
// BPoint(text_point.x-1,text_point.y+2.0) to BPoint(text_point.x-1+label_width,text_point.y+2.0)
//- If focus is true, the text and underline should be in BeFocusBlue, otherwise in Black.
private:
friend class ColumnListView;
friend class CLVColumnLabelView;
friend class CLVListItem;
BRect TruncateText(float column_width);
char *fLabel;
char* fTruncatedText;
BRect fCachedRect;
float fWidth;
float fMinWidth;
float fColumnBegin;
float fColumnEnd;
uint32 fFlags;
bool fPushedByExpander;
CLVSortMode fSortMode;
ColumnListView* fParent;
};
#endif

View File

@ -0,0 +1,788 @@
//ColumnLabelView class source file
//*** LICENSE ***
//ColumnListView, its associated classes and source code, and the other components of Santa's Gift Bag are
//being made publicly available and free to use in freeware and shareware products with a price under $25
//(I believe that shareware should be cheap). For overpriced shareware (hehehe) or commercial products,
//please contact me to negotiate a fee for use. After all, I did work hard on this class and invested a lot
//of time into it. That being said, DON'T WORRY I don't want much. It totally depends on the sort of project
//you're working on and how much you expect to make off it. If someone makes money off my work, I'd like to
//get at least a little something. If any of the components of Santa's Gift Bag are is used in a shareware
//or commercial product, I get a free copy. The source is made available so that you can improve and extend
//it as you need. In general it is best to customize your ColumnListView through inheritance, so that you
//can take advantage of enhancements and bug fixes as they become available. Feel free to distribute the
//ColumnListView source, including modified versions, but keep this documentation and license with it.
//******************************************************************************************************
//**** PROJECT HEADER FILES
//******************************************************************************************************
#include <Region.h>
#include <Application.h>
#include <Window.h>
//******************************************************************************************************
//**** PROJECT HEADER FILES
//******************************************************************************************************
#include "CLVColumnLabelView.h"
#include "ColumnListView.h"
#include "CLVColumn.h"
#include "Cursors.h"
//******************************************************************************************************
//**** FUNCTION DEFINITIONS
//******************************************************************************************************
CLVColumnLabelView::CLVColumnLabelView(BRect Bounds,ColumnListView* parent,const BFont* Font)
: BView(Bounds,NULL,B_FOLLOW_LEFT_RIGHT|B_FOLLOW_TOP,B_WILL_DRAW|B_FRAME_EVENTS),
fDragGroups(10)
{
SetFont(Font);
SetViewColor(BeBackgroundGrey);
SetLowColor(BeBackgroundGrey);
SetHighColor(Black);
fParent = parent;
fDisplayList = &fParent->fColumnDisplayList;
fColumnClicked = NULL;
fColumnDragging = false;
fColumnResizing = false;
fModifiedCursor = false;
font_height FontAttributes;
Font->GetHeight(&FontAttributes);
fFontAscent = ceil(FontAttributes.ascent);
}
CLVColumnLabelView::~CLVColumnLabelView()
{
int32 NumberOfGroups = fDragGroups.CountItems();
for(int32 Counter = 0; Counter < NumberOfGroups; Counter++)
fDragGroups.RemoveItem(int32(0));
}
void CLVColumnLabelView::Draw(BRect update_rect)
{
BRect ViewBounds = Bounds();
//Draw each column label in turn
float ColumnBegin = 0.0;
float ColumnEnd = -1.0;
bool MergeWithLeft = false;
int32 NumberOfColumns = fDisplayList->CountItems();
BPoint Start,Stop;
for(int32 ColumnDraw = 0; ColumnDraw < NumberOfColumns; ColumnDraw++)
{
CLVColumn* ThisColumn = (CLVColumn*)fDisplayList->ItemAt(ColumnDraw);
if(ThisColumn->IsShown())
{
//Figure out where this column is
ColumnBegin = ThisColumn->fColumnBegin;
ColumnEnd = ThisColumn->fColumnEnd;
//Start by figuring out if this column will merge with a shown column to the right
bool MergeWithRight = false;
if(ThisColumn->fFlags & CLV_MERGE_WITH_RIGHT)
{
for(int32 ColumnCounter = ColumnDraw+1; ColumnCounter < NumberOfColumns;
ColumnCounter++)
{
CLVColumn* NextColumn = (CLVColumn*)fDisplayList->ItemAt(ColumnCounter);
if(NextColumn->IsShown())
{
//The next column is shown
MergeWithRight = true;
break;
}
else if(!(NextColumn->fFlags & CLV_MERGE_WITH_RIGHT))
//The next column is not shown and doesn't pass on the merge
break;
}
}
if(update_rect.Intersects(BRect(ColumnBegin,ViewBounds.top,ColumnEnd,
ViewBounds.bottom)))
{
//Need to draw this column
BeginLineArray(4);
//Top line
Start.Set(ColumnBegin,ViewBounds.top);
Stop.Set(ColumnEnd-1.0,ViewBounds.top);
if(MergeWithRight && !(ThisColumn == fColumnClicked && fColumnResizing))
Stop.x = ColumnEnd;
AddLine(Start,Stop,BeHighlight);
//Left line
if(!MergeWithLeft)
AddLine(BPoint(ColumnBegin,ViewBounds.top+1.0),
BPoint(ColumnBegin,ViewBounds.bottom),BeHighlight);
//Bottom line
Start.Set(ColumnBegin+1.0,ViewBounds.bottom);
if(MergeWithLeft)
Start.x = ColumnBegin;
Stop.Set(ColumnEnd-1.0,ViewBounds.bottom);
if(MergeWithRight && !(ThisColumn == fColumnClicked && fColumnResizing))
Stop.x = ColumnEnd;
AddLine(Start,Stop,BeShadow);
//Right line
if(ThisColumn == fColumnClicked && fColumnResizing)
AddLine(BPoint(ColumnEnd,ViewBounds.top),BPoint(ColumnEnd,ViewBounds.bottom),
BeFocusBlue);
else if(!MergeWithRight)
AddLine(BPoint(ColumnEnd,ViewBounds.top),BPoint(ColumnEnd,ViewBounds.bottom),
BeShadow);
EndLineArray();
//Add the label
//Limit the clipping region to the interior of the box
BRect TextRect(ColumnBegin+1.0,ViewBounds.top+1.0,ColumnEnd-1.0,
ViewBounds.bottom-1.0);
BRegion TextRegion;
TextRegion.Include(TextRect);
ConstrainClippingRegion(&TextRegion);
bool focus;
bool sort_key;
if(ThisColumn == fColumnClicked && !fColumnResizing)
focus = true;
else
focus = false;
if(fParent->fSortKeyList.HasItem(ThisColumn) && ThisColumn->fSortMode != NoSort)
sort_key = true;
else
sort_key = false;
ThisColumn->DrawColumnHeader(this,TextRect,sort_key,focus,fFontAscent);
//Restore the clipping region
ConstrainClippingRegion(NULL);
}
//Set MergeWithLeft flag for the next column to the appropriate state
MergeWithLeft = MergeWithRight;
}
}
//Add highlight and shadow to the region after the columns if necessary
if(ColumnEnd < ViewBounds.right)
{
ColumnBegin = ColumnEnd+1.0;
if(update_rect.Intersects(BRect(ColumnEnd+1.0,ViewBounds.top,ViewBounds.right,
ViewBounds.bottom)))
{
BeginLineArray(3);
//Top line
AddLine(BPoint(ColumnBegin,ViewBounds.top),BPoint(ViewBounds.right,ViewBounds.top),
BeHighlight);
//Left line
AddLine(BPoint(ColumnBegin,ViewBounds.top+1.0),BPoint(ColumnBegin,ViewBounds.bottom),
BeHighlight);
//Bottom line
Start.Set(ColumnBegin+1.0,ViewBounds.bottom);
if(MergeWithLeft)
Start.x = ColumnBegin;
Stop.Set(ViewBounds.right,ViewBounds.bottom);
AddLine(Start,Stop,BeShadow);
EndLineArray();
}
}
//Draw the dragging box if necessary
if(fColumnClicked && fColumnDragging)
{
float DragOutlineLeft = fPreviousMousePos.x-fDragBoxMouseHoldOffset;
float GroupBegin = ((CLVDragGroup*)fDragGroups.ItemAt(fDragGroup))->GroupBegin;
if(DragOutlineLeft < GroupBegin && fSnapGroupBefore == -1)
DragOutlineLeft = GroupBegin;
if(DragOutlineLeft > GroupBegin && fSnapGroupAfter == -1)
DragOutlineLeft = GroupBegin;
float DragOutlineRight = DragOutlineLeft + fDragBoxWidth;
BeginLineArray(4);
AddLine(BPoint(DragOutlineLeft,ViewBounds.top),BPoint(DragOutlineRight,
ViewBounds.top),BeFocusBlue);
AddLine(BPoint(DragOutlineLeft,ViewBounds.bottom),BPoint(DragOutlineRight,
ViewBounds.bottom),BeFocusBlue);
AddLine(BPoint(DragOutlineLeft,ViewBounds.top+1.0),BPoint(DragOutlineLeft,
ViewBounds.bottom-1.0),BeFocusBlue);
AddLine(BPoint(DragOutlineRight,ViewBounds.top+1.0),BPoint(DragOutlineRight,
ViewBounds.bottom-1.0),BeFocusBlue);
EndLineArray();
fPrevDragOutlineLeft = DragOutlineLeft;
fPrevDragOutlineRight = DragOutlineRight;
}
}
void CLVColumnLabelView::MouseDown(BPoint Point)
{
//Only pay attention to primary mouse button
bool WatchMouse = false;
BPoint MousePos;
uint32 Buttons;
GetMouse(&MousePos,&Buttons);
if(Buttons == B_PRIMARY_MOUSE_BUTTON)
{
BRect ViewBounds = Bounds();
//Make sure no other column was already clicked. If so, just discard the old one and redraw the
//view
if(fColumnClicked != NULL)
{
Invalidate();
fColumnClicked = NULL;
}
//Find the column that the user clicked, if any
bool GrabbedResizeTab = false;
int32 NumberOfColumns = fDisplayList->CountItems();
int32 ColumnFind;
CLVColumn* ThisColumn = NULL;
for(ColumnFind = 0; ColumnFind < NumberOfColumns; ColumnFind++)
{
ThisColumn = (CLVColumn*)fDisplayList->ItemAt(ColumnFind);
if(ThisColumn->IsShown())
{
float ColumnBegin = ThisColumn->fColumnBegin;
float ColumnEnd = ThisColumn->fColumnEnd;
if(Point.x >= ColumnBegin && Point.x <= ColumnEnd)
{
//User clicked in this column
if(Point.x <= ColumnBegin+2.0)
{
//User clicked the resize tab preceding this column
for(ColumnFind--; ColumnFind >= 0; ColumnFind--)
{
ThisColumn = (CLVColumn*)fDisplayList->ItemAt(ColumnFind);
if(ThisColumn->IsShown())
{
GrabbedResizeTab = true;
break;
}
}
}
else if(Point.x >= ColumnEnd-2.0)
{
//User clicked the resize tab for (after) this column
GrabbedResizeTab = true;
}
else
{
//The user clicked in this column
fColumnClicked = (CLVColumn*)fDisplayList->ItemAt(ColumnFind);
fColumnResizing = false;
fPreviousMousePos = Point;
fMouseClickedPos = Point;
fColumnDragging = false;
SetSnapMinMax();
fDragBoxMouseHoldOffset = Point.x-
((CLVDragGroup*)fDragGroups.ItemAt(fDragGroup))->GroupBegin;
Invalidate(BRect(ColumnBegin+1.0,ViewBounds.top+1.0,ColumnEnd-1.0,
ViewBounds.bottom-1.0));
//Start watching the mouse
WatchMouse = true;
}
break;
}
}
}
if(GrabbedResizeTab)
{
//The user grabbed a resize tab. See if resizing of this column is allowed
if(!(ThisColumn->fFlags & CLV_NOT_RESIZABLE))
{
fColumnClicked = (CLVColumn*)fDisplayList->ItemAt(ColumnFind);
fColumnResizing = true;
fPreviousMousePos = Point;
fMouseClickedPos = Point;
fColumnDragging = false;
fResizeMouseHoldOffset = Point.x-fColumnClicked->fColumnEnd;
Invalidate(BRect(fColumnClicked->fColumnEnd,ViewBounds.top,ThisColumn->fColumnEnd,
ViewBounds.bottom));
//Start watching the mouse
WatchMouse = true;
}
}
}
if(WatchMouse)
SetMouseEventMask(B_POINTER_EVENTS,B_NO_POINTER_HISTORY);
fPreviousMousePos = MousePos;
}
void CLVColumnLabelView::MouseMoved(BPoint where, uint32 code, const BMessage *message)
{
bool should_show_modified_cursor = false;
if(fColumnClicked == NULL)
{
if(code != B_EXITED_VIEW)
{
//Check for whether to switch the mouse to indicate that you can resize
int32 NumberOfColumns = fDisplayList->CountItems();
for(int32 ColumnFind = 0; ColumnFind < NumberOfColumns; ColumnFind++)
{
CLVColumn* ThisColumn = (CLVColumn*)fDisplayList->ItemAt(ColumnFind);
if(ThisColumn->IsShown())
{
float ColumnEnd = ThisColumn->fColumnEnd;
if(where.x >= ColumnEnd-2.0 && where.x <= ColumnEnd+2.0)
{
//User clicked the resize tab for (after) this column
if(!(ThisColumn->fFlags & CLV_NOT_RESIZABLE))
should_show_modified_cursor = true;
break;
}
else if(where.x < ColumnEnd+2.0)
break;
}
}
}
}
else if(!fColumnResizing)
{
//User is clicking or dragging
BRect ViewBounds = Bounds();
if((where.x<fMouseClickedPos.x-2.0 || where.x>fMouseClickedPos.x+2.0) &&
!fColumnDragging)
{
//User is initiating a drag
if(fTheDragGroup->Flags & CLV_NOT_MOVABLE)
{
//Not allowed to drag this column - terminate the click
Invalidate(BRect(fColumnClicked->fColumnBegin,ViewBounds.top,
fColumnClicked->fColumnEnd,ViewBounds.bottom));
fColumnClicked = NULL;
}
else
{
//Actually initiate a drag
fColumnDragging = true;
fPrevDragOutlineLeft = -1.0;
fPrevDragOutlineRight = -1.0;
}
}
//Now deal with dragging
if(fColumnDragging)
{
//User is dragging
if(where.x<fPreviousMousePos.x || where.x>fPreviousMousePos.x)
{
//Mouse moved since I last checked
ViewBounds = Bounds();
bool ColumnSnapped;
do
{
//Live dragging of columns
ColumnSnapped = false;
float ColumnsUpdateLeft = 0.0;
float ColumnsUpdateRight = 0.0;
float MainViewUpdateLeft = 0.0;
float MainViewUpdateRight = 0.0;
CLVColumn* LastSwapColumn = NULL;
if(fSnapMin != -1.0 && where.x < fSnapMin)
{
//Shift the group left
ColumnsUpdateLeft = fTheShownGroupBefore->GroupBegin;
ColumnsUpdateRight = fTheDragGroup->GroupEnd;
MainViewUpdateLeft = ColumnsUpdateLeft;
MainViewUpdateRight = ColumnsUpdateRight;
LastSwapColumn = fTheShownGroupBefore->LastColumnShown;
if(fTheDragGroup->LastColumnShown->fFlags & CLV_MERGE_WITH_RIGHT)
ColumnsUpdateRight += 1.0;
else if(fTheShownGroupBefore->LastColumnShown->fFlags & CLV_MERGE_WITH_RIGHT)
ColumnsUpdateRight += 1.0;
ShiftDragGroup(fSnapGroupBefore);
ColumnSnapped = true;
}
if(fSnapMax != -1.0 && where.x > fSnapMax)
{
//Shift the group right
ColumnsUpdateLeft = fTheDragGroup->GroupBegin;
ColumnsUpdateRight = fTheShownGroupAfter->GroupEnd;
MainViewUpdateLeft = ColumnsUpdateLeft;
MainViewUpdateRight = ColumnsUpdateRight;
LastSwapColumn = fTheDragGroup->LastColumnShown;
if(fTheDragGroup->LastColumnShown->fFlags & CLV_MERGE_WITH_RIGHT)
ColumnsUpdateRight += 1.0;
else if(fTheShownGroupAfter->LastColumnShown->fFlags & CLV_MERGE_WITH_RIGHT)
ColumnsUpdateRight += 1.0;
ShiftDragGroup(fSnapGroupAfter+1);
ColumnSnapped = true;
}
if(ColumnSnapped)
{
//Redraw the snapped column labels
Invalidate(BRect(ColumnsUpdateLeft,ViewBounds.top,ColumnsUpdateRight,
ViewBounds.bottom));
BRect MainViewBounds = fParent->Bounds();
//Modify MainViewUpdateRight if more columns are pushed by expanders
if(LastSwapColumn->fFlags & CLV_EXPANDER ||
(LastSwapColumn->fPushedByExpander && (LastSwapColumn->fFlags &
CLV_PUSH_PASS)))
{
int32 NumberOfColumns = fDisplayList->CountItems();
for(int32 ColumnCounter = fDisplayList->IndexOf(LastSwapColumn)+1;
ColumnCounter < NumberOfColumns; ColumnCounter++)
{
CLVColumn* ThisColumn =
(CLVColumn*)fDisplayList->ItemAt(ColumnCounter);
if(ThisColumn->IsShown())
{
if(ThisColumn->fPushedByExpander)
MainViewUpdateRight = ThisColumn->fColumnEnd;
else
break;
}
}
}
fParent->Invalidate(BRect(MainViewUpdateLeft,MainViewBounds.top,
MainViewUpdateRight,MainViewBounds.bottom));
}
}while(ColumnSnapped);
//Erase and redraw the drag rectangle but not the interior to avoid label flicker
float Min = fPrevDragOutlineLeft;
float Max = fPrevDragOutlineRight;
float Min2 = where.x-fDragBoxMouseHoldOffset;
float GroupBegin = ((CLVDragGroup*)fDragGroups.ItemAt(fDragGroup))->GroupBegin;
if(Min2 < GroupBegin && fSnapGroupBefore == -1)
Min2 = GroupBegin;
if(Min2 > GroupBegin && fSnapGroupAfter == -1)
Min2 = GroupBegin;
float Max2 = Min2 + fDragBoxWidth;
float Temp;
if(Min2 < Min || Min == -1.0)
{Temp = Min2;Min2 = Min;Min = Temp;}
if(Max2 > Max || Max == -1.0)
{Temp = Max2;Max2 = Max;Max = Temp;}
Invalidate(BRect(Min,ViewBounds.top+1.0,Min,ViewBounds.bottom-1.0));
if(Min2 != -1.0)
Invalidate(BRect(Min2,ViewBounds.top+1.0,Min2,ViewBounds.bottom-1.0));
Invalidate(BRect(Max,ViewBounds.top+1.0,Max,ViewBounds.bottom-1.0));
if(Max2 != -1.0)
Invalidate(BRect(Max2,ViewBounds.top+1.0,Max2,ViewBounds.bottom-1.0));
Invalidate(BRect(Min,ViewBounds.top,Max,ViewBounds.top));
Invalidate(BRect(Min,ViewBounds.bottom,Max,ViewBounds.bottom));
}
}
}
else
{
//User is resizing the column
if(where.x<fPreviousMousePos.x || where.x>fPreviousMousePos.x)
{
float NewWidth = where.x - fResizeMouseHoldOffset - fColumnClicked->fColumnBegin;
if(NewWidth < fColumnClicked->fMinWidth)
NewWidth = fColumnClicked->fMinWidth;
if(NewWidth != fColumnClicked->fWidth)
fColumnClicked->SetWidth(NewWidth);
Invalidate();
}
should_show_modified_cursor = true;
}
fPreviousMousePos = where;
if(fModifiedCursor && !should_show_modified_cursor)
{
be_app->SetCursor(B_HAND_CURSOR);
be_app->ShowCursor();
}
if(should_show_modified_cursor && !fModifiedCursor)
{
be_app->SetCursor(c_v_resize_cursor);
be_app->ShowCursor();
}
fModifiedCursor = should_show_modified_cursor;
}
void CLVColumnLabelView::MouseUp(BPoint where)
{
if(fColumnClicked == NULL)
return;
BRect ViewBounds = Bounds();
if(!fColumnDragging && !fColumnResizing)
{
//Column was clicked
uint32 ColumnFlags = fColumnClicked->Flags();
if(ColumnFlags&CLV_SORT_KEYABLE)
{
//The column is a "sortable" column
uint32 Modifiers;
Window()->CurrentMessage()->FindInt32("modifiers",(int32*)&Modifiers);
if(!(Modifiers&B_SHIFT_KEY))
{
//The user wants to select it as the main sorting column
if(fParent->fSortKeyList.ItemAt(0) == fColumnClicked)
//The column was already selected; switch sort modes
fParent->ReverseSortMode(fParent->fColumnList.IndexOf(fColumnClicked));
else
//The user selected this column for sorting
fParent->SetSortKey(fParent->fColumnList.IndexOf(fColumnClicked));
}
else
{
//The user wants to add it as a secondary sorting column
if(fParent->fSortKeyList.HasItem(fColumnClicked))
//The column was already selected; switch sort modes
fParent->ReverseSortMode(fParent->fColumnList.IndexOf(fColumnClicked));
else
//The user selected this column for sorting
fParent->AddSortKey(fParent->fColumnList.IndexOf(fColumnClicked));
}
}
}
else if(fColumnDragging)
{
//Column was dragging; erase the drag box but not the interior to avoid label flicker
Invalidate(BRect(fPrevDragOutlineLeft,ViewBounds.top+1.0,
fPrevDragOutlineLeft,ViewBounds.bottom-1.0));
Invalidate(BRect(fPrevDragOutlineRight,ViewBounds.top+1.0,
fPrevDragOutlineRight,ViewBounds.bottom-1.0));
Invalidate(BRect(fPrevDragOutlineLeft,ViewBounds.top,
fPrevDragOutlineRight,ViewBounds.top));
Invalidate(BRect(fPrevDragOutlineLeft,ViewBounds.bottom,
fPrevDragOutlineRight,ViewBounds.bottom));
}
else
//Column was resizing; erase the drag tab
Invalidate(BRect(fColumnClicked->fColumnEnd,ViewBounds.top,fColumnClicked->fColumnEnd,
ViewBounds.bottom));
//Unhighlight the label and forget the column
Invalidate(BRect(fColumnClicked->fColumnBegin+1.0,ViewBounds.top+1.0,
fColumnClicked->fColumnEnd-1.0,ViewBounds.bottom-1.0));
fColumnClicked = NULL;
fColumnDragging = false;
fColumnResizing = false;
if(fModifiedCursor)
{
be_app->SetCursor(B_HAND_CURSOR);
be_app->ShowCursor();
fModifiedCursor = false;
}
}
void CLVColumnLabelView::ShiftDragGroup(int32 NewPos)
//Shift the drag group into a new position
{
int32 NumberOfGroups = fDragGroups.CountItems();
int32 GroupCounter;
CLVDragGroup* ThisGroup;
int32 NumberOfColumnsInGroup;
int32 ColumnCounter;
BList NewDisplayList;
//Copy the groups up to the new position
for(GroupCounter = 0; GroupCounter < NewPos; GroupCounter++)
{
if(GroupCounter != fDragGroup)
{
ThisGroup = (CLVDragGroup*)fDragGroups.ItemAt(GroupCounter);
NumberOfColumnsInGroup = ThisGroup->GroupStopDispListIndex -
ThisGroup->GroupStartDispListIndex + 1;
for(ColumnCounter = ThisGroup->GroupStartDispListIndex; ColumnCounter <=
ThisGroup->GroupStopDispListIndex; ColumnCounter++)
NewDisplayList.AddItem(fDisplayList->ItemAt(ColumnCounter));
}
}
//Copy the group into the new position
ThisGroup = (CLVDragGroup*)fDragGroups.ItemAt(fDragGroup);
NumberOfColumnsInGroup = ThisGroup->GroupStopDispListIndex - ThisGroup->GroupStartDispListIndex + 1;
for(ColumnCounter = ThisGroup->GroupStartDispListIndex; ColumnCounter <=
ThisGroup->GroupStopDispListIndex; ColumnCounter++)
NewDisplayList.AddItem(fDisplayList->ItemAt(ColumnCounter));
//Copy the rest of the groups, but skip the dragging group
for(GroupCounter = NewPos; GroupCounter < NumberOfGroups; GroupCounter++)
{
if(GroupCounter != fDragGroup)
{
ThisGroup = (CLVDragGroup*)fDragGroups.ItemAt(GroupCounter);
NumberOfColumnsInGroup = ThisGroup->GroupStopDispListIndex -
ThisGroup->GroupStartDispListIndex + 1;
for(ColumnCounter = ThisGroup->GroupStartDispListIndex; ColumnCounter <=
ThisGroup->GroupStopDispListIndex; ColumnCounter++)
NewDisplayList.AddItem(fDisplayList->ItemAt(ColumnCounter));
}
}
//Set the new order
*fDisplayList = NewDisplayList;
//Update columns and drag groups
fParent->UpdateColumnSizesDataRectSizeScrollBars();
UpdateDragGroups();
SetSnapMinMax();
//Inform the program that the display order changed
int32* NewOrder = new int32[fParent->CountColumns()];
fParent->GetDisplayOrder(NewOrder);
fParent->DisplayOrderChanged(NewOrder);
delete[] NewOrder;
}
void CLVColumnLabelView::UpdateDragGroups()
{
//Make a copy of the DragGroups list. Use it to store the CLVDragGroup's for recycling
BList TempList(fDragGroups);
fDragGroups.MakeEmpty();
int32 NumberOfColumns = fDisplayList->CountItems();
bool ContinueGroup = false;
CLVDragGroup* CurrentGroup = NULL;
for(int32 Counter = 0; Counter < NumberOfColumns; Counter++)
{
CLVColumn* CurrentColumn = (CLVColumn*)fDisplayList->ItemAt(Counter);
if(!ContinueGroup)
{
//Recycle or obtain a new CLVDragGroup
CurrentGroup = (CLVDragGroup*)TempList.RemoveItem(int32(0));
if(CurrentGroup == NULL)
CurrentGroup = new CLVDragGroup;
//Add the CLVDragGroup to the DragGroups list
fDragGroups.AddItem(CurrentGroup);
//Set up the new DragGroup
CurrentGroup->GroupStartDispListIndex = Counter;
CurrentGroup->GroupStopDispListIndex = Counter;
CurrentGroup->Flags = 0;
if(CurrentColumn->IsShown())
{
CurrentGroup->GroupBegin = CurrentColumn->fColumnBegin;
CurrentGroup->GroupEnd = CurrentColumn->fColumnEnd;
CurrentGroup->LastColumnShown = CurrentColumn;
CurrentGroup->Shown = true;
if(CurrentColumn->fFlags & CLV_LOCK_AT_BEGINNING)
CurrentGroup->AllLockBeginning = true;
else
CurrentGroup->AllLockBeginning = false;
if(CurrentColumn->fFlags & CLV_LOCK_AT_END)
CurrentGroup->AllLockEnd = true;
else
CurrentGroup->AllLockEnd = false;
}
else
{
CurrentGroup->GroupBegin = -1.0;
CurrentGroup->GroupEnd = -1.0;
CurrentGroup->LastColumnShown = NULL;
CurrentGroup->Shown = false;
if(CurrentColumn->fFlags & CLV_LOCK_AT_BEGINNING)
CurrentGroup->AllLockBeginning = true;
else
CurrentGroup->AllLockBeginning = false;
if(CurrentColumn->fFlags & CLV_LOCK_AT_END)
CurrentGroup->AllLockEnd = true;
else
CurrentGroup->AllLockEnd = false;
}
}
else
{
//Add this column to the current DragGroup
CurrentGroup->GroupStopDispListIndex = Counter;
if(CurrentColumn->IsShown())
{
if(CurrentGroup->GroupBegin == -1.0)
CurrentGroup->GroupBegin = CurrentColumn->fColumnBegin;
CurrentGroup->GroupEnd = CurrentColumn->fColumnEnd;
CurrentGroup->LastColumnShown = CurrentColumn;
CurrentGroup->Shown = true;
}
if(!(CurrentColumn->fFlags & CLV_LOCK_AT_BEGINNING))
CurrentGroup->AllLockBeginning = false;
if(!(CurrentColumn->fFlags & CLV_LOCK_AT_END))
CurrentGroup->AllLockEnd = false;
}
CurrentGroup->Flags |= CurrentColumn->fFlags & (CLV_NOT_MOVABLE|CLV_LOCK_AT_BEGINNING|
CLV_LOCK_AT_END);
//See if I should add more columns to this group
if(CurrentColumn->fFlags & CLV_LOCK_WITH_RIGHT)
ContinueGroup = true;
else
ContinueGroup = false;
}
//If any unused groups remain in TempList, delete them
while((CurrentGroup = (CLVDragGroup*)TempList.RemoveItem(int32(0))) != NULL)
delete CurrentGroup;
}
void CLVColumnLabelView::SetSnapMinMax()
{
//Find the column group that the user is dragging and the shown group before it
int32 NumberOfGroups = fDragGroups.CountItems();
int32 ColumnCount;
fDragGroup = -1;
fTheShownGroupBefore = NULL;
fSnapGroupBefore = -1;
CLVDragGroup* ThisGroup;
int32 GroupCounter;
for(GroupCounter = 0; GroupCounter < NumberOfGroups; GroupCounter++)
{
ThisGroup = (CLVDragGroup*)fDragGroups.ItemAt(GroupCounter);
for(ColumnCount = ThisGroup->GroupStartDispListIndex; ColumnCount <=
ThisGroup->GroupStopDispListIndex; ColumnCount++)
if(fDisplayList->ItemAt(ColumnCount) == fColumnClicked)
{
fDragGroup = GroupCounter;
fTheDragGroup = ThisGroup;
break;
}
if(fDragGroup != -1)
break;
else if(ThisGroup->Shown)
{
fTheShownGroupBefore = ThisGroup;
fSnapGroupBefore = GroupCounter;
}
}
//Find the position of shown group after the one that the user is dragging
fTheShownGroupAfter = NULL;
fSnapGroupAfter = -1;
for(GroupCounter = fDragGroup+1; GroupCounter < NumberOfGroups; GroupCounter++)
{
ThisGroup = (CLVDragGroup*)fDragGroups.ItemAt(GroupCounter);
if(ThisGroup->Shown)
{
fTheShownGroupAfter = ThisGroup;
fSnapGroupAfter = GroupCounter;
break;
}
}
//See if it can actually snap in the given direction
if(fSnapGroupBefore != -1)
{
if(fTheShownGroupBefore->Flags & CLV_LOCK_AT_BEGINNING)
if(!fTheDragGroup->AllLockBeginning)
fSnapGroupBefore = -1;
if(fTheDragGroup->Flags & CLV_LOCK_AT_END)
if(!fTheShownGroupBefore->AllLockEnd)
fSnapGroupBefore = -1;
}
if(fSnapGroupAfter != -1)
{
if(fTheShownGroupAfter->Flags & CLV_LOCK_AT_END)
if(!fTheDragGroup->AllLockEnd)
fSnapGroupAfter = -1;
if(fTheDragGroup->Flags & CLV_LOCK_AT_BEGINNING)
if(!fTheShownGroupAfter->AllLockBeginning)
fSnapGroupAfter = -1;
}
//Find the minumum and maximum positions for the group to snap
fSnapMin = -1.0;
fSnapMax = -1.0;
fDragBoxWidth = fTheDragGroup->GroupEnd-fTheDragGroup->GroupBegin;
if(fSnapGroupBefore != -1)
{
fSnapMin = fTheShownGroupBefore->GroupBegin + fDragBoxWidth;
if(fSnapMin > fTheShownGroupBefore->GroupEnd)
fSnapMin = fTheShownGroupBefore->GroupEnd;
}
if(fSnapGroupAfter != -1)
{
fSnapMax = fTheShownGroupAfter->GroupEnd - fDragBoxWidth;
if(fSnapMax < fTheShownGroupAfter->GroupBegin)
fSnapMax = fTheShownGroupAfter->GroupBegin;
}
}

View File

@ -0,0 +1,99 @@
//CLVColumnLabelView class header file
//*** LICENSE ***
//ColumnListView, its associated classes and source code, and the other components of Santa's Gift Bag are
//being made publicly available and free to use in freeware and shareware products with a price under $25
//(I believe that shareware should be cheap). For overpriced shareware (hehehe) or commercial products,
//please contact me to negotiate a fee for use. After all, I did work hard on this class and invested a lot
//of time into it. That being said, DON'T WORRY I don't want much. It totally depends on the sort of project
//you're working on and how much you expect to make off it. If someone makes money off my work, I'd like to
//get at least a little something. If any of the components of Santa's Gift Bag are is used in a shareware
//or commercial product, I get a free copy. The source is made available so that you can improve and extend
//it as you need. In general it is best to customize your ColumnListView through inheritance, so that you
//can take advantage of enhancements and bug fixes as they become available. Feel free to distribute the
//ColumnListView source, including modified versions, but keep this documentation and license with it.
#ifndef _CLV_COLUMN_LABEL_VIEW_H_
#define _CLV_COLUMN_LABEL_VIEW_H_
//******************************************************************************************************
//**** SYSTEM HEADER FILES
//******************************************************************************************************
#include <View.h>
#include <List.h>
//******************************************************************************************************
//**** PROJECT HEADER FILES AND CLASS NAME DECLARATIONS
//******************************************************************************************************
class ColumnListView;
class CLVColumn;
//******************************************************************************************************
//**** CLASS AND STRUCTURE DECLARATIONS, ASSOCIATED CONSTANTS AND STATIC FUNCTIONS
//******************************************************************************************************
struct CLVDragGroup
{
int32 GroupStartDispListIndex; //Indices in the column display list where this group starts
int32 GroupStopDispListIndex; //and finishes
float GroupBegin,GroupEnd; //-1.0 if whole group is hidden
CLVColumn* LastColumnShown;
bool AllLockBeginning;
bool AllLockEnd;
bool Shown; //False if none of the columns in this group are shown
uint32 Flags; //Uses CLV_NOT_MOVABLE, CLV_LOCK_AT_BEGINNING, CLV_LOCK_AT_END
};
class CLVColumnLabelView : public BView
{
public:
//Constructor and destructor
CLVColumnLabelView(BRect Bounds,ColumnListView* Parent,const BFont* Font);
~CLVColumnLabelView();
//BView overrides
virtual void Draw(BRect UpdateRect);
virtual void MouseDown(BPoint Point);
virtual void MouseMoved(BPoint where, uint32 code, const BMessage *message);
virtual void MouseUp(BPoint where);
private:
friend class ColumnListView;
friend class CLVColumn;
float fFontAscent;
BList* fDisplayList;
//Column select and drag stuff
CLVColumn* fColumnClicked;
BPoint fPreviousMousePos;
BPoint fMouseClickedPos;
bool fColumnDragging;
bool fColumnResizing;
bool fModifiedCursor;
BList fDragGroups; //Groups of CLVColumns that must drag together
int32 fDragGroup; //Index into DragGroups of the group being dragged by user
CLVDragGroup* fTheDragGroup;
CLVDragGroup* fTheShownGroupBefore;
CLVDragGroup* fTheShownGroupAfter;
int32 fSnapGroupBefore, //Index into DragGroups of TheShownGroupBefore and
fSnapGroupAfter; //TheShownGroupAfter, if the group the user is dragging is
//allowed to snap there, otherwise -1
float fDragBoxMouseHoldOffset,fResizeMouseHoldOffset;
float fDragBoxWidth; //Can include multiple columns; depends on CLV_LOCK_WITH_RIGHT
float fPrevDragOutlineLeft,fPrevDragOutlineRight;
float fSnapMin,fSnapMax; //-1.0 indicates the column can't snap in the given direction
ColumnListView* fParent;
//Private functions
void ShiftDragGroup(int32 NewPos);
void UpdateDragGroups();
void SetSnapMinMax();
};
#endif

View File

@ -0,0 +1,576 @@
//CLVListItem source file
//*** LICENSE ***
//ColumnListView, its associated classes and source code, and the other components of Santa's Gift Bag are
//being made publicly available and free to use in freeware and shareware products with a price under $25
//(I believe that shareware should be cheap). For overpriced shareware (hehehe) or commercial products,
//please contact me to negotiate a fee for use. After all, I did work hard on this class and invested a lot
//of time into it. That being said, DON'T WORRY I don't want much. It totally depends on the sort of project
//you're working on and how much you expect to make off it. If someone makes money off my work, I'd like to
//get at least a little something. If any of the components of Santa's Gift Bag are is used in a shareware
//or commercial product, I get a free copy. The source is made available so that you can improve and extend
//it as you need. In general it is best to customize your ColumnListView through inheritance, so that you
//can take advantage of enhancements and bug fixes as they become available. Feel free to distribute the
//ColumnListView source, including modified versions, but keep this documentation and license with it.
//******************************************************************************************************
//**** SYSTEM HEADER FILES
//******************************************************************************************************
#include <string.h>
#include <Region.h>
#include <ClassInfo.h>
#include <String.h>
#include <stdio.h>
//******************************************************************************************************
//**** PROJECT HEADER FILES
//******************************************************************************************************
#include "CLVEasyItem.h"
#include "Colors.h"
#include "CLVColumn.h"
#include "ColumnListView.h"
#include "NewStrings.h"
//******************************************************************************************************
//**** CLVEasyItem CLASS DEFINITION
//******************************************************************************************************
CLVEasyItem::CLVEasyItem(uint32 level, bool superitem, bool expanded, float minheight)
: CLVListItem(level,superitem,expanded,minheight)
{
text_offset = 0.0;
SetTextColor(0,0,0);
fBackgroundColor=NULL;
}
CLVEasyItem::~CLVEasyItem()
{
int num_columns = m_column_types.CountItems();
for(int column = 0; column < num_columns; column++)
{
int32 type = (int32)m_column_types.ItemAt(column);
bool bitmap_is_copy = false;
if(type & CLVColFlagBitmapIsCopy)
bitmap_is_copy = true;
type &= CLVColTypesMask;
if(type == CLVColStaticText || type == CLVColTruncateText)
delete[] ((char*)m_column_content.ItemAt(column));
if(type == CLVColTruncateText)
delete[] ((char*)m_aux_content.ItemAt(column));
if(type == CLVColBitmap && bitmap_is_copy)
delete ((BBitmap*)m_column_content.ItemAt(column));
delete (BRect*)m_cached_rects.ItemAt(column);
}
SetBackgroundColor(NULL);
}
void CLVEasyItem::PrepListsForSet(int column_index)
{
int cur_num_columns = m_column_types.CountItems();
bool delete_old = (cur_num_columns >= column_index-1);
while(cur_num_columns <= column_index)
{
m_column_types.AddItem((void*)CLVColNone);
m_column_content.AddItem(NULL);
m_aux_content.AddItem(NULL);
m_cached_rects.AddItem(new BRect(-1,-1,-1,-1));
cur_num_columns++;
}
if(delete_old)
{
//Column content exists already so delete the old entries
int32 old_type = (int32)m_column_types.ItemAt(column_index);
bool bitmap_is_copy = false;
if(old_type & CLVColFlagBitmapIsCopy)
bitmap_is_copy = true;
old_type &= CLVColTypesMask;
void* old_content = m_column_content.ItemAt(column_index);
char* old_truncated = (char*)m_aux_content.ItemAt(column_index);
if(old_type == CLVColStaticText || old_type == CLVColTruncateText)
delete[] ((char*)old_content);
if(old_type == CLVColTruncateText)
delete[] old_truncated;
if(old_type == CLVColBitmap && bitmap_is_copy)
delete ((BBitmap*)old_content);
((BRect**)m_cached_rects.Items())[column_index]->Set(-1,-1,-1,-1);
}
}
void CLVEasyItem::SetColumnContent(int column_index, const char *text, bool truncate, bool right_justify)
{
PrepListsForSet(column_index);
//Create the new entry
((BRect**)m_cached_rects.Items())[column_index]->Set(-1,-1,-1,-1);
if(text == NULL || text[0] == 0)
{
((int32*)m_column_types.Items())[column_index] = CLVColNone;
((char**)m_column_content.Items())[column_index] = NULL;
((char**)m_aux_content.Items())[column_index] = NULL;
}
else
{
char* copy = Strdup_new(text);
((char**)m_column_content.Items())[column_index] = copy;
if(!truncate)
{
((int32*)m_column_types.Items())[column_index] = CLVColStaticText;
((char**)m_aux_content.Items())[column_index] = NULL;
}
else
{
((int32*)m_column_types.Items())[column_index] = CLVColTruncateText|CLVColFlagNeedsTruncation;
copy = new char[strlen(text)+3];
strcpy(copy,text);
((char**)m_aux_content.Items())[column_index] = copy;
}
if(right_justify)
((int32*)m_column_types.Items())[column_index] |= CLVColFlagRightJustify;
}
}
void CLVEasyItem::SetColumnContent(int column_index, const BBitmap *bitmap, float horizontal_offset, bool copy,
bool right_justify)
{
PrepListsForSet(column_index);
//Create the new entry
if(bitmap == NULL)
{
((int32*)m_column_types.Items())[column_index] = CLVColNone;
((char**)m_column_content.Items())[column_index] = NULL;
((char**)m_aux_content.Items())[column_index] = NULL;
}
else
{
if(copy)
((int32*)m_column_types.Items())[column_index] = CLVColBitmap|CLVColFlagBitmapIsCopy;
else
((int32*)m_column_types.Items())[column_index] = CLVColBitmap;
if(right_justify)
((int32*)m_column_types.Items())[column_index] |= CLVColFlagRightJustify;
BBitmap* the_bitmap;
if(copy)
{
the_bitmap = new BBitmap(bitmap->Bounds(),bitmap->ColorSpace());
int32 copy_ints = bitmap->BitsLength()/4;
int32* source = (int32*)bitmap->Bits();
int32* dest = (int32*)the_bitmap->Bits();
for(int32 i = 0; i < copy_ints; i++)
dest[i] = source[i];
}
else
the_bitmap = (BBitmap*)bitmap;
((BBitmap**)m_column_content.Items())[column_index] = the_bitmap;
((int32*)m_aux_content.Items())[column_index] = (int32)horizontal_offset;
}
}
void CLVEasyItem::SetColumnUserTextContent(int column_index, bool truncate, bool right_justify)
{
PrepListsForSet(column_index);
if(truncate)
((int32*)m_column_types.Items())[column_index] = CLVColTruncateUserText;
else
((int32*)m_column_types.Items())[column_index] = CLVColUserText;
if(right_justify)
((int32*)m_column_types.Items())[column_index] |= CLVColFlagRightJustify;
}
const char* CLVEasyItem::GetColumnContentText(int column_index)
{
int32 type = ((int32)m_column_types.ItemAt(column_index)) & CLVColTypesMask;
if(type == CLVColStaticText || type == CLVColTruncateText)
return (char*)m_column_content.ItemAt(column_index);
if(type == CLVColTruncateUserText || type == CLVColUserText)
return GetUserText(column_index,-1);
return NULL;
}
const BBitmap* CLVEasyItem::GetColumnContentBitmap(int column_index)
{
int32 type = ((int32)m_column_types.ItemAt(column_index)) & CLVColTypesMask;
if(type != CLVColBitmap)
return NULL;
return (BBitmap*)m_column_content.ItemAt(column_index);
}
void CLVEasyItem::DrawItemColumn(BView *owner, BRect item_column_rect, int32 column_index, bool complete)
{
rgb_color color;
bool selected = IsSelected();
if(selected)
color = ((ColumnListView*)owner)->ItemSelectColor();
else{
if(fBackgroundColor)
{
color.red = fBackgroundColor->red;
color.blue = fBackgroundColor->blue;
color.green = fBackgroundColor->green;
complete = true;
}else
color = owner->ViewColor();
}
owner->SetLowColor(color);
owner->SetDrawingMode(B_OP_COPY);
bool highlightTextOnly = ((ColumnListView*)owner)->HighlightTextOnly();
if(selected || complete)
{
owner->SetHighColor(color);
if(!highlightTextOnly && selected)
owner->FillRect(item_column_rect);
else if(!highlightTextOnly &&complete)
owner->FillRect(item_column_rect);
else if(highlightTextOnly&&complete)
owner->Invalidate(item_column_rect);
}
if(column_index == -1)
return;
int32 type = (int32)m_column_types.ItemAt(column_index);
if(type == 0)
return;
bool needs_truncation = false;
if(type & CLVColFlagNeedsTruncation)
needs_truncation = true;
bool right_justify = false;
if(type & CLVColFlagRightJustify)
right_justify = true;
type &= CLVColTypesMask;
BRegion Region;
Region.Include(item_column_rect);
owner->ConstrainClippingRegion(&Region);
BRect* cached_rect = (BRect*)m_cached_rects.ItemAt(column_index);
if(cached_rect != NULL)
*cached_rect = item_column_rect;
if(type == CLVColStaticText || type == CLVColTruncateText || type == CLVColTruncateUserText ||
type == CLVColUserText)
{
const char* text = NULL;
owner->SetDrawingMode(B_OP_COPY);
if(type == CLVColTruncateText)
{
if(needs_truncation)
{
BFont owner_font;
owner->GetFont(&owner_font);
TruncateText(column_index,item_column_rect.right-item_column_rect.left,&owner_font);
((int32*)m_column_types.Items())[column_index] &= (CLVColFlagNeedsTruncation^0xFFFFFFFF);
}
text = (const char*)m_aux_content.ItemAt(column_index);
}
else if(type == CLVColStaticText)
text = (const char*)m_column_content.ItemAt(column_index);
else if(type == CLVColTruncateUserText)
text = GetUserText(column_index,item_column_rect.right-item_column_rect.left);
else if(type == CLVColUserText)
text = GetUserText(column_index,-1);
if(text != NULL)
{
BPoint draw_point;
if(!right_justify)
draw_point.Set(item_column_rect.left+2.0,item_column_rect.top+text_offset);
else
{
BFont font;
owner->GetFont(&font);
float string_width = font.StringWidth(text);
draw_point.Set(item_column_rect.right-2.0-string_width,item_column_rect.top+text_offset);
}
if(selected && highlightTextOnly)
{
float stringWidth = owner->StringWidth(text);
if(stringWidth>0)
{
font_height FontAttributes;
BFont owner_font;
owner->GetFont(&owner_font);
owner_font.GetHeight(&FontAttributes);
float FontHeight = ceil(FontAttributes.ascent) + ceil(FontAttributes.descent);
BRect textRect(item_column_rect);
textRect.right = textRect.left + stringWidth + 3;
float pad = (textRect.Height() - FontHeight)/2.0;
textRect.top += pad;
textRect.bottom = textRect.top + FontHeight;
owner->SetHighColor(color);
owner->FillRoundRect(textRect,1,1);
}
}
owner->SetHighColor(fTextColor);
owner->DrawString(text,draw_point);
}
}
else if(type == CLVColBitmap)
{
const BBitmap* bitmap = (BBitmap*)m_column_content.ItemAt(column_index);
BRect bounds = bitmap->Bounds();
float horizontal_offset = (float)((int32)m_aux_content.ItemAt(column_index));
if(!right_justify)
{
item_column_rect.left += horizontal_offset;
item_column_rect.right = item_column_rect.left + (bounds.right-bounds.left);
}
else
{
item_column_rect.left = item_column_rect.right-horizontal_offset-(bounds.right-bounds.left);
item_column_rect.right -= horizontal_offset;
}
item_column_rect.top += ceil(((item_column_rect.bottom-item_column_rect.top)-(bounds.bottom-bounds.top))/2.0);
item_column_rect.bottom = item_column_rect.top + (bounds.bottom-bounds.top);
if(selected && highlightTextOnly)
{
BRect selectRect(item_column_rect);
owner->SetDrawingMode(B_OP_COPY);
owner->SetHighColor(color);
selectRect.bottom += 2;
selectRect.right += 3;
selectRect.top -= 2;
selectRect.left -= 1;
owner->FillRoundRect(selectRect,2,2);
}
owner->SetDrawingMode(B_OP_OVER);
owner->DrawBitmap(bitmap,item_column_rect);
owner->SetDrawingMode(B_OP_COPY);
}
owner->ConstrainClippingRegion(NULL);
}
void CLVEasyItem::Update(BView *owner, const BFont *font)
{
CLVListItem::Update(owner,font);
font_height FontAttributes;
BFont owner_font;
owner->GetFont(&owner_font);
owner_font.GetHeight(&FontAttributes);
float FontHeight = ceil(FontAttributes.ascent) + ceil(FontAttributes.descent);
text_offset = ceil(FontAttributes.ascent) + (Height()-FontHeight)/2.0;
}
int CLVEasyItem::CompareItems(const CLVListItem *a_Item1, const CLVListItem *a_Item2, int32 KeyColumn)
{
const CLVEasyItem* Item1 = cast_as(a_Item1,const CLVEasyItem);
const CLVEasyItem* Item2 = cast_as(a_Item2,const CLVEasyItem);
if(Item1 == NULL || Item2 == NULL || Item1->m_column_types.CountItems() <= KeyColumn ||
Item2->m_column_types.CountItems() <= KeyColumn)
return 0;
int32 type1 = ((int32)Item1->m_column_types.ItemAt(KeyColumn)) & CLVColTypesMask;
int32 type2 = ((int32)Item2->m_column_types.ItemAt(KeyColumn)) & CLVColTypesMask;
if(!((type1 == CLVColStaticText || type1 == CLVColTruncateText || type1 == CLVColTruncateUserText ||
type1 == CLVColUserText) && (type2 == CLVColStaticText || type2 == CLVColTruncateText ||
type2 == CLVColTruncateUserText || type2 == CLVColUserText)))
return 0;
const char* text1 = NULL;
const char* text2 = NULL;
if(type1 == CLVColStaticText || type1 == CLVColTruncateText)
text1 = (const char*)Item1->m_column_content.ItemAt(KeyColumn);
else if(type1 == CLVColTruncateUserText || type1 == CLVColUserText)
text1 = Item1->GetUserText(KeyColumn,-1);
if(type2 == CLVColStaticText || type2 == CLVColTruncateText)
text2 = (const char*)Item2->m_column_content.ItemAt(KeyColumn);
else if(type2 == CLVColTruncateUserText || type2 == CLVColUserText)
text2 = Item2->GetUserText(KeyColumn,-1);
return strcasecmp(text1,text2);
}
BRect CLVEasyItem::TruncateText(int32 column_index, float column_width, BFont* font)
{
column_width -= 4;
//Because when I draw the text I start drawing 2 pixels to the right from the column's left edge, and want
//to stop 2 pixels before the right edge
BRect invalid(-1,-1,-1,-1);
char* full_text = (char*)m_column_content.ItemAt(column_index);
char new_text[256];
char* truncated_text = (char*)m_aux_content.ItemAt(column_index);
BString STR = full_text;
font->TruncateString(&STR,B_TRUNCATE_END,column_width);
::strncpy(new_text,STR.String(),256);
//GetTruncatedString(full_text,new_text,column_width,256,font);
if(strcmp(truncated_text,new_text)!=0)
{
//The truncated text has changed
BRect* temp = (BRect*)m_cached_rects.ItemAt(column_index);
if(temp != NULL && *temp != BRect(-1,-1,-1,-1))
{
invalid = *temp;
//Figure out which region just got changed
int32 cmppos;
int32 cmplen = strlen(new_text);
char remember = 0;
for(cmppos = 0; cmppos <= cmplen; cmppos++)
if(new_text[cmppos] != truncated_text[cmppos])
{
remember = new_text[cmppos];
new_text[cmppos] = 0;
break;
}
invalid.left += 2 + font->StringWidth(new_text);
new_text[cmppos] = remember;
}
//Remember the new truncated text
strcpy(truncated_text,new_text);
}
return invalid;
}
void CLVEasyItem::ColumnWidthChanged(int32 column_index, float column_width, ColumnListView *the_view)
{
BRect* cached_rect = (BRect*)m_cached_rects.ItemAt(column_index);
if(cached_rect == NULL || *cached_rect == BRect(-1,-1,-1,-1))
return;
float width_delta = column_width-(cached_rect->right-cached_rect->left);
cached_rect->right += width_delta;
int num_columns = m_cached_rects.CountItems();
for(int column = 0; column < num_columns; column++)
if(column != column_index)
{
BRect* other_rect = (BRect*)m_cached_rects.ItemAt(column);
if(other_rect->left > cached_rect->left)
other_rect->OffsetBy(width_delta,0);
}
int32 type = (int32)m_column_types.ItemAt(column_index);
bool right_justify = (type&CLVColFlagRightJustify);
type &= CLVColTypesMask;
BRect invalid;
BFont view_font;
if(type == CLVColTruncateText)
{
BRect bounds = the_view->Bounds();
if(cached_rect->top <= bounds.bottom && cached_rect->bottom >= bounds.top)
{
//If it's onscreen, truncate and invalidate the changed area
the_view->GetFont(&view_font);
invalid = TruncateText(column_index, column_width,&view_font);
((int32*)m_column_types.Items())[column_index] &= (CLVColFlagNeedsTruncation^0xFFFFFFFF);
if(invalid != BRect(-1.0,-1.0,-1.0,-1.0))
{
if(!right_justify)
the_view->Invalidate(invalid);
else
the_view->Invalidate(*cached_rect);
}
}
else
//If it's not onscreen flag it for truncation the next time it's drawn
((int32*)m_column_types.Items())[column_index] |= CLVColFlagNeedsTruncation;
}
if(type == CLVColTruncateUserText)
{
char old_text[256];
Strtcpy(old_text,GetUserText(column_index,column_width-width_delta),256);
char new_text[256];
Strtcpy(new_text,GetUserText(column_index,column_width),256);
if(strcmp(old_text,new_text) != 0)
{
BRect* temp = (BRect*)m_cached_rects.ItemAt(column_index);
if(temp != NULL && *temp != BRect(-1,-1,-1,-1))
{
invalid = *temp;
if(!right_justify)
{
//The truncation changed, so find the point of divergence.
int change_pos = 0;
while(old_text[change_pos] == new_text[change_pos])
change_pos++;
new_text[change_pos] = 0;
the_view->GetFont(&view_font);
invalid.left += 2 + view_font.StringWidth(new_text);
the_view->Invalidate(invalid);
}
else
the_view->Invalidate(*cached_rect);
}
}
}
}
void CLVEasyItem::FrameChanged(int32 column_index, BRect new_frame, ColumnListView *the_view)
{
BRect* cached_rect = (BRect*)m_cached_rects.ItemAt(column_index);
if(cached_rect == NULL)
return;
int32 type = ((int32)m_column_types.ItemAt(column_index)) & CLVColTypesMask;
if(type == CLVColTruncateText)
if(*cached_rect != new_frame)
{
*cached_rect = new_frame;
//If it's onscreen, truncate and invalidate the changed area
if(new_frame.Intersects(the_view->Bounds()))
{
BFont view_font;
the_view->GetFont(&view_font);
BRect invalid = TruncateText(column_index, new_frame.right-new_frame.left,&view_font);
((int32*)m_column_types.Items())[column_index] &= (CLVColFlagNeedsTruncation^0xFFFFFFFF);
if(invalid != BRect(-1.0,-1.0,-1.0,-1.0))
the_view->Invalidate(invalid);
}
else
//If it's not onscreen flag it for truncation the next time it's drawn
((int32*)m_column_types.Items())[column_index] |= CLVColFlagNeedsTruncation;
}
}
const char* CLVEasyItem::GetUserText(int32 column_index, float column_width) const
{
return NULL;
}
void CLVEasyItem::SetTextColor(uchar red,uchar green,uchar blue)
{
fTextColor.red = red;
fTextColor.green = green;
fTextColor.blue = blue;
}
void CLVEasyItem::SetBackgroundColor(rgb_color *color)
{
delete fBackgroundColor;
if(color)
{
fBackgroundColor = new rgb_color;
fBackgroundColor->red = color->red;
fBackgroundColor->blue = color->blue;
fBackgroundColor->green = color->green;
}else
fBackgroundColor = NULL;
}

View File

@ -0,0 +1,98 @@
//CLVEasyItem header file
//*** LICENSE ***
//ColumnListView, its associated classes and source code, and the other components of Santa's Gift Bag are
//being made publicly available and free to use in freeware and shareware products with a price under $25
//(I believe that shareware should be cheap). For overpriced shareware (hehehe) or commercial products,
//please contact me to negotiate a fee for use. After all, I did work hard on this class and invested a lot
//of time into it. That being said, DON'T WORRY I don't want much. It totally depends on the sort of project
//you're working on and how much you expect to make off it. If someone makes money off my work, I'd like to
//get at least a little something. If any of the components of Santa's Gift Bag are is used in a shareware
//or commercial product, I get a free copy. The source is made available so that you can improve and extend
//it as you need. In general it is best to customize your ColumnListView through inheritance, so that you
//can take advantage of enhancements and bug fixes as they become available. Feel free to distribute the
//ColumnListView source, including modified versions, but keep this documentation and license with it.
#ifndef _CLV_EASY_ITEM_H_
#define _CLV_EASY_ITEM_H_
//******************************************************************************************************
//**** SYSTEM HEADER FILES
//******************************************************************************************************
#include <List.h>
#include <GraphicsDefs.h>
//******************************************************************************************************
//**** PROJECT HEADER FILES AND CLASS NAME DECLARATIONS
//******************************************************************************************************
#include "CLVListItem.h"
//******************************************************************************************************
//**** TYPE DEFINITIONS AND CONSTANTS
//******************************************************************************************************
enum
{
CLVColNone = 0x00000000,
CLVColStaticText = 0x00000001,
CLVColTruncateText = 0x00000002,
CLVColBitmap = 0x00000003,
CLVColUserText = 0x00000004,
CLVColTruncateUserText = 0x00000005,
CLVColTypesMask = 0x00000007,
CLVColFlagBitmapIsCopy = 0x00000008,
CLVColFlagNeedsTruncation = 0x00000010,
CLVColFlagRightJustify = 0x00000020
};
//******************************************************************************************************
//**** CLVEasyItem CLASS DECLARATION
//******************************************************************************************************
class CLVEasyItem : public CLVListItem
{
public:
//Constructor and destructor
CLVEasyItem(uint32 level = 0, bool superitem = false, bool expanded = false, float minheight = 0.0);
virtual ~CLVEasyItem();
virtual void SetColumnContent(int column_index, const BBitmap *bitmap, float horizontal_offset = 2.0,
bool copy = true, bool right_justify = false);
virtual void SetColumnContent(int column_index, const char *text, bool truncate = true,
bool right_justify = false);
virtual void SetColumnUserTextContent(int column_index, bool truncate = true, bool right_justify = false);
const char* GetColumnContentText(int column_index);
const BBitmap* GetColumnContentBitmap(int column_index);
virtual void DrawItemColumn(BView* owner, BRect item_column_rect, int32 column_index, bool complete);
virtual void Update(BView *owner, const BFont *font);
static int CompareItems(const CLVListItem* a_Item1, const CLVListItem* a_Item2, int32 KeyColumn);
BRect TruncateText(int32 column_index, float column_width, BFont* font);
//Returns the area that needs to be redrawn, or BRect(-1,-1,-1,-1) if nothing
virtual void ColumnWidthChanged(int32 column_index, float column_width, ColumnListView* the_view);
virtual void FrameChanged(int32 column_index, BRect new_frame, ColumnListView* the_view);
inline float GetTextOffset() {return text_offset;}
virtual const char* GetUserText(int32 column_index, float column_width) const;
void SetTextColor(uchar red,uchar green,uchar blue);
void SetBackgroundColor(rgb_color *color);
BList m_column_types; //List of int32's converted from CLVColumnTypes
BList m_column_content; //List of char* (full content) or BBitmap*
private:
void PrepListsForSet(int column_index);
BList m_aux_content; //List of char* (truncated content) or int32 for bitmap horizontal offset
BList m_cached_rects; //List of BRect for truncated text
protected:
float text_offset;
rgb_color fTextColor;
rgb_color *fBackgroundColor;
};
#endif

View File

@ -0,0 +1,231 @@
//CLVListItem source file
//*** LICENSE ***
//ColumnListView, its associated classes and source code, and the other components of Santa's Gift Bag are
//being made publicly available and free to use in freeware and shareware products with a price under $25
//(I believe that shareware should be cheap). For overpriced shareware (hehehe) or commercial products,
//please contact me to negotiate a fee for use. After all, I did work hard on this class and invested a lot
//of time into it. That being said, DON'T WORRY I don't want much. It totally depends on the sort of project
//you're working on and how much you expect to make off it. If someone makes money off my work, I'd like to
//get at least a little something. If any of the components of Santa's Gift Bag are is used in a shareware
//or commercial product, I get a free copy. The source is made available so that you can improve and extend
//it as you need. In general it is best to customize your ColumnListView through inheritance, so that you
//can take advantage of enhancements and bug fixes as they become available. Feel free to distribute the
//ColumnListView source, including modified versions, but keep this documentation and license with it.
//******************************************************************************************************
//**** PROJECT HEADER FILES
//******************************************************************************************************
#include <Region.h>
//******************************************************************************************************
//**** PROJECT HEADER FILES
//******************************************************************************************************
#include "CLVListItem.h"
#include "ColumnListView.h"
#include "CLVColumn.h"
//******************************************************************************************************
//**** CLVItem CLASS DEFINITION
//******************************************************************************************************
CLVListItem::CLVListItem(uint32 level, bool superitem, bool expanded, float minheight)
: BListItem(level, expanded),
fExpanderButtonRect(-1.0,-1.0,-1.0,-1.0),
fExpanderColumnRect(-1.0,-1.0,-1.0,-1.0)
{
fSuperItem = superitem;
fOutlineLevel = level;
fMinHeight = minheight;
}
CLVListItem::~CLVListItem()
{ }
BRect CLVListItem::ItemColumnFrame(int32 column_index, ColumnListView* owner)
{
BList* ColumnList = &owner->fColumnList;
CLVColumn* ThisColumn = (CLVColumn*)ColumnList->ItemAt(column_index);
if(!ThisColumn->IsShown())
return BRect(-1,-1,-1,-1);
int32 DisplayIndex = owner->IndexOf(this);
BRect itemRect;
if(DisplayIndex >= 0)
itemRect = owner->ItemFrame(DisplayIndex);
else
return BRect(-1,-1,-1,-1);
//Figure out what the limit is for expanders pushing other columns
float PushMax = 100000;
if(ThisColumn->fPushedByExpander || (ThisColumn->fFlags & CLV_EXPANDER))
{
BList* DisplayList = &owner->fColumnDisplayList;
int32 NumberOfColumns = DisplayList->CountItems();
for(int32 Counter = 0; Counter < NumberOfColumns; Counter++)
{
CLVColumn* SomeColumn = (CLVColumn*)DisplayList->ItemAt(Counter);
if((SomeColumn->fFlags & CLV_EXPANDER) || SomeColumn->fPushedByExpander)
PushMax = SomeColumn->fColumnEnd;
}
}
BRect ThisColumnRect = itemRect;
ThisColumnRect.left = ThisColumn->fColumnBegin;
ThisColumnRect.right = ThisColumn->fColumnEnd;
if(ThisColumn->fFlags & CLV_EXPANDER)
{
ThisColumnRect.right += OutlineLevel() * 20.0;
if(ThisColumnRect.right > PushMax)
ThisColumnRect.right = PushMax;
}
else
{
if(ThisColumn->fPushedByExpander)
{
float Shift = OutlineLevel() * 20.0;
ThisColumnRect.left += Shift;
ThisColumnRect.right += Shift;
if(Shift > 0.0 && ThisColumnRect.right > PushMax)
ThisColumnRect.right = PushMax;
}
}
if(ThisColumnRect.right >= ThisColumnRect.left)
return ThisColumnRect;
else
return BRect(-1,-1,-1,-1);
}
void CLVListItem::DrawItem(BView* owner, BRect itemRect, bool complete)
{
BList* DisplayList = &((ColumnListView*)owner)->fColumnDisplayList;
int32 NumberOfColumns = DisplayList->CountItems();
float PushMax = itemRect.right;
CLVColumn* ThisColumn;
BRect ThisColumnRect = itemRect;
float ExpanderDelta = OutlineLevel() * 20.0;
//Figure out what the limit is for expanders pushing other columns
for(int32 Counter = 0; Counter < NumberOfColumns; Counter++)
{
ThisColumn = (CLVColumn*)DisplayList->ItemAt(Counter);
if((ThisColumn->fFlags & CLV_EXPANDER) || ThisColumn->fPushedByExpander)
PushMax = ThisColumn->fColumnEnd;
}
BRegion ClippingRegion;
if(!complete)
owner->GetClippingRegion(&ClippingRegion);
else
ClippingRegion.Set(itemRect);
float LastColumnEnd = -1.0;
//Draw the columns
for(int32 Counter = 0; Counter < NumberOfColumns; Counter++)
{
ThisColumn = (CLVColumn*)DisplayList->ItemAt(Counter);
if(!ThisColumn->IsShown())
continue;
ThisColumnRect.left = ThisColumn->fColumnBegin;
ThisColumnRect.right = LastColumnEnd = ThisColumn->fColumnEnd;
float Shift = 0.0;
if((ThisColumn->fFlags & CLV_EXPANDER) || ThisColumn->fPushedByExpander)
Shift = ExpanderDelta;
if(ThisColumn->fFlags & CLV_EXPANDER)
{
ThisColumnRect.right += Shift;
if(ThisColumnRect.right > PushMax)
ThisColumnRect.right = PushMax;
fExpanderColumnRect = ThisColumnRect;
if(ClippingRegion.Intersects(ThisColumnRect))
{
//Give the programmer a chance to do his kind of highlighting if the item is selected
DrawItemColumn(owner, ThisColumnRect,
((ColumnListView*)owner)->fColumnList.IndexOf(ThisColumn),complete);
if(fSuperItem)
{
//Draw the expander, clip manually
float TopOffset = ceil((ThisColumnRect.bottom-ThisColumnRect.top-10.0)/2.0);
float LeftOffset = ThisColumn->fColumnEnd + Shift - 3.0 - 10.0;
float RightClip = LeftOffset + 10.0 - ThisColumnRect.right;
if(RightClip < 0.0)
RightClip = 0.0;
BBitmap* Arrow;
if(IsExpanded())
Arrow = &((ColumnListView*)owner)->fDownArrow;
else
Arrow = &((ColumnListView*)owner)->fRightArrow;
if(LeftOffset <= ThisColumnRect.right)
{
fExpanderButtonRect.Set(LeftOffset,ThisColumnRect.top+TopOffset,
LeftOffset+10.0-RightClip,ThisColumnRect.top+TopOffset+10.0);
owner->SetDrawingMode(B_OP_OVER);
owner->DrawBitmap(Arrow, BRect(0.0,0.0,10.0-RightClip,10.0),fExpanderButtonRect);
owner->SetDrawingMode(B_OP_COPY);
}
else
fExpanderButtonRect.Set(-1.0,-1.0,-1.0,-1.0);
}
}
}
else
{
ThisColumnRect.left += Shift;
ThisColumnRect.right += Shift;
if(Shift > 0.0 && ThisColumnRect.right > PushMax)
ThisColumnRect.right = PushMax;
if(ThisColumnRect.right >= ThisColumnRect.left && ClippingRegion.Intersects(ThisColumnRect))
DrawItemColumn(owner, ThisColumnRect,
((ColumnListView*)owner)->fColumnList.IndexOf(ThisColumn),complete);
}
}
//Fill the area after all the columns (so the select highlight goes all the way across)
ThisColumnRect.left = LastColumnEnd + 1.0;
ThisColumnRect.right = owner->Bounds().right;
if(ThisColumnRect.left <= ThisColumnRect.right && ClippingRegion.Intersects(ThisColumnRect))
DrawItemColumn(owner, ThisColumnRect,-1,complete);
}
float CLVListItem::ExpanderShift(int32 column_index, ColumnListView* owner)
{
CLVColumn* ThisColumn = owner->ColumnAt(column_index);
if(!(ThisColumn->fPushedByExpander || ThisColumn->fFlags & CLV_EXPANDER))
return 0.0;
else
return OutlineLevel() * 20.0;
}
void CLVListItem::Update(BView* owner, const BFont* font)
{
BListItem::Update(owner,font);
float ItemHeight = Height();
if(ItemHeight < fMinHeight)
ItemHeight = fMinHeight;
SetHeight(ItemHeight);
}
void CLVListItem::ColumnWidthChanged(int32 column_index, float column_width, ColumnListView* the_view)
{
//Get rid of a warning
column_index = 0;
column_width = 0;
the_view = NULL;
}
void CLVListItem::FrameChanged(int32 column_index, BRect new_frame, ColumnListView* the_view)
{
//Get rid of a warning
column_index = 0;
new_frame.left = 0;
the_view = NULL;
}

View File

@ -0,0 +1,82 @@
//CLVListItem header file
//*** LICENSE ***
//ColumnListView, its associated classes and source code, and the other components of Santa's Gift Bag are
//being made publicly available and free to use in freeware and shareware products with a price under $25
//(I believe that shareware should be cheap). For overpriced shareware (hehehe) or commercial products,
//please contact me to negotiate a fee for use. After all, I did work hard on this class and invested a lot
//of time into it. That being said, DON'T WORRY I don't want much. It totally depends on the sort of project
//you're working on and how much you expect to make off it. If someone makes money off my work, I'd like to
//get at least a little something. If any of the components of Santa's Gift Bag are is used in a shareware
//or commercial product, I get a free copy. The source is made available so that you can improve and extend
//it as you need. In general it is best to customize your ColumnListView through inheritance, so that you
//can take advantage of enhancements and bug fixes as they become available. Feel free to distribute the
//ColumnListView source, including modified versions, but keep this documentation and license with it.
#ifndef _CLV_LIST_ITEM_H_
#define _CLV_LIST_ITEM_H_
//******************************************************************************************************
//**** SYSTEM HEADER FILES
//******************************************************************************************************
#include <ListItem.h>
//******************************************************************************************************
//**** PROJECT HEADER FILES AND CLASS NAME DECLARATIONS
//******************************************************************************************************
class ColumnListView;
//******************************************************************************************************
//**** CLVListItem CLASS DECLARATION
//******************************************************************************************************
class CLVListItem : public BListItem
{
public:
//Constructor and destructor
CLVListItem(uint32 level = 0, bool superitem = false, bool expanded = false, float minheight = 0.0);
virtual ~CLVListItem();
//Archival stuff
/* Not implemented yet
CLVItem(BMessage* archive);
static CLVItem* Instantiate(BMessage* data);
virtual status_t Archive(BMessage* data, bool deep = true) const;
*/
virtual void DrawItemColumn(BView* owner, BRect item_column_rect, int32 column_index,
bool complete) = 0; //column_index (0-N) is based on the order in which the columns were added
//to the ColumnListView, not the display order. An index of -1 indicates
//that the program needs to draw a blank area beyond the last column. The
//main purpose is to allow the highlighting bar to continue all the way to
//the end of the ColumnListView, even after the end of the last column.
virtual void DrawItem(BView* owner, BRect itemRect, bool complete);
//In general, you don't need or want to override DrawItem().
BRect ItemColumnFrame(int32 column_index, ColumnListView* owner);
float ExpanderShift(int32 column_index, ColumnListView* owner);
virtual void Update(BView* owner, const BFont* font);
inline bool IsSuperItem() const {return fSuperItem;}
inline void SetSuperItem(bool superitem) {fSuperItem = superitem;}
inline uint32 OutlineLevel() const {return fOutlineLevel;}
inline void SetOutlineLevel(uint32 level) {fOutlineLevel = level;}
inline bool ExpanderRectContains(BPoint where) {return fExpanderButtonRect.Contains(where);}
virtual void ColumnWidthChanged(int32 column_index, float column_width, ColumnListView* the_view);
virtual void FrameChanged(int32 column_index, BRect new_frame, ColumnListView* the_view);
private:
friend class ColumnListView;
bool fSuperItem;
uint32 fOutlineLevel;
float fMinHeight;
BRect fExpanderButtonRect;
BRect fExpanderColumnRect;
BList* fSortingContextBList;
ColumnListView* fSortingContextCLV;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,238 @@
//Column list view header file
//*** LICENSE ***
//ColumnListView, its associated classes and source code, and the other components of Santa's Gift Bag are
//being made publicly available and free to use in freeware and shareware products with a price under $25
//(I believe that shareware should be cheap). For overpriced shareware (hehehe) or commercial products,
//please contact me to negotiate a fee for use. After all, I did work hard on this class and invested a lot
//of time into it. That being said, DON'T WORRY I don't want much. It totally depends on the sort of project
//you're working on and how much you expect to make off it. If someone makes money off my work, I'd like to
//get at least a little something. If any of the components of Santa's Gift Bag are is used in a shareware
//or commercial product, I get a free copy. The source is made available so that you can improve and extend
//it as you need. In general it is best to customize your ColumnListView through inheritance, so that you
//can take advantage of enhancements and bug fixes as they become available. Feel free to distribute the
//ColumnListView source, including modified versions, but keep this documentation and license with it.
#ifndef _CLV_COLUMN_LIST_VIEW_H_
#define _CLV_COLUMN_LIST_VIEW_H_
//******************************************************************************************************
//**** SYSTEM HEADER FILES
//******************************************************************************************************
#include <ListView.h>
//******************************************************************************************************
//**** PROJECT HEADER FILES AND CLASS NAME DECLARATIONS
//******************************************************************************************************
#include "Colors.h"
#include "CLVColumn.h"
class CLVListItem;
class CLVColumnLabelView;
class CLVFillerView;
class CLVContainerView;
#include "PrefilledBitmap.h"
#include "BetterScrollView.h"
//******************************************************************************************************
//**** CONSTANTS AND TYPE DEFINITIONS
//******************************************************************************************************
typedef int (*CLVCompareFuncPtr)(const CLVListItem* item1, const CLVListItem* item2, int32 sort_key);
//******************************************************************************************************
//**** ColumnListView CLASS DECLARATION
//******************************************************************************************************
class ColumnListView : public BListView
{
public:
//Constructor and destructor
ColumnListView( BRect Frame,
CLVContainerView** ContainerView, //Used to get back a pointer to the container
//view that will hold the ColumnListView, the
//the CLVColumnLabelView, and the scrollbars.
//If no scroll bars or border are asked for,
//this will act like a plain BView container.
const char* Name = NULL,
uint32 ResizingMode = B_FOLLOW_LEFT | B_FOLLOW_TOP,
uint32 flags = B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE,
list_view_type Type = B_SINGLE_SELECTION_LIST,
bool hierarchical = false,
bool horizontal = true, //Which scroll bars should I add, if any
bool vertical = true,
bool scroll_view_corner = true,
border_style border = B_NO_BORDER, //What type of border to add, if any
const BFont* LabelFont = be_plain_font);
virtual ~ColumnListView();
//Archival stuff
/*** Not implemented yet
ColumnListView(BMessage* archive);
static ColumnListView* Instantiate(BMessage* data);
virtual status_t Archive(BMessage* data, bool deep = true) const;
***/
//Column setup functions
virtual bool AddColumn(CLVColumn* Column); //Note that a column may only be added to
//one ColumnListView at a time, and may not
//be added more than once to the same
//ColumnListView without removing it
//inbetween
virtual bool AddColumnList(BList* NewColumns);
virtual bool RemoveColumn(CLVColumn* Column);
virtual bool RemoveColumns(CLVColumn* Column, int32 Count); //Finds Column in ColumnList
//and removes Count columns and
//their data from the view
//and its items
int32 CountColumns() const;
int32 IndexOfColumn(CLVColumn* column) const;
CLVColumn* ColumnAt(int32 column_index) const;
virtual bool SetDisplayOrder(const int32* Order);
//Sets the display order: each int32 in the Order list specifies the column index of the
//next column to display. Note that this DOES NOT get called if the user drags a
//column, so overriding it will not inform you of user changes. If you need that info,
//override DisplayOrderChanged instead. Also note that SetDisplayOrder does call
//DisplayOrderChanged(false).
void GetDisplayOrder(int32* order) const;
//Fills a caller-provided array with the display order of the columns in the same format
//as used in calling SetDisplayOrder. The order pointer should point to an array
//int32 order[n] where n is the number of columns in the ColumnListView.
virtual void ColumnWidthChanged(int32 ColumnIndex, float NewWidth);
virtual void DisplayOrderChanged(const int32* order);
//Override this if you want to find out when the display order changed.
virtual void SetSortKey(int32 ColumnIndex, bool doSort = true);
//Set it to -1 to remove the sort key.
virtual void AddSortKey(int32 ColumnIndex, bool doSort = true);
void ReverseSortMode(int32 ColumnIndex, bool doSort = true);
virtual void SetSortMode(int32 ColumnIndex,CLVSortMode Mode, bool doSort = true);
//Override this to filter the modes
int32 GetSorting(int32* SortKeys, CLVSortMode* SortModes) const;
//Returns the number of used sort keys, and fills the provided arrays with the sort keys
//by column index and sort modes, in priority order. The pointers should point to an array
//int32 SortKeys[n], and an array CLVSortMode SortModes[n] where n is the number of sortable
//columns in the ColumnListView. Note: sorting will only occur if the key column is shown.
void SetSorting(int32 NumberOfKeys, int32* SortKeys, CLVSortMode* SortModes, bool doSort = true);
//Sets the sorting parameters using the same format returned by Sorting
virtual void SortingChanged(); //Override this to detect changes to the sorting keys or modes.
inline void SetItemSelectColor(bool window_active, rgb_color color)
{
if(window_active) fSelectedItemColorWindowActive = color;
else fSelectedItemColorWindowInactive = color;
}
inline rgb_color ItemSelectColor(bool window_active) const
{
if(window_active) return fSelectedItemColorWindowActive;
return fSelectedItemColorWindowInactive;
}
inline rgb_color ItemSelectColor() const
{
if(fWindowActive) return fSelectedItemColorWindowActive;
return fSelectedItemColorWindowInactive;
}
//BView overrides
virtual void FrameResized(float Width, float Height);
virtual void ScrollTo(BPoint point);
virtual void MouseDown(BPoint point);
virtual void MouseUp(BPoint where);
virtual void MouseMoved(BPoint where, uint32 code, const BMessage* message);
virtual void WindowActivated(bool active);
//List functions
virtual bool AddUnder(BListItem* item, BListItem* superitem);
virtual bool AddItem(BListItem* item, int32 fullListIndex);
virtual bool AddItem(BListItem* item);
virtual bool AddList(BList* newItems); //This must be a BList of
//BListItem*'s, NOT CLVListItem*'s
virtual bool AddList(BList* newItems, int32 fullListIndex); //This must be a BList of
//BListItem*'s, NOT CLVListItem*'s
virtual bool EfficientAddListHierarchical(BList* newItems, int32 fullListIndex, bool expanded);
//visible indicates whether or not the items are under an item which is expanded. If
//expanded is false, NONE of these items will be showing. If expanded is true, ALL of
//them will be, so make sure that all of them are in an expanded state. Not checking is
//done for you. The list is of BListItem*'s, NOT CLVListItem*'s.
virtual bool RemoveItem(BListItem* item);
virtual BListItem* RemoveItem(int32 fullListIndex); //Actually returns CLVListItem
virtual bool RemoveItems(int32 fullListIndex, int32 count);
virtual void MakeEmpty();
CLVListItem* FullListItemAt(int32 fullListIndex) const;
int32 FullListIndexOf(const CLVListItem* item) const;
int32 FullListIndexOf(BPoint point) const;
CLVListItem* FullListFirstItem() const;
CLVListItem* FullListLastItem() const;
bool FullListHasItem(const CLVListItem* item) const;
int32 FullListCountItems() const;
bool FullListIsEmpty() const;
int32 FullListCurrentSelection(int32 index = 0) const;
void FullListDoForEach(bool (*func)(CLVListItem*));
void FullListDoForEach(bool (*func)(CLVListItem*, void*), void* arg2);
CLVListItem* Superitem(const CLVListItem* item) const;
int32 FullListNumberOfSubitems(const CLVListItem* item) const;
virtual void Expand(CLVListItem* item);
virtual void Collapse(CLVListItem* item);
bool IsExpanded(int32 fullListIndex) const;
void SetSortFunction(CLVCompareFuncPtr compare);
void SortItems();
virtual CLVContainerView* CreateContainer(bool horizontal, bool vertical, bool scroll_view_corner,
border_style border, uint32 ResizingMode, uint32 flags);
bool HighlightTextOnly() {return fHighlightTextOnly;}
void SetHighlightTextOnly(bool enable) {fHighlightTextOnly = enable;}
private:
friend class CLVMainView;
friend class CLVColumn;
friend class CLVColumnLabelView;
friend class CLVListItem;
void UpdateColumnSizesDataRectSizeScrollBars(bool scrolling_allowed = true);
void ColumnsChanged();
void EmbedInContainer(bool horizontal, bool vertical, bool scroll_view_corner, border_style border,
uint32 ResizingMode, uint32 flags);
void SortListArray(CLVListItem** SortArray, int32 NumberOfItems);
void MakeEmptyPrivate();
bool AddHierarchicalListPrivate(BList* newItems, int32 fullListIndex);
bool AddItemPrivate(CLVListItem* item, int32 fullListIndex);
void SortFullListSegment(int32 OriginalListStartIndex, int32 InsertionPoint, BList* NewList);
BList* SortItemsInThisLevel(int32 OriginalListStartIndex);
static int PlainBListSortFunc(BListItem** item1, BListItem** item2);
static int HierarchicalBListSortFunc(BListItem** item1, BListItem** item2);
CLVColumnLabelView* fColumnLabelView;
CLVContainerView* fScrollView;
bool fHierarchical;
BList fColumnList;
BList fColumnDisplayList;
BList fSortKeyList; //List contains CLVColumn pointers
BList fFullItemList; //List contains CLVListItem pointers
PrefilledBitmap fRightArrow;
PrefilledBitmap fDownArrow;
int32 fExpanderColumn;
CLVCompareFuncPtr fCompare;
bool fWatchingForDrag;
BPoint fLastMouseDown;
int32 fNoKeyMouseDownItemIndex;
rgb_color fSelectedItemColorWindowActive;
rgb_color fSelectedItemColorWindowInactive;
bool fWindowActive;
bool fHighlightTextOnly;
};
class CLVContainerView : public BetterScrollView
{
public:
CLVContainerView(ColumnListView* target, uint32 resizingMode, uint32 flags, bool horizontal, bool vertical,
bool scroll_view_corner, border_style border);
~CLVContainerView();
private:
friend class ColumnListView;
bool IsBeingDestroyed;
};
#endif

View File

@ -0,0 +1,166 @@
//******************************************************************************************************
//**** PROJECT HEADER FILES
//******************************************************************************************************
#include "Cursors.h"
//******************************************************************************************************
//**** CONSTANT DEFINITIONS
//******************************************************************************************************
uint8 c_v_resize_cursor_data[68] =
{
16,1,7,7,
0,0, //0000000000000000
1,0, //0000000100000000
1,0, //0000000100000000
1,0, //0000000100000000
1,0, //0000000100000000
17,16, //0001000100010000
49,24, //0011000100011000
113,28, //0111000100011100
49,24, //0011000100011000
17,16, //0001000100010000
1,0, //0000000100000000
1,0, //0000000100000000
1,0, //0000000100000000
1,0, //0000000100000000
0,0, //0000000000000000
0,0, //0000000000000000
3,128, //0000001110000000
3,128, //0000001110000000
3,128, //0000001110000000
3,128, //0000001110000000
27,176, //0001101110110000
59,184, //0011101110111000
123,188,//0111101110111100
251,190,//1111101110111110
123,188,//0111101110111100
59,184, //0011101110111000
27,176, //0001101110110000
3,128, //0000001110000000
3,128, //0000001110000000
3,128, //0000001110000000
3,128, //0000001110000000
0,0 //0000000000000000
};
uint8 c_h_resize_cursor_data[68] =
{
16,1,7,7,
0,0, //0000000000000000
1,0, //0000000100000000
3,128, //0000001110000000
7,192, //0000011111000000
0,0, //0000000000000000
0,0, //0000000000000000
0,0, //0000000000000000
127,252,//0111111111111100
0,0, //0000000000000000
0,0, //0000000000000000
0,0, //0000000000000000
7,192, //0000011111000000
3,128, //0000001110000000
1,0, //0000000100000000
0,0, //0000000000000000
0,0, //0000000000000000
1,0, //0000000100000000
3,128, //0000001110000000
7,192, //0000011111000000
15,224, //0000111111100000
15,224, //0000111111100000
0,0, //0000000000000000
255,254,//1111111111111110
255,254,//1111111111111110
255,254,//1111111111111110
0,0, //0000000000000000
15,224, //0000111111100000
15,224, //0000111111100000
7,192, //0000011111000000
3,128, //0000001110000000
1,0, //0000000100000000
0,0 //0000000000000000
};
uint8 c_crosshairs_cursor_data[68] =
{
16,1,7,7,
0,0, //0000000000000000
1,0, //0000000100000000
1,0, //0000000100000000
1,0, //0000000100000000
1,0, //0000000100000000
1,0, //0000000100000000
0,0, //0000000000000000
125,124,//0111110101111100
0,0, //0000000000000000
1,0, //0000000100000000
1,0, //0000000100000000
1,0, //0000000100000000
1,0, //0000000100000000
1,0, //0000000100000000
0,0, //0000000000000000
0,0, //0000000000000000
3,128, //0000001110000000
3,128, //0000001110000000
3,128, //0000001110000000
3,128, //0000001110000000
3,128, //0000001110000000
3,128, //0000001110000000
255,254,//1111111111111110
255,254,//1111111111111110
255,254,//1111111111111110
3,128, //0000001110000000
3,128, //0000001110000000
3,128, //0000001110000000
3,128, //0000001110000000
3,128, //0000001110000000
3,128, //0000001110000000
0,0 //0000000000000000
};
uint8 c_magnify_cursor_data[68] =
{
16,1,6,6,
0,0, //0000000000000000
7,0, //0000011100000000
24,192, //0001100011000000
32,32, //0010000000100000
32,32, //0010000000100000
64,16, //0100000000010000
64,16, //0100000000010000
64,16, //0100000000010000
32,32, //0010000000100000
32,32, //0010000000100000
24,240, //0001100011110000
7,56, //0000011100111000
0,28, //0000000000011100
0,14, //0000000000001110
0,4, //0000000000000100
0,0, //0000000000000000
7,0, //0000011100000000
31,192, //0001111111000000
63,224, //0011111111100000
120,240,//0111100011110000
112,112,//0111000001110000
224,56, //1110000000111000
224,56, //1110000000111000
224,56, //1110000000111000
112,112,//0111000001110000
120,240,//0111100011110000
63,248, //0011111111111000
31,252, //0001111111111100
7,62, //0000011100111110
0,31, //0000000000011111
0,14, //0000000000001110
0,4, //0000000000000100
};
const void* c_v_resize_cursor = c_v_resize_cursor_data;
const void* c_h_resize_cursor = c_h_resize_cursor_data;
const void* c_crosshairs_cursor = c_crosshairs_cursor_data;
const void* c_magnify_cursor = c_magnify_cursor_data;

View File

@ -0,0 +1,20 @@
#ifndef _SGB_CURSORS_H_
#define _SGB_CURSORS_H_
//******************************************************************************************************
//**** SYSTEM HEADER FILES
//******************************************************************************************************
#include <SupportDefs.h>
//******************************************************************************************************
//**** CONSTANT DEFINITIONS
//******************************************************************************************************
extern const void* c_v_resize_cursor;
extern const void* c_h_resize_cursor;
extern const void* c_crosshairs_cursor;
extern const void* c_magnify_cursor;
#endif //_SGB_CURSORS_H_

View File

@ -0,0 +1,19 @@
const unsigned char kDescendingIconBits [] = {
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,
0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
};

View File

@ -0,0 +1,244 @@
//*** LICENSE ***
//ColumnListView, its associated classes and source code, and the other components of Santa's Gift Bag are
//being made publicly available and free to use in freeware and shareware products with a price under $25
//(I believe that shareware should be cheap). For overpriced shareware (hehehe) or commercial products,
//please contact me to negotiate a fee for use. After all, I did work hard on this class and invested a lot
//of time into it. That being said, DON'T WORRY I don't want much. It totally depends on the sort of project
//you're working on and how much you expect to make off it. If someone makes money off my work, I'd like to
//get at least a little something. If any of the components of Santa's Gift Bag are is used in a shareware
//or commercial product, I get a free copy. The source is made available so that you can improve and extend
//it as you need. In general it is best to customize your ColumnListView through inheritance, so that you
//can take advantage of enhancements and bug fixes as they become available. Feel free to distribute the
//ColumnListView source, including modified versions, but keep this documentation and license with it.
#include <string.h>
#include <Font.h>
#include <Looper.h>
#include <Rect.h>
#include "NewStrings.h"
float GetStringsMaxWidth(const char** strings, int32 num_strings, const BFont* font,
float* string_widths)
//Fills the stringsWidths array with the width of each individual string in the array using
//BFont::GetStringWidths(), then finds the longest string width in the array and returns that width.
//If a string_widths array is provided, it fills it in with the length of each string.
{
int32 strlens_fixed[20];
int32* strlens;
bool release_string_lengths = false;
if(num_strings <= 20)
strlens = strlens_fixed;
else
{
strlens = new int32[num_strings];
release_string_lengths = true;
}
float strwidths_fixed[20];
bool release_string_widths = false;
if(string_widths == NULL)
{
if(num_strings <= 20)
string_widths = strwidths_fixed;
else
{
string_widths = new float[num_strings];
release_string_widths = true;
}
}
int32 i;
for(i = 0; i < num_strings; i++)
strlens[i] = strlen(strings[i]);
font->GetStringWidths(strings,strlens,num_strings,string_widths);
float max = string_widths[0];
for(i = 1; i < num_strings; i++)
if(string_widths[i] > max)
max = string_widths[i];
if(release_string_widths)
delete[] string_widths;
if(release_string_lengths)
delete[] strlens;
return max;
}
BRect StringBoundingBox(const char* string, const BFont* font, bool full_height, bool round_up)
{
const char* string_array[1] = {string};
struct escapement_delta delta_array[1] = {{0,0}};
BRect rect_array[1];
font->GetBoundingBoxesForStrings(string_array,1,B_SCREEN_METRIC,delta_array,rect_array);
if(full_height)
{
struct font_height font_height_struct;
font->GetHeight(&font_height_struct);
rect_array[0].top = 0-font_height_struct.ascent;
rect_array[0].bottom = font_height_struct.descent;
}
if(round_up)
{
rect_array[0].left = floor(rect_array[0].left);
rect_array[0].top = floor(rect_array[0].top);
rect_array[0].right = ceil(rect_array[0].right);
rect_array[0].bottom = ceil(rect_array[0].bottom);
}
return rect_array[0];
}
BRect GetMaxStringBoundingBoxes(const char** strings, int32 num_strings, const BFont* font,
BRect* rects, bool full_height, bool round_up)
{
BRect rects_fixed[20];
bool release_rects = false;
if(rects == NULL)
{
if(num_strings <= 20)
rects = rects_fixed;
else
{
rects = new BRect[num_strings];
release_rects = true;
}
}
struct escapement_delta delta_array_fixed[20];
bool release_deltas = false;
struct escapement_delta* delta_array = delta_array_fixed;
if(num_strings > 20)
{
delta_array = new escapement_delta[num_strings];
release_deltas = true;
}
int32 i;
for(i=0; i<num_strings; i++)
{
delta_array[i].nonspace = 0;
delta_array[i].space = 0;
}
font->GetBoundingBoxesForStrings(strings,num_strings,B_SCREEN_METRIC,delta_array,rects);
struct font_height font_height_struct;
if(full_height)
{
font->GetHeight(&font_height_struct);
if(round_up)
{
font_height_struct.ascent = ceil(font_height_struct.ascent);
font_height_struct.descent = ceil(font_height_struct.descent);
}
for(i=0; i<num_strings; i++)
{
rects[i].top = 0-font_height_struct.ascent;
rects[i].bottom = font_height_struct.descent;
}
}
if(round_up)
for(i=0; i<num_strings; i++)
{
rects[i].left = floor(rects[i].left);
rects[i].right = ceil(rects[i].right);
if(!full_height)
{
rects[i].top = floor(rects[i].top);
rects[i].bottom = ceil(rects[i].bottom);
}
}
BRect max = rects[0];
for(i=0; i<num_strings; i++)
{
if(max.left > rects[i].left)
max.left = rects[i].left;
if((!full_height) && max.top > rects[i].top)
max.top = rects[i].top;
if(max.right < rects[i].right)
max.right = rects[i].right;
if((!full_height) && max.bottom < rects[i].bottom)
max.bottom = rects[i].bottom;
}
if(release_rects)
delete[] rects;
if(release_deltas)
delete[] delta_array;
return max;
}
char* Strdup_new(const char* source)
{
char* dest = new char[strlen(source)+1];
strcpy(dest,source);
return dest;
}
char* Strcat_new(const char* string_1, const char* string_2)
{
int len_1 = strlen(string_1);
char* dest = new char[len_1+strlen(string_2)+1];
strcpy(dest,string_1);
strcpy(&dest[len_1],string_2);
return dest;
}
char* Strcat_new(const char* string_1, const char* string_2, const char* string_3)
{
int len_1 = strlen(string_1);
int len_2 = strlen(string_2);
char* dest = new char[len_1+len_2+strlen(string_3)+1];
strcpy(dest,string_1);
strcpy(&dest[len_1],string_2);
strcpy(&dest[len_1+len_2],string_3);
return dest;
}
char* Strcat_new(const char* string_1, const char* string_2, const char* string_3, const char* string_4)
{
int len_1 = strlen(string_1);
int len_2 = strlen(string_2);
int len_3 = strlen(string_3);
char* dest = new char[len_1+len_2+len_3+strlen(string_4)+1];
strcpy(dest,string_1);
strcpy(&dest[len_1],string_2);
strcpy(&dest[len_1+len_2],string_3);
strcpy(&dest[len_1+len_2+len_3],string_4);
return dest;
}
char *Strtcpy(char *dst, const char *src, int len)
{
strncpy(dst,src,len-1);
dst[len-1] = 0;
return dst;
}
void StrToUpper(char* string)
{
while(*string != 0)
{
if(*string >= 'a' && *string <= 'z')
*string = *string - 'a' + 'A';
string++;
}
}
void StrToLower(char* string)
{
while(*string != 0)
{
if(*string >= 'A' && *string <= 'Z')
*string = *string - 'A' + 'a';
string++;
}
}

View File

@ -0,0 +1,48 @@
//*** LICENSE ***
//ColumnListView, its associated classes and source code, and the other components of Santa's Gift Bag are
//being made publicly available and free to use in freeware and shareware products with a price under $25
//(I believe that shareware should be cheap). For overpriced shareware (hehehe) or commercial products,
//please contact me to negotiate a fee for use. After all, I did work hard on this class and invested a lot
//of time into it. That being said, DON'T WORRY I don't want much. It totally depends on the sort of project
//you're working on and how much you expect to make off it. If someone makes money off my work, I'd like to
//get at least a little something. If any of the components of Santa's Gift Bag are is used in a shareware
//or commercial product, I get a free copy. The source is made available so that you can improve and extend
//it as you need. In general it is best to customize your ColumnListView through inheritance, so that you
//can take advantage of enhancements and bug fixes as they become available. Feel free to distribute the
//ColumnListView source, including modified versions, but keep this documentation and license with it.
#ifndef _SGB_NEW_STRINGS_H_
#define _SGB_NEW_STRINGS_H_
#include <SupportDefs.h>
//Fills the StringsWidths array with the width of each individual string in the array using
//BFont::GetStringWidths(), then finds the longest string width in the array and returns that width.
//If a string_widths array is provided, it fills it in with the length of each string.
float GetStringsMaxWidth(const char** strings, int32 num_strings, const BFont* font,
float* string_widths = NULL);
//Gets the bounding box for one or more strings. If full_height is true, it increases the top and bottom to
//the font's ascent and descent. If round_up is true, it rounds each edge up to the next pixel to get the
//box that will actually be occupied onscreen.
BRect StringBoundingBox(const char* string, const BFont* font, bool full_height = false,
bool round_up = true);
BRect GetMaxStringBoundingBoxes(const char** strings, int32 num_strings, const BFont* font,
BRect* rects = NULL, bool full_height = false, bool round_up = true);
//Each of these functions either duplicates or concatenates the strings into a new char array allocated
//with new. The resulting char array must be delete[]'d when finished with it.
char *Strdup_new(const char *source);
char *Strcat_new(const char *string_1, const char *string_2);
char *Strcat_new(const char *string_1, const char *string_2, const char *string_3);
char *Strcat_new(const char *string_1, const char *string_2, const char *string_3, const char *string_4);
//Added because due to some error, the Be libraries on x86 don't export strtcopy. Len includes the null
//terminator.
char *Strtcpy(char *dst, const char *src, int len);
//Just some handy functions....
void StrToUpper(char* string);
void StrToLower(char* string);
#endif

View File

@ -0,0 +1,32 @@
//*** LICENSE ***
//ColumnListView, its associated classes and source code, and the other components of Santa's Gift Bag are
//being made publicly available and free to use in freeware and shareware products with a price under $25
//(I believe that shareware should be cheap). For overpriced shareware (hehehe) or commercial products,
//please contact me to negotiate a fee for use. After all, I did work hard on this class and invested a lot
//of time into it. That being said, DON'T WORRY I don't want much. It totally depends on the sort of project
//you're working on and how much you expect to make off it. If someone makes money off my work, I'd like to
//get at least a little something. If any of the components of Santa's Gift Bag are is used in a shareware
//or commercial product, I get a free copy. The source is made available so that you can improve and extend
//it as you need. In general it is best to customize your ColumnListView through inheritance, so that you
//can take advantage of enhancements and bug fixes as they become available. Feel free to distribute the
//ColumnListView source, including modified versions, but keep this documentation and license with it.
#include "PrefilledBitmap.h"
PrefilledBitmap::PrefilledBitmap(BRect bounds, color_space space, const void *data, int32 length,
bool acceptsViews, bool needsContiguousMemory)
: BBitmap(bounds, space, acceptsViews, needsContiguousMemory)
{
if(length == 0)
{
if(space == B_CMAP8)
length = ((int32(bounds.right-bounds.left+1)+3)&0xFFFFFFFC)*int32(bounds.bottom-bounds.top+1);
else if(space == B_RGB32)
length = int32(bounds.right-bounds.left+1)*int32(bounds.bottom-bounds.top+1)*3;
}
SetBits(data, length, 0, space);
}
PrefilledBitmap::~PrefilledBitmap()
{ }

View File

@ -0,0 +1,42 @@
//*** LICENSE ***
//ColumnListView, its associated classes and source code, and the other components of Santa's Gift Bag are
//being made publicly available and free to use in freeware and shareware products with a price under $25
//(I believe that shareware should be cheap). For overpriced shareware (hehehe) or commercial products,
//please contact me to negotiate a fee for use. After all, I did work hard on this class and invested a lot
//of time into it. That being said, DON'T WORRY I don't want much. It totally depends on the sort of project
//you're working on and how much you expect to make off it. If someone makes money off my work, I'd like to
//get at least a little something. If any of the components of Santa's Gift Bag are is used in a shareware
//or commercial product, I get a free copy. The source is made available so that you can improve and extend
//it as you need. In general it is best to customize your ColumnListView through inheritance, so that you
//can take advantage of enhancements and bug fixes as they become available. Feel free to distribute the
//ColumnListView source, including modified versions, but keep this documentation and license with it.
//*** DESCRIPTION ***
//Useful until be implements BBitmap::BBitmap(BRect bounds, color_space space, const void *data,
// bool acceptsViews, bool needsContiguousMemory)
//or something like it...
#ifndef _SGB_PREFILLED_BITMAP_H_
#define _SGB_PREFILLED_BITMAP_H_
//******************************************************************************************************
//**** SYSTEM HEADER FILES
//******************************************************************************************************
#include <Bitmap.h>
//******************************************************************************************************
//**** CLASS DECLARATIONS
//******************************************************************************************************
class PrefilledBitmap : public BBitmap
{
public:
PrefilledBitmap(BRect bounds, color_space space, const void *data, int32 length = 0,
bool acceptsViews = false, bool needsContiguousMemory = false);
//length can be automatically calculated for B_CMAP8 and B_RGB32
~PrefilledBitmap();
};
#endif

View File

@ -0,0 +1,41 @@
//*** LICENSE ***
//ColumnListView, its associated classes and source code, and the other components of Santa's Gift Bag are
//being made publicly available and free to use in freeware and shareware products with a price under $25
//(I believe that shareware should be cheap). For overpriced shareware (hehehe) or commercial products,
//please contact me to negotiate a fee for use. After all, I did work hard on this class and invested a lot
//of time into it. That being said, DON'T WORRY I don't want much. It totally depends on the sort of project
//you're working on and how much you expect to make off it. If someone makes money off my work, I'd like to
//get at least a little something. If any of the components of Santa's Gift Bag are is used in a shareware
//or commercial product, I get a free copy. The source is made available so that you can improve and extend
//it as you need. In general it is best to customize your ColumnListView through inheritance, so that you
//can take advantage of enhancements and bug fixes as they become available. Feel free to distribute the
//ColumnListView source, including modified versions, but keep this documentation and license with it.
#include <ScrollBar.h>
#include "Colors.h"
#include "ScrollViewCorner.h"
ScrollViewCorner::ScrollViewCorner(float Left,float Top)
: BView(BRect(Left,Top,Left+B_V_SCROLL_BAR_WIDTH,Top+B_H_SCROLL_BAR_HEIGHT),NULL,B_FOLLOW_RIGHT |
B_FOLLOW_BOTTOM,B_WILL_DRAW)
{
SetHighColor(BeShadow);
SetViewColor(BeInactiveGrey);
}
ScrollViewCorner::~ScrollViewCorner()
{ }
void ScrollViewCorner::Draw(BRect Update)
{
if(Update.bottom >= B_H_SCROLL_BAR_HEIGHT)
StrokeLine(BPoint(0.0,B_H_SCROLL_BAR_HEIGHT),BPoint(B_V_SCROLL_BAR_WIDTH,B_H_SCROLL_BAR_HEIGHT));
if(Update.right >= B_V_SCROLL_BAR_WIDTH)
StrokeLine(BPoint(B_V_SCROLL_BAR_WIDTH,0.0),
BPoint(B_V_SCROLL_BAR_WIDTH,B_H_SCROLL_BAR_HEIGHT-1.0));
}

View File

@ -0,0 +1,46 @@
//*** LICENSE ***
//ColumnListView, its associated classes and source code, and the other components of Santa's Gift Bag are
//being made publicly available and free to use in freeware and shareware products with a price under $25
//(I believe that shareware should be cheap). For overpriced shareware (hehehe) or commercial products,
//please contact me to negotiate a fee for use. After all, I did work hard on this class and invested a lot
//of time into it. That being said, DON'T WORRY I don't want much. It totally depends on the sort of project
//you're working on and how much you expect to make off it. If someone makes money off my work, I'd like to
//get at least a little something. If any of the components of Santa's Gift Bag are is used in a shareware
//or commercial product, I get a free copy. The source is made available so that you can improve and extend
//it as you need. In general it is best to customize your ColumnListView through inheritance, so that you
//can take advantage of enhancements and bug fixes as they become available. Feel free to distribute the
//ColumnListView source, including modified versions, but keep this documentation and license with it.
//*** DESCRIPTION ***
//If you have a BScrollView with horizontal and vertical sliders that isn't
//seated to the lower-right corner of a B_DOCUMENT_WINDOW, there's a "hole"
//between the sliders that needs to be filled. You can use this to fill it.
//In general, it looks best to set the ScrollViewCorner color to
//BeInactiveControlGrey if the vertical BScrollBar is inactive, and the color
//to BeBackgroundGrey if the vertical BScrollBar is active. Have a look at
//Demo3 of ColumnListView to see what I mean if this is unclear.
#ifndef _SGB_SCROLL_VIEW_CORNER_H_
#define _SGB_SCROLL_VIEW_CORNER_H_
//******************************************************************************************************
//**** SYSTEM HEADER FILES
//******************************************************************************************************
#include <View.h>
//******************************************************************************************************
//**** CLASS DECLARATIONS
//******************************************************************************************************
class ScrollViewCorner : public BView
{
public:
ScrollViewCorner(float Left,float Top);
~ScrollViewCorner();
virtual void Draw(BRect Update);
};
#endif

View File

@ -0,0 +1,525 @@
//Name: TextEntryAlert.cpp
//Author: Brian Tietz
//Conventions:
// Global constants (declared with const) and #defines - begin with "c_" followed by lowercase
// words separated by underscores.
// (E.G., #define c_my_constant 5).
// (E.G., const int c_my_constant = 5;).
// Global variables - begin with "g_" followed by lowercase words separated by underscores.
// (E.G., int g_my_global;).
// New data types (classes, structs, typedefs, etc.) - begin with an uppercase letter followed by
// lowercase words separated by uppercase letters. Enumerated constants contain a prefix
// associating them with a particular enumerated set.
// (E.G., typedef int MyTypedef;).
// (E.G., enum MyEnumConst {c_mec_one, c_mec_two};)
// Private member variables - begin with "m_" followed by lowercase words separated by underscores.
// (E.G., int m_my_member;).
// Public or friend-accessible member variables - all lowercase words separated by underscores.
// (E.G., int public_member;).
// Argument and local variables - begin with a lowercase letter followed by
// lowercase words separated by underscores. If the name is already taken by a public member
// variable, prefix with a_ or l_
// (E.G., int my_local; int a_my_arg, int l_my_local).
// Functions (member or global) - begin with an uppercase letter followed by lowercase words
// separated by uppercase letters.
// (E.G., void MyFunction(void);).
//******************************************************************************************************
//**** System header files
//******************************************************************************************************
#include <string.h>
#include <Button.h>
#include <Screen.h>
//******************************************************************************************************
//**** Project header files
//******************************************************************************************************
#include "TextEntryAlert.h"
#include "Colors.h"
#include "NewStrings.h"
//******************************************************************************************************
//**** Constants
//******************************************************************************************************
const float c_text_border_width = 2;
const float c_text_margin = 1;
const float c_item_spacing = 9;
const float c_non_inline_label_spacing = 5;
const float c_inline_label_spacing = 7;
const float c_usual_button_width = 74;
//******************************************************************************************************
//**** BMessage what constants for internal use
//******************************************************************************************************
const uint32 c_button_pressed = 0;
//******************************************************************************************************
//**** TextEntryAlert
//******************************************************************************************************
TextEntryAlert::TextEntryAlert(const char* title, const char* info_text, const char* initial_entry_text,
const char* button_0_label, const char* button_1_label, bool inline_label, float min_text_box_width,
int min_text_box_rows, button_width width_style, bool multi_line, const BRect* frame,
window_look look, window_feel feel, uint32 flags)
: BWindow((frame?*frame:BRect(50,50,60,60)),title,look,feel,flags)
{
//Get unadjusted sizes of the two text boxes
BRect info_text_box;
BRect entry_text_rect;
const char* strings[2] = {info_text?info_text:"",initial_entry_text?initial_entry_text:""};
escapement_delta zero_escapements[2] = {{0,0},{0,0}};
BRect result_rects[2];
be_plain_font->GetBoundingBoxesForStrings(strings,2,B_SCREEN_METRIC,zero_escapements,result_rects);
struct font_height plain_font_height;
be_plain_font->GetHeight(&plain_font_height);
info_text_box = result_rects[0];
info_text_box.bottom = info_text_box.top + ceil(plain_font_height.ascent) +
ceil(plain_font_height.descent);
entry_text_rect = result_rects[1];
entry_text_rect.bottom = entry_text_rect.top +
(ceil(plain_font_height.ascent)+ceil(plain_font_height.descent))*min_text_box_rows +
ceil(plain_font_height.leading)*(min_text_box_rows-1);
entry_text_rect.InsetBy(0-(c_text_margin+c_text_border_width),
0-(c_text_margin+c_text_border_width));
if(entry_text_rect.Width() < min_text_box_width)
entry_text_rect.right = entry_text_rect.left+min_text_box_width;
entry_text_rect.bottom -= 1; //To make it look like BTextControl
//Position and create label
m_label_view = NULL;
if(info_text)
{
info_text_box.OffsetTo(c_item_spacing,c_item_spacing);
if(inline_label)
info_text_box.OffsetBy(0,c_text_border_width+c_text_margin);
else
info_text_box.OffsetBy(c_non_inline_label_spacing,-2);
info_text_box.bottom += 2; //Compensate for offset used by BTextView
info_text_box.right += 1;
m_label_view = new BTextView(info_text_box,NULL,info_text_box.OffsetToCopy(0,0),
B_FOLLOW_LEFT|B_FOLLOW_TOP,B_WILL_DRAW);
m_label_view->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
m_label_view->SetText(info_text);
m_label_view->MakeEditable(false);
m_label_view->MakeSelectable(false);
m_label_view->SetWordWrap(false);
}
else
{
info_text_box.Set(0,0,0,0);
inline_label = false;
}
//Create buttons
m_buttons[0] = NULL;
m_buttons[1] = NULL;
BMessage* message;
float button_0_width, button_1_width, buttons_height;
float max_buttons_width = 0;
if(button_0_label != NULL)
{
message = new BMessage(c_button_pressed);
message->AddInt32("which",0);
m_buttons[0] = new BButton(BRect(0,0,0,0),button_0_label,button_0_label,message,B_FOLLOW_LEFT|
B_FOLLOW_BOTTOM);
m_buttons[0]->GetPreferredSize(&button_0_width,&buttons_height);
max_buttons_width = button_0_width;
}
if(button_1_label != NULL)
{
message = new BMessage(c_button_pressed);
message->AddInt32("which",1);
m_buttons[1] = new BButton(BRect(0,0,0,0),button_1_label,button_1_label,message,B_FOLLOW_RIGHT|
B_FOLLOW_BOTTOM);
m_buttons[1]->GetPreferredSize(&button_1_width,&buttons_height);
if(max_buttons_width < button_1_width)
max_buttons_width = button_1_width;
}
//Position and create text entry box
if(inline_label)
entry_text_rect.OffsetTo(info_text_box.right+c_inline_label_spacing,c_item_spacing);
else
entry_text_rect.OffsetTo(c_item_spacing,info_text_box.bottom+c_non_inline_label_spacing-4);
//-5 is to compensate for extra pixels that BeOS adds to the font height
if(frame != NULL)
{
if(entry_text_rect.left + min_text_box_width < frame->Width()-c_item_spacing)
entry_text_rect.right = frame->Width()+c_item_spacing;
else
entry_text_rect.right = entry_text_rect.left + min_text_box_width;
if(min_text_box_rows > 1)
{
float bottom = frame->Height()-c_item_spacing;
if(button_0_label != NULL || button_1_label != NULL)
bottom -= (buttons_height+c_item_spacing);
if(bottom > entry_text_rect.bottom)
entry_text_rect.bottom = bottom;
}
}
m_text_entry_view = new TextEntryAlertTextEntryView(entry_text_rect.InsetByCopy(c_text_border_width,
c_text_border_width),multi_line);
if(initial_entry_text)
m_text_entry_view->SetText(initial_entry_text);
//Position the buttons
if(m_buttons[0] != NULL && m_buttons[1] != NULL)
{
float button_left;
if(inline_label)
button_left = info_text_box.left;
else
button_left = entry_text_rect.left;
m_buttons[0]->MoveTo(button_left,entry_text_rect.bottom+c_item_spacing);
if(width_style == B_WIDTH_AS_USUAL)
m_buttons[0]->ResizeTo(c_usual_button_width,buttons_height);
else if(width_style == B_WIDTH_FROM_LABEL)
m_buttons[0]->ResizeTo(button_0_width,buttons_height);
else //if(width_style == B_WIDTH_FROM_WIDEST)
m_buttons[0]->ResizeTo(max_buttons_width,buttons_height);
}
BButton* right_button = NULL;
if(m_buttons[1] != NULL)
right_button = m_buttons[1];
else if(m_buttons[0] != NULL)
right_button = m_buttons[0];
if(right_button != NULL)
{
if(width_style == B_WIDTH_AS_USUAL)
right_button->ResizeTo(c_usual_button_width,buttons_height);
else if(width_style == B_WIDTH_FROM_LABEL)
right_button->ResizeTo(right_button==m_buttons[1]?button_1_width:button_0_width,
buttons_height);
else //(width_style == B_WIDTH_FROM_WIDEST)
right_button->ResizeTo(max_buttons_width,buttons_height);
right_button->MoveTo(entry_text_rect.right-right_button->Frame().Width()+1,
entry_text_rect.bottom+c_item_spacing);
if(!multi_line)
right_button->MakeDefault(true);
}
//Resize the window
float height;
if(m_buttons[0])
height = m_buttons[0]->Frame().bottom+c_item_spacing-1;
else if(m_buttons[1])
height = m_buttons[1]->Frame().bottom+c_item_spacing-1;
else
height = entry_text_rect.bottom+c_item_spacing-1;
ResizeTo(entry_text_rect.right+c_item_spacing,height);
BRect bounds = Bounds();
if(frame == NULL)
{
BRect screen_frame = BScreen().Frame();
MoveTo(ceil((screen_frame.Width()-bounds.Width())/2),
ceil((screen_frame.Height()-bounds.Height()-19)/2));
}
//Create the background view and add the children
BView* filler = new TextEntryAlertBackgroundView(bounds,entry_text_rect);
if(m_label_view)
filler->AddChild(m_label_view);
filler->AddChild(m_text_entry_view);
if(m_buttons[0])
filler->AddChild(m_buttons[0]);
if(m_buttons[1])
filler->AddChild(m_buttons[1]);
AddChild(filler);
//Complete the setup
m_invoker = NULL;
m_done_mutex = B_ERROR;
m_text_entry_buffer = NULL;
m_button_pressed = NULL;
float min_width = c_item_spacing;
if(m_buttons[0])
min_width += (m_buttons[0]->Frame().Width() + c_item_spacing);
if(m_buttons[1])
min_width += (m_buttons[0]->Frame().Width() + c_item_spacing);
if(min_width < 120)
min_width = 120;
float min_height = entry_text_rect.top;
min_height += (ceil(plain_font_height.ascent)+ceil(plain_font_height.descent));
min_height += ((c_text_margin+c_text_border_width)*2 + c_item_spacing);
if(m_buttons[0] || m_buttons[1])
min_height += (buttons_height + c_item_spacing);
min_width -= 2; //Need this for some reason
min_height -= 2;
SetSizeLimits(min_width,100000,min_height,100000);
AddCommonFilter(new BMessageFilter(B_KEY_DOWN,KeyDownFilterStatic));
m_shortcut[0] = 0;
m_shortcut[1] = 0;
}
TextEntryAlert::~TextEntryAlert()
{
if(m_invoker)
delete m_invoker;
}
int32 TextEntryAlert::Go(char* text_entry_buffer, int32 buffer_size)
{
int32 button_pressed = -1;
m_text_entry_buffer = text_entry_buffer;
m_button_pressed = &button_pressed;
m_buffer_size = buffer_size;
sem_id done_mutex = create_sem(0,"text_entry_alert_done");
m_done_mutex = done_mutex;
if(done_mutex < B_NO_ERROR)
{
Quit();
return -1;
}
m_text_entry_view->MakeFocus(true);
m_text_entry_view->SetMaxBytes(buffer_size);
Show();
acquire_sem(done_mutex);
delete_sem(done_mutex);
return button_pressed;
}
status_t TextEntryAlert::Go(BInvoker *invoker)
{
m_invoker = invoker;
m_text_entry_view->MakeFocus(true);
Show();
return B_NO_ERROR;
}
void TextEntryAlert::SetShortcut(int32 index, char shortcut)
{
m_shortcut[index] = shortcut;
}
char TextEntryAlert::Shortcut(int32 index) const
{
return m_shortcut[index];
}
filter_result TextEntryAlert::KeyDownFilterStatic(BMessage* message, BHandler** target,
BMessageFilter* filter)
{
return ((TextEntryAlert*)filter->Looper())->KeyDownFilter(message);
}
filter_result TextEntryAlert::KeyDownFilter(BMessage *message)
{
if(message->what == B_KEY_DOWN)
{
char byte;
if(message->FindInt8("byte",(int8*)&byte) == B_NO_ERROR && !message->HasInt8("byte",1))
{
char space = B_SPACE;
if(m_shortcut[0] && byte == m_shortcut[0])
{
m_buttons[0]->KeyDown(&space,1);
return B_SKIP_MESSAGE;
}
else if(m_shortcut[1] && byte == m_shortcut[1])
{
m_buttons[1]->KeyDown(&space,1);
return B_SKIP_MESSAGE;
}
}
}
return B_DISPATCH_MESSAGE;
}
BTextView* TextEntryAlert::LabelView(void) const
{
return m_label_view;
}
BTextView* TextEntryAlert::TextEntryView(void) const
{
return m_text_entry_view;
}
void TextEntryAlert::SetTabAllowed(bool allow)
{
m_text_entry_view->SetTabAllowed(allow);
}
bool TextEntryAlert::TabAllowed()
{
return m_text_entry_view->TabAllowed();
}
void TextEntryAlert::MessageReceived(BMessage* message)
{
if(message->what == c_button_pressed)
{
int32 which;
if(message->FindInt32("which",&which) == B_NO_ERROR)
{
const char* text = m_text_entry_view->Text();
if(text == NULL)
text = "";
if(m_done_mutex < B_NO_ERROR)
{
//Asynchronous version: add the necessary fields
BMessage* message = m_invoker->Message();
if(message && (message->AddInt32("which",which) == B_NO_ERROR ||
message->ReplaceInt32("which",which) == B_NO_ERROR) &&
(message->AddString("entry_text",text) == B_NO_ERROR ||
message->ReplaceString("entry_text",text) == B_NO_ERROR))
m_invoker->Invoke();
}
else
{
//Synchronous version: set the result button and text buffer, then release the thread
//that created me
*m_button_pressed = which;
if(m_text_entry_buffer)
Strtcpy(m_text_entry_buffer,text,m_buffer_size);
release_sem(m_done_mutex);
m_done_mutex = B_ERROR;
}
PostMessage(B_QUIT_REQUESTED);
}
}
}
void TextEntryAlert::Quit()
{
//Release the mutex if I'm synchronous and I haven't released it yet
if(m_done_mutex >= B_NO_ERROR)
release_sem(m_done_mutex);
BWindow::Quit();
}
//******************************************************************************************************
//**** TextEntryAlertBackgroundView
//******************************************************************************************************
TextEntryAlertBackgroundView::TextEntryAlertBackgroundView(BRect frame, BRect entry_text_rect)
: BView(frame,NULL,B_FOLLOW_ALL_SIDES,B_WILL_DRAW|B_FRAME_EVENTS)
{
m_entry_text_rect = entry_text_rect;
m_cached_bounds = Bounds();
rgb_color background_color = ui_color(B_PANEL_BACKGROUND_COLOR);
m_dark_1_color = tint_color(background_color,B_DARKEN_1_TINT);
m_dark_2_color = tint_color(background_color,B_DARKEN_4_TINT);
SetViewColor(background_color);
SetDrawingMode(B_OP_COPY);
}
void TextEntryAlertBackgroundView::Draw(BRect update_rect)
{
if(update_rect.Intersects(m_entry_text_rect))
{
SetHighColor(m_dark_1_color);
StrokeLine(BPoint(m_entry_text_rect.left,m_entry_text_rect.top),
BPoint(m_entry_text_rect.right,m_entry_text_rect.top));
StrokeLine(BPoint(m_entry_text_rect.left,m_entry_text_rect.top+1),
BPoint(m_entry_text_rect.left,m_entry_text_rect.bottom));
SetHighColor(White);
StrokeLine(BPoint(m_entry_text_rect.right,m_entry_text_rect.top+1),
BPoint(m_entry_text_rect.right,m_entry_text_rect.bottom-1));
StrokeLine(BPoint(m_entry_text_rect.left+1,m_entry_text_rect.bottom),
BPoint(m_entry_text_rect.right,m_entry_text_rect.bottom));
SetHighColor(m_dark_2_color);
StrokeLine(BPoint(m_entry_text_rect.left+1,m_entry_text_rect.top+1),
BPoint(m_entry_text_rect.right-1,m_entry_text_rect.top+1));
StrokeLine(BPoint(m_entry_text_rect.left+1,m_entry_text_rect.top+2),
BPoint(m_entry_text_rect.left+1,m_entry_text_rect.bottom-1));
}
}
void TextEntryAlertBackgroundView::FrameResized(float width, float heigh)
{
BRect new_bounds = Bounds();
float width_delta = new_bounds.right - m_cached_bounds.right;
float height_delta = new_bounds.bottom - m_cached_bounds.bottom;
BRect new_entry_text_rect = m_entry_text_rect;
new_entry_text_rect.right += width_delta;
new_entry_text_rect.bottom += height_delta;
float right_min = m_entry_text_rect.right;
if(right_min > new_entry_text_rect.right)
right_min = new_entry_text_rect.right;
float right_max = m_entry_text_rect.right;
if(right_max < new_entry_text_rect.right)
right_max = new_entry_text_rect.right;
float bottom_min = m_entry_text_rect.bottom;
if(bottom_min > new_entry_text_rect.bottom)
bottom_min = new_entry_text_rect.bottom;
float bottom_max = m_entry_text_rect.bottom;
if(bottom_max < new_entry_text_rect.bottom)
bottom_max = new_entry_text_rect.bottom;
if(new_entry_text_rect.right != m_entry_text_rect.right)
Invalidate(BRect(right_min-1,new_entry_text_rect.top,right_max,bottom_max));
if(new_entry_text_rect.bottom != m_entry_text_rect.bottom)
Invalidate(BRect(new_entry_text_rect.left,bottom_min-1,right_max,bottom_max));
m_entry_text_rect = new_entry_text_rect;
m_cached_bounds = new_bounds;
}
//******************************************************************************************************
//**** TextEntryAlertTextEntryView
//******************************************************************************************************
TextEntryAlertTextEntryView::TextEntryAlertTextEntryView(BRect frame, bool multi_line)
: BTextView(frame,NULL,frame.OffsetToCopy(0,0).InsetByCopy(c_text_margin,c_text_margin),
multi_line?B_FOLLOW_ALL_SIDES:B_FOLLOW_LEFT_RIGHT,B_WILL_DRAW|B_NAVIGABLE)
{
m_tab_allowed = false;
m_multi_line = multi_line;
SetViewColor(White);
SetWordWrap(true);
}
void TextEntryAlertTextEntryView::SetTabAllowed(bool allow)
{
m_tab_allowed = allow;
}
void TextEntryAlertTextEntryView::KeyDown(const char* bytes, int32 num_bytes)
{
if(num_bytes == 1 && (bytes[0] == B_TAB && (!m_tab_allowed)) || (bytes[0] == B_ENTER && (!m_multi_line)))
{
BView::KeyDown(bytes,num_bytes);
return;
}
BTextView::KeyDown(bytes,num_bytes);
}
void TextEntryAlertTextEntryView::AttachedToWindow()
{
BTextView::AttachedToWindow();
const char* text = Text();
if(text)
{
int32 length = strlen(text);
Select(length,length);
}
}

View File

@ -0,0 +1,165 @@
//Name: TextEntryAlert.h
//Author: Brian Tietz
//Conventions:
// Global constants (declared with const) and #defines - begin with "c_" followed by lowercase
// words separated by underscores.
// (E.G., #define c_my_constant 5).
// (E.G., const int c_my_constant = 5;).
// Global variables - begin with "g_" followed by lowercase words separated by underscores.
// (E.G., int g_my_global;).
// New data types (classes, structs, typedefs, etc.) - begin with an uppercase letter followed by
// lowercase words separated by uppercase letters. Enumerated constants contain a prefix
// associating them with a particular enumerated set.
// (E.G., typedef int MyTypedef;).
// (E.G., enum MyEnumConst {c_mec_one, c_mec_two};)
// Private member variables - begin with "m_" followed by lowercase words separated by underscores.
// (E.G., int m_my_member;).
// Public or friend-accessible member variables - all lowercase words separated by underscores.
// (E.G., int public_member;).
// Argument and local variables - begin with a lowercase letter followed by
// lowercase words separated by underscores. If the name is already taken by a public member
// variable, prefix with a_ or l_
// (E.G., int my_local; int a_my_arg, int l_my_local).
// Functions (member or global) - begin with an uppercase letter followed by lowercase words
// separated by uppercase letters.
// (E.G., void MyFunction(void);).
#ifndef _TEXT_ENTRY_ALERT_H_
#define _TEXT_ENTRY_ALERT_H_
//******************************************************************************************************
//**** System header files
//******************************************************************************************************
#include <Window.h>
#include <TextView.h>
#include <MessageFilter.h>
//******************************************************************************************************
//**** Forward name declarations
//******************************************************************************************************
class TextEntryAlertTextEntryView;
//******************************************************************************************************
//**** TextEntryAlert
//******************************************************************************************************
class TextEntryAlert : public BWindow
{
public:
TextEntryAlert(const char* title, const char* info_text, const char* initial_entry_text,
const char* button_0_label, const char* button_1_label = NULL,
bool inline_label = true, float min_text_box_width = 120, int min_text_box_rows = 1,
button_width width_style = B_WIDTH_AS_USUAL, bool multi_line = false,
const BRect* frame = NULL, window_look look = B_MODAL_WINDOW_LOOK,
window_feel feel = B_MODAL_APP_WINDOW_FEEL,
uint32 flags = B_NOT_RESIZABLE|B_ASYNCHRONOUS_CONTROLS);
//The title is not displayed unless you use a window_look other than the default. info_text
//is the label applied to the text entry box. If inline_label is true, then the label is
//placed to the left of the text entry box, otherwise it is placed above it. If a frame is
//not specified, the TextEntryAlert will be auto-sized and auto-positioned to accommodate
//the text. The minimum text entry box size can be specified using min_text_box_width and
//text_box_rows. Multi-line specifies whether the user can hit return and enter multiple
//lines of text, regardless of the height of the text box.
~TextEntryAlert();
int32 Go(char* text_entry_buffer, int32 buffer_size);
//Synchronous version: The function doesn't return until the user has clicked a button and
//the panel has been removed from the screen. The value it returns is the index of
//the clicked button (0 or 1, left-to-right). The user-entered (or unchanged) text is
//stored in text_entry_buffer. The TextEntryAlert is deleted before it returns, and should
//be considered invalid after Go is called. If the TextEntryAlert is sent a
//B_QUIT_REQUESTED message while the panel is still on-screen, it returns -1.
status_t Go(BInvoker* invoker);
//Asynchronous version: The function returns immediately (with B_OK) and the button index is
//delivered as the int32 "which" field of the BMessage that's sent to the BInvoker's target,
//and the text message is delivered as the string "entry_text" field of the BMessage. The
//TextEntryBox will take posession of the BInvoker and delete it when finished. The
//TextEntryAlert is deleted when the user hits any of the buttons, and should be considered
//invalid after Go is called. If the TextEntryAlert is sent a B_QUIT_REQUESTED message
//while the panel is still on-screen, it suppresses sending of the message.
void SetShortcut(int32 index, char shortcut);
char Shortcut(int32 index) const;
//These functions set and return the shortcut character that's mapped to the button at
//index. A given button can have only one shortcut except for the rightmost button, which,
//in addition to the shortcut that you give it here, is always mapped to B_ENTER. If you
//create a "Cancel" button, you should give it a shortcut of B_ESCAPE. If multi_line was
//specified as true in the TextEntryAlert constructor, no shortcuts are allowed, including
//the built-in B_ENTER shortcut on the rightmost button.
void SetTabAllowed(bool allow);
//By default, tab is not allowed, being used for navigation instead. Set this to true to
//allow the user to actually enter a tab character in the text entry box.
bool TabAllowed();
BTextView* LabelView(void) const;
BTextView* TextEntryView(void) const;
//Returns a pointer to the BTextView object that contains the textual information or the
//user-editable text that's displayed in the panel, respectively. You can fiddle with these
//objects but you mustn't delete them.
//BWindow overrides
virtual void MessageReceived(BMessage* message);
virtual void Quit();
private:
BTextView* m_label_view;
TextEntryAlertTextEntryView* m_text_entry_view;
static filter_result KeyDownFilterStatic(BMessage* message, BHandler** target,
BMessageFilter* filter);
filter_result KeyDownFilter(BMessage* message);
BButton* m_buttons[2];
char m_shortcut[2];
//For the synchronous version (pointers point to data areas owned by thread that called Go)
sem_id m_done_mutex; //Mutex to release when the user hits a button or the window closes
char* m_text_entry_buffer; //Buffer to store the user-entered text when the user hits a button
int32* m_button_pressed; //Place to store the button index that the user hit
int32 m_buffer_size;
//For the asynchronous version
BInvoker *m_invoker; //I own this object and will delete it when done.
};
//******************************************************************************************************
//**** TextEntryAlertBackgroundView
//******************************************************************************************************
class TextEntryAlertBackgroundView : public BView
{
public:
TextEntryAlertBackgroundView(BRect frame, BRect entry_text_rect);
virtual void Draw(BRect update_rect);
virtual void FrameResized(float width, float heigh);
private:
BRect m_entry_text_rect;
BRect m_cached_bounds;
rgb_color m_dark_1_color;
rgb_color m_dark_2_color;
};
//******************************************************************************************************
//**** TextEntryAlertTextEntryView
//******************************************************************************************************
class TextEntryAlertTextEntryView : public BTextView
{
public:
TextEntryAlertTextEntryView(BRect frame, bool multi_line);
void SetTabAllowed(bool allow);
inline bool TabAllowed() {return m_tab_allowed;}
virtual void KeyDown(const char* bytes, int32 num_bytes);
virtual void AttachedToWindow();
private:
bool m_tab_allowed;
bool m_multi_line;
};
#endif //_TEXT_ENTRY_ALERT_H_

View File

@ -0,0 +1,20 @@
#ifndef __HBITMAPVIEW_H__
#define __HBITMAPVIEW_H__
#include <View.h>
#include <Bitmap.h>
class BitmapView :public BView {
public:
BitmapView(BRect rect,
const char* name,
uint32 resizing_mode,
BBitmap *bitmap,
rgb_color bgcolor = ui_color(B_PANEL_BACKGROUND_COLOR));
virtual ~BitmapView();
virtual void Draw(BRect rect);
void SetBitmap(BBitmap *bitmap);
protected:
BBitmap *fBitmap;
};
#endif

View File

@ -0,0 +1,118 @@
//*** LICENSE ***
//ColumnListView, its associated classes and source code, and the other components of Santa's Gift Bag are
//being made publicly available and free to use in freeware and shareware products with a price under $25
//(I believe that shareware should be cheap). For overpriced shareware (hehehe) or commercial products,
//please contact me to negotiate a fee for use. After all, I did work hard on this class and invested a lot
//of time into it. That being said, DON'T WORRY I don't want much. It totally depends on the sort of project
//you're working on and how much you expect to make off it. If someone makes money off my work, I'd like to
//get at least a little something. If any of the components of Santa's Gift Bag are is used in a shareware
//or commercial product, I get a free copy. The source is made available so that you can improve and extend
//it as you need. In general it is best to customize your ColumnListView through inheritance, so that you
//can take advantage of enhancements and bug fixes as they become available. Feel free to distribute the
//ColumnListView source, including modified versions, but keep this documentation and license with it.
#include "CTextView.h"
CTextView::CTextView(BRect a_frame,const char* a_name,int32 a_resize_mode,int32 a_flags)
: BTextView(a_frame, a_name, BRect(4.0,4.0,a_frame.right-a_frame.left-4.0,a_frame.bottom-a_frame.top-4.0),
a_resize_mode,a_flags)
{
ResetTextRect();
m_modified = false;
m_modified_disabled = false;
}
CTextView::~CTextView()
{ }
void CTextView::DetachedFromWindow()
{
//This is sort of what the destructor should do, but... Derived class's destructors get called before
//CTextView's destructor so StoreChange won't get to the derived class's version of it.
if(m_modified)
{
StoreChange();
m_modified = false;
}
}
void CTextView::FrameResized(float a_width, float a_height)
{
ResetTextRect();
BTextView::FrameResized(a_width,a_height);
}
void CTextView::MakeFocus(bool a_focused)
{
BTextView::MakeFocus(a_focused);
if(!a_focused && m_modified)
{
StoreChange();
m_modified = false;
}
}
void CTextView::InsertText(const char *a_text, int32 a_length, int32 a_offset,
const text_run_array *a_runs)
{
BTextView::InsertText(a_text, a_length, a_offset, a_runs);
if(!m_modified_disabled)
Modified();
}
void CTextView::Modified()
{
m_modified = true;
}
void CTextView::SetText(const char *text, int32 length, const text_run_array *runs)
{
m_modified_disabled = true;
BTextView::SetText(text,length,runs);
m_modified_disabled = false;
}
void CTextView::SetText(const char *text, const text_run_array *runs)
{
m_modified_disabled = true;
BTextView::SetText(text,runs);
m_modified_disabled = false;
}
void CTextView::SetText(BFile *file, int32 offset, int32 length, const text_run_array *runs)
{
m_modified_disabled = true;
BTextView::SetText(file,offset,length,runs);
m_modified_disabled = false;
}
void CTextView::StoreChange()
{ }
void CTextView::ResetTextRect()
{
BRect textRect = Bounds();
textRect.left = 4.0;
textRect.top = 4.0;
textRect.right -= 4.0;
textRect.bottom -= 4.0;
SetTextRect(textRect);
}
bool CTextView::HasBeenModified()
{
return m_modified;
}

View File

@ -0,0 +1,58 @@
//*** LICENSE ***
//ColumnListView, its associated classes and source code, and the other components of Santa's Gift Bag are
//being made publicly available and free to use in freeware and shareware products with a price under $25
//(I believe that shareware should be cheap). For overpriced shareware (hehehe) or commercial products,
//please contact me to negotiate a fee for use. After all, I did work hard on this class and invested a lot
//of time into it. That being said, DON'T WORRY I don't want much. It totally depends on the sort of project
//you're working on and how much you expect to make off it. If someone makes money off my work, I'd like to
//get at least a little something. If any of the components of Santa's Gift Bag are is used in a shareware
//or commercial product, I get a free copy. The source is made available so that you can improve and extend
//it as you need. In general it is best to customize your ColumnListView through inheritance, so that you
//can take advantage of enhancements and bug fixes as they become available. Feel free to distribute the
//ColumnListView source, including modified versions, but keep this documentation and license with it.
//*** DESCRIPTION ***
//CTextView class
//Extends BTextView functionality somewhat. Automates setting up the TextRect and updating it during resizing
//so that re-wrapping is live. Also provides a StoreChanges hook function that is called when the CTextView
//loses focus or is removed from the window or destroyed. This is useful if the view is of an object that can
//exist without the view, so that this more persistent object can be kept up-to-date when the text is modified,
//without having to update the other object every time a character is entered.
#ifndef _CTEXT_VIEW_H_
#define _CTEXT_VIEW_H_
#include <TextView.h>
class CTextView : public BTextView
{
public:
CTextView(BRect a_frame,const char* a_name,int32 a_resize_mode,int32 a_flags);
virtual ~CTextView();
//BTextView overrides
virtual void DetachedFromWindow();
virtual void FrameResized(float a_width, float a_height);
virtual void MakeFocus(bool a_focused);
void SetText(const char *text, int32 length, const text_run_array *runs = NULL);
void SetText(const char *text, const text_run_array *runs = NULL);
void SetText(BFile *file, int32 offset, int32 length, const text_run_array *runs = NULL);
virtual void StoreChange();
virtual void Modified();
bool HasBeenModified();
void ResetTextRect();
protected:
virtual void InsertText(const char *a_text, int32 a_length, int32 a_offset, const text_run_array *a_runs);
private:
bool m_modified;
bool m_modified_disabled;
};
#endif

View File

@ -0,0 +1,363 @@
#include "HAboutWindow.h"
#include <Screen.h>
#include <String.h>
#include <Message.h>
#include <Roster.h>
#include <Path.h>
#include <Application.h>
#include <NodeInfo.h>
#include <Resources.h>
#include <Alert.h>
#include <ClassInfo.h>
#include "BitmapView.h"
#include "CTextView.h"
#include "URLView.h"
#define ICON_OFFSET 60
/***********************************************************
* Constructor
***********************************************************/
HAboutWindow::HAboutWindow(const char* app_name,
const char* built_date,
const char* comment,
const char* url,
const char* email)
:BWindow(BRect(-1,-1,-1,-1),
"",
B_FLOATING_WINDOW_LOOK,
B_MODAL_APP_WINDOW_FEEL,
B_NOT_RESIZABLE|B_NOT_ZOOMABLE)
{
BString window_title = "About ";
window_title << app_name;
SetTitle(window_title.String());
this->AddShortcut('W',0,new BMessage(B_QUIT_REQUESTED));
CalcFontHeights();
/********** Get max width **********/
BString build = "Built date: ";
build << built_date;
float max_width = ICON_OFFSET;
BFont font(be_plain_font);
font.SetSize(10);
max_width = font.StringWidth(build.String());
BFont boldfont(be_bold_font);
boldfont.SetSize(14);
if(max_width < boldfont.StringWidth(app_name) )
max_width = boldfont.StringWidth(app_name);
/**************************************/
InitGUI();
LoadIcon();
SetComment(comment);
int32 lines = fComment->CountLines();
float result = 0;
for(register int32 i = 0; i < lines ; i++)
{
float tmp = fComment->LineWidth(i);
if(tmp > result )
result = tmp;
}
if(max_width < result)
max_width = result;
if(max_width < font.StringWidth(url))
max_width = font.StringWidth(url);
float width = max_width + ICON_OFFSET + 20;
int32 isUrlMail = 0;
if(url)
isUrlMail++;
if(email)
isUrlMail++;
float height = fPlainHeight* (2+lines + isUrlMail) + fBoldHeight + 35;
ResizeTo(width,height);
BRect screen_limits = BScreen().Frame();
MoveTo(screen_limits.left+floor((screen_limits.Width()-width)/2),
screen_limits.top+floor((screen_limits.Height()-height)/2));
/************** url view ************/
float start_pos = fComment->Frame().bottom;
BRect url_rect(ICON_OFFSET,start_pos,width,start_pos+fPlainHeight);
HAboutView *bgview = cast_as(FindView("aboutview"),HAboutView);
if(url)
{
BString url_string = url;
int32 index = url_string.FindFirst("http:");
if(index != B_ERROR)
{
url_string = &url[index];
}
//(new BAlert("",url_string.String(),"OK"))->Go();
URLView *urlView = new URLView(url_rect,"url",url,url_string.String());
urlView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
urlView->SetFont(&font);
bgview->AddChild(urlView);
url_rect.OffsetBy(0,fPlainHeight+3);
}
/************** mail view ****************/
if(email)
{
BString mail = email;
int32 index = mail.FindLast(" ");
if(index != B_ERROR)
{
mail = &email[index+1];
}else{
index = mail.FindFirst(":");
if(index != B_ERROR)
{
mail = &email[index+1];
}
}
BString mail_uri = "mailto:";
mail_uri << mail;
URLView *mailView = new URLView(url_rect,"email",email,mail_uri.String());
mailView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
mailView->SetFont(&font);
bgview->AddChild(mailView);
}
SetAppName(app_name);
SetBuiltDate(built_date);
LoadVersion();
}
/***********************************************************
* InitGUI
***********************************************************/
void
HAboutWindow::InitGUI()
{
BRect bitmapRect(0,0,31,31);
bitmapRect.OffsetBy(20,5);
fAboutView = new HAboutView(Bounds());
BRect rect(ICON_OFFSET,15,ICON_OFFSET,15+fBoldHeight);
fAppName = new BStringView(rect,"name","",B_FOLLOW_NONE);
fAboutView->AddChild(fAppName);
rect.OffsetBy(0,fBoldHeight);
rect.bottom = rect.top + fPlainHeight;
fVersion = new BStringView(rect,"ver","",B_FOLLOW_NONE);
fAboutView->AddChild(fVersion);
rect.OffsetBy(0,fPlainHeight);
fBuiltDate = new BStringView(rect,"build","",B_FOLLOW_NONE);
fAboutView->AddChild(fBuiltDate);
rect.OffsetBy(0,fPlainHeight);
fComment = new CTextView(rect,"comment",B_FOLLOW_NONE,B_WILL_DRAW);
fAboutView->AddChild(fComment);
AddChild(fAboutView);
}
/***********************************************************
* Load icon from application resource
***********************************************************/
void
HAboutWindow::LoadIcon()
{
//Extract title bitmap
BBitmap *icon = new BBitmap(BRect(0,0,31,31),B_COLOR_8_BIT);
app_info info;
BPath path;
be_app->GetAppInfo(&info);
BNodeInfo::GetTrackerIcon(&info.ref,icon,B_LARGE_ICON);
fAboutView->SetBitmap(icon);
}
/***********************************************************
* Load versino from application resource.
***********************************************************/
void
HAboutWindow::LoadVersion()
{
//Extract version resource
BString version = "";
BResources* app_version_resource = BApplication::AppResources();
if(app_version_resource)
{
size_t resource_size;
const char* app_version_data = (const char*)app_version_resource->LoadResource('APPV',
"BEOS:APP_VERSION",&resource_size);
if(app_version_data && resource_size > 20)
{
const char* status[] = {"Development","Alpha","Beta","Gamma",
"Golden master","Final"};
app_version_info *info = (app_version_info*)app_version_data;
uint32 v1 = info->v1;
uint32 v2 = info->v2;
uint32 v3 = info->v3;
version << v1 << "." << v2 << "." << v3;
if(info->status != 5)
version << " " <<status[info->status];
if(info->rel != 0)
version << " Release " << info->rel;
SetVersion(version.String());
}
}
}
/***********************************************************
* Set applicaiton name
***********************************************************/
void
HAboutWindow::SetAppName(const char* name)
{
BFont font(be_bold_font);
font.SetSize(14);
BRect rect(0,0,font.StringWidth(name),fBoldHeight);
BRect old_rect = fAppName->Bounds();
fAppName->SetFont(&font);
fAppName->ResizeBy(rect.Width()-old_rect.Width(),rect.Height()-old_rect.Height());
fAppName->SetText(name);
}
/***********************************************************
* Set application built date.
***********************************************************/
void
HAboutWindow::SetBuiltDate(const char* date)
{
BString title="Built date: ";
title << date;
BFont font(be_plain_font);
font.SetSize(10);
BRect rect(0,0,font.StringWidth(title.String()),fPlainHeight);
BRect old_rect = fBuiltDate->Bounds();
fBuiltDate->SetFont(&font);
fBuiltDate->ResizeBy(rect.Width()-old_rect.Width(),rect.Height()-old_rect.Height());
fBuiltDate->SetText(title.String());
}
/***********************************************************
* Set comments
***********************************************************/
void
HAboutWindow::SetComment(const char* text)
{
BFont font(be_plain_font);
font.SetSize(10);
fComment->SetFont(&font);
fComment->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
fComment->MakeEditable(false);
fComment->MakeFocus(false);
fComment->MakeSelectable(false);
fComment->SetWordWrap(false);
fComment->SetText(text);
int32 lines = fComment->CountLines();
BRect rect(0,0,font.StringWidth(text)+4,fPlainHeight*lines+4);
BRect old_rect = fComment->Bounds();
fComment->ResizeBy(rect.Width()-old_rect.Width(),rect.Height()-old_rect.Height());
}
/***********************************************************
* Set version info
***********************************************************/
void
HAboutWindow::SetVersion(const char* version)
{
BString title="Version ";
title << version;
BFont font(be_plain_font);
font.SetSize(10);
BRect rect(0,0,font.StringWidth(title.String()),fPlainHeight);
BRect old_rect = fVersion->Bounds();
fVersion->SetFont(&font);
fVersion->ResizeBy(rect.Width()-old_rect.Width(),rect.Height()-old_rect.Height());
fVersion->SetText(title.String());
}
/***********************************************************
* Calc font height
***********************************************************/
void
HAboutWindow::CalcFontHeights()
{
BFont plainfont(be_plain_font);
plainfont.SetSize(10);
font_height FontAttributes;
plainfont.GetHeight(&FontAttributes);
fPlainHeight = ceil(FontAttributes.ascent) + ceil(FontAttributes.descent);
BFont boldfont(be_bold_font);
boldfont.SetSize(14);
boldfont.GetHeight(&FontAttributes);
fBoldHeight = ceil(FontAttributes.ascent) + ceil(FontAttributes.descent);
}
/***************************************************************************
* HAboutView
****************************************************************************/
const float kBorderWidth = 32.0f;
const rgb_color kDarkBorderColor = {184, 184, 184, 255};
/***********************************************************
* Constructor
***********************************************************/
HAboutView::HAboutView(BRect rect,BBitmap *icon)
:BView(rect,"aboutview",B_FOLLOW_ALL,B_WILL_DRAW)
{
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
fIcon = icon;
}
/***********************************************************
* Destructor
***********************************************************/
HAboutView::~HAboutView()
{
delete fIcon;
}
/***********************************************************
* Draw
***********************************************************/
void
HAboutView::Draw(BRect updateRect)
{
BRect drawBounds(Bounds());
drawBounds.right = kBorderWidth;
SetHighColor(kDarkBorderColor);
FillRect(drawBounds);
if(fIcon != NULL)
{
BRect bitmapRect(0,0,31,31);
bitmapRect.OffsetBy(20,5);
drawing_mode mode = DrawingMode();
SetDrawingMode(B_OP_ALPHA);
DrawBitmap(fIcon,bitmapRect);
SetDrawingMode(mode);
}
}
/***********************************************************
* Set view bitmap
***********************************************************/
void
HAboutView::SetBitmap(BBitmap *bitmap)
{
delete fIcon;
fIcon = bitmap;
}

View File

@ -0,0 +1,66 @@
#ifndef __ABOUTWINDOW_H__
#define __ABOUTWINDOW_H__
#include <Window.h>
#include <View.h>
#include <Bitmap.h>
#include <StringView.h>
class CTextView;
class BitmapView;
typedef struct {
uint32 v1;
uint32 v2;
uint32 v3;
uint32 status;
uint32 rel;
} app_version_info;
/**********************************************************************
* HAboutView
**********************************************************************/
class HAboutView : public BView
{
public:
HAboutView(BRect rect,BBitmap *icon = NULL);
virtual ~HAboutView();
void SetBitmap(BBitmap* bitmap);
protected:
virtual void Draw(BRect);
private:
BBitmap* fIcon;
};
/**********************************************************************
* HAboutWindow
**********************************************************************/
class HAboutWindow :public BWindow {
public:
HAboutWindow(const char* app_name,
const char* built_data,
const char* comment,
const char* url = NULL,
const char* mail = NULL);
protected:
void InitGUI();
void SetAppName(const char* app_name);
void SetBuiltDate(const char* text);
void SetVersion(const char* version);
void SetComment(const char* text);
void LoadIcon();
void LoadVersion();
void CalcFontHeights();
private:
BStringView* fAppName;
BStringView* fVersion;
BStringView* fBuiltDate;
HAboutView* fAboutView;
CTextView* fComment;
float fBoldHeight;
float fPlainHeight;
};
#endif

View File

@ -0,0 +1,107 @@
#include "IconMenuItem.h"
#include <Bitmap.h>
/***********************************************************
* Constructor.
***********************************************************/
IconMenuItem::IconMenuItem(const char* label,BMessage *message,char shortcut,uint32 modifiers,BBitmap *bitmap,bool copy,bool free)
:BMenuItem(label,message,shortcut,modifiers)
,fBitmap(NULL)
,fCopy(copy)
{
SetBitmap(bitmap,free);
fHeightDelta = 0;
}
/***********************************************************
* Constructor.
***********************************************************/
IconMenuItem::IconMenuItem(BMenu *submenu,BMessage *message,char shortcut,uint32 modifiers,BBitmap *bitmap,bool copy,bool free)
:BMenuItem(submenu,message)
,fBitmap(NULL)
,fCopy(copy)
{
SetBitmap(bitmap,free);
fHeightDelta = 0;
SetShortcut(shortcut,modifiers);
}
/***********************************************************
* Destructor.
***********************************************************/
IconMenuItem::~IconMenuItem()
{
if(fCopy)
delete fBitmap;
}
/***********************************************************
* Draw menu icon.
***********************************************************/
void
IconMenuItem::DrawContent()
{
if(fBitmap != NULL)
{
BPoint drawPoint(ContentLocation());
// center text and icon.
drawing_mode mode = Menu()->DrawingMode();
Menu()->SetDrawingMode(B_OP_ALPHA);
//Menu()->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
Menu()->SetLowColor( Menu()->ViewColor());
//Menu()->SetHighColor(B_TRANSPARENT_32_BIT);
if( !IsEnabled() )
{
Menu()->SetDrawingMode(B_OP_BLEND);
Menu()->DrawBitmap(fBitmap,drawPoint);
Menu()->SetDrawingMode(B_OP_OVER);
}else
Menu()->DrawBitmap(fBitmap,drawPoint);
// offset to title point.
drawPoint.y += ceil( fHeightDelta/2 );
drawPoint.x += 20;
// Move draw point.
Menu()->MovePenTo(drawPoint);
Menu()->SetDrawingMode(mode);
}
BMenuItem::DrawContent();
}
/***********************************************************
* Extruct content width
***********************************************************/
void
IconMenuItem::GetContentSize(float *width, float *height)
{
BMenuItem::GetContentSize(width,height);
(*width) += 20;
fHeightDelta = 16 - (*height);
if( (*height) < 16)
(*height) = 16;
}
/***********************************************************
* Set the other bitmap.
***********************************************************/
void
IconMenuItem::SetBitmap(BBitmap *bitmap,bool free)
{
if(fCopy)
delete fBitmap;
if(!fCopy)
fBitmap = bitmap;
else{
if(bitmap)
{
fBitmap = new BBitmap(bitmap);
if(free) delete bitmap;
}else
fBitmap = NULL;
}
}

View File

@ -0,0 +1,40 @@
/*************************************************************
* IconMenu
*
* Toolbar is a usefull UI component.
*
* @author Atsushi Takamatsu (tak_atsu@tau.bekkoame.ne.jp)
**************************************************************/
#ifndef __ICONMENUITEM_H__
#define __ICONMENUITEM_H__
#include <MenuItem.h>
class IconMenuItem :public BMenuItem {
public:
IconMenuItem(const char* label
,BMessage *message
,char shortcut = 0
,uint32 modifiers = 0
,BBitmap *bitmap = NULL
,bool copyBitmap = true
,bool deleteInputBitmap = true);
IconMenuItem(BMenu *submenu
,BMessage *message
,char shortcut = 0
,uint32 modifiers = 0
,BBitmap *bitmap = NULL
,bool copyBitmap = true
,bool deleteInputBitmap = true);
virtual ~IconMenuItem();
void SetBitmap(BBitmap *bitmap,bool freePointer=false);
protected:
virtual void DrawContent();
virtual void GetContentSize(float *width ,float *height);
private:
BBitmap *fBitmap;
bool fCopy;
float fHeightDelta;
};
#endif

View File

@ -0,0 +1,59 @@
#include "RectUtils.h"
#include <Screen.h>
#include <Roster.h>
#include <Application.h>
#include <fs_attr.h>
/*
* Constructor
*/
BRect
RectUtils::CenterRect(float width,float height)
{
BRect frame = BScreen().Frame();
BRect rect;
rect.left = frame.Width()/2.0 - width/2.0;
rect.right = rect.left + width;
rect.top = frame.Height()/2.0 - height/2.0;
rect.bottom = rect.top + height;
return rect;
}
/*
* Load rect data from the application file attribute.
*/
bool
RectUtils::LoadRectFromApp(const char* name,BRect *rect)
{
app_info info;
be_app->GetAppInfo(&info);
BEntry entry(&info.ref);
bool rc = false;
BFile appfile(&entry,B_READ_ONLY);
attr_info ainfo;
status_t err = appfile.GetAttrInfo(name,&ainfo);
if(err == B_OK)
{
appfile.ReadAttr(name,B_RECT_TYPE,0,rect,sizeof(BRect));
rc = true;
} else {
rc = false;
}
return rc;
}
/*
* Save rect data to the application file attribute.
*/
void
RectUtils::SaveRectToApp(const char* name,BRect rect)
{
app_info info;
be_app->GetAppInfo(&info);
BEntry entry(&info.ref);
BFile appfile(&entry,B_WRITE_ONLY);
appfile.WriteAttr(name,B_RECT_TYPE,0,&rect,sizeof(BRect));
}

View File

@ -0,0 +1,14 @@
#ifndef __RECTUTILS_H__
#define __RECTUTILS_H__
#include <Rect.h>
class RectUtils {
public:
RectUtils(){};
~RectUtils(){};
BRect CenterRect(float width,float height);
void SaveRectToApp(const char* name,BRect rect);
bool LoadRectFromApp(const char* name,BRect *rect);
};
#endif

View File

@ -0,0 +1,279 @@
#include "ResourceUtils.h"
#include <Application.h>
#include <stdio.h>
#include <String.h>
ResourceUtils::ResourceUtils(BResources *rsrc)
:BLocker()
,fResource(NULL)
{
if(rsrc == NULL)
fResource = BApplication::AppResources();
else
fResource = rsrc;
}
ResourceUtils::ResourceUtils(const char *path)
:BLocker()
,fResource(NULL)
{
if(path == NULL)
fResource = BApplication::AppResources();
else{
BFile file(path, B_READ_WRITE);
BResources *res = new BResources();
status_t err;
if((err = res->SetTo(&file)) != B_OK)
fResource = BApplication::AppResources();
else
fResource = res;
}
}
ResourceUtils::~ResourceUtils()
{
//if(fResource != BApplication::AppResources())
// delete fResource;
}
/*
* Load icon by id.
*/
status_t
ResourceUtils::GetIconResource(int32 id, icon_size size, BBitmap *dest)
{
if (size != B_LARGE_ICON && size != B_MINI_ICON )
return B_ERROR;
size_t len = 0;
this->Lock();
const void *data = fResource->LoadResource(size == B_LARGE_ICON ? 'ICON' : 'MICN',
id, &len);
this->Unlock();
if (data == 0 || len != (size_t)(size == B_LARGE_ICON ? 1024 : 256)) {
return B_ERROR;
}
dest->SetBits(data, (int32)len, 0, B_COLOR_8_BIT);
return B_OK;
}
/*
* Load icon by name.
*/
status_t
ResourceUtils::GetIconResource(const char* name, icon_size size, BBitmap *dest)
{
if (size != B_LARGE_ICON && size != B_MINI_ICON )
return B_ERROR;
size_t len = 0;
this->Lock();
const void *data = fResource->LoadResource(size == B_LARGE_ICON ? 'ICON' : 'MICN',
name, &len);
this->Unlock();
if (data == 0 || len != (size_t)(size == B_LARGE_ICON ? 1024 : 256)) {
return B_ERROR;
}
dest->SetBits(data, (int32)len, 0, B_COLOR_8_BIT);
return B_OK;
}
/*
* Load bitmap by id.
*/
status_t
ResourceUtils::GetBitmapResource(type_code type, int32 id, BBitmap **out)
{
*out = NULL;
size_t len = 0;
this->Lock();
const void *data = fResource->LoadResource(type, id, &len);
this->Unlock();
if (data == NULL) {
return B_ERROR;
}
BMemoryIO stream(data, len);
// Try to read as an archived bitmap.
stream.Seek(0, SEEK_SET);
BMessage archive;
status_t err = archive.Unflatten(&stream);
if (err != B_OK)
return err;
*out = new BBitmap(&archive);
if (!*out)
return B_ERROR;
err = (*out)->InitCheck();
if (err != B_OK) {
delete *out;
*out = NULL;
}
return err;
}
/*
* Load bitmap by name.
*/
status_t
ResourceUtils::GetBitmapResource(type_code type, const char* name, BBitmap **out)
{
*out = NULL;
size_t len = 0;
this->Lock();
const void *data = fResource->LoadResource(type, name, &len);
this->Unlock();
if (data == NULL) {
return B_ERROR;
}
BMemoryIO stream(data, len);
// Try to read as an archived bitmap.
stream.Seek(0, SEEK_SET);
BMessage archive;
status_t err = archive.Unflatten(&stream);
if (err != B_OK)
return err;
*out = new BBitmap(&archive);
if (!*out)
return B_ERROR;
err = (*out)->InitCheck();
if (err != B_OK) {
delete *out;
*out = NULL;
}
return err;
}
/*
* Load bitmap by name.
*/
BBitmap*
ResourceUtils::GetBitmapResource(type_code type, const char* name)
{
size_t len = 0;
this->Lock();
const void *data = fResource->LoadResource(type, name, &len);
this->Unlock();
if (data == NULL) {
return NULL;
}
BMemoryIO stream(data, len);
// Try to read as an archived bitmap.
stream.Seek(0, SEEK_SET);
BMessage archive;
status_t err = archive.Unflatten(&stream);
if (err != B_OK)
return NULL;
BBitmap* out = new BBitmap(&archive);
if (!out)
return NULL;
err = (out)->InitCheck();
if (err != B_OK) {
delete out;
out = NULL;
}
return out;
}
/*
* Get string from resources.
*/
const char*
ResourceUtils::GetString(const char* name)
{
size_t len = 0;
this->Lock();
const void* data;
if(fResource->HasResource('CSTR',name))
{
data = fResource->LoadResource('CSTR', name, &len);
}else
data = name;
this->Unlock();
return (const char*)data;
}
/*
* Get string from resources.
*/
status_t
ResourceUtils::GetString(const char* name,BString &outStr)
{
size_t len = 0;
this->Lock();
const void* data;
status_t err = B_OK;
if(fResource->HasResource('CSTR',name))
{
data = fResource->LoadResource('CSTR', name, &len);
outStr = (const char*)data;
}else{
err = B_ERROR;
outStr = name;
}
this->Unlock();
return err;
}
/*
* Set resources.
*/
void
ResourceUtils::SetResource(BResources *rsrc)
{
fResource = rsrc;
}
/*
* Set resources.
*/
void
ResourceUtils::SetResource(const char* path)
{
BFile file(path, B_READ_WRITE);
BResources *res = new BResources();
status_t err;
if((err = res->SetTo(&file)) != B_OK)
fResource = BApplication::AppResources();
else
fResource = res;
}
/*
* Free fResource. But You must not free when load app resource.
*/
void
ResourceUtils::FreeResource()
{
delete fResource;
}
/*
* Preload resources.
*/
void
ResourceUtils::Preload(type_code type)
{
fResource->PreloadResourceType(type);
}

View File

@ -0,0 +1,37 @@
#ifndef __RESOURCEUTILS_H__
#define __RESOURCEUTILS_H__
#include <Resources.h>
#include <Bitmap.h>
#include <SupportDefs.h>
#include <Mime.h>
#include <Errors.h>
#include <TypeConstants.h>
#include <Application.h>
#include <Locker.h>
class ResourceUtils :public BLocker {
public:
ResourceUtils(BResources *rsrc = NULL);
ResourceUtils(const char* path);
~ResourceUtils();
void SetResource(BResources *rsrc);
void SetResource(const char* path);
void Preload(type_code type);
void FreeResource();
status_t GetIconResource(int32 id, icon_size size, BBitmap *dest);
status_t GetIconResource(const char* name, icon_size size, BBitmap *dest);
status_t GetBitmapResource(type_code type, int32 id, BBitmap **out);
status_t GetBitmapResource(type_code type, const char* name, BBitmap **out);
BBitmap* GetBitmapResource(type_code type, const char* name);
const char* GetString(const char* name);
status_t GetString(const char* name,BString &outStr);
BResources* Resources() {return fResource;}
protected:
BResources *fResource;
};
#endif

View File

@ -0,0 +1,913 @@
/* URLView 2.0
written by William Kakes of Tall Hill Software.
This class provides an underlined and clickable BStringView
that will launch the web browser, e-mail program, or FTP client
when clicked on. Other features include hover-highlighting,
right-click menus, and drag-and-drop support.
You are free to use URLView in your own programs (both open-source
and closed-source) free of charge, but a mention in your read me
file or your program's about box would be appreciated. See
http://www.tallhill.com for current contact information.
URLView is provided as-is, with no warranties of any kind. If
you use it, you are on your own.
*/
#include "URLView.h"
#include <Alert.h>
#include <Application.h>
#include <Bitmap.h>
#include <fs_attr.h>
#include <MenuItem.h>
#include <NodeInfo.h>
#include <Path.h>
#include <Roster.h>
#include <unistd.h>
URLView::URLView( BRect frame, const char *name, const char *label,
const char *url, uint32 resizingMode, uint32 flags )
: BStringView( frame, name, label, resizingMode, flags ) {
// Set the instance variables.
this->url = new BString( url );
// Set the default values for the other definable instance variables.
this->color = blue;
this->clickColor = red;
this->hoverColor = dark_blue;
this->hoverEnabled = true;
this->draggable = true;
this->iconSize = 16;
this->underlineThickness = 1;
// Create the cursor to use when over the link.
this->linkCursor = new BCursor( url_cursor );
// The link is not currently selected.
selected = false;
// The URL is currently not hover-colored.
hovering = false;
// The user has not dragged out of the view.
draggedOut = false;
// The user has not yet opened the popup menu.
inPopup = false;
// Initialize the attributes list (there are 14 standard
// Person attributes).
attributes = new BList( 14 );
}
URLView::~URLView() {
delete url;
delete linkCursor;
// Delete all the attributes.
KeyPair *item;
for( int i = 0; (item = (KeyPair *) attributes->ItemAt(i)); i++ ) {
delete item->key;
delete item->value;
delete item;
}
delete attributes;
}
void URLView::AttachedToWindow() {
// When the view is first attached, we want to draw the link
// in the normal color. Also, we want to set our background color
// to meet that of our parent.
SetHighColor( color );
if( Parent() != NULL ) {
SetLowColor( Parent()->ViewColor() );
SetViewColor( Parent()->ViewColor() );
}
}
void URLView::Draw( BRect updateRect ) {
BRect rect = Frame();
rect.OffsetTo( B_ORIGIN );
// We want 'g's, etc. to go below the underline. When the BeOS can
// do underlining of any font, this code can be removed.
font_height height;
GetFontHeight( &height );
float descent = height.descent / 2;
// Draw the underline in the requested thickness.
FillRect( BRect( (float) rect.left,
(float) (rect.bottom - descent - underlineThickness + 1),
(float) StringWidth( Text() ),
(float) rect.bottom - descent ) );
// Note: DrawString() draws the text at one pixel above the pen's
// current y coordinate.
MovePenTo( BPoint( rect.left, rect.bottom - descent -
(float) underlineThickness ) );
DrawString( Text() );
}
void URLView::MessageReceived( BMessage *message ) {
// Is this a message from Tracker in response to our drag-and-drop?
if( message->what == 'DDCP' ) {
// Tracker will send back the name and path of the created file.
// We need to read this information.
entry_ref ref;
message->FindRef( "directory", &ref );
BEntry entry( &ref );
BPath path( &entry );
BString *fullName = new BString( path.Path() );
fullName->Append( "/" );
fullName->Append( message->FindString( "name" ) );
BString *title = new BString( Text() );
// Set the new file as a bookmark or as a person as appropriate.
if( IsEmailLink() ) {
CreatePerson( fullName, title );
}
else CreateBookmark( fullName, title );
delete fullName;
delete title;
}
}
void URLView::MouseDown( BPoint point ) {
// See which mouse buttons were clicked.
int32 buttons = Window()->CurrentMessage()->FindInt32( "buttons" );
// We want to highlight the text if the user clicks on
// the URL. We want to be sure to only register a click
// if the user clicks on the link text itself and not just
// anywhere in the view.
if( GetTextRect().Contains( point ) ) {
SetHighColor( clickColor );
Redraw();
// Set the link as selected and track the mouse.
selected = true;
SetMouseEventMask( B_POINTER_EVENTS );
// Remember where the user clicked.
dragOffset = point;
// Pop up the context menu?
if( buttons == B_SECONDARY_MOUSE_BUTTON ) inPopup = true;
}
}
void URLView::MouseMoved( BPoint point, uint32 transit,
const BMessage *message ) {
// Make sure the window is the active one.
if( !Window()->IsActive() ) return;
// See which mouse buttons were clicked.
int32 buttons = Window()->CurrentMessage()->FindInt32( "buttons" );
// Is the user currently dragging the link? (i.e. is a mouse button
// currently down?)
bool alreadyDragging = (buttons != 0);
switch( transit ) {
case( B_ENTERED_VIEW ):
// Should we set the cursor to the link cursor?
if( GetTextRect().Contains( point ) && !draggedOut ) {
if( !alreadyDragging ) be_app->SetCursor( linkCursor );
// Did the user leave and re-enter the view while
// holding down the mouse button? If so, highlight
// the link.
if( selected ) {
SetHighColor( clickColor );
Redraw();
}
// Should we hover-highlight the link?
else if( hoverEnabled && !alreadyDragging ) {
if( buttons == 0 ) {
SetHighColor( hoverColor );
Redraw();
hovering = true;
}
}
}
break;
case( B_EXITED_VIEW ):
// We want to restore the link to it normal color and the
// mouse cursor to the normal hand. However, we should only
// set the color and re-draw if it is needed.
if( selected && !draggedOut ) {
be_app->SetCursor( B_HAND_CURSOR );
SetHighColor( color );
Redraw();
// Is the user drag-and-dropping a bookmark or person?
if( draggable ) {
draggedOut = true;
if( IsEmailLink() ) DoPersonDrag();
else DoBookmarkDrag();
}
}
// Is the link currently hover-highlighted? If so, restore
// the normal color now.
else if( hovering && !alreadyDragging ) {
be_app->SetCursor( B_HAND_CURSOR );
SetHighColor( color );
Redraw();
hovering = false;
}
// Change the cursor back to the hand.
else {
be_app->SetCursor( B_HAND_CURSOR );
}
break;
case( B_INSIDE_VIEW ):
// The user could either be moving out of the view or
// back into it here, so we must handle both cases.
// In the first case, the cursor is now over the link.
if( GetTextRect().Contains( point ) && !draggedOut ) {
// We only want to change the cursor if not dragging.
if( !alreadyDragging ) be_app->SetCursor( linkCursor );
if( selected ) {
if( draggable ) {
// If the user moves the mouse more than ten
// pixels, begin the drag.
if( (point.x - dragOffset.x) > 10 ||
(dragOffset.x - point.x) > 10 ||
(point.y - dragOffset.y) > 10 ||
(dragOffset.y - point.y) > 10 ) {
draggedOut = true;
// Draw the appropriate drag object, etc.
if( IsEmailLink() ) DoPersonDrag();
else DoBookmarkDrag();
SetHighColor( color );
Redraw();
}
}
else {
// Since the link is not draggable, highlight it
// as long as the user holds the button down and
// has the mouse cursor over it (like a standard
// button).
SetHighColor( clickColor );
Redraw();
}
}
// The link isn't currently selected? If hover-highlighting
// is enabled, highlight the link.
else if( hoverEnabled && !alreadyDragging ) {
SetHighColor( hoverColor );
Redraw();
hovering = true;
}
}
// In this case, the mouse cursor is not over the link, so we
// need to restore the original link color, etc.
else if( !draggedOut ) {
be_app->SetCursor( B_HAND_CURSOR );
if( selected ) {
SetHighColor( color );
Redraw();
// Is the user dragging the link?
if( draggable ) {
draggedOut = true;
if( IsEmailLink() ) DoPersonDrag();
else DoBookmarkDrag();
}
}
// Is the mouse cursor hovering over the link?
else if( hovering ) {
SetHighColor( color );
Redraw();
hovering = false;
}
}
break;
}
}
void URLView::MouseUp( BPoint point ) {
// Do we want to show the right-click menu?
if( inPopup && GetTextRect().Contains( point ) ) {
BPopUpMenu *popup = CreatePopupMenu();
// Work around a current bug in Be's popup menus.
point.y = point.y - 6;
// Display the popup menu.
BMenuItem *selected = popup->Go( ConvertToScreen( point ) , false, true );
// Did the user select an item?
if( selected ) {
BString label( selected->Label() );
// Did the user select the first item? If so, launch the URL.
if( label.FindFirst( "Open" ) != B_ERROR ||
label.FindFirst( "Send" ) != B_ERROR ||
label.FindFirst( "Connect" ) != B_ERROR ) {
LaunchURL();
}
// Did the user select the second item?
else if( label.FindFirst( "Copy" ) != B_ERROR ) {
CopyToClipboard();
}
}
// If not, restore the normal link color.
else {
SetHighColor( color );
Redraw();
}
}
// If the link was clicked on (and not dragged), run the program
// that should handle the URL.
if( selected && GetTextRect().Contains( point ) &&
!draggedOut && !inPopup ) {
LaunchURL();
}
selected = false;
draggedOut = false;
inPopup = false;
// Should we restore the hovering-highlighted color or the original
// link color?
if( GetTextRect().Contains( point ) && !draggedOut &&
!inPopup && hoverEnabled ) {
SetHighColor( hoverColor );
}
else if( !hovering ) SetHighColor( color );
Redraw();
}
void URLView::AddAttribute( const char *name, const char *value ) {
// Add an attribute (name and corresponding value) to the object
// that will be dragged out (i.e. to fill in Person fields, etc.)
KeyPair *newPair = new KeyPair;
newPair->key = new BString( name );
newPair->value = new BString( value );
attributes->AddItem( newPair );
}
void URLView::SetColor( rgb_color color ) {
// Set the normal link color.
this->color = color;
}
void URLView::SetClickColor( rgb_color color ) {
// Set the link color used when the link is clicked.
clickColor = color;
}
void URLView::SetDraggable( bool draggable ) {
// Set whether or not this link is draggable.
this->draggable = draggable;
}
void URLView::SetHoverColor( rgb_color color ) {
// Set the link color used when the mouse cursor is over it.
hoverColor = color;
}
void URLView::SetHoverEnabled( bool hover ) {
// Set whether or not to hover-highlight the link.
hoverEnabled = hover;
}
void URLView::SetIconSize( icon_size iconSize ) {
// Set the size of the icon that will be shown when the link is dragged.
if( iconSize == B_MINI_ICON ) this->iconSize = 16;
else this->iconSize = 32;
}
void URLView::SetUnderlineThickness( int thickness ) {
// Set the thickness of the underline in pixels.
underlineThickness = thickness;
}
void URLView::CopyToClipboard() {
// Copy the URL to the clipboard.
BClipboard clipboard( "system" );
BMessage *clip = (BMessage *) NULL;
// Get the important URL (i.e. trim off "mailto:", etc.).
BString newclip = GetImportantURL();
// Be sure to lock the clipboard first.
if( clipboard.Lock() ) {
clipboard.Clear();
if( (clip = clipboard.Data()) ) {
clip->AddData( "text/plain", B_MIME_TYPE, newclip.String(),
newclip.Length() + 1 );
clipboard.Commit();
}
clipboard.Unlock();
}
}
void URLView::CreateBookmark( const BString *fullName, const BString *title ) {
// Read the file defined by the path and the title.
BFile *file = new BFile( fullName->String(), B_WRITE_ONLY );
// Set the file's MIME type to be a bookmark.
BNodeInfo *nodeInfo = new BNodeInfo( file );
nodeInfo->SetType( "application/x-vnd.Be-bookmark" );
delete nodeInfo;
delete file;
// Add all the attributes, both those inherrent to bookmarks and any
// the developer may have defined using AddAttribute().
DIR *d;
int fd;
d = fs_open_attr_dir( fullName->String() );
if( d ) {
fd = open( fullName->String(), O_WRONLY );
fs_write_attr( fd, "META:title", B_STRING_TYPE, 0, title->String(), title->Length() + 1 );
fs_write_attr( fd, "META:url", B_STRING_TYPE, 0, url->String(), url->Length() + 1 );
WriteAttributes( fd );
close( fd );
fs_close_attr_dir( d );
}
}
void URLView::CreatePerson( const BString *fullName, const BString *title ) {
// Read the file defined by the path and the title.
BFile *file = new BFile( fullName->String(), B_WRITE_ONLY );
// Set the file's MIME type to be a person.
BNodeInfo *nodeInfo = new BNodeInfo( file );
nodeInfo->SetType( "application/x-person" );
delete nodeInfo;
delete file;
// Add all the attributes, both those inherrent to person files and any
// the developer may have defined using AddAttribute().
DIR *d;
int fd;
d = fs_open_attr_dir( fullName->String() );
if( d ) {
fd = open( fullName->String(), O_WRONLY );
fs_write_attr( fd, "META:name", B_STRING_TYPE, 0, title->String(), title->Length() + 1 );
BString email = GetImportantURL();
fs_write_attr( fd, "META:email", B_STRING_TYPE, 0, email.String(), email.Length() + 1 );
WriteAttributes( fd );
close( fd );
fs_close_attr_dir( d );
}
}
BPopUpMenu * URLView::CreatePopupMenu() {
// Create the right-click popup menu.
BPopUpMenu *returnMe = new BPopUpMenu( "URLView Popup", false, false );
returnMe->SetAsyncAutoDestruct( true );
entry_ref app;
// Set the text of the first item according to the link type.
if( IsEmailLink() ) {
// Find the name of the default e-mail client.
if( be_roster->FindApp( "text/x-email", &app ) == B_OK ) {
BEntry entry( &app );
BString openLabel( "Send e-mail to this address using " );
char name[B_FILE_NAME_LENGTH];
entry.GetName( name );
openLabel.Append( name );
returnMe->AddItem( new BMenuItem( openLabel.String(), NULL ) );
}
}
else if( IsFTPLink() ) {
// Find the name of the default FTP client.
if( be_roster->FindApp( "application/x-vnd.Be.URL.ftp", &app ) == B_OK ) {
BEntry entry( &app );
BString openLabel( "Connect to this server using " );
char name[B_FILE_NAME_LENGTH];
entry.GetName( name );
openLabel.Append( name );
returnMe->AddItem( new BMenuItem( openLabel.String(), NULL ) );
}
}
else {
// Find the name of the default HTML handler (browser).
if( be_roster->FindApp( "text/html", &app ) == B_OK ) {
BEntry entry( &app );
BString openLabel( "Open this link using " );
char name[B_FILE_NAME_LENGTH];
entry.GetName( name );
openLabel.Append( name );
returnMe->AddItem( new BMenuItem( openLabel.String(), NULL ) );
}
}
returnMe->AddItem( new BMenuItem( "Copy this link to the clipboard", NULL ) );
return returnMe;
}
void URLView::DoBookmarkDrag() {
// Handle all of the bookmark dragging. This includes setting up
// the drag message and drawing the dragged bitmap.
// Set up the drag message to support both BTextView dragging (using
// the URL) and file dropping (to Tracker).
BMessage *dragMessage = new BMessage( B_MIME_DATA );
dragMessage->AddInt32( "be:actions", B_COPY_TARGET );
dragMessage->AddString( "be:types", "application/octet-stream" );
dragMessage->AddString( "be:filetypes", "application/x-vnd.Be-bookmark" );
dragMessage->AddString( "be:type_descriptions", "bookmark" );
dragMessage->AddString( "be:clip_name", Text() );
dragMessage->AddString( "be:url", url->String() );
// This allows the user to drag the URL into a standard BTextView.
BString link = GetImportantURL();
dragMessage->AddData( "text/plain", B_MIME_DATA, link.String(),
link.Length() + 1 );
// Query for the system's icon for bookmarks.
BBitmap *bookmarkIcon = new BBitmap( BRect( 0, 0, iconSize - 1,
iconSize - 1 ), B_CMAP8 );
BMimeType mime( "application/x-vnd.Be-bookmark" );
if( iconSize == 16 ) mime.GetIcon( bookmarkIcon, B_MINI_ICON );
else mime.GetIcon( bookmarkIcon, B_LARGE_ICON );
// Find the size of the bitmap to drag. If the text is bigger than the
// icon, use that size. Otherwise, use the icon's. Center the icon
// vertically in the bitmap.
BRect urlRect = GetURLRect();
BRect rect = urlRect;
rect.right += iconSize + 4;
if( (rect.bottom - rect.top) < iconSize ) {
int adjustment = (int) ((iconSize - (rect.bottom - rect.top)) / 2) + 1;
rect.top -= adjustment;
rect.bottom += adjustment;
}
// Make sure the rectangle starts at 0,0.
rect.bottom += 0 - rect.top;
rect.top = 0;
// Create the bitmap to draw the dragged image in.
BBitmap *dragBitmap = new BBitmap( rect, B_RGBA32, true );
BView *dragView = new BView( rect, "Drag View", 0, 0 );
dragBitmap->Lock();
dragBitmap->AddChild( dragView );
BRect frameRect = dragView->Frame();
// Make the background of the dragged image transparent.
dragView->SetHighColor( B_TRANSPARENT_COLOR );
dragView->FillRect( frameRect );
// We want 'g's, etc. to go below the underline. When the BeOS can
// do underlining of any font, this code can be removed.
font_height height;
GetFontHeight( &height );
float descent = height.descent;
// Find the vertical center of the view so we can vertically
// center everything.
int centerPixel = (int) ((frameRect.bottom - frameRect.top) / 2);
int textCenter = (int) (descent + underlineThickness) + centerPixel;
// We want to draw everything only half opaque.
dragView->SetDrawingMode( B_OP_ALPHA );
dragView->SetHighColor( color.red, color.green, color.blue, 128.0 );
dragView->SetBlendingMode( B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE );
// Center the icon in the view.
dragView->MovePenTo( BPoint( frameRect.left,
centerPixel - (iconSize / 2) ) );
dragView->DrawBitmap( bookmarkIcon );
// Draw the text in the same font (size, etc.) as the link view.
// Note: DrawString() draws the text at one pixel above the pen's
// current y coordinate.
BFont font;
GetFont( &font );
dragView->SetFont( &font );
dragView->MovePenTo( BPoint( frameRect.left + iconSize + 4, textCenter ) );
dragView->DrawString( url->String() );
// Draw the underline in the requested thickness.
dragView->FillRect( BRect( (float) frameRect.left + iconSize + 4,
(float) (textCenter + 1),
(float) StringWidth( url->String() ) + iconSize + 4,
(float) textCenter + underlineThickness ) );
// Be sure to flush the view buffer so everything is drawn.
dragView->Flush();
dragBitmap->Unlock();
// The URL's label is probably not the same size as the URL's
// address, which is what we're going to draw. So horizontally
// offset the bitmap proportionally to where the user clicked
// on the link.
float horiz = dragOffset.x / GetTextRect().Width();
dragOffset.x = horiz * frameRect.right;
DragMessage( dragMessage, dragBitmap, B_OP_ALPHA,
BPoint( dragOffset.x, (rect.Height() / 2) + 2 ), this );
delete dragMessage;
draggedOut = true;
}
void URLView::DoPersonDrag() {
// Handle all of the bookmark dragging. This includes setting up
// the drag message and drawing the dragged bitmap.
// Set up the drag message to support both BTextView dragging (using
// the e-mail address) and file dropping (to Tracker).
BMessage *dragMessage = new BMessage( B_MIME_DATA );
dragMessage->AddInt32( "be:actions", B_COPY_TARGET );
dragMessage->AddString( "be:types", "application/octet-stream" );
dragMessage->AddString( "be:filetypes", "application/x-person" );
dragMessage->AddString( "be:type_descriptions", "person" );
dragMessage->AddString( "be:clip_name", Text() );
// This allows the user to drag the e-mail address into a
// standard BTextView.
BString email = GetImportantURL();
dragMessage->AddData( "text/plain", B_MIME_DATA, email.String(),
email.Length() + 1 );
// Query for the system's icon for bookmarks.
BBitmap *personIcon = new BBitmap( BRect( 0, 0, iconSize - 1,
iconSize - 1 ), B_CMAP8 );
BMimeType mime( "application/x-person" );
if( iconSize == 16 ) mime.GetIcon( personIcon, B_MINI_ICON );
else mime.GetIcon( personIcon, B_LARGE_ICON );
// Find the size of the bitmap to drag. If the text is bigger than the
// icon, use that size. Otherwise, use the icon's. Center the icon
// vertically in the bitmap.
BRect rect = GetTextRect();
rect.right += iconSize + 4;
if( (rect.bottom - rect.top) < iconSize ) {
int adjustment = (int) ((iconSize - (rect.bottom - rect.top)) / 2) + 1;
rect.top -= adjustment;
rect.bottom += adjustment;
}
// Make sure the rectangle starts at 0,0.
rect.bottom += 0 - rect.top;
rect.top = 0;
// Create the bitmap to draw the dragged image in.
BBitmap *dragBitmap = new BBitmap( rect, B_RGBA32, true );
BView *dragView = new BView( rect, "Drag View", 0, 0 );
dragBitmap->Lock();
dragBitmap->AddChild( dragView );
BRect frameRect = dragView->Frame();
// Make the background of the dragged image transparent.
dragView->SetHighColor( B_TRANSPARENT_COLOR );
dragView->FillRect( frameRect );
// We want 'g's, etc. to go below the underline. When the BeOS can
// do underlining of any font, this code can be removed.
font_height height;
GetFontHeight( &height );
float descent = height.descent;
// Find the vertical center of the view so we can vertically
// center everything.
int centerPixel = (int) ((frameRect.bottom - frameRect.top) / 2);
int textCenter = (int) (descent + underlineThickness) + centerPixel;
// We want to draw everything only half opaque.
dragView->SetDrawingMode( B_OP_ALPHA );
dragView->SetHighColor( 0.0, 0.0, 0.0, 128.0 );
dragView->SetBlendingMode( B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE );
// Center the icon in the view.
dragView->MovePenTo( BPoint( frameRect.left,
centerPixel - (iconSize / 2) ) );
dragView->DrawBitmap( personIcon );
// Draw the text in the same font (size, etc.) as the link view.
// Note: DrawString() draws the text at one pixel above the pen's
// current y coordinate.
BFont font;
GetFont( &font );
dragView->SetFont( &font );
dragView->MovePenTo( BPoint( frameRect.left + iconSize + 4, textCenter ) );
dragView->DrawString( Text() );
// Be sure to flush the view buffer so everything is drawn.
dragView->Flush();
dragBitmap->Unlock();
// The Person icon adds some width to the bitmap that we are
// going to draw. So horizontally offset the bitmap proportionally
// to where the user clicked on the link.
float horiz = dragOffset.x / GetTextRect().Width();
dragOffset.x = horiz * frameRect.right;
DragMessage( dragMessage, dragBitmap, B_OP_ALPHA,
BPoint( dragOffset.x,
(rect.Height() + underlineThickness) / 2 + 2), this );
delete dragMessage;
draggedOut = true;
}
BString URLView::GetImportantURL() {
// Return the relevant portion of the URL (i.e. strip off "mailto:" from
// e-mail address URLs).
BString returnMe;
if( IsEmailLink() ) url->CopyInto( returnMe, 7, url->CountChars() - 6 );
else url->CopyInto( returnMe, 0, url->CountChars() );
return returnMe;
}
BRect URLView::GetTextRect() {
// This function will return a BRect that contains only the text
// and the underline, so the mouse can change and the link will
// be activated only when the mouse is over the text itself, not
// just within the view.
BRect frame = Frame();
frame.OffsetTo( B_ORIGIN );
// Get the height of the current font.
font_height height;
GetFontHeight( &height );
float stringHeight = underlineThickness + height.ascent - 1;
// Get the rectangle of just the string.
return BRect( frame.left, frame.bottom - stringHeight,
frame.left + StringWidth( Text() ), frame.bottom - 1 );
}
BRect URLView::GetURLRect() {
// This function will return a BRect that contains only the text
// and the underline, so the mouse can change and the link will
// be activated only when the mouse is over the text itself, not
// just within the view.
BRect frame = Frame();
frame.OffsetTo( B_ORIGIN );
// Get the height of the current font.
font_height height;
GetFontHeight( &height );
float stringHeight = underlineThickness + height.ascent - 1;
// Get the rectangle of just the string.
return BRect( frame.left, frame.bottom - stringHeight,
frame.left + StringWidth( url->String() ),
frame.bottom - 1 );
}
bool URLView::IsEmailLink() {
// Is this link an e-mail link?
return( url->FindFirst( "mailto:" ) == 0 );
}
bool URLView::IsFTPLink() {
// Is this link an FTP link?
return( url->FindFirst( "ftp://" ) == 0 );
}
void URLView::LaunchURL() {
// Is the URL a mail link or HTTP?
if( IsEmailLink() ) {
// Lock the string buffer and pass it to the mail program.
char *link = url->LockBuffer( 0 );
status_t result = be_roster->Launch( "text/x-email", 1, &link );
url->UnlockBuffer();
if( result == B_ALREADY_RUNNING||result == B_BAD_VALUE)
{
char app_sig[B_MIME_TYPE_LENGTH];
BMimeType("text/x-email").GetPreferredApp(app_sig);
//result = be_roster->Launch( app_sig, 1, &link );
BMessenger messenger(app_sig);
BMessage msg(B_ARGV_RECEIVED);
msg.AddInt32("argc",2);
msg.AddString("argv","app");
msg.AddString("argv",link);
msg.AddString("cmd","/");
BMessage reply;
result = messenger.SendMessage(&msg,&reply);
}
// Make sure the user has an e-mail program.
if( result != B_NO_ERROR && result != B_ALREADY_RUNNING ) {
BAlert *alert = new BAlert( "E-mail Warning",
"There is no e-mail program on your machine that is configured as the default program to send e-mail.",
"Ok", NULL, NULL, B_WIDTH_AS_USUAL,
B_WARNING_ALERT );
alert->Go();
}
}
// Handle an HTTP link.
else if( (url->FindFirst( "http://" ) == 0) ||
(url->FindFirst( "file://" ) == 0) ) {
// Lock the string buffer and pass it to the web browser.
char *link = url->LockBuffer( 0 );
status_t result = be_roster->Launch( "text/html", 1, &link );
url->UnlockBuffer();
// Make sure the user has a web browser.
if( result != B_NO_ERROR && result != B_ALREADY_RUNNING ) {
BAlert *alert = new BAlert( "Web Browser Warning",
"There is no web browser on your machine that is configured as the default program to view web pages.",
"Ok", NULL, NULL, B_WIDTH_AS_USUAL,
B_WARNING_ALERT );
alert->Go();
}
}
// Handle an FTP link.
else if( IsFTPLink() ) {
// Lock the string buffer and pass it to the FTP client.
char *link = url->LockBuffer( 0 );
status_t result = be_roster->Launch( "application/x-vnd.Be.URL.ftp",
1, &link );
url->UnlockBuffer();
// Make sure the user has an FTP client.
if( result != B_NO_ERROR && result != B_ALREADY_RUNNING ) {
BAlert *alert = new BAlert( "FTP Warning",
"There is no FTP client on your machine that is configured as the default program to connect to an FTP server.",
"Ok", NULL, NULL, B_WIDTH_AS_USUAL,
B_WARNING_ALERT );
alert->Go();
}
}
// We don't know how to handle anything else.
}
void URLView::Redraw() {
// Redraw the link without flicker.
BRect frame = Frame();
frame.OffsetTo( B_ORIGIN );
Draw( frame );
}
void URLView::WriteAttributes( int fd ) {
// Write the developer-defined attributes to the newly-created file.
KeyPair *item;
for( int i = 0; (item = (KeyPair *) attributes->ItemAt(i)); i++ ) {
fs_write_attr( fd, item->key->String(), B_STRING_TYPE, 0, item->value->String(), item->value->Length() + 1 );
}
}

View File

@ -0,0 +1,127 @@
/* URLView 2.0
written by William Kakes of Tall Hill Software.
This class provides an underlined and clickable BStringView
that will launch the web browser, e-mail program, or FTP client
when clicked on. Other features include hover-highlighting,
right-click menus, and drag-and-drop support.
You are free to use URLView in your own programs (both open-source
and closed-source) free of charge, but a mention in your read me
file or your program's about box would be appreciated. See
http://www.tallhill.com for current contact information.
URLView is provided as-is, with no warranties of any kind. If
you use it, you are on your own.
*/
#ifndef TH_URL_VIEW_H
#define TH_URL_VIEW_H
#include <Cursor.h>
#include <List.h>
#include <Mime.h>
#include <PopUpMenu.h>
#include <String.h>
#include <StringView.h>
// This is the link's mouse cursor (a replica of NetPositive's link cursor).
const uint8 url_cursor[] = { 16, 1, 1, 2,
// This is the cursor data.
0x00, 0x00, 0x38, 0x00, 0x24, 0x00, 0x24, 0x00,
0x13, 0xe0, 0x12, 0x5c, 0x09, 0x2a, 0x08, 0x01,
0x3c, 0x21, 0x4c, 0x71, 0x42, 0x71, 0x30, 0xf9,
0x0c, 0xf9, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00,
// This is the cursor mask.
0x00, 0x00, 0x38, 0x00, 0x3c, 0x00, 0x3c, 0x00,
0x1f, 0xe0, 0x1f, 0xfc, 0x0f, 0xfe, 0x0f, 0xff,
0x3f, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x3f, 0xff,
0x0f, 0xff, 0x03, 0xfe, 0x01, 0xf8, 0x00, 0x00,
};
// The default link color, blue.
const rgb_color blue = { 0, 0, 255 };
// The default clicked-link color, red.
const rgb_color red = { 255, 0, 0 };
// The default link hover color, dark blue.
const rgb_color dark_blue = { 0, 0, 120 };
class URLView : public BStringView {
public:
URLView( BRect frame, const char *name, const char *label, const char *url,
uint32 resizingMode = B_FOLLOW_LEFT | B_FOLLOW_TOP,
uint32 flags = B_WILL_DRAW );
~URLView();
void AttachedToWindow();
void Draw( BRect updateRect );
void MessageReceived( BMessage *message );
void MouseDown( BPoint point );
void MouseMoved( BPoint point, uint32 transit, const BMessage *message );
void MouseUp( BPoint point );
void AddAttribute( const char *name, const char *value );
void SetColor( rgb_color color );
void SetClickColor( rgb_color color );
void SetDraggable( bool draggable );
void SetHoverColor( rgb_color color );
void SetHoverEnabled( bool hover );
void SetIconSize( icon_size iconSize );
void SetUnderlineThickness( int thickness );
private:
void CopyToClipboard();
void CreateBookmark( const BString *fullName, const BString *title );
void CreatePerson( const BString *fullName, const BString *title );
BPopUpMenu *CreatePopupMenu();
void DoBookmarkDrag();
void DoPersonDrag();
BString GetImportantURL();
BRect GetTextRect();
BRect GetURLRect();
bool IsEmailLink();
bool IsFTPLink();
void LaunchURL();
void Redraw();
void WriteAttributes( int fd );
BString *url;
rgb_color color;
rgb_color clickColor;
rgb_color hoverColor;
bool hoverEnabled;
bool draggable;
int underlineThickness;
int iconSize;
bool selected;
bool hovering;
bool draggedOut;
bool inPopup;
const BCursor *linkCursor;
BPoint dragOffset;
BList *attributes;
typedef struct kp {
BString *key;
BString *value;
} KeyPair;
};
#endif // TH_URL_VIEW

View File

@ -0,0 +1,8 @@
#include "HApp.h"
int main()
{
HApp app;
app.Run();
return 0;
}