Files
Yab2Cpp/src/SplitPane.cpp
2015-04-13 13:40:27 -07:00

727 lines
22 KiB
C++

/*******************************************************
* SplitPane©
*
* SplitPane is a usefull UI component. It alows the
* use to ajust two view Horizontaly or Vertacly so
* that they are a desired size. This type of Pane
* shows up most comonly in Mail/News Readers.
*
* @author YNOP (ynop@acm.org)
* @version beta
* @date Dec. 10 1999
*******************************************************/
#include <AppKit.h>
#include <InterfaceKit.h>
#include <StorageKit.h>
#include <String.h>
#include <Path.h>
#include <TranslationKit.h>
#include <TranslationUtils.h>
//#include <stdio.h>
#include "SplitPane.h"
//#include "SplitPaneConfig.h"
/*******************************************************
* Setup the main view. Add in all the niffty components
* we have made and get things rolling
*******************************************************/
SplitPane::SplitPane(BRect frame, const char* name, BView *one, BView *two,uint32 Mode):BView(frame, name, Mode,B_WILL_DRAW|B_FRAME_EVENTS){
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); // This is default get from parent if exist
//SetViewColor(B_TRANSPARENT_32_BIT); // go tran so we have control over drawing
BRect b;
b = Bounds();
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
PaneOne = one;
PaneTwo = two;
align = B_VERTICAL; // Most people use it this way
pos = (int)b.Width()/2; // Center is a good start place
thickness = 10;
jump = 1; // 1 makes a smother slide
VOneDetachable = false;
VTwoDetachable = false;
pad = 1;
MinSizeOne = 0; // full left
MinSizeTwo = 0; // full right
poslocked = false; // free movement
alignlocked = false; // free alignment
Draggin = false;
attached = false;
WinOne = NULL;
WinTwo = NULL;
ConfigWindow = NULL;
AddChild(one);
AddChild(two);
}
/*******************************************************
* When ready grap the parents color and refreash.
*******************************************************/
void SplitPane::AttachedToWindow(){
//SetViewColor(Parent()->ViewColor());
attached = true;
Update();
}
/*******************************************************
* If we are being resized. Fix the stuff we need to fix
*******************************************************/
void SplitPane::FrameResized(float,float){
// if bar is on the left side follow left
// else if it is on the right side follow the right
// Need to implements smart follow still
Update();
Invalidate();
}
/*******************************************************
* The main draw stuff here. basicly just the slider
*******************************************************/
void SplitPane::Draw(BRect f){
SetHighColor(160,160,160);
if(align == B_VERTICAL){
SetHighColor(185,185,185);
// SetHighColor(145,145,145);
//FillRect(BRect(pos,Bounds().top+pad+1,pos,Bounds().bottom-pad-1)); // 145
FillRect(BRect(pos,Bounds().top+pad+1,pos,Bounds().bottom-pad-1)); // 145
SetHighColor(255,255,255);
FillRect(BRect(pos+1,Bounds().top+pad+1,pos+2,Bounds().bottom-pad-1)); // 255
//SetHighColor(216,216,216);
SetHighColor(Parent()->ViewColor());
FillRect(BRect(pos+2,Bounds().top+pad+1,pos+thickness-2,Bounds().bottom-pad-1));// 216
if(thickness>9)
{
float y = (Bounds().bottom - Bounds().top)/2;
float x = pos + (thickness/2);
SetPenSize(2);
SetHighColor(255,255,255);
StrokeLine(BPoint(x-3,y-11),BPoint(x+3,y-5));
StrokeLine(BPoint(x-3,y-7),BPoint(x+3,y-1));
StrokeLine(BPoint(x-3,y-3),BPoint(x+3,y+3));
StrokeLine(BPoint(x-3,y+1),BPoint(x+3,y+7));
StrokeLine(BPoint(x-3,y+5),BPoint(x+3,y+11));
SetPenSize(1);
SetHighColor(145,145,145);
StrokeLine(BPoint(x-3,y-10),BPoint(x+3,y-4));
StrokeLine(BPoint(x-3,y-6),BPoint(x+3,y+0));
StrokeLine(BPoint(x-3,y-2),BPoint(x+3,y+4));
StrokeLine(BPoint(x-3,y+2),BPoint(x+3,y+8));
StrokeLine(BPoint(x-3,y+6),BPoint(x+3,y+12));
}
SetHighColor(185,185,185);
// SetHighColor(145,145,145);
FillRect(BRect(pos+thickness-2,Bounds().top+pad+1,pos+thickness-2,Bounds().bottom-pad-1)) ;// 145
SetHighColor(145,145,145);
FillRect(BRect(pos+thickness-1,Bounds().top+pad+1,pos+thickness-1,Bounds().bottom-pad-1)); // 96
//FillRect(BRect(pos+thickness,Bounds().top+pad+1,pos+thickness,Bounds().bottom-pad-1));
}else{
SetHighColor(185,185,185);
// SetHighColor(145,145,145);
//FillRect(BRect(Bounds().left+pad+1,pos,Bounds().right-pad-1,pos)); // 145
FillRect(BRect(Bounds().left+pad+1,pos,Bounds().right-pad-1,pos)); // 145
SetHighColor(255,255,255);
FillRect(BRect(Bounds().left+pad+1,pos+1,Bounds().right-pad-1,pos+2)); // 255
//SetHighColor(216,216,216);
SetHighColor(Parent()->ViewColor());
FillRect(BRect(Bounds().left+pad+1,pos+2,Bounds().right-pad-1,pos+thickness-2));// 216
if(thickness>9)
{
SetHighColor(255,255,255);
float x = (Bounds().right - Bounds().left)/2;
float y = pos + (thickness/2);
SetPenSize(2);
StrokeLine(BPoint(x-11,y-3),BPoint(x-5,y+3));
StrokeLine(BPoint(x-7,y-3),BPoint(x-1,y+3));
StrokeLine(BPoint(x-3,y-3),BPoint(x+3,y+3));
StrokeLine(BPoint(x+1,y-3),BPoint(x+7,y+3));
StrokeLine(BPoint(x+5,y-3),BPoint(x+11,y+3));
SetPenSize(1);
SetHighColor(145,145,145);
StrokeLine(BPoint(x-10,y-3),BPoint(x-4,y+3));
StrokeLine(BPoint(x-6,y-3),BPoint(x+0,y+3));
StrokeLine(BPoint(x-2,y-3),BPoint(x+4,y+3));
StrokeLine(BPoint(x+2,y-3),BPoint(x+8,y+3));
StrokeLine(BPoint(x+6,y-3),BPoint(x+12,y+3));
}
SetHighColor(185,185,185);
// SetHighColor(145,145,145);
FillRect(BRect(Bounds().left+pad+1,pos+thickness-2,Bounds().right-pad-1,pos+thickness-2)) ;// 145
SetHighColor(145,145,145);
// SetHighColor(96,96,96);
FillRect(BRect(Bounds().left+pad+1,pos+thickness-1,Bounds().right-pad-1,pos+thickness-1)); // 96
//FillRect(BRect(Bounds().left+pad+1,pos+thickness,Bounds().right-pad-1,pos+thickness));
}
}
/*******************************************************
* Keeps Modes for both panles uptodate and acctually
* is the func that sets the location of the slider
*******************************************************/
void SplitPane::Update(){
Window()->Lock();
if(align == B_VERTICAL){
PaneOne->SetResizingMode(B_FOLLOW_LEFT|B_FOLLOW_TOP_BOTTOM);
PaneTwo->SetResizingMode(B_FOLLOW_LEFT_RIGHT|B_FOLLOW_TOP_BOTTOM);
if(pos > (Bounds().Width()-thickness-MinSizeTwo)){
if(!poslocked){
pos = (int)Bounds().Width()-thickness-MinSizeTwo;
}
}
if(pos < MinSizeOne){
if(!poslocked){
pos = MinSizeOne;
}
}
}else{
PaneOne->SetResizingMode(B_FOLLOW_LEFT_RIGHT|B_FOLLOW_TOP);
PaneTwo->SetResizingMode(B_FOLLOW_LEFT_RIGHT|B_FOLLOW_TOP_BOTTOM);
if(pos > (Bounds().Height()-thickness-MinSizeTwo)){
if(!poslocked){
pos = (int)Bounds().Height()-thickness-MinSizeTwo;
}
}
if(pos < MinSizeOne){
if(!poslocked){
pos = MinSizeOne;
}
}
}
// this block should go in FrameResized .. think about it
if(align == B_VERTICAL){
if(pos >= (Bounds().IntegerWidth()/2)){
//pos should follow the right side
// staying the same distans from it that
// it is right now
}
}else{
if(pos >= (Bounds().IntegerHeight()/2)){
//should follow bottom and stay the
// same distance that we are way from
// it now
}
}
if(PaneOne){
if(!WinOne){
if(align == B_VERTICAL){
PaneOne->MoveTo(pad,Bounds().top+pad);
PaneOne->ResizeTo(pos-pad, Bounds().Height()-pad-pad); // widht x height
}else{
PaneOne->MoveTo(pad,Bounds().top+pad);
PaneOne->ResizeTo(Bounds().Width()-pad-pad, pos-pad-pad); // widht x height
}
}
}
if(PaneTwo){
if(!WinTwo){
if(align == B_VERTICAL){
PaneTwo->MoveTo(pos+thickness,Bounds().top+pad);
PaneTwo->ResizeTo(Bounds().Width()-(pos+thickness)-pad, Bounds().Height()-pad-pad);
}else{
PaneTwo->MoveTo(Bounds().left+pad,pos+thickness);
PaneTwo->ResizeTo(Bounds().Width()-pad-pad, Bounds().Height()-pos-pad-thickness);
}
}
}
Window()->Unlock();
}
/*******************************************************
* Hook for when we click. This takes care of all the
* little stuff - Like where is the mouse and what is
* going on.
*******************************************************/
void SplitPane::MouseDown(BPoint where){
Window()->Lock();
BMessage *currentMsg = Window()->CurrentMessage();
if (currentMsg->what == B_MOUSE_DOWN) {
uint32 buttons = 0;
currentMsg->FindInt32("buttons", (int32 *)&buttons);
uint32 modifiers = 0;
currentMsg->FindInt32("modifiers", (int32 *)&modifiers);
uint32 clicks = 0;
currentMsg->FindInt32("clicks",(int32*)&clicks);
if (buttons & B_SECONDARY_MOUSE_BUTTON){
if(!alignlocked){
switch(align){
case B_VERTICAL:
align = B_HORIZONTAL;
break;
case B_HORIZONTAL:
align = B_VERTICAL;
break;
}
Update();
Invalidate();
}
/*if(VOneDetachable){
WinOne = new BWindow(ConvertToScreen(PaneOne->Bounds()),"PanelOne",B_TITLED_WINDOW,B_ASYNCHRONOUS_CONTROLS);
RemoveChild(PaneOne);
WinOne->AddChild(PaneOne);
PaneOne->SetResizingMode(B_FOLLOW_ALL_SIDES);
// PaneOne->SetTarget(this);
WinOne->Show();
}*/
}
// if((buttons & B_PRIMARY_MOUSE_BUTTON) && (clicks >= 2)){
//Config window for split pane
// (new BAlert(NULL,"This is - or will be - a configuration panel for SplitPane.","Ok"))->Go();
//ConfigWindow = new SplitPaneConfig(this);
//ConfigWindow->Show();
//}else
if((buttons & B_PRIMARY_MOUSE_BUTTON) && !Draggin){
if(!poslocked){
Draggin= true; // this is so we can drag
here = where;
}
SetMouseEventMask(B_POINTER_EVENTS,B_LOCK_WINDOW_FOCUS);
}
}
Window()->Unlock();
}
/*******************************************************
* If we unclick then stop dragging or whatever it is
* we are doing
*******************************************************/
void SplitPane::MouseUp(BPoint where){
Draggin = false; // stop following mouse
}
/*******************************************************
* If the mouse moves while we dragg. Then follow it
* Also Invalidate so we update the views
*******************************************************/
void SplitPane::MouseMoved(BPoint where,uint32 info,const BMessage *m){
if(Draggin){
switch(align){
case B_HORIZONTAL:
pos = (int)(where.y)-(thickness/2);//- here.x
break;
case B_VERTICAL:
pos = (int)(where.x)-(thickness/2);
break;
}
/*
// This code figures out which jump we are closest
// to and if needed we "snap" to that.
int c = Bounds().IntegerWidth() / pos
Jump * c ... hmmm this is not right at all
*/
if(pos < MinSizeOne){
pos = MinSizeOne;
}
if(align == B_VERTICAL){
if(pos > (Bounds().Width() - thickness - MinSizeTwo)){
pos = (int)(Bounds().Width() - thickness - MinSizeTwo + 1);
}
}else{
if(pos > (Bounds().Height() - thickness - MinSizeTwo)){
pos = (int)(Bounds().Height() - thickness - MinSizeTwo + 1);
}
}
Update();
Invalidate();
}
}
/*******************************************************
* If you already have a view One, but want to change
* if for some odd reason. This should work.
*******************************************************/
void SplitPane::AddChildOne(BView *v){
RemoveChild(PaneOne);
PaneOne = v;
AddChild(PaneOne);
}
/*void SplitPane::MakePaneTwoFocus()
{
if(PaneTwo)
PaneTwo->MakeFocus();
}
*/
/*******************************************************
* If you already have a view Two, and want to put
* another view there, this is what to use.
*******************************************************/
void SplitPane::AddChildTwo(BView* v,bool IsAdded,bool ShowAfterHide)
{
if(!v->IsHidden())
v->Hide();
PaneTwo = v;
//WinTwo = NULL;
PaneTwo = v;
if(IsAdded)
{
Update();
if(ShowAfterHide)
{
if(v->IsHidden())
v->Show();
}
}
if(!IsAdded)
{
AddChild(PaneTwo);
}
PaneTwo = v;
}
/*******************************************************
* Sets is we are horizontal or Vertical. We use the
* standard B_HORIZONTAL and B_VERTICAL flags for this
*******************************************************/
void SplitPane::SetAlignment(uint a){
align = a;
if(attached){
Update();
}
Invalidate();
}
/*******************************************************
* Returns wheather the slider is horizontal or vertical
*******************************************************/
uint SplitPane::GetAlignment(){
return align;
}
/*******************************************************
* Sets the location of the bar. (we do no bounds
* checking for you so if its off the window thats
* your problem)
*******************************************************/
void SplitPane::SetBarPosition(int i){
pos = i;
if(attached){
Update();
}
Invalidate();
}
/*******************************************************
* Returns about where the bar is ...
*******************************************************/
int SplitPane::GetBarPosition(){
return pos;
}
/*******************************************************
* Sets how thick the bar should be.
*******************************************************/
void SplitPane::SetBarThickness(int i){
thickness = i;
if(attached){
Update();
}
Invalidate();
}
/*******************************************************
* Retuns to us the thickness of the slider bar
*******************************************************/
int SplitPane::GetBarThickness(){
return thickness;
}
/*******************************************************
* Sets the amount of jump the bar has when it is
* moved. This can also be though of as snap. The bar
* will start at 0 and jump(snap) to everry J pixels.
*******************************************************/
void SplitPane::SetJump(int i){
jump = i;
if(attached){
Update();
}
}
/*******************************************************
* Lets you know what the jump is .. see SetJump
*******************************************************/
int SplitPane::GetJump(){
return jump;
}
/*******************************************************
* Do we have a View One or is it NULL
*******************************************************/
bool SplitPane::HasViewOne(){
if(PaneOne) return true;
return false;
}
/*******************************************************
* Do we have a View Two .. or is it NULL too
*******************************************************/
bool SplitPane::HasViewTwo(){
if(PaneTwo) return true;
return false;
}
/*******************************************************
* Sets wheather View one is detachable from the
* slider view and from the app. This will creat a
* window that is detached (floating) from the app.
*******************************************************/
void SplitPane::SetViewOneDetachable(bool b){
VOneDetachable = b;
}
/*******************************************************
* Sets view tow detachable or not
*******************************************************/
void SplitPane::SetViewTwoDetachable(bool b){
VTwoDetachable = b;
}
/*******************************************************
* Returns whether the view is detachable
*******************************************************/
bool SplitPane::IsViewOneDetachable(){
return VOneDetachable;
}
/*******************************************************
* Returns if this view is detachable
*******************************************************/
bool SplitPane::IsViewTwoDetachable(){
return VTwoDetachable;
}
/*******************************************************
* Tells the view if the user is alowed to open the
* configuration window for the slider.
*******************************************************/
void SplitPane::SetEditable(bool b){
//ADD CODE HERE YNOP
}
/*******************************************************
* Tells use if the split pane is user editable
*******************************************************/
bool SplitPane::IsEditable(){
return true; //ADD SOME MORE CODE HERE
}
/*******************************************************
* Sets the inset that the view has.
*******************************************************/
void SplitPane::SetViewInsetBy(int i){
pad = i;
if(attached){
Update();
} Invalidate();
}
/*******************************************************
* Returns to use the padding around the views
*******************************************************/
int SplitPane::GetViewInsetBy(){
return pad;
}
/*******************************************************
* This sets the minimum size that View one can be.
* if the user trys to go past this .. we just stop
* By default the minimum size is set to 0 (zero) so
* the user can put the slider anywhere.
*******************************************************/
void SplitPane::SetMinSizeOne(int i){
MinSizeOne = i;
}
/*******************************************************
* Gives us the minimum size that one can be.
*******************************************************/
int SplitPane::GetMinSizeOne(){
return MinSizeOne;
}
/*******************************************************
* This sets the Minimum size that the second view
* can be.
*******************************************************/
void SplitPane::SetMinSizeTwo(int i){
MinSizeTwo = i;
}
/*******************************************************
* Lets us know what that minimum size is.
*******************************************************/
int SplitPane::GetMinSizeTwo(){
return MinSizeTwo;
}
/*******************************************************
* Locks the bar from being moved by the User. The
* system can still move the bar (via SetBarPosition)
*******************************************************/
void SplitPane::SetBarLocked(bool b){
poslocked = b;
}
/*******************************************************
* Returns to use if the bar is in a locked state or
* not.
*******************************************************/
bool SplitPane::IsBarLocked(){
return poslocked;
}
/*******************************************************
* Locks the alignment of the bar. The user can no
* longer toggle between Horizontal and Vertical
* Slider bar. Again you can still progomaticly set
* the position how ever you want.
*******************************************************/
void SplitPane::SetBarAlignmentLocked(bool b){
alignlocked = b;
}
/*******************************************************
* Lets us know about the lock state of the bar
*******************************************************/
bool SplitPane::IsBarAlignmentLocked(){
return alignlocked;
}
/*******************************************************
* Gets the Total state of the bar, alignment, size,
* position and many other things that are required
* to fully capture the state of the SplitPane.
* We pack all of this into a cute little BMessage
* so that it is esally expandable and can be saved
* off easyaly too. The SplitPane System does not
* however save the state for you. Your program must
* grab the state and save it in its config file.
*******************************************************/
BMessage* SplitPane::GetState(){
BMessage *state;
state = new BMessage(SPLITPANE_STATE);
state->AddBool("onedetachable",VOneDetachable);
state->AddBool("twodetachable",VTwoDetachable);
state->AddInt32("align",align);
state->AddInt32("pos",pos);
state->AddInt32("thick",thickness);
state->AddInt32("jump",jump);
state->AddInt32("pad",pad);
state->AddInt32("minsizeone",MinSizeOne);
state->AddInt32("minsizetwo",MinSizeTwo);
state->AddBool("poslock",poslocked);
state->AddBool("alignlock",alignlocked);
return state;
// delete state;
}
/*******************************************************
* Sets the state of the SplitPane from a BMessage
* like the one recived from GetState().
* This is one of three ways the user can rebuild the
* state of the SplitPane. The second is to simply
* send the SplitPane the state message, it is the
* same as calling SetState but it ashyncronouse.
* The third way is to use all the Get/Set methouds
* for each element of the SplitPane, this way is
* long and boarding. I suggest you just send the
* View a message :)
*******************************************************/
void SplitPane::SetState(BMessage *state){
int32 Npos,Nthickness,Njump,Npad,NMSO,NMST;
int32 Nalign;//uint
if(state->FindBool("onedetachable",&VOneDetachable) != B_OK){
VOneDetachable = false;
}
if(state->FindBool("towdetachable",&VTwoDetachable) != B_OK){
VTwoDetachable = false;
}
if(state->FindInt32("align",&Nalign) == B_OK){
align = Nalign;
}
if(state->FindInt32("pos",&Npos) == B_OK){
pos = Npos;
}
if(state->FindInt32("thick",&Nthickness) == B_OK){
thickness = Nthickness;
}
if(state->FindInt32("jump",&Njump) == B_OK){
jump = Njump;
}
if(state->FindInt32("pad",&Npad) == B_OK){
pad = Npad;
}
if(state->FindInt32("minsizeonw",&NMSO) == B_OK){
MinSizeOne = NMSO;
}
if(state->FindInt32("minsizetwo",&NMST) == B_OK){
MinSizeTwo = NMST;
}
if(state->FindBool("poslock",&poslocked) != B_OK){
poslocked = false;
}
if(state->FindBool("alignlock",&alignlocked) != B_OK){
alignlocked = false;
}
Update();
Invalidate();
}
/*******************************************************
* Ok, hmm what does this do. NOT MUCH. if we get a
* STATE message then lets set the state. This is here
* to provide a asyncronuse way of seting the state and
* also to make life easyer.
*******************************************************/
void SplitPane::MessageReceived(BMessage *msg){
switch(msg->what){
case SPLITPANE_STATE:
SetState(msg);
break;
default:
BView::MessageReceived(msg);
break;
}
}