/////////////////////////////////////////////////////////////////////////////
// Name:        wxTabbedWindow, wxPagingCtrl
// Purpose:     Contrib. demo
// Author:      Aleksandras Gluchovas (email: alex@soften.klu.lt)
//
// Modified by: 19990908 : mj10777@mj10777.de
//              - rename to tabpgwin
//              - restruction of Variable declaration
//              - to prevent Warnings under MingGW32
// Modified by: 19990909 : Mark Johnson, mj10777@mj10777.de
//              - mNoVertScroll TRUE = no / FALSE = Original Code
//                the Original Code Paints a Vertical Scroll in wxPagingCtrl
//                which is not needed in this Version. Use TRUE for this.
// Modified by: 20000401 : mj10777@mj10777.de
//              - Added a member Variable in class twTabInfo
//                - int       mTabID;     // tab ID Number
//              - Added a Variable Parameter to class wxTabbedWindow::AddTab
//                - int       tabID,
//              In this way a Tab can be identified by a unique ID-Number :
//              - The unique ID-Number for a View is the same as a Menu-ID
//              - The Menu has been activated
//              - A routine can look through all the Tabs looking for the ID
//                - if found     : the Tab will be activated : SetActiveTab(num)
//                - if not found : the View should be created
// Modified by: 20001001 : mj10777@mj10777.de
//              - Support for Transparity-Colour in Tab
//              - Support for Right-Mouse click  in Tab
// Created:     07/09/98
// Copyright:   (c) Aleksandras Gluchovas
// Licence:    wxWindows license
// RCS-ID:      $Id: pagectrl.cpp,v 1.4 2001/04/23 19:47:16 hansvl Exp $
/////////////////////////////////////////////////////////////////////////////

#ifdef __GNUG__
#pragma implementation "controlarea.h"
// #pragma interface
#endif

// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif

#include <math.h>
#include <stdlib.h>

#include "wx/string.h"
#include "wx/utils.h"     // import wxMin/wxMax macros and wxFileExist(..)

#include "wx/wspp/pagectrl.h"
//---------------------------------------------------------------------------
// Implementation for class twTabInfo
//---------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(twTabInfo, wxObject)
  //---------------------------------------------------------------------------
  twTabInfo::twTabInfo()
    : mpContent(0)
{}
//---------------------------------------------------------------------------
twTabInfo::~twTabInfo()
{
	// FOR NOW:: nothing
}
//---------------------------------------------------------------------------
int twTabInfo::ImgWidth()
{
	if (mBitMap.Ok()) return mBitMap.GetWidth();
	else return 0;
}
//---------------------------------------------------------------------------
int twTabInfo::ImgHeight()
{
	if (mBitMap.Ok()) return mBitMap.GetHeight();
	else return 0;
}
//---------------------------------------------------------------------------
int twTabInfo::ImageToTxtGap(int prefGap)
{
	if (mBitMap.Ok()) return prefGap;
	else return 0;
}
//---------------------------------------------------------------------------
bool twTabInfo::HasImg()
{
	return mBitMap.Ok();
}
//---------------------------------------------------------------------------
// bool twTabInfo::HasText();
unsigned int twTabInfo::HasText()
{
	return mText.Length();
}
//---------------------------------------------------------------------------
wxBitmap& twTabInfo::GetImg()
{
	return mBitMap;
}
//---------------------------------------------------------------------------
wxString& twTabInfo::GetText()
{
	return mText;
}
//---------------------------------------------------------------------------
wxWindow& twTabInfo::GetContent()
{
	return *mpContent;
}
//---------------------------------------------------------------------------
// Implementation for class wxTabbedWindow
//---------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(wxTabbedWindow, wxPanel)
  //---------------------------------------------------------------------------
  BEGIN_EVENT_TABLE(wxTabbedWindow, wxPanel)
  EVT_SIZE (wxTabbedWindow::OnSize)
  EVT_PAINT(wxTabbedWindow::OnPaint)
  EVT_LEFT_DOWN(wxTabbedWindow::OnLButtonDown)
  EVT_RIGHT_DOWN(wxTabbedWindow::OnRButtonDown)
  // TDB:: filciker reduction
  // EVT_ERASE_BACKGROUND(wxTabbedWindow::OnBkErase)
END_EVENT_TABLE()
//---------------------------------------------------------------------------

wxTabbedWindow::wxTabbedWindow( wxWindow* parent, wxWindowID id, 
								const wxPoint& pos, 
								const wxSize& size, 
								long style, const wxString& name )
	: wxPanel( parent, id, pos, size, style, name )
{
	DoInitTabbedWindow();
	
	SetBackgroundColour( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_3DFACE ) );
}

wxTabbedWindow::wxTabbedWindow()
{
	DoInitTabbedWindow();
}

void wxTabbedWindow::DoInitTabbedWindow()
{
	mpTabScroll   = NULL;
	mpHorizScroll = NULL;
	mpVertScroll  = NULL;
	
	mVertGap  = 0;
	mHorizGap = 0;
	
	mTitleVertGap  = 3;
	mTitleHorizGap = 4;
	mImageTextGap  = 2;
	mFirstTitleGap = 11;
	
	mBorderOnlyWidth = 8;
	
	mWhitePen = wxPen(wxColour(255,255,255), 1, wxSOLID);
	mGrayPen  = wxPen(wxColour(192,192,192), 1, wxSOLID);
	mDarkPen  = wxPen(wxColour(128,128,128), 1, wxSOLID);
	mBlackPen = wxPen(wxColour( 0,  0,  0), 1, wxSOLID);
	
	// state variables
	
	mActiveTab   = 0;
	mTitleHeight = 0;
	mLayoutType  = wxTITLE_IMG_AND_TEXT;
	
	mGenerateMasks = FALSE;
	mMaskColor     = *wxWHITE;
}

//---------------------------------------------------------------------------
wxTabbedWindow::~wxTabbedWindow()
{
	wxNode* pTab = mTabs.First();
	
	while(pTab)
    {
		delete ((twTabInfo*)pTab->Data());
		pTab = pTab->Next();
    }
}
//---------------------------------------------------------------------------
void wxTabbedWindow::SizeTabs(int x,int y, int width, int height, bool repant)
{
	wxNode* pTabNode = mTabs.First();
	int n = 0;
	
	while(pTabNode)
	{
		twTabInfo& info = *((twTabInfo*)pTabNode->Data());
		
		if (n == mActiveTab)
		{
			//wxSizeEvent evt;
			//info.mpContent->GetEventHandler()->ProcessEvent(evt);
			
			info.mpContent->SetSize(x, y, width, height, 0);
			info.mpContent->Show(TRUE);
			info.mpContent->Refresh();
			
		}
		else
		{
			info.mpContent->Show(FALSE);
		}
		
		pTabNode = pTabNode->Next();
		++n;
	}
}
//---------------------------------------------------------------------------
void wxTabbedWindow::AddTab(wxWindow* pContent,
							wxString  tabText,
							int       tabID,              // tab ID Number
							wxString  imageFileName,
							long      imageType)
{
	twTabInfo* pTab = new twTabInfo();
	
	pTab->mpContent = pContent;
	pTab->mText     = tabText;
	pTab->mTabID    = tabID;
	
	if (wxFileExists(imageFileName) &&
		
		pTab->mBitMap.LoadFile(imageFileName, imageType))
    {
		pTab->mImageFile = imageFileName;
		pTab->mImageType = imageType;
		
		PrepareBitmap(pTab->mBitMap);
    }
	
	
	if (pContent->GetParent() == NULL)
		pContent->Create(this, -1);
	
	mTabs.Append((wxObject*)pTab);
	
	RecalcLayout(TRUE);
	
	OnTabAdded(pTab);
}
//---------------------------------------------------------------------------
void wxTabbedWindow::AddTab(wxWindow* pContent,
							wxString  tabText,
							int       tabID,              // tab ID Number  
							wxBitmap* pImage  )
{
	twTabInfo* pTab = new twTabInfo();
	
	pTab->mpContent = pContent;
	pTab->mText     = tabText;
	pTab->mTabID    = tabID;
	
	if (pImage)
	{
		pTab->mBitMap = *pImage;
		PrepareBitmap(pTab->mBitMap);
	}
	
	if (pContent->GetParent() == NULL)
		pContent->Create(this, -1);
	
	mTabs.Append((wxObject*)pTab);
	RecalcLayout(TRUE);
	OnTabAdded(pTab);
}

void wxTabbedWindow::PrepareBitmap(wxBitmap& bmp)
{
	if (!mGenerateMasks) return;
	
	if (bmp.GetMask() != NULL) return;
	
	bmp.SetMask(new wxMask(bmp, GetMaskColor()));
}

//---------------------------------------------------------------------------
void wxTabbedWindow::RemoveTab(int tabNo)
{
	twTabInfo* pTab = ((twTabInfo*)(mTabs.Nth(tabNo)->Data()));
	pTab->mpContent->Destroy();
	delete pTab;
	mTabs.DeleteNode(mTabs.Nth(tabNo));
	// if (mActiveTab >= mTabs.Number());
	if (mActiveTab >= mTabs.Number())
		mActiveTab = mTabs.Number() - 1;
	SetActiveTab(mActiveTab);
}
//---------------------------------------------------------------------------
int wxTabbedWindow::GetTabCount()
{
	return mTabs.Number();
}
//---------------------------------------------------------------------------
wxWindow* wxTabbedWindow::GetTab(int tabNo)
{
	return ((twTabInfo*)(mTabs.Nth(tabNo)->Data()))->mpContent;
}
//---------------------------------------------------------------------------
wxWindow* wxTabbedWindow::GetActiveTab()
{
	// FIMXE:: this is lame
	return GetTab(mActiveTab);
}
//---------------------------------------------------------------------------
void wxTabbedWindow::SetActiveTab(int tabNo)
{
	mActiveTab = tabNo;
	RecalcLayout(TRUE);
	Refresh();

	// Get the Scrollbars update... with a trick
	// I hope this works for all controls with wxTabbedWindow aware scrolling...
	if (mActiveTab < mTabs.Number())
	{
		wxSizeEvent event; // Fake event. It just to get OnSize to be called.
		GetActiveTab()->GetEventHandler()->ProcessEvent(event);

		GetActiveTab()->SetFocus();
	}
}

void wxTabbedWindow::SetGenerateMasks(bool on)
{
	mGenerateMasks = on;
}

bool wxTabbedWindow::GetGenerateMasks()
{
	return mGenerateMasks;
}

void wxTabbedWindow::SetMaskColor(const wxColour& col)
{
	mMaskColor = col;
}

wxColour& wxTabbedWindow::GetMaskColor()
{
	return mMaskColor;
}


//---------------------------------------------------------------------------
// width of the decorations border (4 shade-lines), should not be changed
//---------------------------------------------------------------------------
#define BORDER_SZ 4
//---------------------------------------------------------------------------
void wxTabbedWindow::DrawShadedRect(int x, int y, int width, int height,
									wxPen& upperPen, wxPen& lowerPen, wxDC& dc
									)
{
	// darw the lightened upper-left sides of the rectangle
	
	dc.SetPen(upperPen);
	dc.DrawLine(x,y, x, y + height - 1); // vert
	dc.DrawLine(x,y, x + width - 1,  y); // horiz
	
	// draw the unenlightened lower-right sides of the rectangle
	
	dc.SetPen(lowerPen);
	dc.DrawLine(x + width - 1,  y, x + width - 1, y + height - 1); // vert
	dc.DrawLine(x, y + height - 1, x + width, y + height - 1);     // horiz
}
//---------------------------------------------------------------------------
void wxTabbedWindow::DrawDecorations(wxDC& dc)
{
	// Protability NOTE::: DrawLine(..) draws a line from the first position,
	//                     but not including the point specified by last position.
	//                     This way Windows draws lines, not sure how Motif and Gtk
	//                     prots behave...
	
	int width, height;
	GetClientSize(&width, &height);
	
	// check if there's at least a bit of space to draw things
	
	if (width  < mHorizGap*2 + BORDER_SZ*2+1 ||
		height < mVertGap*2  + BORDER_SZ*2+1 + mTitleHeight
		)
		return;
	
	// step #1 - draw border around the tab content area
	
	// setup position for kind of "pencil"
	int curX = mHorizGap;
	int curY = mVertGap;
	
	int xSize = width  - mHorizGap*2;
	int ySize = height - mVertGap *2  - mTitleHeight;
	
	// layer 1 (upper white)
	DrawShadedRect(curX+0, curY+0, xSize-0, ySize-0,
		mWhitePen, mBlackPen, dc );
	
	// layer 2 (upper gray)
	DrawShadedRect(curX+1, curY+1, xSize-2-1, ySize-2-1,
		mGrayPen, mGrayPen, dc );
	
	// layer 3 (upper darkGray)
	DrawShadedRect(curX+2, curY+2, xSize-3-2, ySize-3-2,
		mDarkPen, mWhitePen, dc );
	
	// layer 4 (upper black)
	DrawShadedRect(curX+3, curY+3, xSize-4-3, ySize-4-3,
		mBlackPen, mGrayPen, dc );
	
	// add non-siemtric layer from the lower-right side (confroming to MFC-look)
	
	dc.SetPen(mDarkPen);
	dc.DrawLine(curX+1, curY + ySize - 2, curX + xSize - 1, curY + ySize - 2);   // horiz
	dc.DrawLine(curX + xSize - 2, curY + 1, curX + xSize - 2, curY + ySize - 2); // vert
	
	// step #2 - draw tab title bars
	
	curX = mFirstTitleGap;
	curY = height - mVertGap - mTitleHeight;
	
	int tabNo = 0;
	wxNode* pNode = mTabs.First();
	
	while(pNode)
    {
		// "hard-coded metafile" for decorations
		
		twTabInfo& tab = *((twTabInfo*)(pNode->Data()));
		
		xSize = tab.mDims.x;
		ySize = mTitleHeight;
		
		if (tabNo == mActiveTab)
		{
			dc.SetPen(mGrayPen);
			dc.DrawLine(curX+1, curY-2, curX+xSize-2, curY-2);
			dc.DrawLine(curX+1, curY-1, curX+xSize-2, curY-1);
		}
		
		dc.SetPen(mWhitePen);
		
		if (tabNo == mActiveTab)
			dc.DrawLine(curX, curY-2, curX, curY+ySize-2);
		else
			dc.DrawLine(curX, curY, curX, curY+ySize-2);
		
		dc.SetPen(mDarkPen);
		dc.DrawLine(curX+1, curY+ySize-3, curX+1, curY+ySize-1); // to pix down
		dc.DrawLine(curX+2, curY+ySize-2, curX+xSize-2, curY+ySize-2);
		dc.DrawLine(curX+xSize-3, curY+ySize-3, curX+xSize-2, curY+ySize-3);
		if (tabNo == mActiveTab)
			dc.DrawLine(curX+xSize-2, curY+ySize-3, curX+xSize-2, curY-3);
		else
			dc.DrawLine(curX+xSize-2, curY+ySize-3, curX+xSize-2, curY-1);
		
		dc.SetPen(mBlackPen);
		dc.DrawLine(curX+xSize-1, curY, curX+xSize-1, curY+ySize-2);
		dc.DrawLine(curX+xSize-2, curY+ySize-2, curX+xSize-3, curY+ySize-2);
		dc.DrawLine(curX+xSize-3, curY+ySize-1, curX+1, curY+ySize-1);
		
		pNode = pNode->Next();
		++tabNo;
		
		// darw image and (or without) text centered within the
		// title bar rectangle
		
		if (mLayoutType != wxTITLE_BORDER_ONLY && tab.HasImg())
		{
			/*
			// OLD STUFF::
			wxMemoryDC tmpDc;
			tmpDc.SelectObject(tab.GetImg());
		
			dc.Blit(curX + mTitleHorizGap,
					curY + (ySize - tab.ImgHeight()) / 2,
					tab.ImgWidth(),
					tab.ImgHeight(),
					&tmpDc, 0, 0, wxCOPY
					);
			*/
			
			wxBitmap& bmp = tab.GetImg();
			
			if (bmp.Ok())
			{
				bool hasMask = bmp.GetMask() != NULL;
				
				dc.DrawBitmap(bmp, curX + mTitleHorizGap, curY + (ySize - tab.ImgHeight()) / 2,
					hasMask);
			}
			
		}
		
		if (mLayoutType == wxTITLE_IMG_AND_TEXT && tab.HasText())
		{
			long x,w,h;
			
			// set select default font of the window into it's device context
			//dc.SetFont(GetLabelingFont());
			
			dc.SetTextBackground(GetBackgroundColour());
			
			dc.GetTextExtent(tab.mText, &w, &h);
			
			x = curX + mTitleHorizGap +
				tab.ImgWidth() + tab.ImageToTxtGap(mImageTextGap);
			
			dc.DrawText(tab.GetText(), x, curY + (ySize - h) / 2);
		}
		curX += xSize;
		
    } // end of `while (pNode)'
}
//---------------------------------------------------------------------------
int wxTabbedWindow::HitTest(const wxPoint& pos)
{
	int width, height;
	GetClientSize(&width, &height);
	
	int curX = mFirstTitleGap;
	int curY = height - mVertGap - mTitleHeight;
	
	int     tabNo = 0;
	wxNode* pNode = mTabs.First();
	
	while(pNode)
    {
		twTabInfo& tab = *((twTabInfo*)(pNode->Data()));
		
		int w,h;
		w = tab.mDims.x;
		h = tab.mDims.y;
		// hit test rectangle of the currnet tab title bar
		if (pos.x >= curX && pos.x < curX + tab.mDims.x  &&
			pos.y >= curY && pos.y < curY + tab.mDims.y
			)
		{
			return tabNo;
		}
		
		curX += tab.mDims.x;
		
		pNode = pNode->Next();
		++tabNo;
    }
	
	return -1;
}
//---------------------------------------------------------------------------
void wxTabbedWindow::HideInactiveTabs(bool andRepaint)
{
	if (!andRepaint)
		return;
	
	wxNode* pNode = mTabs.First();
	int     tabNo = 0;
	
	while(pNode)
    {
		if (tabNo != mActiveTab)
		{
			twTabInfo& tab = *((twTabInfo*)(pNode->Data()));
			tab.mpContent->Show(FALSE);
		}
		
		pNode = pNode->Next();
		++tabNo;
    }
}
//---------------------------------------------------------------------------
wxFont wxTabbedWindow::GetLabelingFont()
{
	wxFont font;
#ifdef __WINDOWS__
	font.SetFaceName("MS Sans Serif");
#else
	font.SetFamily(wxSWISS);
#endif
	
	font.SetStyle(40);
	font.SetWeight(40);
	font.SetPointSize(8);
	
#ifdef __WINDOWS__
	font.RealizeResource();
#endif
	
	return font;
}
//---------------------------------------------------------------------------
void wxTabbedWindow::RecalcLayout(bool andRepaint)
{
	HideInactiveTabs(andRepaint);
	
	// resetup position of the active tab
	
	int width, height;
	GetClientSize(&width, &height);
	
	int curX = mHorizGap + BORDER_SZ;
	int curY = mVertGap  + BORDER_SZ;
	
	int xSize = width  - mHorizGap*2 - BORDER_SZ*2-1;
	int ySize = height - mVertGap*2  - BORDER_SZ*2-1 - mTitleHeight;
	
	SizeTabs(curX, curY, xSize, ySize, andRepaint);
	
	// pass #1 - try to layout assuming it's wxTITLE_IMG_AND_TEXT
	
	mLayoutType = wxTITLE_IMG_AND_TEXT;
	
	wxNode* pNode = mTabs.First();
	
	curX = mFirstTitleGap; // the left-side gap
	mTitleHeight = 0;
	
	while(pNode)
    {
		twTabInfo& tab = *((twTabInfo*)(pNode->Data()));
		
		wxWindowDC dc(this);
		
		long w,h;
		
		// set select default font of the window into it's device context
		//dc.SetFont(GetLabelingFont());
		
		dc.GetTextExtent(tab.mText, &w, &h);
		
		tab.mDims.x = w + tab.ImageToTxtGap(mImageTextGap) +
			tab.ImgWidth() + mTitleHorizGap*2;
		
		tab.mDims.y  = wxMax(h, tab.ImgHeight()) + mTitleVertGap*2;
		mTitleHeight = wxMax(mTitleHeight, tab.mDims.y);
		
		curX += tab.mDims.x;
		
		pNode = pNode->Next();
    }
	
	curX += mHorizGap; // the right-side gap
	
	// make all title bars of equel height
	
	pNode = mTabs.First();
	
	while(pNode)
    {
		((twTabInfo*)(pNode->Data()))->mDims.y = mTitleHeight;;
		pNode = pNode->Next();
    }
	
	// if curX has'nt ran out of bounds, leave TITLE_IMG layout and return
	if (curX < width - mHorizGap)
		return;
	
	// pass #2 - try to layout assuming wxTITLE_IMG_ONLY
	
	mLayoutType = wxTITLE_IMG_ONLY;
	
	pNode = mTabs.First();
	
	curX = mFirstTitleGap; // the left-side gap
	
	int denomiator = mTabs.Number();
	if (denomiator == 0) ++denomiator;
	
	mBorderOnlyWidth = (width - mFirstTitleGap - mHorizGap) / denomiator;
	
	while(pNode)
    {
		twTabInfo& tab = *((twTabInfo*)(pNode->Data()));
		
		if (tab.HasImg())
		{
			tab.mDims.x = tab.ImgWidth()  + mTitleHorizGap*2;
			tab.mDims.y = tab.ImgHeight() + mTitleVertGap*2;
		}
		else
		{
			tab.mDims.x = mBorderOnlyWidth;
			tab.mDims.y = mTitleHeight;
		}
		
		curX += tab.mDims.x;
		
		pNode = pNode->Next();
    }
	
	curX += mHorizGap; // the right-side gap
	
	// if curX has'nt ran out of bounds, leave IMG_ONLY layout and return
	if (curX < width  - mHorizGap)
		return;
	
	// pass #3 - set the narrowest layout wxTITLE_BORDER_ONLY
	
	mLayoutType = wxTITLE_BORDER_ONLY;
	
	pNode = mTabs.First();
	
	while(pNode)
    {
		twTabInfo& tab = *((twTabInfo*)(pNode->Data()));
		
		tab.mDims.x = mBorderOnlyWidth;
		tab.mDims.y = mTitleHeight;
		
		pNode = pNode->Next();
    }
}
//---------------------------------------------------------------------------
// wx event handlers
//---------------------------------------------------------------------------
void wxTabbedWindow::OnPaint(wxPaintEvent& event)
{
	wxPaintDC dc(this);
	DrawDecorations(dc);
}
//---------------------------------------------------------------------------
void wxTabbedWindow::OnSize (wxSizeEvent& event)
{
	RecalcLayout(TRUE);
}
//---------------------------------------------------------------------------
void wxTabbedWindow::OnBkErase(wxEraseEvent& event)
{
	// do nothing
}
//---------------------------------------------------------------------------
void wxTabbedWindow::OnLButtonDown(wxMouseEvent& event)
{
	// floats, why?
	int x = (int)event.m_x;
	int y = (int)event.m_y;
	
	int tabNo = HitTest(wxPoint(x,y));
	
	if (tabNo != -1)
	{
		SetActiveTab(tabNo);
		twTabInfo* pInfo = (twTabInfo*)mTabs.Nth(tabNo)->GetData();
		
		wxCommandEvent menuEvent(wxEVT_TAB_LEFT_CLICKED, pInfo->mTabID); 
		menuEvent.SetEventObject(this);
		
		this->ProcessEvent(menuEvent);
	}
}

void wxTabbedWindow::OnRButtonDown(wxMouseEvent& event)
{
	int x = (int)event.m_x;
	int y = (int)event.m_y;
	
	int tabNo = HitTest(wxPoint(x,y));
	
	if (tabNo != -1)
	{
		twTabInfo* pInfo = (twTabInfo*)mTabs.Nth(tabNo)->GetData();
		
		wxCommandEvent menuEvent(wxEVT_TAB_RIGHT_CLICKED, pInfo->mTabID); 
		menuEvent.SetEventObject(this);
		
		this->ProcessEvent(menuEvent);
	}
}

//---------------------------------------------------------------------------
// Implementation for class wxPagingCtrl
//---------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(wxPagingCtrl, wxTabbedWindow)
  //---------------------------------------------------------------------------
  BEGIN_EVENT_TABLE(wxPagingCtrl, wxTabbedWindow)
  EVT_SIZE     (wxPagingCtrl::OnSize       )
  EVT_PAINT    (wxPagingCtrl::OnPaint      )
  EVT_LEFT_DOWN(wxPagingCtrl::OnLButtonDown)
  EVT_LEFT_UP  (wxPagingCtrl::OnLButtonUp  )
  EVT_MOTION   (wxPagingCtrl::OnMouseMove  )
  EVT_SCROLL   (wxPagingCtrl::OnScroll     )
  END_EVENT_TABLE()
  //---------------------------------------------------------------------------
  // border for pagged-window is 2 shaded-lines
  //---------------------------------------------------------------------------
#undef  BORDER_SZ
#define BORDER_SZ 2
  //---------------------------------------------------------------------------

wxPagingCtrl::wxPagingCtrl( wxWindow* parent, wxWindowID id, 
					const wxPoint& pos,
					const wxSize& size,
					long style, const wxString& name )
  
	: wxTabbedWindow( parent, id, pos, size, style, name )
{
	DoInitPagingCtrl();
}

wxPagingCtrl::wxPagingCtrl()
	: wxTabbedWindow()
{
	DoInitPagingCtrl();
}

void wxPagingCtrl::DoInitPagingCtrl()
{
	mScrollEventInProgress = FALSE;
	
	mTabTrianGap = 4;
	
	mWhiteBrush = wxBrush( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_WINDOW ), wxSOLID );
	mGrayBrush  = wxBrush( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_SCROLLBAR ), wxSOLID);
	SetBackgroundColour( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_WINDOW ));
	
	mCurentRowOfs = 0;
	mAdjustableTitleRowLen = 300;
	
	mIsDragged		= FALSE;
	mDagOrigin		= 0;
	mCursorChanged	= FALSE;
	mResizeCursor	= wxCURSOR_SIZEWE;
	mNormalCursor	= wxCURSOR_ARROW;
	
	mTitleVertGap	= 2;
	mTitleHorizGap	= 10;
	mNoVertScroll	= TRUE;       // Horizontale Scroll abschalten

	// Clean Init
	mScrollResult	= false; 
	mCaller			= NULL;
}

//---------------------------------------------------------------------------
wxPagingCtrl::~wxPagingCtrl()
{
	// nothing (base class handles destruction)
}
//---------------------------------------------------------------------------
wxFont wxPagingCtrl::GetLabelingFont()
{
	wxFont font;
	
#ifdef __WINDOWS__
	font.SetFaceName("Comic Sans MS");
#else
	font.SetFamily(wxSWISS);
#endif
	
	font.SetStyle(40);
	font.SetWeight(40);
	font.SetPointSize(8);
	
	return font;
}
//---------------------------------------------------------------------------
void wxPagingCtrl::OnTabAdded(twTabInfo* pInfo)
{
	int units = GetWholeTabRowLen() / 20;
	
	mpTabScroll->SetScrollbar(0, 1, units, 1, FALSE);
}
//---------------------------------------------------------------------------
wxScrollBar& wxPagingCtrl::GetVerticalScrollBar()
{
	return *mpVertScroll;
}
//---------------------------------------------------------------------------
wxScrollBar& wxPagingCtrl::GetHorizontalScrollBar()
{
	return *mpHorizScroll;
}
//---------------------------------------------------------------------------
int wxPagingCtrl::GetWholeTabRowLen()
{
	wxNode* pNode = mTabs.First();
	
	int len = 0;
	
	while(pNode)
    {
		twTabInfo& tab = *((twTabInfo*)(pNode->Data()));
		
		len += tab.mDims.x;
		pNode = pNode->Next();
    }
	
	return len;
}
//---------------------------------------------------------------------------
void wxPagingCtrl::DrawPaperBar(twTabInfo& tab, int x, int y,
								wxBrush& brush, wxPen& pen, wxDC& dc)
{
	wxPoint poly[4];
	
	// draw organizer-style paper outlet
	
	poly[0].x = x - mTabTrianGap;
	poly[0].y = y;
	
	poly[1].x = x + mTabTrianGap;
	poly[1].y = y + tab.mDims.y-1;
	
	poly[2].x = x + tab.mDims.x - mTabTrianGap;
	poly[2].y = y + tab.mDims.y-1;
	
	poly[3].x = x + tab.mDims.x + mTabTrianGap;
	poly[3].y = y;
	
	dc.SetPen(pen);
	dc.SetBrush(brush);
	
	dc.DrawPolygon(4, poly);
	
	long w,h;
	
	// set select default font of the window into it's device context
	//dc.SetFont(GetLabelingFont());
	
	dc.SetTextBackground(brush.GetColour());
	
	dc.GetTextExtent(tab.mText, &w, &h);
	
	if (tab.HasImg() && tab.GetImg().Ok())
    {
		/* OLD STUFF::
		wxMemoryDC tmpDc;
		tmpDc.SelectObject(tab.GetImg());
	
		dc.Blit(x + mTitleHorizGap,
				y + (tab.mDims.y - tab.ImgHeight()) / 2,
				tab.ImgWidth(),
				tab.ImgHeight(),
				&tmpDc, 0, 0, wxCOPY
				);
		*/
		
		wxBitmap& bmp = tab.GetImg();
		
		bool hasMask = bmp.GetMask() != NULL;
		
		dc.DrawBitmap(bmp, x + mTitleHorizGap,
			y + (tab.mDims.y - tab.ImgHeight()) / 2,
			hasMask);
    }
	
	if (tab.HasText())
    {
		int tx = x + mTitleHorizGap +
			tab.ImgWidth() + tab.ImageToTxtGap(mImageTextGap);
		
		dc.DrawText(tab.GetText(), tx, y + (tab.mDims.y - h) / 2);
    }
}
//---------------------------------------------------------------------------
void wxPagingCtrl::DrawDecorations(wxDC& dc)
{
	// FIXME:: the is big body have to be split!
	
	int width, height;
	GetClientSize(&width, &height);
	
	int curX = mHorizGap;
	int curY = mVertGap;
	
	int xSize = width  - mHorizGap*2;
	int ySize = height - mVertGap*2;
	
	DrawShadedRect(curX, curY, xSize, ySize,
		mDarkPen, mWhitePen, dc);
	
	DrawShadedRect(curX+1, curY+1, xSize-2, ySize-2,
		mBlackPen, mGrayPen, dc);
	
	// draw inactive tab title bars frist (left-to-right)
	
	wxNode* pNode = mTabs.First();
	int     tabNo = 0;
	
	/* OLD STUFF::
	curX = mTitleRowStart;
	curY = height - mVertGap - BORDER_SZ - mTitleHeight;
	*/
	
	curX = mTabTrianGap;
	curY = 0;
	
	// FOR NOW:: avoid creating bitmap with invalid dimensions
	
	if (mTitleRowLen < 1 || mTitleHeight < 1) return;
	
	wxMemoryDC tmpDc;
	wxBitmap   tmpBmp(mTitleRowLen, mTitleHeight);
	
	tmpDc.SelectObject(tmpBmp);
	tmpDc.SetPen(mGrayPen);
	tmpDc.SetBrush(mGrayBrush);
	tmpDc.DrawRectangle(0,0, mTitleRowLen, mTitleHeight);
	
	tmpDc.SetDeviceOrigin(mCurentRowOfs, 0);
	
	while(pNode)
    {
		twTabInfo& tab = *((twTabInfo*)(pNode->Data()));
		
		if (tabNo != mActiveTab)
			DrawPaperBar(tab, curX, curY, mGrayBrush, mBlackPen, tmpDc);
		
		curX += tab.mDims.x;
		
		pNode = pNode->Next();
		++tabNo;
    }
	
	// finally, draw the active tab (white-filled)
	
	pNode = mTabs.First();
	tabNo = 0;
	
	curX = mTabTrianGap;
	
	while(pNode)
    {
		twTabInfo& tab = *((twTabInfo*)(pNode->Data()));
		
		if (tabNo == mActiveTab)
		{
			DrawPaperBar(tab, curX, curY, mWhiteBrush, mBlackPen, tmpDc);
			
			wxPen tmpPen(mWhiteBrush.GetColour(), 1, wxSOLID);
			tmpDc.SetPen(tmpPen);
			
			tmpDc.DrawLine(curX - mTabTrianGap+1, curY,
				curX + tab.mDims.x + mTabTrianGap, curY);
			break;
		}
		curX += tab.mDims.x;
		
		pNode = pNode->Next();
		++tabNo;
    }
	
	// back to initial device origin
	
	tmpDc.SetDeviceOrigin(0, 0);
	
	// draw resize-hint-stick
	
	curX = mTitleRowLen - 6;
	
	DrawShadedRect(curX+0, 0+0, 6,   mTitleHeight,   mGrayPen,  mBlackPen, tmpDc);
	DrawShadedRect(curX+1, 0+1, 6-2, mTitleHeight-2, mWhitePen, mDarkPen,  tmpDc);
	DrawShadedRect(curX+2, 0+2, 6-4, mTitleHeight-4, mGrayPen,  mGrayPen,  tmpDc);
	
	
	
	dc.Blit(mTitleRowStart,
		height - mVertGap - BORDER_SZ - mTitleHeight,
		mTitleRowLen, mTitleHeight,
		&tmpDc, 0,0, wxCOPY);
}
//---------------------------------------------------------------------------
int wxPagingCtrl::HitTest(const wxPoint& pos)
{
	return wxTabbedWindow::HitTest(pos);
}
//---------------------------------------------------------------------------
void wxPagingCtrl::RecalcLayout(bool andRepaint)
{
	mTitleRowLen = mAdjustableTitleRowLen;
	
	if (int(mpTabScroll) == -1) return;
	
	// scroll bars should be created after Create() for this window is called
	if (!mpTabScroll)
    {
		mpTabScroll   =
			new wxScrollBar(this, -1,wxDefaultPosition,wxDefaultSize, wxSB_HORIZONTAL);
		
		mpHorizScroll =
			new wxScrollBar(this, -1,wxDefaultPosition,wxDefaultSize, wxSB_HORIZONTAL);
		if (!mNoVertScroll)       // Vertical Scroll (Original)
			mpVertScroll = new wxScrollBar(this, -1,wxDefaultPosition,wxDefaultSize, wxSB_VERTICAL);
    }
	
	{
		int units = GetWholeTabRowLen() / 20;
		
		mpTabScroll->SetScrollbar(0, 1, units, 1, FALSE);
	}
	
	// resetup position of the active tab
	
	int thumbLen = 16; // FOR NOW:: hardcoded
	
	int width, height;
	GetClientSize(&width, &height);
	
	mTitleHeight = thumbLen;
	
	int curX = mHorizGap + BORDER_SZ;
	int curY = mVertGap  + BORDER_SZ;
	
	int xSize;
	if (!mNoVertScroll)       // Vertical Scroll (Original)
		xSize = width  - mHorizGap*2 - BORDER_SZ*2 - thumbLen;
	else
		xSize = width  - mHorizGap*2 - BORDER_SZ*2;
	
	int ySize = height - mVertGap*2  - BORDER_SZ*2 - mTitleHeight;
	
	SizeTabs(curX, curY, xSize, ySize, andRepaint);
	
	// setup title bar LINES's horizontal scroll bar
	
	curY = height - mVertGap - BORDER_SZ - thumbLen;
	
	mpTabScroll->SetSize(curX, curY, thumbLen*2, thumbLen);
	
	// setup view's HORIZONTAL scroll bar
	curX += thumbLen*2;
	
	mTitleRowStart = curX;
	mFirstTitleGap = curX + mCurentRowOfs + mTabTrianGap;
	
	mTitleRowLen = wxMin(mAdjustableTitleRowLen,
		width - mHorizGap - BORDER_SZ - thumbLen*4 - curX);
	
	curX += mTitleRowLen;
	
	if (!mNoVertScroll)       // Vertical Scroll (Original)
		mpHorizScroll->SetSize(curX, curY,width - curX - mHorizGap - BORDER_SZ - thumbLen, thumbLen);
	else
		mpHorizScroll->SetSize(curX, curY,width - curX - mHorizGap - BORDER_SZ, thumbLen);
	
	// setup view's VERTICAL scroll bar
	if (!mNoVertScroll)       // Vertical Scroll (Original)
    {
		curX = width - mHorizGap - BORDER_SZ - thumbLen;
		curY = mVertGap  + BORDER_SZ;
		mpVertScroll->SetSize(curX, curY, thumbLen,height - curY - mVertGap - BORDER_SZ - thumbLen);
    }
	// layout tab title bars
	
	mLayoutType = wxTITLE_IMG_AND_TEXT;
	
	wxNode* pNode = mTabs.First();
	
	while(pNode)
    {
		twTabInfo& tab = *((twTabInfo*)(pNode->Data()));
		
		wxWindowDC dc(this);
		
		long w,h;
		
		// set select default font of the window into it's device context
		//dc.SetFont(GetLabelingFont());
		dc.GetTextExtent(tab.mText, &w, &h);
		
		tab.mDims.x = w + tab.ImageToTxtGap(mImageTextGap) +
			tab.ImgWidth() + mTitleHorizGap*2;
		
		tab.mDims.y  = mTitleHeight;
		
		pNode = pNode->Next();
    }
	
	// disable title-bar scroller if there's nowhere to scroll to
	
	mpTabScroll->Enable(mTitleRowLen < GetWholeTabRowLen() || mCurentRowOfs < 0 );
}
// ----------------------------------------------------------------------------
// New scrolling implementation.
// ----------------------------------------------------------------------------
void wxPagingCtrl::SetScrollbar(int orientation, int position, int thumbSize, int range, bool refresh)
{
	// Note::pageSize is set to to thumbSize, because its not known...
		
	mScrollResult = false;

	wxList test;

	if (mActiveTab < test.Number() && mCaller != GetActiveTab())
		return;
	
	if (orientation == wxVERTICAL && mpVertScroll)
	{
		mpVertScroll->SetScrollbar(position, thumbSize, range, thumbSize, refresh);
		mScrollResult = true;
	}
	
	if (orientation == wxHORIZONTAL && mpHorizScroll)
	{
		mpHorizScroll->SetScrollbar(position, thumbSize, range, thumbSize, refresh);
		mScrollResult = true;
	}
}
//---------------------------------------------------------------------------
void wxPagingCtrl::SetScrollPos(int orientation, int pos, bool refresh)
{
	// Note::We ignore the refresh parameter.
	
	mScrollResult = false;

	if (mActiveTab < mTabs.Number() && mCaller != GetActiveTab())
		return;
	
	if (orientation == wxVERTICAL && mpVertScroll)
	{
		mpVertScroll->SetThumbPosition(pos);
		mScrollResult = true;
	}
	
	if (orientation == wxHORIZONTAL && mpHorizScroll)
	{
		mpHorizScroll->SetThumbPosition(pos);
		mScrollResult = true;
	}
}
//---------------------------------------------------------------------------
int wxPagingCtrl::GetScrollThumb(int orientation)
{
	int result = 0;
	
	mScrollResult = false;

	if (mActiveTab < mTabs.Number() && mCaller != GetActiveTab())
		return result;
	
	if (orientation == wxVERTICAL && mpVertScroll)
	{
		result = mpVertScroll->GetThumbSize();
		mScrollResult = true;
	}
	
	if (orientation == wxHORIZONTAL && mpHorizScroll)
	{
		result = mpHorizScroll->GetThumbSize();
		mScrollResult = true;
	}
	
	return result;
}
//---------------------------------------------------------------------------
int wxPagingCtrl::GetScrollPos(int orientation)
{
	int result = 0;
	
	mScrollResult = false;

	if (mActiveTab < mTabs.Number() && mCaller != GetActiveTab())
		return result;
	
	if (orientation == wxVERTICAL && mpVertScroll)
	{
		result = mpVertScroll->GetThumbPosition();
		mScrollResult = true;
	}
	
	if (orientation == wxHORIZONTAL && mpHorizScroll)
	{
		result = mpHorizScroll->GetThumbPosition();
		mScrollResult = true;
	}
	
	return result;
}
//---------------------------------------------------------------------------
int wxPagingCtrl::GetScrollRange(int orientation)
{
	int result = 0;
	
	mScrollResult = false;

	if (mActiveTab < mTabs.Number() && mCaller != GetActiveTab())
		return result;
	
	if (orientation == wxVERTICAL && mpVertScroll)
	{
		result = mpVertScroll->GetRange();
		mScrollResult = true;
	}
	
	if (orientation == wxHORIZONTAL && mpHorizScroll)
	{
		result = mpHorizScroll->GetRange();
		mScrollResult = true;
	}
	
	return result;
}

//---------------------------------------------------------------------------
// Extra functions for scroll communication.
//---------------------------------------------------------------------------
bool wxPagingCtrl::ResultLastScrollAction()
{
	bool tmpScrollResult = mScrollResult;
	
	// This function can only be called once for every scroll 
	// function redirection, so clean it...
	mScrollResult = false; 

	// Clean this too
	mCaller = NULL;
	
	return tmpScrollResult;
}
//---------------------------------------------------------------------------
void wxPagingCtrl::SetNextCaller(wxWindow *caller)
{
	mCaller = caller;
}

//---------------------------------------------------------------------------
// event handlers
//---------------------------------------------------------------------------
void wxPagingCtrl::OnPaint(wxPaintEvent& event)
{
	wxPaintDC dc(this);
	DrawDecorations(dc);
}
//---------------------------------------------------------------------------
void wxPagingCtrl::OnSize (wxSizeEvent& event)
{
	wxTabbedWindow::OnSize(event);
}
//---------------------------------------------------------------------------
void wxPagingCtrl::OnLButtonDown(wxMouseEvent& event)
{
	if (mCursorChanged)
    {
		mIsDragged = TRUE;
		mDagOrigin = event.m_x;
		
		mOriginalTitleRowLen = mAdjustableTitleRowLen;
		
		CaptureMouse();
    }
	else
    {
		wxTabbedWindow::OnLButtonDown(event);
    }
}
//---------------------------------------------------------------------------
void wxPagingCtrl::OnLButtonUp(wxMouseEvent& event)
{
	if (mIsDragged)
    {
		mIsDragged     = FALSE;
		mCursorChanged = FALSE;
		SetCursor(mNormalCursor);
		
		ReleaseMouse();
    }
}
//---------------------------------------------------------------------------
void wxPagingCtrl::OnMouseMove(wxMouseEvent& event)
{
	int width, height;
	GetClientSize(&width, &height);
	
	if (!mIsDragged)
    {
		int y = height - mVertGap - BORDER_SZ - mTitleHeight;
		int x = mTitleRowStart + mTitleRowLen - 6;
		
		if (event.m_x >= x && event.m_y >= y &&
			event.m_x <  x + 6               &&
			event.m_y <  y + mTitleHeight
			)
		{
			if (!mCursorChanged)
			{
				SetCursor(mResizeCursor);
				
				mCursorChanged = TRUE;
			}
		}
		else
			if (mCursorChanged)
			{
				SetCursor(mNormalCursor);
				
				mCursorChanged = FALSE;
			}
    }
	else
		if (mIsDragged)
		{
			mAdjustableTitleRowLen = mOriginalTitleRowLen + (event.m_x - mDagOrigin);
			
			// FOR NOW:: fixed
			if (mAdjustableTitleRowLen < 6) mAdjustableTitleRowLen = 6;
			
			wxWindowDC dc(this);
			DrawDecorations(dc);
			
			RecalcLayout(FALSE);
			
			//Refresh();
		}
}
//---------------------------------------------------------------------------
void wxPagingCtrl::OnScroll(wxScrollEvent& event)
{
	wxScrollBar* pSender = (wxScrollBar*)event.GetEventObject();
	// wxMessageBox("wxPagingCtrl::OnScroll","-I->");
	if (pSender == mpTabScroll)
    {
		
		int maxUnits = GetWholeTabRowLen() / 20;
		
		mCurentRowOfs = -event.GetPosition()*maxUnits;
		
		mFirstTitleGap = mTitleRowStart + mCurentRowOfs + mTabTrianGap;
		
		// let' it automatically disable itself if it's time
		mpTabScroll->Enable(mTitleRowLen < GetWholeTabRowLen() || mCurentRowOfs < 0);
		
		// repaint title bars
		wxWindowDC dc(this);
		DrawDecorations(dc);
    }
	else
	{
		if (!mScrollEventInProgress)
		{
			//mScrollEventInProgress = TRUE;
			
			// Make new Event... a scroll Event that can be handled by 
			// a wxWindow object
			wxScrollWinEvent newEvent;
			
			newEvent.SetPosition(event.GetPosition());
			
			if (pSender == mpHorizScroll)
				newEvent.SetOrientation(wxHORIZONTAL);
			if (pSender == mpVertScroll)
				newEvent.SetOrientation(wxVERTICAL);
			
			newEvent.m_eventObject = event.m_eventObject;
			
			// This is not very clean way converting a scrollbar event 
			// type too a scrolled window event. A switch does not work 
			// because wxEVT_XXXX_XXXX are not constant, 
			// and thus not allowed in a switch.
			newEvent.m_eventType = (event.m_eventType - wxEVT_SCROLL_TOP) + wxEVT_SCROLLWIN_TOP;
			
			GetActiveTab()->GetEventHandler()->ProcessEvent(newEvent);
			// GetActiveTab()->GetEventHandler()->ProcessEvent(event); // Old way.

			GetActiveTab()->SetFocus();
		}
		else
		{
			// event bounced back to us, from here we
			// know that it has traveled the loop - thus it's processed!
			
			mScrollEventInProgress = FALSE;
		}
	}
}
//---------------------------------------------------------------------------

