diff --git a/src/prefs/sounds/Colors.h b/src/prefs/sounds/Colors.h new file mode 100644 index 0000000000..42a582fa72 --- /dev/null +++ b/src/prefs/sounds/Colors.h @@ -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 + + +//****************************************************************************************************** +//**** 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 diff --git a/src/prefs/sounds/HApp.cpp b/src/prefs/sounds/HApp.cpp new file mode 100644 index 0000000000..3c5e351f51 --- /dev/null +++ b/src/prefs/sounds/HApp.cpp @@ -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(); +} diff --git a/src/prefs/sounds/HApp.h b/src/prefs/sounds/HApp.h new file mode 100644 index 0000000000..e0e3215395 --- /dev/null +++ b/src/prefs/sounds/HApp.h @@ -0,0 +1,13 @@ +#ifndef __HAPP_H__ +#define __HAPP_H__ + +#include + +class HApp :public BApplication{ +public: + HApp(); + virtual ~HApp(); +protected: + virtual void AboutRequested(); +}; +#endif \ No newline at end of file diff --git a/src/prefs/sounds/HEventItem.cpp b/src/prefs/sounds/HEventItem.cpp new file mode 100644 index 0000000000..c94303192d --- /dev/null +++ b/src/prefs/sounds/HEventItem.cpp @@ -0,0 +1,82 @@ +#include "HEventItem.h" +#include "ResourceUtils.h" +#include +#include +#include +#include +#include +#include +#include + +/*********************************************************** + * 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,"",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,"",false,false);// falta + } +} + +/*********************************************************** + * Remove + ***********************************************************/ +void +HEventItem::Remove() +{ + BMediaFiles mfiles; + + mfiles.RemoveItem(BMediaFiles::B_SOUNDS,Name()); +} + +/*********************************************************** + * + ***********************************************************/ +const char* +HEventItem::Path() const +{ + return fPath.String(); +} diff --git a/src/prefs/sounds/HEventItem.h b/src/prefs/sounds/HEventItem.h new file mode 100644 index 0000000000..c64de1b1e7 --- /dev/null +++ b/src/prefs/sounds/HEventItem.h @@ -0,0 +1,26 @@ +#ifndef __HEVENTITEM_H__ +#define __HEVENTITEM_H__ + +#include "CLVEasyItem.h" +#include +#include + + +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 diff --git a/src/prefs/sounds/HEventList.cpp b/src/prefs/sounds/HEventList.cpp new file mode 100644 index 0000000000..d7e142fdec --- /dev/null +++ b/src/prefs/sounds/HEventList.cpp @@ -0,0 +1,242 @@ +#include "HEventList.h" +#include "HEventItem.h" + +#include "HWindow.h" +#include "IconMenuItem.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*********************************************************** + * 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("",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); +} diff --git a/src/prefs/sounds/HEventList.h b/src/prefs/sounds/HEventList.h new file mode 100644 index 0000000000..484f1e18b4 --- /dev/null +++ b/src/prefs/sounds/HEventList.h @@ -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 diff --git a/src/prefs/sounds/HTypeList.h b/src/prefs/sounds/HTypeList.h new file mode 100644 index 0000000000..a086797cea --- /dev/null +++ b/src/prefs/sounds/HTypeList.h @@ -0,0 +1,22 @@ +#ifndef __HTYPELIST_H__ +#define __HTYPELIST_H__ + +#include + +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 \ No newline at end of file diff --git a/src/prefs/sounds/HWindow.cpp b/src/prefs/sounds/HWindow.cpp new file mode 100644 index 0000000000..081aaea720 --- /dev/null +++ b/src/prefs/sounds/HWindow.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*********************************************************** + * 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("",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("")->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(""); + 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; +} \ No newline at end of file diff --git a/src/prefs/sounds/HWindow.h b/src/prefs/sounds/HWindow.h new file mode 100644 index 0000000000..2e60141c20 --- /dev/null +++ b/src/prefs/sounds/HWindow.h @@ -0,0 +1,44 @@ +#ifndef __HWINDOW_H__ +#define __HWINDOW_H__ + +#include +#include +#include + +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 diff --git a/src/prefs/sounds/Icon.rsrc b/src/prefs/sounds/Icon.rsrc new file mode 100644 index 0000000000..e93496fd19 Binary files /dev/null and b/src/prefs/sounds/Icon.rsrc differ diff --git a/src/prefs/sounds/Resource.rsrc b/src/prefs/sounds/Resource.rsrc new file mode 100644 index 0000000000..85b3760d2d Binary files /dev/null and b/src/prefs/sounds/Resource.rsrc differ diff --git a/src/prefs/sounds/Santa/BetterScrollView.cpp b/src/prefs/sounds/Santa/BetterScrollView.cpp new file mode 100644 index 0000000000..e67fc75cf2 --- /dev/null +++ b/src/prefs/sounds/Santa/BetterScrollView.cpp @@ -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 +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(); + } + } +} + diff --git a/src/prefs/sounds/Santa/BetterScrollView.h b/src/prefs/sounds/Santa/BetterScrollView.h new file mode 100644 index 0000000000..ed24c162f8 --- /dev/null +++ b/src/prefs/sounds/Santa/BetterScrollView.h @@ -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 + + +//****************************************************************************************************** +//**** 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 diff --git a/src/prefs/sounds/Santa/CLVColumn.cpp b/src/prefs/sounds/Santa/CLVColumn.cpp new file mode 100644 index 0000000000..e2085fb8e3 --- /dev/null +++ b/src/prefs/sounds/Santa/CLVColumn.cpp @@ -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 +#include +#include + +//****************************************************************************************************** +//**** 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(®ion); + view->DrawBitmap(bitmap,iconBounds); + } + delete bitmap; + } + fCachedRect = header_rect; + } +} diff --git a/src/prefs/sounds/Santa/CLVColumn.h b/src/prefs/sounds/Santa/CLVColumn.h new file mode 100644 index 0000000000..983a17efbe --- /dev/null +++ b/src/prefs/sounds/Santa/CLVColumn.h @@ -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 +#include + + +//****************************************************************************************************** +//**** 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 diff --git a/src/prefs/sounds/Santa/CLVColumnLabelView.cpp b/src/prefs/sounds/Santa/CLVColumnLabelView.cpp new file mode 100644 index 0000000000..a15fdd6663 --- /dev/null +++ b/src/prefs/sounds/Santa/CLVColumnLabelView.cpp @@ -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 +#include +#include + + +//****************************************************************************************************** +//**** 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.xfMouseClickedPos.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.xfPreviousMousePos.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.xfPreviousMousePos.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; + } +} diff --git a/src/prefs/sounds/Santa/CLVColumnLabelView.h b/src/prefs/sounds/Santa/CLVColumnLabelView.h new file mode 100644 index 0000000000..0f9c351051 --- /dev/null +++ b/src/prefs/sounds/Santa/CLVColumnLabelView.h @@ -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 +#include + + +//****************************************************************************************************** +//**** 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 diff --git a/src/prefs/sounds/Santa/CLVEasyItem.cpp b/src/prefs/sounds/Santa/CLVEasyItem.cpp new file mode 100644 index 0000000000..d9ca854fd4 --- /dev/null +++ b/src/prefs/sounds/Santa/CLVEasyItem.cpp @@ -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 +#include +#include +#include +#include + +//****************************************************************************************************** +//**** 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; +} diff --git a/src/prefs/sounds/Santa/CLVEasyItem.h b/src/prefs/sounds/Santa/CLVEasyItem.h new file mode 100644 index 0000000000..dbd3201724 --- /dev/null +++ b/src/prefs/sounds/Santa/CLVEasyItem.h @@ -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 +#include + +//****************************************************************************************************** +//**** 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 diff --git a/src/prefs/sounds/Santa/CLVListItem.cpp b/src/prefs/sounds/Santa/CLVListItem.cpp new file mode 100644 index 0000000000..97f5f734b0 --- /dev/null +++ b/src/prefs/sounds/Santa/CLVListItem.cpp @@ -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 + + +//****************************************************************************************************** +//**** 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; + +} diff --git a/src/prefs/sounds/Santa/CLVListItem.h b/src/prefs/sounds/Santa/CLVListItem.h new file mode 100644 index 0000000000..7be90947c8 --- /dev/null +++ b/src/prefs/sounds/Santa/CLVListItem.h @@ -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 + + +//****************************************************************************************************** +//**** 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 diff --git a/src/prefs/sounds/Santa/ColumnListView.cpp b/src/prefs/sounds/Santa/ColumnListView.cpp new file mode 100644 index 0000000000..41f048c597 --- /dev/null +++ b/src/prefs/sounds/Santa/ColumnListView.cpp @@ -0,0 +1,1691 @@ +//Column list view 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 +#include + + +//****************************************************************************************************** +//**** PROJECT HEADER FILES +//****************************************************************************************************** +#include "ColumnListView.h" +#include "CLVColumnLabelView.h" +#include "CLVColumn.h" +#include "CLVListItem.h" + + +//****************************************************************************************************** +//**** BITMAPS +//****************************************************************************************************** +uint8 CLVRightArrowData[132] = +{ + 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x12, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x12, 0x12, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x12, 0x12, 0x12, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x12, 0x12, 0x12, 0x12, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x12, 0x12, 0x12, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x12, 0x12, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x12, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; +uint8 CLVDownArrowData[132] = +{ + 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x00, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x12, 0x12, 0x12, 0x12, 0x12, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x12, 0x12, 0x12, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x12, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 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 +}; + + +//****************************************************************************************************** +//**** ColumnListView CLASS DEFINITION +//****************************************************************************************************** +CLVContainerView::CLVContainerView(ColumnListView* target, uint32 resizingMode, uint32 flags, + bool horizontal, bool vertical, bool scroll_view_corner, border_style border) : +BetterScrollView(NULL,target,resizingMode,flags,horizontal,vertical,scroll_view_corner,border) +{ + IsBeingDestroyed = false; +}; + + +CLVContainerView::~CLVContainerView() +{ + IsBeingDestroyed = true; +} + + +//****************************************************************************************************** +//**** ColumnListView CLASS DEFINITION +//****************************************************************************************************** +ColumnListView::ColumnListView(BRect Frame, CLVContainerView **ContainerView, const char *Name, + uint32 ResizingMode, uint32 flags, list_view_type Type, bool hierarchical, bool horizontal, + bool vertical, bool scroll_view_corner, border_style border, const BFont *LabelFont) +: BListView(Frame,Name,Type,B_FOLLOW_ALL_SIDES,flags), +fColumnList(6), +fColumnDisplayList(6), +fSortKeyList(6), +fFullItemList(32), +fRightArrow(BRect(0.0,0.0,10.0,10.0),B_COLOR_8_BIT,CLVRightArrowData,false,false), +fDownArrow(BRect(0.0,0.0,10.0,10.0),B_COLOR_8_BIT,CLVDownArrowData,false,false), +fHighlightTextOnly(false) +{ + fHierarchical = hierarchical; + + //Create the column titles bar view + font_height FontAttributes; + LabelFont->GetHeight(&FontAttributes); + float fLabelFontHeight = ceil(FontAttributes.ascent) + ceil(FontAttributes.descent); + float ColumnLabelViewBottom = Frame.top+1.0+fLabelFontHeight+3.0; + fColumnLabelView = new CLVColumnLabelView(BRect(Frame.left,Frame.top,Frame.right, + ColumnLabelViewBottom),this,LabelFont); + + //Create the container view + EmbedInContainer(horizontal,vertical,scroll_view_corner,border,ResizingMode,flags); + *ContainerView = fScrollView; + + //Complete the setup + UpdateColumnSizesDataRectSizeScrollBars(); + fColumnLabelView->UpdateDragGroups(); + fExpanderColumn = -1; + fCompare = NULL; + fWatchingForDrag = false; + fSelectedItemColorWindowActive = BeListSelectGrey; + fSelectedItemColorWindowInactive = BeListSelectGrey; + fWindowActive = false; +} + + +ColumnListView::~ColumnListView() +{ + //Delete all list columns + int32 ColumnCount = fColumnList.CountItems(); + for(int32 Counter = ColumnCount-1; Counter >= 0; Counter--) + { + CLVColumn* Item = (CLVColumn*)fColumnList.RemoveItem(Counter); + if(Item) + delete Item; + } + //Remove and delete the container view if necessary + if(!fScrollView->IsBeingDestroyed) + { + fScrollView->RemoveChild(this); + delete fScrollView; + } +} + + +CLVContainerView* ColumnListView::CreateContainer(bool horizontal, bool vertical, bool scroll_view_corner, + border_style border, uint32 ResizingMode, uint32 flags) +{ + return new CLVContainerView(this,ResizingMode,flags,horizontal,vertical,scroll_view_corner,border); +} + + +void ColumnListView::EmbedInContainer(bool horizontal, bool vertical, bool scroll_view_corner, border_style border, + uint32 ResizingMode, uint32 flags) +{ + BRect ViewFrame = Frame(); + BRect LabelsFrame = fColumnLabelView->Frame(); + + fScrollView = CreateContainer(horizontal,vertical,scroll_view_corner,border,ResizingMode, + flags&(B_NAVIGABLE^0xFFFFFFFF)); + BRect NewFrame = Frame(); + + //Resize the main view to make room for the CLVColumnLabelView + ResizeTo(ViewFrame.right-ViewFrame.left,ViewFrame.bottom-LabelsFrame.bottom-1.0); + MoveTo(NewFrame.left,NewFrame.top+(LabelsFrame.bottom-LabelsFrame.top+1.0)); + fColumnLabelView->MoveTo(NewFrame.left,NewFrame.top); + + //Add the ColumnLabelView + fScrollView->AddChild(fColumnLabelView); + + //Remove and re-add the BListView so that it will draw after the CLVColumnLabelView + fScrollView->RemoveChild(this); + fScrollView->AddChild(this); +} + + +void ColumnListView::UpdateColumnSizesDataRectSizeScrollBars(bool scrolling_allowed) +{ + //Figure out the width + float ColumnBegin; + float ColumnEnd = -1.0; + float DataWidth = 0.0; + bool NextPushedByExpander = false; + int32 NumberOfColumns = fColumnDisplayList.CountItems(); + for(int32 Counter = 0; Counter < NumberOfColumns; Counter++) + { + CLVColumn* Column = (CLVColumn*)fColumnDisplayList.ItemAt(Counter); + if(NextPushedByExpander) + Column->fPushedByExpander = true; + else + Column->fPushedByExpander = false; + if(Column->IsShown()) + { + float ColumnWidth = Column->Width(); + ColumnBegin = ColumnEnd + 1.0; + ColumnEnd = ColumnBegin + ColumnWidth; + Column->fColumnBegin = ColumnBegin; + Column->fColumnEnd = ColumnEnd; + DataWidth = Column->fColumnEnd; + if(NextPushedByExpander) + if(!(Column->fFlags & CLV_PUSH_PASS)) + NextPushedByExpander = false; + if(Column->fFlags & CLV_EXPANDER) + //Set the next column to be pushed + NextPushedByExpander = true; + } + } + + //Figure out the height + float DataHeight = 0.0; + int32 NumberOfItems = CountItems(); + for(int32 Counter2 = 0; Counter2 < NumberOfItems; Counter2++) + DataHeight += ItemAt(Counter2)->Height()+1.0; + if(NumberOfItems > 0) + DataHeight -= 1.0; + + //Update the scroll bars + fScrollView->SetDataRect(BRect(0,0,DataWidth,DataHeight),scrolling_allowed); +} + + +void ColumnListView::ColumnsChanged() +{ + //Any previous column dragging/resizing will get corrupted, so deselect + if(fColumnLabelView->fColumnClicked) + fColumnLabelView->fColumnClicked = NULL; + + //Update the internal sizes and grouping of the columns and sizes of drag groups + UpdateColumnSizesDataRectSizeScrollBars(); + fColumnLabelView->UpdateDragGroups(); + fColumnLabelView->Invalidate(); + Invalidate(); +} + + +bool ColumnListView::AddColumn(CLVColumn* Column) +//Adds a column to the ColumnListView at the end of the list. Returns true if successful. +{ + int32 NumberOfColumns = fColumnList.CountItems(); + int32 DisplayIndex = NumberOfColumns; + + //Make sure a second Expander is not being added + if(Column->fFlags & CLV_EXPANDER) + { + if(!fHierarchical) + return false; + for(int32 Counter = 0; Counter < NumberOfColumns; Counter++) + if(((CLVColumn*)fColumnList.ItemAt(Counter))->fFlags & CLV_EXPANDER) + return false; + if(Column->IsShown()) + fExpanderColumn = NumberOfColumns; + } + + //Make sure this column hasn't already been added to another ColumnListView + if(Column->fParent != NULL) + return false; + + //Check if this should be locked at the beginning or end, and adjust its position if necessary + if(!Column->Flags() & CLV_LOCK_AT_END) + { + bool Repeat; + if(Column->Flags() & CLV_LOCK_AT_BEGINNING) + { + //Move it to the beginning, after the last CLV_LOCK_AT_BEGINNING item + DisplayIndex = 0; + Repeat = true; + while(Repeat && DisplayIndex < NumberOfColumns) + { + Repeat = false; + CLVColumn* LastColumn = (CLVColumn*)fColumnDisplayList.ItemAt(DisplayIndex); + if(LastColumn->Flags() & CLV_LOCK_AT_BEGINNING) + { + DisplayIndex++; + Repeat = true; + } + } + } + else + { + //Make sure it isn't after a CLV_LOCK_AT_END item + Repeat = true; + while(Repeat && DisplayIndex > 0) + { + Repeat = false; + CLVColumn* LastColumn = (CLVColumn*)fColumnDisplayList.ItemAt(DisplayIndex-1); + if(LastColumn->Flags() & CLV_LOCK_AT_END) + { + DisplayIndex--; + Repeat = true; + } + } + } + } + + //Add the column to the display list in the appropriate position + fColumnDisplayList.AddItem(Column, DisplayIndex); + + //Add the column to the end of the column list + fColumnList.AddItem(Column); + + //Tell the column it belongs to me now + Column->fParent = this; + + //Set the scroll bars and tell views to update + ColumnsChanged(); + return true; +} + + +bool ColumnListView::AddColumnList(BList* NewColumns) +//Adds a BList of CLVColumn's to the ColumnListView at the position specified, or at the end of the list +//if AtIndex == -1. Returns true if successful. +{ + int32 NumberOfColumns = int32(fColumnList.CountItems()); + int32 NumberOfColumnsToAdd = int32(NewColumns->CountItems()); + + //Make sure a second CLVExpander is not being added + int32 Counter; + int32 NumberOfExpanders = 0; + for(Counter = 0; Counter < NumberOfColumns; Counter++) + if(((CLVColumn*)fColumnList.ItemAt(Counter))->fFlags & CLV_EXPANDER) + NumberOfExpanders++; + int32 SetfExpanderColumnTo = -1; + for(Counter = 0; Counter < NumberOfColumnsToAdd; Counter++) + { + CLVColumn* ThisColumn = (CLVColumn*)NewColumns->ItemAt(Counter); + if(ThisColumn->fFlags & CLV_EXPANDER) + { + NumberOfExpanders++; + if(ThisColumn->IsShown()) + SetfExpanderColumnTo = NumberOfColumns + Counter; + } + } + if(NumberOfExpanders != 0 && !fHierarchical) + return false; + if(NumberOfExpanders > 1) + return false; + if(SetfExpanderColumnTo != -1) + fExpanderColumn = SetfExpanderColumnTo; + + //Make sure none of these columns have already been added to a ColumnListView + for(Counter = 0; Counter < NumberOfColumnsToAdd; Counter++) + if(((CLVColumn*)NewColumns->ItemAt(Counter))->fParent != NULL) + return false; + //Make sure none of these columns are being added twice + for(Counter = 0; Counter < NumberOfColumnsToAdd-1; Counter++) + for(int32 Counter2 = Counter+1; Counter2 < NumberOfColumnsToAdd; Counter2++) + if(NewColumns->ItemAt(Counter) == NewColumns->ItemAt(Counter2)) + return false; + + for(Counter = 0; Counter < NumberOfColumnsToAdd; Counter++) + { + CLVColumn* Column = (CLVColumn*)NewColumns->ItemAt(Counter); + //Check if this should be locked at the beginning or end, and adjust its position if necessary + int32 DisplayIndex = NumberOfColumns; + if(!Column->Flags() & CLV_LOCK_AT_END) + { + bool Repeat; + if(Column->Flags() & CLV_LOCK_AT_BEGINNING) + { + //Move it to the beginning, after the last CLV_LOCK_AT_BEGINNING item + DisplayIndex = 0; + Repeat = true; + while(Repeat && DisplayIndex < NumberOfColumns) + { + Repeat = false; + CLVColumn* LastColumn = (CLVColumn*)fColumnDisplayList.ItemAt(DisplayIndex); + if(LastColumn->Flags() & CLV_LOCK_AT_BEGINNING) + { + DisplayIndex++; + Repeat = true; + } + } + } + else + { + //Make sure it isn't after a CLV_LOCK_AT_END item + Repeat = true; + while(Repeat && DisplayIndex > 0) + { + Repeat = false; + CLVColumn* LastColumn = (CLVColumn*)fColumnDisplayList.ItemAt(DisplayIndex-1); + if(LastColumn->Flags() & CLV_LOCK_AT_END) + { + DisplayIndex--; + Repeat = true; + } + } + } + } + + //Add the column to the display list in the appropriate position + fColumnDisplayList.AddItem(Column, DisplayIndex); + + //Tell the column it belongs to me now + Column->fParent = this; + + NumberOfColumns++; + } + + //Add the columns to the end of the column list + fColumnList.AddList(NewColumns); + + //Set the scroll bars and tell views to update + ColumnsChanged(); + return true; +} + + +bool ColumnListView::RemoveColumn(CLVColumn* Column) +//Removes a CLVColumn from the ColumnListView. Returns true if successful. +{ + if(!fColumnList.HasItem(Column)) + return false; + int32 ColumnIndex = fSortKeyList.IndexOf(Column); + if(ColumnIndex >= 0) + fSortKeyList.RemoveItem(ColumnIndex); + + if(Column->fFlags & CLV_EXPANDER) + fExpanderColumn = -1; + + //Remove Column from the column and display lists + fColumnDisplayList.RemoveItem(Column); + fColumnList.RemoveItem(Column); + + //Tell the column it has been removed + Column->fParent = NULL; + + //Set the scroll bars and tell views to update + ColumnsChanged(); + return true; +} + + +bool ColumnListView::RemoveColumns(CLVColumn* Column, int32 Count) +//Finds Column in ColumnList and removes Count columns and their data from the view and its items +{ + int32 ColumnIndex = fColumnList.IndexOf(Column); + if(ColumnIndex < 0) + return false; + if(ColumnIndex + Count > fColumnList.CountItems()) + return false; + + //Remove columns from the column and display lists + for(int32 Counter = ColumnIndex; Counter < ColumnIndex+Count; Counter++) + { + CLVColumn* ThisColumn = (CLVColumn*)fColumnList.ItemAt(Counter); + fColumnDisplayList.RemoveItem(ThisColumn); + + int32 SortIndex = fSortKeyList.IndexOf(Column); + if(SortIndex >= 0) + fSortKeyList.RemoveItem(SortIndex); + + if(ThisColumn->fFlags & CLV_EXPANDER) + fExpanderColumn = -1; + + //Tell the column it has been removed + ThisColumn->fParent = NULL; + } + fColumnList.RemoveItems(ColumnIndex,Count); + + //Set the scroll bars and tell views to update + ColumnsChanged(); + return true; +} + + +int32 ColumnListView::CountColumns() const +{ + return fColumnList.CountItems(); +} + + +int32 ColumnListView::IndexOfColumn(CLVColumn* column) const +{ + return fColumnList.IndexOf(column); +} + + +CLVColumn* ColumnListView::ColumnAt(int32 column_index) const +{ + return (CLVColumn*)fColumnList.ItemAt(column_index); +} + + +bool ColumnListView::SetDisplayOrder(const int32* ColumnOrder) +//Sets the display order using a BList of CLVColumn's +{ + //Add the items to the display list in order + fColumnDisplayList.MakeEmpty(); + int32 ColumnsToSet = fColumnList.CountItems(); + for(int32 Counter = 0; Counter < ColumnsToSet; Counter++) + { + if(ColumnOrder[Counter] >= ColumnsToSet) + return false; + for(int32 Counter2 = 0; Counter2 < Counter; Counter2++) + if(ColumnOrder[Counter] == ColumnOrder[Counter2]) + return false; + fColumnDisplayList.AddItem(fColumnList.ItemAt(ColumnOrder[Counter])); + } + + //Update everything about the columns + ColumnsChanged(); + + //Let the program know that the display order changed. + DisplayOrderChanged(ColumnOrder); + return true; +} + +void ColumnListView::ColumnWidthChanged(int32 ColumnIndex, float NewWidth) +{ + CLVColumn* ThisColumn = (CLVColumn*)fColumnList.ItemAt(ColumnIndex); + if(ThisColumn->fFlags & CLV_TELL_ITEMS_WIDTH) + { + //Figure out what the limit is for expanders pushing other columns + float PushMax = 100000; + if(ThisColumn->fPushedByExpander || (ThisColumn->fFlags & CLV_EXPANDER)) + { + int32 NumberOfColumns = fColumnDisplayList.CountItems(); + for(int32 Counter = 0; Counter < NumberOfColumns; Counter++) + { + CLVColumn* SomeColumn = (CLVColumn*)fColumnDisplayList.ItemAt(Counter); + if((SomeColumn->fFlags & CLV_EXPANDER) || SomeColumn->fPushedByExpander) + PushMax = SomeColumn->fColumnEnd; + } + } + + int32 number_of_items; + if(fHierarchical) + number_of_items = fFullItemList.CountItems(); + else + number_of_items = CountItems(); + float ColumnEnd = ThisColumn->fColumnEnd; + for(int32 Counter = 0; Counter < number_of_items; Counter++) + { + CLVListItem* ThisItem; + if(fHierarchical) + ThisItem = (CLVListItem*)fFullItemList.ItemAt(Counter); + else + ThisItem = (CLVListItem*)ItemAt(Counter); + if(ThisColumn->fPushedByExpander || (ThisColumn->fFlags & CLV_EXPANDER)) + { + float ColumnWidth = NewWidth; + float ExpanderShift = ThisItem->OutlineLevel() * 20.0; + if(ThisColumn->fFlags & CLV_EXPANDER) + { + if(ColumnEnd + ExpanderShift > PushMax) + ColumnWidth += PushMax - ColumnEnd; + else + ColumnWidth += ExpanderShift; + } + else + { + if(ColumnEnd + ExpanderShift > PushMax) + { + ColumnWidth -= (ColumnEnd + ExpanderShift - PushMax); + if(ColumnWidth < 0) + ColumnWidth = 0; + } + } + if(ExpanderShift > 0) + { + int32 DisplayIndex = IndexOf(ThisItem); + if(DisplayIndex >= 0) + { + //The column scrolling scrolled some content that is encroaching into the next + //column and needs to be invalidated now + BRect InvalidateRect = ItemFrame(DisplayIndex); + InvalidateRect.left = ColumnEnd; + InvalidateRect.right = ColumnEnd + ExpanderShift; + if(InvalidateRect.right > PushMax) + InvalidateRect.right = PushMax; + if(InvalidateRect.right >= InvalidateRect.left) + Invalidate(InvalidateRect); + } + } + ThisItem->ColumnWidthChanged(ColumnIndex,ColumnWidth,this); + } + else + ThisItem->ColumnWidthChanged(ColumnIndex,NewWidth,this); + } + } +} + + +void ColumnListView::DisplayOrderChanged(const int32* order) +{ + int num_columns = fColumnList.CountItems(); + + //Figure out what the limit is for expanders pushing other columns + float PushMax = 100000; + CLVColumn* ThisColumn; + if(fHierarchical) + for(int32 Counter = 0; Counter < num_columns; Counter++) + { + ThisColumn = (CLVColumn*)fColumnDisplayList.ItemAt(Counter); + if((ThisColumn->fFlags & CLV_EXPANDER) || ThisColumn->fPushedByExpander) + PushMax = ThisColumn->fColumnEnd; + } + + int32 number_of_items; + if(fHierarchical) + number_of_items = fFullItemList.CountItems(); + else + number_of_items = CountItems(); + for(int column = 0; column < num_columns; column++) + { + ThisColumn = (CLVColumn*)fColumnList.ItemAt(column); + if(ThisColumn->fFlags & CLV_TELL_ITEMS_WIDTH) + { + float ColumnLeft = ThisColumn->fColumnBegin; + float ColumnRight = ThisColumn->fColumnEnd; + for(int32 item_index = 0; item_index < number_of_items; item_index++) + { + CLVListItem* item; + if(fHierarchical) + item = (CLVListItem*)fFullItemList.ItemAt(item_index); + else + item = (CLVListItem*)ItemAt(item_index); + int32 DisplayIndex = IndexOf(item); + if(DisplayIndex >= 0 && ThisColumn->IsShown()) + { + BRect ThisColumnRect = ItemFrame(DisplayIndex); + ThisColumnRect.left = ColumnLeft; + ThisColumnRect.right = ColumnRight; + if(ThisColumn->fFlags & CLV_EXPANDER) + { + ThisColumnRect.right += item->OutlineLevel() * 20.0; + if(ThisColumnRect.right > PushMax) + ThisColumnRect.right = PushMax; + } + else + { + if(ThisColumn->fPushedByExpander) + { + float Shift = item->OutlineLevel() * 20.0; + ThisColumnRect.left += Shift; + ThisColumnRect.right += Shift; + if(Shift > 0.0 && ThisColumnRect.right > PushMax) + ThisColumnRect.right = PushMax; + } + } + + if(ThisColumnRect.right >= ThisColumnRect.left) + item->FrameChanged(column,ThisColumnRect,this); + else + item->FrameChanged(column,BRect(-1,-1,-1,-1),this); + } + else + item->FrameChanged(column,BRect(-1,-1,-1,-1),this); + } + } + } + //Get rid of a warning: + order = NULL; +} + + +void ColumnListView::GetDisplayOrder(int32* order) const +{ + int32 ColumnsInList = fColumnList.CountItems(); + for(int32 Counter = 0; Counter < ColumnsInList; Counter++) + order[Counter] = int32(fColumnList.IndexOf(fColumnDisplayList.ItemAt(Counter))); +} + + +void ColumnListView::SetSortKey(int32 ColumnIndex, bool doSort) +{ + CLVColumn* Column; + if(ColumnIndex >= 0) + { + Column = (CLVColumn*)fColumnList.ItemAt(ColumnIndex); + if(!(Column->Flags()&CLV_SORT_KEYABLE)) + return; + } + else + Column = NULL; + if(fSortKeyList.ItemAt(0) != Column || Column == NULL) + { + BRect LabelBounds = fColumnLabelView->Bounds(); + //Need to remove old sort keys and erase all the old underlines + int32 SortKeyCount = fSortKeyList.CountItems(); + for(int32 Counter = 0; Counter < SortKeyCount; Counter++) + { + CLVColumn* UnderlineColumn = (CLVColumn*)fSortKeyList.ItemAt(Counter); + if(UnderlineColumn->fSortMode != NoSort) + fColumnLabelView->Invalidate(BRect(UnderlineColumn->fColumnBegin,LabelBounds.top, + UnderlineColumn->fColumnEnd,LabelBounds.bottom)); + } + fSortKeyList.MakeEmpty(); + + if(Column) + { + fSortKeyList.AddItem(Column); + if(Column->fSortMode == NoSort) + SetSortMode(ColumnIndex,Ascending); + if(doSort) + { + SortItems(); + //Need to draw new underline + fColumnLabelView->Invalidate(BRect(Column->fColumnBegin,LabelBounds.top,Column->fColumnEnd, + LabelBounds.bottom)); + } + } + } + + SortingChanged(); +} + + +void ColumnListView::AddSortKey(int32 ColumnIndex, bool doSort) +{ + CLVColumn* Column; + if(ColumnIndex >= 0) + { + Column = (CLVColumn*)fColumnList.ItemAt(ColumnIndex); + if(!(Column->Flags()&CLV_SORT_KEYABLE)) + return; + } + else + Column = NULL; + if(Column && !fSortKeyList.HasItem(Column)) + { + BRect LabelBounds = fColumnLabelView->Bounds(); + fSortKeyList.AddItem(Column); + if(Column->fSortMode == NoSort) + SetSortMode(ColumnIndex,Ascending); + if(doSort) + { + SortItems(); + //Need to draw new underline + fColumnLabelView->Invalidate(BRect(Column->fColumnBegin,LabelBounds.top,Column->fColumnEnd, + LabelBounds.bottom)); + } + } + + SortingChanged(); +} + + +void ColumnListView::SetSortMode(int32 ColumnIndex,CLVSortMode Mode, bool doSort) +{ + CLVColumn* Column; + if(ColumnIndex >= 0) + { + Column = (CLVColumn*)fColumnList.ItemAt(ColumnIndex); + if(!(Column->Flags()&CLV_SORT_KEYABLE)) + return; + } + else + return; + if(Column->fSortMode != Mode) + { + BRect LabelBounds = fColumnLabelView->Bounds(); + Column->fSortMode = Mode; + if(Mode == NoSort && fSortKeyList.HasItem(Column)) + fSortKeyList.RemoveItem(Column); + if(doSort) + { + SortItems(); + //Need to draw or erase underline + fColumnLabelView->Invalidate(BRect(Column->fColumnBegin,LabelBounds.top,Column->fColumnEnd, + LabelBounds.bottom)); + } + } + + SortingChanged(); +} + + +void ColumnListView::ReverseSortMode(int32 ColumnIndex, bool doSort) +{ + CLVColumn* Column; + if(ColumnIndex >= 0) + { + Column = (CLVColumn*)fColumnList.ItemAt(ColumnIndex); + if(!(Column->Flags()&CLV_SORT_KEYABLE)) + return; + } + else + return; + if(Column->fSortMode == Ascending) + SetSortMode(ColumnIndex,Descending,doSort); + else if(Column->fSortMode == Descending) + SetSortMode(ColumnIndex,NoSort,doSort); + else if(Column->fSortMode == NoSort) + SetSortMode(ColumnIndex,Ascending,doSort); +} + + +int32 ColumnListView::GetSorting(int32* SortKeys, CLVSortMode* SortModes) const +{ + int32 NumberOfKeys = fSortKeyList.CountItems(); + for(int32 Counter = 0; Counter < NumberOfKeys; Counter++) + { + CLVColumn* Column = (CLVColumn*)fSortKeyList.ItemAt(Counter); + SortKeys[Counter] = IndexOfColumn(Column); + SortModes[Counter] = Column->SortMode(); + } + return NumberOfKeys; +} + + +void ColumnListView::SetSorting(int32 NumberOfKeys, int32* SortKeys, CLVSortMode* SortModes, bool doSort) +{ + + //Need to remove old sort keys and erase all the old underlines + BRect LabelBounds = fColumnLabelView->Bounds(); + int32 SortKeyCount = fSortKeyList.CountItems(); + for(int32 Counter = 0; Counter < SortKeyCount; Counter++) + { + CLVColumn* UnderlineColumn = (CLVColumn*)fSortKeyList.ItemAt(Counter); + if(UnderlineColumn->fSortMode != NoSort) + fColumnLabelView->Invalidate(BRect(UnderlineColumn->fColumnBegin,LabelBounds.top, + UnderlineColumn->fColumnEnd,LabelBounds.bottom)); + } + fSortKeyList.MakeEmpty(); + + for(int32 Counter = 0; Counter < NumberOfKeys; Counter++) + { + if(Counter == 0) + SetSortKey(SortKeys[0],false); + else + AddSortKey(SortKeys[Counter],false); + SetSortMode(SortKeys[Counter],SortModes[Counter],doSort && Counter == NumberOfKeys-1); + } + + SortingChanged(); +} + + +void ColumnListView::SortingChanged() +{ } + + +void ColumnListView::FrameResized(float width, float height) +{ + UpdateColumnSizesDataRectSizeScrollBars(); + BListView::FrameResized(width,height); +} + + +void ColumnListView::ScrollTo(BPoint point) +{ + BListView::ScrollTo(point); + fColumnLabelView->ScrollTo(BPoint(point.x,0.0)); +} + + +void ColumnListView::MouseDown(BPoint where) +{ + //BListView::MouseDown gives undesirable behavior during click-and drag (it reselects instead of + //doing nothing to allow for initiating drag & drop), so I need to replace the selection behavior, + //and while I'm at it, I'll just do an InitiateDrag hook function. + BMessage* message = Window()->CurrentMessage(); + int32 item_index = IndexOf(where); + int32 modifier_keys = message->FindInt32("modifiers"); + int32 clicks = message->FindInt32("clicks"); + if(message->FindInt32("buttons") != B_PRIMARY_MOUSE_BUTTON) + return; + if(item_index >= 0) + { + CLVListItem* clicked_item = (CLVListItem*)BListView::ItemAt(item_index); + if(clicked_item->IsSuperItem() && clicked_item->ExpanderRectContains(where)) + { + if(clicked_item->IsExpanded()) + Collapse(clicked_item); + else + Expand(clicked_item); + } + else + { + if(!clicked_item->IsSelected()) + { + //Clicked a new item...select it. + list_view_type type = ListType(); + if((modifier_keys & B_SHIFT_KEY) && type != B_SINGLE_SELECTION_LIST) + { + //If shift held down, expand the selection to include all intervening items. + int32 min_selection = 0x7FFFFFFF; + int32 max_selection = -1; + int32 selection_index = 0; + int32 selection_item_index; + while((selection_item_index = CurrentSelection(selection_index)) != -1) + { + if(min_selection > selection_item_index) + min_selection = selection_item_index; + if(max_selection < selection_item_index) + max_selection = selection_item_index; + selection_index++; + } + if(min_selection == 0x7FFFFFFF) + Select(item_index,false); + else + { + if(min_selection > item_index) + min_selection = item_index; + if(max_selection < item_index) + max_selection = item_index; + Select(min_selection,max_selection,false); + } + } + else if((modifier_keys & B_OPTION_KEY) && type != B_SINGLE_SELECTION_LIST) + //If option held down, expand the selection to include just it. + Select(item_index,true); + else + //If neither key held down, select this item alone. + Select(item_index,false); + + //Also watch for drag of the new selection + fLastMouseDown = where; + fNoKeyMouseDownItemIndex = -1; + fWatchingForDrag = true; + } + else + { + //Clicked an already selected item... + if(modifier_keys & B_OPTION_KEY) + //if option held down, remove it. + Deselect(item_index); + else if(modifier_keys & B_SHIFT_KEY) + { + //If shift held down, ignore it and just watch for drag. + fLastMouseDown = where; + fNoKeyMouseDownItemIndex = -1; + fWatchingForDrag = true; + } + else + { + if(clicks > 1) + Invoke(InvocationMessage()); + else + { + //If neither key held down, watch for drag, but if no drag and just a click on + //this item, select it alone at mouse up. + fLastMouseDown = where; + fNoKeyMouseDownItemIndex = item_index; + fWatchingForDrag = true; + } + } + } + if(fWatchingForDrag) + SetMouseEventMask(B_POINTER_EVENTS,B_NO_POINTER_HISTORY); + } + } + else + { + //Clicked outside of any items. If no shift or option key, deselect all. + if((!(modifier_keys & B_SHIFT_KEY)) && (!(modifier_keys & B_OPTION_KEY))) + DeselectAll(); + } +} + + +void ColumnListView::MouseUp(BPoint where) +{ + if(fWatchingForDrag && fNoKeyMouseDownItemIndex != -1) + Select(fNoKeyMouseDownItemIndex,false); + fNoKeyMouseDownItemIndex = -1; + fWatchingForDrag = false; +} + + +void ColumnListView::MouseMoved(BPoint where, uint32 code, const BMessage *message) +{ + if(fWatchingForDrag && (where.xfLastMouseDown.x+4 || + where.yfLastMouseDown.y+4)) + { + InitiateDrag(fLastMouseDown,IndexOf(fLastMouseDown),true); + fNoKeyMouseDownItemIndex = -1; + fWatchingForDrag = false; + SetMouseEventMask(0); + } +} + + +void ColumnListView::WindowActivated(bool active) +{ + fWindowActive = active; + if(fSelectedItemColorWindowActive.red != fSelectedItemColorWindowInactive.red || + fSelectedItemColorWindowActive.green != fSelectedItemColorWindowInactive.green || + fSelectedItemColorWindowActive.blue != fSelectedItemColorWindowInactive.blue || + fSelectedItemColorWindowActive.alpha != fSelectedItemColorWindowInactive.alpha) + { + int32 index=0; + int32 selected; + while((selected = CurrentSelection(index)) >= 0) + { + InvalidateItem(selected); + index++; + } + } + BListView::WindowActivated(active); +} + + +bool ColumnListView::AddUnder(BListItem* a_item, BListItem* a_superitem) +{ + if(!fHierarchical) + return false; + + //Get the CLVListItems + CLVListItem* item = cast_as(a_item,CLVListItem); + CLVListItem* superitem cast_as(a_superitem,CLVListItem); + if(item == NULL || superitem == NULL) + return false; + + //Find the superitem in the full list and display list (if shown) + int32 SuperItemPos = fFullItemList.IndexOf(superitem); + if(SuperItemPos < 0) + return false; + uint32 SuperItemLevel = superitem->fOutlineLevel; + + //Add the item under the superitem in the full list + int32 ItemPos = SuperItemPos + 1; + item->fOutlineLevel = SuperItemLevel + 1; + while(true) + { + CLVListItem* Temp = (CLVListItem*)fFullItemList.ItemAt(ItemPos); + if(Temp) + { + if(Temp->fOutlineLevel > SuperItemLevel) + ItemPos++; + else + break; + } + else + break; + } + return AddItemPrivate(item,ItemPos); +} + + +bool ColumnListView::AddItem(BListItem* a_item, int32 fullListIndex) +{ + //Get the CLVListItems + CLVListItem* item = cast_as(a_item,CLVListItem); + if(item == NULL) + return false; + return AddItemPrivate(item,fullListIndex); +} + + +bool ColumnListView::AddItem(BListItem* a_item) +{ + //Get the CLVListItems + CLVListItem* item = cast_as(a_item,CLVListItem); + if(item == NULL) + return false; + if(fHierarchical) + return AddItemPrivate(item,fFullItemList.CountItems()); + else + return AddItemPrivate(item,CountItems()); +} + + +bool ColumnListView::AddItemPrivate(CLVListItem* item, int32 fullListIndex) +{ + if(fHierarchical) + { + uint32 ItemLevel = item->OutlineLevel(); + + //Figure out whether it is visible (should it be added to visible list) + bool Visible = true; + + //Find the item that contains it in the full list + int32 SuperItemPos; + if(ItemLevel == 0) + SuperItemPos = -1; + else + SuperItemPos = fullListIndex - 1; + CLVListItem* SuperItem; + while(SuperItemPos >= 0) + { + SuperItem = (CLVListItem*)fFullItemList.ItemAt(SuperItemPos); + if(SuperItem) + { + if(SuperItem->fOutlineLevel >= ItemLevel) + SuperItemPos--; + else + break; + } + else + return false; + } + if(SuperItemPos >= 0 && SuperItem) + { + if(!SuperItem->IsExpanded()) + //SuperItem's contents aren't visible + Visible = false; + if(!HasItem(SuperItem)) + //SuperItem itself isn't showing + Visible = false; + } + + //Add the item to the full list + if(!fFullItemList.AddItem(item,fullListIndex)) + return false; + else + { + //Add the item to the display list + if(Visible) + { + //Find the previous item, or -1 if the item I'm adding will be the first one + int32 PreviousItemPos = fullListIndex - 1; + CLVListItem* PreviousItem; + while(PreviousItemPos >= 0) + { + PreviousItem = (CLVListItem*)fFullItemList.ItemAt(PreviousItemPos); + if(PreviousItem && HasItem(PreviousItem)) + break; + else + PreviousItemPos--; + } + + //Add the item after the previous item, or first on the list + bool Result; + if(PreviousItemPos >= 0) + Result = BListView::AddItem((BListItem*)item,IndexOf(PreviousItem)+1); + else + Result = BListView::AddItem((BListItem*)item,0); + if(Result == false) + fFullItemList.RemoveItem(item); + return Result; + } + return true; + } + } + else + return BListView::AddItem(item,fullListIndex); +} + + +bool ColumnListView::AddList(BList* newItems) +{ + if(fHierarchical) + return AddHierarchicalListPrivate(newItems,fFullItemList.CountItems()); + else + return BListView::AddList(newItems); +} + + +bool ColumnListView::AddList(BList* newItems, int32 fullListIndex) +{ + if(fHierarchical) + return AddHierarchicalListPrivate(newItems,fullListIndex); + else + return BListView::AddList(newItems,fullListIndex); +} + + +bool ColumnListView::EfficientAddListHierarchical(BList* newItems, int32 fullListIndex, bool expanded) +{ + if(!fHierarchical) + return false; + if(expanded) + { + int32 index = 0; + if(fullListIndex > 0) + index = IndexOf((CLVListItem*)fFullItemList.ItemAt(fullListIndex-1))+1; + if(!BListView::AddList(newItems,index)) + return false; + } + int32 numItems = newItems->CountItems(); + void** items = (void**)newItems->Items(); + for(int32 i=0; iCountItems(); + for(int32 count = 0; count < NumberOfItems; count++) + if(!AddItemPrivate((CLVListItem*)(BListItem*)newItems->ItemAt(count),fullListIndex+count)) + return false; + return true; +} + + +bool ColumnListView::RemoveItem(BListItem* a_item) +{ + //Get the CLVListItems + CLVListItem* item = cast_as(a_item,CLVListItem); + if(item == NULL) + return false; + if(fHierarchical) + { + if(!fFullItemList.HasItem(item)) + return false; + int32 ItemsToRemove = 1 + FullListNumberOfSubitems(item); + return RemoveItems(fFullItemList.IndexOf(item),ItemsToRemove); + } + else + return BListView::RemoveItem((BListItem*)item); +} + + +BListItem* ColumnListView::RemoveItem(int32 fullListIndex) +{ + if(fHierarchical) + { + CLVListItem* TheItem = (CLVListItem*)fFullItemList.ItemAt(fullListIndex); + if(TheItem) + { + int32 ItemsToRemove = 1 + FullListNumberOfSubitems(TheItem); + if(RemoveItems(fullListIndex,ItemsToRemove)) + return TheItem; + else + return NULL; + } + else + return NULL; + } + else + return BListView::RemoveItem(fullListIndex); +} + + +bool ColumnListView::RemoveItems(int32 fullListIndex, int32 count) +{ + CLVListItem* TheItem; + if(fHierarchical) + { + uint32 LastSuperItemLevel = ULONG_MAX; + int32 Counter; + int32 DisplayItemsToRemove = 0; + int32 FirstDisplayItemToRemove = -1; + for(Counter = fullListIndex; Counter < fullListIndex+count; Counter++) + { + TheItem = FullListItemAt(Counter); + if(TheItem->fOutlineLevel < LastSuperItemLevel) + LastSuperItemLevel = TheItem->fOutlineLevel; + if(BListView::HasItem((BListItem*)TheItem)) + { + DisplayItemsToRemove++; + if(FirstDisplayItemToRemove == -1) + FirstDisplayItemToRemove = BListView::IndexOf(TheItem); + } + } + while(true) + { + TheItem = FullListItemAt(Counter); + if(TheItem && TheItem->fOutlineLevel > LastSuperItemLevel) + { + count++; + Counter++; + if(BListView::HasItem((BListItem*)TheItem)) + { + DisplayItemsToRemove++; + if(FirstDisplayItemToRemove == -1) + FirstDisplayItemToRemove = BListView::IndexOf((BListItem*)TheItem); + } + } + else + break; + } + while(DisplayItemsToRemove > 0) + { + if(BListView::RemoveItem(FirstDisplayItemToRemove) == NULL) + return false; + DisplayItemsToRemove--; + } + return fFullItemList.RemoveItems(fullListIndex,count); + } + else + return BListView::RemoveItems(fullListIndex,count); +} + + +CLVListItem* ColumnListView::FullListItemAt(int32 fullListIndex) const +{ + return (CLVListItem*)fFullItemList.ItemAt(fullListIndex); +} + + +int32 ColumnListView::FullListIndexOf(const CLVListItem* item) const +{ + return fFullItemList.IndexOf((CLVListItem*)item); +} + + +int32 ColumnListView::FullListIndexOf(BPoint point) const +{ + int32 DisplayListIndex = IndexOf(point); + CLVListItem* TheItem = (CLVListItem*)ItemAt(DisplayListIndex); + if(TheItem) + return FullListIndexOf(TheItem); + else + return -1; +} + + +CLVListItem* ColumnListView::FullListFirstItem() const +{ + return (CLVListItem*)fFullItemList.FirstItem(); +} + + +CLVListItem* ColumnListView::FullListLastItem() const +{ + return (CLVListItem*)fFullItemList.LastItem(); +} + + +bool ColumnListView::FullListHasItem(const CLVListItem* item) const +{ + return fFullItemList.HasItem((CLVListItem*)item); +} + + +int32 ColumnListView::FullListCountItems() const +{ + return fFullItemList.CountItems(); +} + + +void ColumnListView::MakeEmpty() +{ + fFullItemList.MakeEmpty(); + BListView::MakeEmpty(); +} + + +void ColumnListView::MakeEmptyPrivate() +{ + fFullItemList.MakeEmpty(); + BListView::MakeEmpty(); +} + + +bool ColumnListView::FullListIsEmpty() const +{ + return fFullItemList.IsEmpty(); +} + + +int32 ColumnListView::FullListCurrentSelection(int32 index) const +{ + int32 Selection = CurrentSelection(index); + CLVListItem* SelectedItem = (CLVListItem*)ItemAt(Selection); + return FullListIndexOf(SelectedItem); +} + + +void ColumnListView::FullListDoForEach(bool (*func)(CLVListItem*)) +{ + int32 NumberOfItems = fFullItemList.CountItems(); + for(int32 Counter = 0; Counter < NumberOfItems; Counter++) + if(func((CLVListItem*)fFullItemList.ItemAt(Counter)) == true) + return; +} + + +void ColumnListView::FullListDoForEach(bool (*func)(CLVListItem*, void*), void* arg2) +{ + int32 NumberOfItems = fFullItemList.CountItems(); + for(int32 Counter = 0; Counter < NumberOfItems; Counter++) + if(func((CLVListItem*)fFullItemList.ItemAt(Counter),arg2) == true) + return; +} + + +CLVListItem* ColumnListView::Superitem(const CLVListItem* item) const +{ + int32 SuperItemPos; + uint32 ItemLevel = item->fOutlineLevel; + if(ItemLevel == 0) + SuperItemPos = -1; + else + SuperItemPos = fFullItemList.IndexOf((CLVListItem*)item) - 1; + CLVListItem* SuperItem; + while(SuperItemPos >= 0) + { + SuperItem = (CLVListItem*)fFullItemList.ItemAt(SuperItemPos); + if(SuperItem) + { + if(SuperItem->fOutlineLevel >= ItemLevel) + SuperItemPos--; + else + break; + } + else + return NULL; + } + if(SuperItemPos >= 0) + return SuperItem; + else + return NULL; +} + + +int32 ColumnListView::FullListNumberOfSubitems(const CLVListItem* item) const +{ + if(!fHierarchical) + return 0; + int32 ItemPos = FullListIndexOf(item); + int32 SubItemPos; + uint32 SuperItemLevel = item->fOutlineLevel; + if(ItemPos >= 0) + { + for(SubItemPos = ItemPos + 1; SubItemPos >= 1; SubItemPos++) + { + CLVListItem* TheItem = FullListItemAt(SubItemPos); + if(TheItem == NULL || TheItem->fOutlineLevel <= SuperItemLevel) + break; + } + } + else + return 0; + return SubItemPos-ItemPos-1; +} + + +void ColumnListView::Expand(CLVListItem* item) +{ + if(!(item->fSuperItem)) + item->fSuperItem = true; + if(item->IsExpanded()) + return; + item->SetExpanded(true); + if(!fHierarchical) + return; + + int32 DisplayIndex = IndexOf(item); + if(DisplayIndex >= 0) + { + if(fExpanderColumn >= 0) + { + //Change the state of the arrow + item->DrawItemColumn(this,item->fExpanderColumnRect,fExpanderColumn,true); + SetDrawingMode(B_OP_OVER); + DrawBitmap(&fDownArrow, BRect(0.0,0.0,item->fExpanderButtonRect.right- + item->fExpanderButtonRect.left,10.0),item->fExpanderButtonRect); + SetDrawingMode(B_OP_COPY); + } + + //Add the items under it + int32 FullListIndex = fFullItemList.IndexOf(item); + uint32 ItemLevel = item->fOutlineLevel; + int32 Counter = FullListIndex + 1; + int32 AddPos = DisplayIndex + 1; + while(true) + { + CLVListItem* NextItem = (CLVListItem*)fFullItemList.ItemAt(Counter); + if(NextItem == NULL) + break; + if(NextItem->fOutlineLevel > ItemLevel) + { + BListView::AddItem((BListItem*)NextItem,AddPos++); + if(NextItem->fSuperItem && !NextItem->IsExpanded()) + { + //The item I just added is collapsed, so skip all its children + uint32 SkipLevel = NextItem->fOutlineLevel + 1; + while(true) + { + Counter++; + NextItem = (CLVListItem*)fFullItemList.ItemAt(Counter); + if(NextItem == NULL) + break; + if(NextItem->fOutlineLevel < SkipLevel) + break; + } + } + else + Counter++; + } + else + break; + } + } +} + + +void ColumnListView::Collapse(CLVListItem* item) +{ + if(!(item->fSuperItem)) + item->fSuperItem = true; + if(!(item->IsExpanded())) + return; + item->SetExpanded(false); + if(!fHierarchical) + return; + + int32 DisplayIndex = IndexOf((BListItem*)item); + if(DisplayIndex >= 0) + { + if(fExpanderColumn >= 0) + { + //Change the state of the arrow + item->DrawItemColumn(this,item->fExpanderColumnRect,fExpanderColumn,true); + SetDrawingMode(B_OP_OVER); + DrawBitmap(&fRightArrow, BRect(0.0,0.0,item->fExpanderButtonRect.right- + item->fExpanderButtonRect.left,10.0),item->fExpanderButtonRect); + SetDrawingMode(B_OP_COPY); + } + + //Remove the items under it + uint32 ItemLevel = item->fOutlineLevel; + int32 NextItemIndex = DisplayIndex+1; + while(true) + { + CLVListItem* NextItem = (CLVListItem*)ItemAt(NextItemIndex); + if(NextItem) + { + if(NextItem->fOutlineLevel > ItemLevel) + BListView::RemoveItem(NextItemIndex); + else + break; + } + else + break; + } + } +} + + +bool ColumnListView::IsExpanded(int32 fullListIndex) const +{ + BListItem* TheItem = (BListItem*)fFullItemList.ItemAt(fullListIndex); + if(TheItem) + return TheItem->IsExpanded(); + else + return false; +} + + +void ColumnListView::SetSortFunction(CLVCompareFuncPtr compare) +{ + fCompare = compare; +} + + +void ColumnListView::SortItems() +{ + int32 NumberOfItems; + if(!fHierarchical) + NumberOfItems = CountItems(); + else + NumberOfItems = fFullItemList.CountItems(); + if(NumberOfItems == 0) + return; + int32 Counter; + if(!fHierarchical) + { + //Plain sort + //Remember the list context for each item + for(Counter = 0; Counter < NumberOfItems; Counter++) + ((CLVListItem*)ItemAt(Counter))->fSortingContextCLV = this; + //Do the actual sort + BListView::SortItems((int (*)(const void*, const void*))ColumnListView::PlainBListSortFunc); + } + else + { + //Block-by-block sort + BList NewList; + SortFullListSegment(0,0,&NewList); + fFullItemList = NewList; + //Remember the list context for each item + for(Counter = 0; Counter < NumberOfItems; Counter++) + ((CLVListItem*)fFullItemList.ItemAt(Counter))->fSortingContextBList = &fFullItemList; + //Do the actual sort + BListView::SortItems((int (*)(const void*, const void*))ColumnListView::HierarchicalBListSortFunc); + } +} + + +int ColumnListView::PlainBListSortFunc(BListItem** a_item1, BListItem** a_item2) +{ + CLVListItem* item1 = (CLVListItem*)*a_item1; + CLVListItem* item2 = (CLVListItem*)*a_item2; + ColumnListView* SortingContext = item1->fSortingContextCLV; + int32 SortDepth = SortingContext->fSortKeyList.CountItems(); + int CompareResult = 0; + if(SortingContext->fCompare) + for(int32 SortIteration = 0; SortIteration < SortDepth && CompareResult == 0; SortIteration++) + { + CLVColumn* Column = (CLVColumn*)SortingContext->fSortKeyList.ItemAt(SortIteration); + CompareResult = SortingContext->fCompare(item1,item2,SortingContext->fColumnList.IndexOf(Column)); + if(Column->fSortMode == Descending) + CompareResult = 0-CompareResult; + } + return CompareResult; +} + + +int ColumnListView::HierarchicalBListSortFunc(BListItem** a_item1, BListItem** a_item2) +{ + CLVListItem* item1 = (CLVListItem*)*a_item1; + CLVListItem* item2 = (CLVListItem*)*a_item2; + if(item1->fSortingContextBList->IndexOf(item1) < item1->fSortingContextBList->IndexOf(item2)) + return -1; + else + return 1; +} + + +void ColumnListView::SortFullListSegment(int32 OriginalListStartIndex, int32 InsertionPoint, + BList* NewList) +{ + //Identify and sort the items at this level + BList* ItemsInThisLevel = SortItemsInThisLevel(OriginalListStartIndex); + int32 NewItemsStopIndex = InsertionPoint + ItemsInThisLevel->CountItems(); + NewList->AddList(ItemsInThisLevel,InsertionPoint); + delete ItemsInThisLevel; + + //Identify and sort the subitems + for(int32 Counter = InsertionPoint; Counter < NewItemsStopIndex; Counter++) + { + CLVListItem* ThisItem = (CLVListItem*)NewList->ItemAt(Counter); + CLVListItem* NextItem = (CLVListItem*)fFullItemList.ItemAt(fFullItemList.IndexOf(ThisItem)+1); + if(ThisItem->IsSuperItem() && NextItem && ThisItem->fOutlineLevel < NextItem->fOutlineLevel) + { + int32 OldListSize = NewList->CountItems(); + SortFullListSegment(fFullItemList.IndexOf(ThisItem)+1,Counter+1,NewList); + int32 NewListSize = NewList->CountItems(); + NewItemsStopIndex += NewListSize - OldListSize; + Counter += NewListSize - OldListSize; + } + } +} + + +BList* ColumnListView::SortItemsInThisLevel(int32 OriginalListStartIndex) +{ + uint32 ThisLevel = ((CLVListItem*)fFullItemList.ItemAt(OriginalListStartIndex))->fOutlineLevel; + + //Create a new BList of the items in this level + int32 Counter = OriginalListStartIndex; + int32 ItemsInThisLevel = 0; + BList* ThisLevelItems = new BList(16); + while(true) + { + CLVListItem* ThisItem = (CLVListItem*)fFullItemList.ItemAt(Counter); + if(ThisItem == NULL) + break; + uint32 ThisItemLevel = ThisItem->fOutlineLevel; + if(ThisItemLevel == ThisLevel) + { + ThisLevelItems->AddItem(ThisItem); + ItemsInThisLevel++; + } + else if(ThisItemLevel < ThisLevel) + break; + Counter++; + } + + //Sort the BList of the items in this level + CLVListItem** SortArray = new CLVListItem*[ItemsInThisLevel]; + CLVListItem** ListItems = (CLVListItem**)ThisLevelItems->Items(); + for(Counter = 0; Counter < ItemsInThisLevel; Counter++) + SortArray[Counter] = ListItems[Counter]; + SortListArray(SortArray,ItemsInThisLevel); + for(Counter = 0; Counter < ItemsInThisLevel; Counter++) + ListItems[Counter] = SortArray[Counter]; + delete[] SortArray; + return ThisLevelItems; +} + + +void ColumnListView::SortListArray(CLVListItem** SortArray, int32 NumberOfItems) +{ + if(fCompare == NULL) + //No sorting function + return; + int32 SortDepth = fSortKeyList.CountItems(); + for(int32 Counter1 = 0; Counter1 < NumberOfItems-1; Counter1++) + for(int32 Counter2 = Counter1+1; Counter2 < NumberOfItems; Counter2++) + { + int CompareResult = 0; + for(int32 SortIteration = 0; SortIteration < SortDepth && CompareResult == 0; SortIteration++) + { + CLVColumn* Column = (CLVColumn*)fSortKeyList.ItemAt(SortIteration); + CompareResult = fCompare(SortArray[Counter1],SortArray[Counter2],fColumnList.IndexOf(Column)); + if(CompareResult > 0) + CompareResult = 1; + else if(CompareResult < 0) + CompareResult = -1; + if(Column->fSortMode == Descending) + CompareResult = 0-CompareResult; + } + if(CompareResult == 1) + { + CLVListItem* Temp = SortArray[Counter1]; + SortArray[Counter1] = SortArray[Counter2]; + SortArray[Counter2] = Temp; + } + } +} + diff --git a/src/prefs/sounds/Santa/ColumnListView.h b/src/prefs/sounds/Santa/ColumnListView.h new file mode 100644 index 0000000000..6edc976346 --- /dev/null +++ b/src/prefs/sounds/Santa/ColumnListView.h @@ -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 + + +//****************************************************************************************************** +//**** 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 diff --git a/src/prefs/sounds/Santa/Cursors.cpp b/src/prefs/sounds/Santa/Cursors.cpp new file mode 100644 index 0000000000..162b353d69 --- /dev/null +++ b/src/prefs/sounds/Santa/Cursors.cpp @@ -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; + diff --git a/src/prefs/sounds/Santa/Cursors.h b/src/prefs/sounds/Santa/Cursors.h new file mode 100644 index 0000000000..0c10a8113a --- /dev/null +++ b/src/prefs/sounds/Santa/Cursors.h @@ -0,0 +1,20 @@ +#ifndef _SGB_CURSORS_H_ +#define _SGB_CURSORS_H_ + + +//****************************************************************************************************** +//**** SYSTEM HEADER FILES +//****************************************************************************************************** +#include + + +//****************************************************************************************************** +//**** 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_ diff --git a/src/prefs/sounds/Santa/Descending.h b/src/prefs/sounds/Santa/Descending.h new file mode 100644 index 0000000000..f3f2257b26 --- /dev/null +++ b/src/prefs/sounds/Santa/Descending.h @@ -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 +}; + diff --git a/src/prefs/sounds/Santa/NewStrings.cpp b/src/prefs/sounds/Santa/NewStrings.cpp new file mode 100644 index 0000000000..705e4c0aab --- /dev/null +++ b/src/prefs/sounds/Santa/NewStrings.cpp @@ -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 +#include +#include +#include + +#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; iGetBoundingBoxesForStrings(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 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++; + } +} diff --git a/src/prefs/sounds/Santa/NewStrings.h b/src/prefs/sounds/Santa/NewStrings.h new file mode 100644 index 0000000000..143c3e7680 --- /dev/null +++ b/src/prefs/sounds/Santa/NewStrings.h @@ -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 + +//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 \ No newline at end of file diff --git a/src/prefs/sounds/Santa/PrefilledBitmap.cpp b/src/prefs/sounds/Santa/PrefilledBitmap.cpp new file mode 100644 index 0000000000..0729e33652 --- /dev/null +++ b/src/prefs/sounds/Santa/PrefilledBitmap.cpp @@ -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() +{ } \ No newline at end of file diff --git a/src/prefs/sounds/Santa/PrefilledBitmap.h b/src/prefs/sounds/Santa/PrefilledBitmap.h new file mode 100644 index 0000000000..5797c72ec8 --- /dev/null +++ b/src/prefs/sounds/Santa/PrefilledBitmap.h @@ -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 + + +//****************************************************************************************************** +//**** 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 diff --git a/src/prefs/sounds/Santa/ScrollViewCorner.cpp b/src/prefs/sounds/Santa/ScrollViewCorner.cpp new file mode 100644 index 0000000000..416056bb6d --- /dev/null +++ b/src/prefs/sounds/Santa/ScrollViewCorner.cpp @@ -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 +#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)); +} + + diff --git a/src/prefs/sounds/Santa/ScrollViewCorner.h b/src/prefs/sounds/Santa/ScrollViewCorner.h new file mode 100644 index 0000000000..4c051faccc --- /dev/null +++ b/src/prefs/sounds/Santa/ScrollViewCorner.h @@ -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 + + +//****************************************************************************************************** +//**** CLASS DECLARATIONS +//****************************************************************************************************** +class ScrollViewCorner : public BView +{ + public: + ScrollViewCorner(float Left,float Top); + ~ScrollViewCorner(); + virtual void Draw(BRect Update); +}; + + +#endif diff --git a/src/prefs/sounds/Santa/TextEntryAlert.cpp b/src/prefs/sounds/Santa/TextEntryAlert.cpp new file mode 100644 index 0000000000..e996b116fb --- /dev/null +++ b/src/prefs/sounds/Santa/TextEntryAlert.cpp @@ -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 +#include +#include + + +//****************************************************************************************************** +//**** 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); + } +} + + diff --git a/src/prefs/sounds/Santa/TextEntryAlert.h b/src/prefs/sounds/Santa/TextEntryAlert.h new file mode 100644 index 0000000000..7a00f1d67a --- /dev/null +++ b/src/prefs/sounds/Santa/TextEntryAlert.h @@ -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 +#include +#include + + +//****************************************************************************************************** +//**** 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_ diff --git a/src/prefs/sounds/Utils/BitmapView.h b/src/prefs/sounds/Utils/BitmapView.h new file mode 100644 index 0000000000..618c231fa1 --- /dev/null +++ b/src/prefs/sounds/Utils/BitmapView.h @@ -0,0 +1,20 @@ +#ifndef __HBITMAPVIEW_H__ +#define __HBITMAPVIEW_H__ + +#include +#include + +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 \ No newline at end of file diff --git a/src/prefs/sounds/Utils/CTextView.cpp b/src/prefs/sounds/Utils/CTextView.cpp new file mode 100644 index 0000000000..e121ee6c2b --- /dev/null +++ b/src/prefs/sounds/Utils/CTextView.cpp @@ -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; +} diff --git a/src/prefs/sounds/Utils/CTextView.h b/src/prefs/sounds/Utils/CTextView.h new file mode 100644 index 0000000000..bcb1d3e308 --- /dev/null +++ b/src/prefs/sounds/Utils/CTextView.h @@ -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 + + +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 diff --git a/src/prefs/sounds/Utils/HAboutWindow.cpp b/src/prefs/sounds/Utils/HAboutWindow.cpp new file mode 100644 index 0000000000..e43eba8427 --- /dev/null +++ b/src/prefs/sounds/Utils/HAboutWindow.cpp @@ -0,0 +1,363 @@ +#include "HAboutWindow.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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]; + 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; +} \ No newline at end of file diff --git a/src/prefs/sounds/Utils/HAboutWindow.h b/src/prefs/sounds/Utils/HAboutWindow.h new file mode 100644 index 0000000000..5b2943c86b --- /dev/null +++ b/src/prefs/sounds/Utils/HAboutWindow.h @@ -0,0 +1,66 @@ +#ifndef __ABOUTWINDOW_H__ +#define __ABOUTWINDOW_H__ + +#include +#include +#include +#include + +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 \ No newline at end of file diff --git a/src/prefs/sounds/Utils/IconMenuItem.cpp b/src/prefs/sounds/Utils/IconMenuItem.cpp new file mode 100644 index 0000000000..9ddb2449cd --- /dev/null +++ b/src/prefs/sounds/Utils/IconMenuItem.cpp @@ -0,0 +1,107 @@ +#include "IconMenuItem.h" + +#include + +/*********************************************************** + * 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; + } +} + diff --git a/src/prefs/sounds/Utils/IconMenuItem.h b/src/prefs/sounds/Utils/IconMenuItem.h new file mode 100644 index 0000000000..fd74f27201 --- /dev/null +++ b/src/prefs/sounds/Utils/IconMenuItem.h @@ -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 + +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 diff --git a/src/prefs/sounds/Utils/RectUtils.cpp b/src/prefs/sounds/Utils/RectUtils.cpp new file mode 100644 index 0000000000..f87dfd6193 --- /dev/null +++ b/src/prefs/sounds/Utils/RectUtils.cpp @@ -0,0 +1,59 @@ +#include "RectUtils.h" +#include +#include +#include +#include + +/* +* 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)); +} diff --git a/src/prefs/sounds/Utils/RectUtils.h b/src/prefs/sounds/Utils/RectUtils.h new file mode 100644 index 0000000000..99d8687331 --- /dev/null +++ b/src/prefs/sounds/Utils/RectUtils.h @@ -0,0 +1,14 @@ +#ifndef __RECTUTILS_H__ +#define __RECTUTILS_H__ + +#include + +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 \ No newline at end of file diff --git a/src/prefs/sounds/Utils/ResourceUtils.cpp b/src/prefs/sounds/Utils/ResourceUtils.cpp new file mode 100644 index 0000000000..ce7724c62d --- /dev/null +++ b/src/prefs/sounds/Utils/ResourceUtils.cpp @@ -0,0 +1,279 @@ +#include "ResourceUtils.h" +#include +#include +#include + +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); +} diff --git a/src/prefs/sounds/Utils/ResourceUtils.h b/src/prefs/sounds/Utils/ResourceUtils.h new file mode 100644 index 0000000000..06b8c8531a --- /dev/null +++ b/src/prefs/sounds/Utils/ResourceUtils.h @@ -0,0 +1,37 @@ +#ifndef __RESOURCEUTILS_H__ +#define __RESOURCEUTILS_H__ + +#include +#include +#include +#include +#include +#include +#include +#include + +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 diff --git a/src/prefs/sounds/Utils/URLView.cpp b/src/prefs/sounds/Utils/URLView.cpp new file mode 100644 index 0000000000..fda2afeffb --- /dev/null +++ b/src/prefs/sounds/Utils/URLView.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#include + + + +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 ); + } +} \ No newline at end of file diff --git a/src/prefs/sounds/Utils/URLView.h b/src/prefs/sounds/Utils/URLView.h new file mode 100644 index 0000000000..cda8f54f64 --- /dev/null +++ b/src/prefs/sounds/Utils/URLView.h @@ -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 +#include +#include +#include +#include +#include + + + +// 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 \ No newline at end of file diff --git a/src/prefs/sounds/main.cpp b/src/prefs/sounds/main.cpp new file mode 100644 index 0000000000..242de3c244 --- /dev/null +++ b/src/prefs/sounds/main.cpp @@ -0,0 +1,8 @@ +#include "HApp.h" + +int main() +{ + HApp app; + app.Run(); + return 0; +} \ No newline at end of file