/////////////////////////////////////////////////////////////////////////////
// Name:        No names yet.
// Purpose:     Contrib. demo
// Author:      Aleksandras Gluchovas
// Modified by:
// Created:     06/09/98
// RCS-ID:      $Id: panedrawpl.cpp,v 1.6 2001/04/21 11:42:33 georgetasker Exp $
// Copyright:   (c) Aleksandras Gluchovas
// Licence:   	wxWindows license
/////////////////////////////////////////////////////////////////////////////

#ifdef __GNUG__
#pragma implementation "panedrawpl.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/utils.h"     // import wxMin,wxMax macros

#include "wx/fl/panedrawpl.h"

// bitmap bits used by bar-resizing brush

#define _A  0xAA
#define _B  0x00
#define _C  0x55
#define _D  0x00

static const unsigned char _gCheckerImg[16] = { _A,_B,_C,_D,
											    _A,_B,_C,_D,
												_A,_B,_C,_D,
												_A,_B,_C,_D
											  };

static void set_cursor_bits( const char** img, char* bits, int width, int height )
{
	for( int i = 0; i != (width*height)/8; ++i )
		bits[i] = 0;

	for( int y = 0; y != height; ++y )
	{
		const char* row = img[0];

		for( int x = 0; x != width; ++x )
		{
			int bitNo = y*width + x;

			char value = ( row[x] != '.' ) ? 1 : 0;

			bits[ bitNo / sizeof(char) ] |= 
				( ( bitNo %sizeof(char) ) << value );
		}

		++img;
	}
}

/***** Implementation for class cbPaneDrawPlugin *****/

IMPLEMENT_DYNAMIC_CLASS( cbPaneDrawPlugin, cbPluginBase )

BEGIN_EVENT_TABLE( cbPaneDrawPlugin, cbPluginBase )

	EVT_PL_LEFT_DOWN		   ( cbPaneDrawPlugin::OnLButtonDown         )
	EVT_PL_LEFT_UP			   ( cbPaneDrawPlugin::OnLButtonUp		   )
//	EVT_PL_LEFT_DCLICK		   ( cbPaneDrawPlugin::OnLDblClick		   )
	EVT_PL_RIGHT_UP			   ( cbPaneDrawPlugin::OnRButtonUp		   )
	EVT_PL_MOTION			   ( cbPaneDrawPlugin::OnMouseMove		   )


	EVT_PL_DRAW_PANE_BKGROUND  ( cbPaneDrawPlugin::OnDrawPaneBackground  )
	EVT_PL_DRAW_PANE_DECOR     ( cbPaneDrawPlugin::OnDrawPaneDecorations )

	EVT_PL_DRAW_ROW_DECOR      ( cbPaneDrawPlugin::OnDrawRowDecorations  )
	EVT_PL_DRAW_ROW_HANDLES    ( cbPaneDrawPlugin::OnDrawRowHandles      )
	EVT_PL_DRAW_ROW_BKGROUND   ( cbPaneDrawPlugin::OnDrawRowBackground   )

	EVT_PL_SIZE_BAR_WND		   ( cbPaneDrawPlugin::OnSizeBarWindow       )
	EVT_PL_DRAW_BAR_DECOR	   ( cbPaneDrawPlugin::OnDrawBarDecorations  )
	EVT_PL_DRAW_BAR_HANDLES	   ( cbPaneDrawPlugin::OnDrawBarHandles      )

	EVT_PL_START_DRAW_IN_AREA  ( cbPaneDrawPlugin::OnStartDrawInArea     )
	EVT_PL_FINISH_DRAW_IN_AREA ( cbPaneDrawPlugin::OnFinishDrawInArea    )

END_EVENT_TABLE()

cbPaneDrawPlugin::cbPaneDrawPlugin(void)

	: mResizeStarted          ( FALSE ),

	  mResizeCursorOn         ( FALSE ),
	  mpDraggedBar			  ( NULL  ),
	  mpResizedRow            ( NULL  ),

     mRowHandleHitted        ( FALSE ),
	  mIsUpperHandle          ( FALSE ),
	  mBarHandleHitted        ( FALSE ),
	  mIsLeftHandle           ( FALSE ),
	  mBarContentHitted       ( FALSE ),

	  mpClntDc ( NULL ),
	  mpPane   ( NULL )
{}

cbPaneDrawPlugin::cbPaneDrawPlugin( wxFrameLayout* pPanel, int paneMask )

	: cbPluginBase( pPanel, paneMask ),
	
	  // bar-row resizing state varaibles

	  mResizeStarted          ( FALSE ),

	  mResizeCursorOn         ( FALSE ),
	  mpDraggedBar			  ( NULL ),
	  mpResizedRow            ( NULL ),

	  mRowHandleHitted        ( FALSE ),
	  mIsUpperHandle          ( FALSE ),
	  mBarHandleHitted        ( FALSE ),
	  mIsLeftHandle           ( FALSE ),
	  mBarContentHitted       ( FALSE ),

	  mpClntDc ( NULL ),
	  mpPane   ( NULL )
{}

cbPaneDrawPlugin::~cbPaneDrawPlugin()
{
	// DBG::
	wxASSERT( mpClntDc == NULL );
}

void cbPaneDrawPlugin::DrawDraggedHandle( const wxPoint& pos, cbDockPane& pane )
{
	wxScreenDC dc;
	int ofsX = 0;
	int ofsY = 0;

	wxPoint fpos = pos;
	pane.PaneToFrame( &fpos.x, &fpos.y );

	// short-cut
	int resizeHndSize = pane.mProps.mResizeHandleSize;

    // "Required for X to specify that
    // that we wish to draw on top of all windows
    // - and we optimise by specifying the area
    // for creating the overlap window." --J.S.

	wxScreenDC::StartDrawingOnTop(&mpLayout->GetParentFrame());

	mpLayout->GetParentFrame().ClientToScreen( &ofsX, &ofsY );

	int prevLF = dc.GetLogicalFunction();

	// BUG BUG BUG (wx):: somehow stippled brush works only  
	//					  when the bitmap created on stack, not
	//					  as a member of the class

	wxBitmap checker( (const char*)_gCheckerImg, 8,8 );

	wxBrush checkerBrush( checker );

	dc.SetPen( mpLayout->mNullPen );
	dc.SetBrush( checkerBrush );
	dc.SetLogicalFunction( wxXOR );

	if ( mHandleIsVertical )
	{
		int delta = pos.x - mDragOrigin.x;

		if ( !pane.IsHorizontal() )

			delta = pos.y - mDragOrigin.y;

		int realHndOfs;
		realHndOfs = pane.mBoundsInParent.x + pane.mLeftMargin + mHandleOfs;

		int newX = realHndOfs + delta;

		if ( newX + resizeHndSize > mHandleDragArea.x + mHandleDragArea.width )

			newX = mHandleDragArea.x + mHandleDragArea.width  - 1;

		if ( newX < mHandleDragArea.x ) 

			newX = mHandleDragArea.x;

		mDraggedDelta = newX - realHndOfs;

		dc.DrawRectangle( newX + ofsX, mHandleDragArea.y + ofsY,
			              resizeHndSize + 1,
						  mHandleDragArea.height+1 );
	}
	else
	{
		// otherwise, draw horizontal handle

		int delta = pos.y - mDragOrigin.y;

		if ( !pane.IsHorizontal() )

			delta = pos.x - mDragOrigin.x;

		int realHndOfs;
		realHndOfs = pane.mBoundsInParent.y + pane.mTopMargin + mHandleOfs;

		int newY = realHndOfs + delta;

		if ( newY + resizeHndSize > mHandleDragArea.y + mHandleDragArea.height )

			newY = mHandleDragArea.y + mHandleDragArea.height - 1;

		if ( newY < mHandleDragArea.y ) 

			newY = mHandleDragArea.y;

		mDraggedDelta = newY - realHndOfs;

		dc.DrawRectangle( mHandleDragArea.x + ofsX, newY + ofsY,
			              mHandleDragArea.width + 1,
						  resizeHndSize + 1 );
	}

	dc.SetLogicalFunction( prevLF );

    // "End drawing on top (frees the window used for drawing
    // over the screen)" --J.S.
	wxScreenDC::EndDrawingOnTop();
}

void cbPaneDrawPlugin::OnMouseMove( cbMotionEvent& event ) 
{
	if ( !mResizeStarted )
	{
		// if nothing is started, do hit-tests

		bool prevWasRowHandle = mRowHandleHitted;

		mBarContentHitted = FALSE;
		mBarHandleHitted  = FALSE;
		mRowHandleHitted  = FALSE;

		int testResult =  
			event.mpPane->HitTestPaneItems( event.mPos,	    // in pane's coordiantes
										    &mpResizedRow,
										    &mpDraggedBar );

		if ( testResult != CB_NO_ITEMS_HITTED )
		{
			if ( testResult == CB_BAR_CONTENT_HITTED )
			{
				// restore cursor, if non of the handles were hit
				if ( mResizeCursorOn )
				{
					// remove resizing hints

					mpLayout->ReleaseEventsFromPane( event.mpPane );
					mpLayout->ReleaseEventsFromPlugin( this );
	
					mResizeCursorOn = FALSE;

					mBarContentHitted = TRUE;

					mpLayout->GetParentFrame().SetCursor( *mpLayout->mpNormalCursor );
				}

				// TBD:: fire something like "mouse-over-bar" event

				event.Skip(); // pass event to the next handler in the chain
				return;
			}

			wxCursor* pCurs = NULL;

			if ( testResult == CB_UPPER_ROW_HANDLE_HITTED ||
				 testResult == CB_LOWER_ROW_HANDLE_HITTED)
			{
				if ( event.mpPane->IsHorizontal() )

					pCurs = mpLayout->mpVertCursor;
				else
					pCurs = mpLayout->mpHorizCursor;

				mRowHandleHitted = TRUE;
				mIsUpperHandle    = ( testResult == CB_UPPER_ROW_HANDLE_HITTED );
			}
			else
			{
				// otherwise, if inter-bar handle was hitted

				if ( event.mpPane->IsHorizontal() )

					pCurs = mpLayout->mpHorizCursor;
				else
					pCurs = mpLayout->mpVertCursor;

				mBarHandleHitted = TRUE;
				mIsLeftHandle    = ( testResult == CB_LEFT_BAR_HANDLE_HITTED );
			}

			// avoid setting the same cursor twice

			if ( !mResizeCursorOn || prevWasRowHandle != mRowHandleHitted )
			{
				if ( !mResizeCursorOn )
				{
					// caputre if not captured yet
					mpLayout->CaptureEventsForPane( event.mpPane );
					mpLayout->CaptureEventsForPlugin( this );
				}

				mpLayout->GetParentFrame().SetCursor( *pCurs );
			}

			mResizeCursorOn = TRUE;

			// handled is being dragged now, thus event is "eaten" by this plugin

			return;

		} // end of if (HitTestBarHandles())

		// restore cursor, if non of the handles were hit
		if ( mResizeCursorOn )
		{
			mpLayout->ReleaseEventsFromPane( event.mpPane );
			mpLayout->ReleaseEventsFromPlugin( this );

			mpLayout->GetParentFrame().SetCursor( *mpLayout->mpNormalCursor );

			mResizeCursorOn = FALSE;
		}

		event.Skip(); // pass event to the next plugin
	}

	// othewise series of actions, if something has already started

	else
	if ( mResizeStarted )
	{
		// apply xor-mask twice 
		DrawDraggedHandle( mPrevPos,   *event.mpPane );

		// draw handle in the new position
		DrawDraggedHandle( event.mPos, *event.mpPane );
		mPrevPos = event.mPos;

		// handled is dragged, thus event is "eaten" by this plugin
	}
	else
		event.Skip(); // pass event to the next plugin
}

void cbPaneDrawPlugin::OnLDblClick( cbLeftDClickEvent& event )
{
	if ( !mResizeCursorOn )
	{
		cbBarInfo* pBarToFloat;

		if ( event.mpPane->HitTestPaneItems( event.mPos, 	  // in pane's coordiantes
										     &mpResizedRow,
											 &pBarToFloat ) == CB_BAR_CONTENT_HITTED
		   )
			{
				// TBD: ????
				return;

				mpLayout->SetBarState( pBarToFloat, wxCBAR_FLOATING, TRUE );

				mpLayout->RepositionFloatedBar( pBarToFloat );

				return; // event is "eaten" by this plugin
			}

		event.Skip();
	}
}

void cbPaneDrawPlugin::OnLButtonDown( cbLeftDownEvent& event ) 
{
	wxASSERT( !mResizeStarted );

	if ( mResizeCursorOn )
	{
		mResizeStarted = TRUE;
		mDragOrigin    = event.mPos;

		cbBarInfo* pInfo = NULL;
		
		// setup constraints for the dragging handle

		int from, till;
		mHandleOfs        = 0;
		mHandleIsVertical = FALSE;

		if ( mRowHandleHitted )

			event.mpPane->GetRowResizeRange( mpResizedRow, &from, &till, mIsUpperHandle );
		else
			// otherwise if bar handle was hitted
			event.mpPane->GetBarResizeRange( mpDraggedBar, &from, &till, mIsLeftHandle );
		 
		if ( mRowHandleHitted )
		{
			mHandleIsVertical = ( event.mpPane->IsHorizontal() ) ? FALSE : TRUE;

			mHandleDragArea.x      = 0;
			mHandleDragArea.width  = event.mpPane->mPaneWidth;

			mHandleDragArea.y      = from;
			mHandleDragArea.height = till - from;

			if ( mIsUpperHandle )

				mHandleOfs = mpResizedRow->mRowY;
			else
				mHandleOfs = mpResizedRow->mRowY + 
				             mpResizedRow->mRowHeight -
							 event.mpPane->mProps.mResizeHandleSize;
		}
		else
		{
			// otehrwise if bar handle dragged

			cbRowInfo& rowInfo     = *mpDraggedBar->mpRow;
			wxRect& bounds         = mpDraggedBar->mBounds;

			mHandleIsVertical = ( event.mpPane->IsHorizontal() ) ? TRUE : FALSE;

			mHandleDragArea.x      = from;
			mHandleDragArea.width  = till - from;


			mHandleDragArea.y      = bounds.y;
			mHandleDragArea.height = bounds.height;

			// left-side-handle mBounds
			if ( mIsLeftHandle )

				mHandleOfs = bounds.x;
			else
				mHandleOfs = bounds.x + 
							 bounds.width - event.mpPane->mProps.mResizeHandleSize;

		}

		event.mpPane->PaneToFrame( &mHandleDragArea );
		DrawDraggedHandle(mDragOrigin, *event.mpPane);

		mPrevPos = mDragOrigin;

		return;
		// handled is dragged, thus event is "eaten" by this plugin
	}
	else
	{
		cbBarInfo* pDraggedBar;

		if ( event.mpPane->HitTestPaneItems( event.mPos, 	  // in pane's coordiantes
										     &mpResizedRow,
											 &pDraggedBar ) == CB_BAR_CONTENT_HITTED 
		   )
		{
			int x = event.mPos.x,
			    y = event.mPos.y;

			event.mpPane->PaneToFrame( &x, &y );

			cbStartBarDraggingEvent dragEvt( pDraggedBar, wxPoint(x,y), event.mpPane );

			mpLayout->FirePluginEvent( dragEvt );

			return; // event is "eaten" by this plugin
		}
	}

	event.Skip(); // pass event to the next plugin in the chain
}

void cbPaneDrawPlugin::OnLButtonUp( cbLeftUpEvent& event ) 
{
	if ( mResizeStarted )
	{
		DrawDraggedHandle( event.mPos, *event.mpPane );

		mResizeStarted  = FALSE;
		mResizeCursorOn = FALSE;

		mpLayout->ReleaseEventsFromPane( event.mpPane );
		mpLayout->ReleaseEventsFromPlugin( this );

		mpLayout->GetParentFrame().SetCursor( *mpLayout->mpNormalCursor );

		if ( mRowHandleHitted )
		{
			event.mpPane->ResizeRow( mpResizedRow, 
									 mDraggedDelta,
									 mIsUpperHandle );
		}
		else
		{
			event.mpPane->ResizeBar( mpDraggedBar, 
								     mDraggedDelta,
									 mIsLeftHandle );
		}

		mpDraggedBar = NULL;
		mpResizedRow = NULL;

		// handled dragging action was finished by this mouse-up, 
		// thus event is "eaten" by this plugin

		return;
	}

	event.Skip(); // pass event to the next plugin
}

void cbPaneDrawPlugin::OnRButtonUp( cbRightUpEvent&   event )
{
	wxPoint fpos = event.mPos;
	event.mpPane->PaneToFrame( &fpos.x, &fpos.y );

	cbBarInfo* pDraggedBar;

	// user clicks inside the bar contnet, fire bar-customization event

	if ( event.mpPane->HitTestPaneItems( event.mPos, 	  // in pane's coordiantes
										 &mpResizedRow,
										 &pDraggedBar  ) == CB_BAR_CONTENT_HITTED
	   )
	{
		cbCustomizeBarEvent cbEvt( pDraggedBar, fpos, event.mpPane );

		mpLayout->FirePluginEvent( cbEvt );

		return; // event is "eaten" by this plugin
	}

	// otherwise fire whole-layout customization event

	cbCustomizeLayoutEvent csEvt( fpos );

	mpLayout->FirePluginEvent( csEvt );

	// event is "eaten" by this plugin
}

void cbPaneDrawPlugin::OnSizeBarWindow( cbSizeBarWndEvent& event ) 
{
	cbBarInfo& bar = *event.mpBar;
	mpPane         = event.mpPane;

	// it's possible that a bar does not have it's own window!
	if ( !bar.mpBarWnd ) return;

	wxRect& bounds = event.mBoundsInParent;

	// check visibility
	if ( bounds.height != 0 )
	{
		// size smaller than bounds, to leave space for shade lines

		// FIXME:: +/- 1s

		bar.mpBarWnd->wxWindow::SetSize( bounds.x      + 1 + bar.mDimInfo.mHorizGap,     
										 bounds.y      + 1 + bar.mDimInfo.mVertGap,
										 bounds.width  - 2 - bar.mDimInfo.mHorizGap*2,
										 bounds.height - 2 - bar.mDimInfo.mVertGap *2 , 
										 0 
									   );

		if ( !bar.mpBarWnd->IsShown() )

			bar.mpBarWnd->Show( TRUE );
	}
	else
		// hide bar if not visible
		bar.mpBarWnd->Show( FALSE );

	event.Skip(); // pass event to the next plugin in the chain
}

void cbPaneDrawPlugin::OnDrawRowDecorations( cbDrawRowDecorEvent& event )
{
	DrawPaneShadeForRow( event.mpRow, *event.mpDc );

	event.Skip(); // pass event to the next plugin
}

void cbPaneDrawPlugin::DrawUpperRowHandle( cbRowInfo* pRow, wxDC& dc )
{
	wxRect& bounds = pRow->mBoundsInParent;

	if ( mpPane->IsHorizontal() )
	{
		 if ( pRow->mHasUpperHandle )
		
			mpPane->DrawHorizHandle( dc, bounds.x, 
									 bounds.y-1, 
							 		 pRow->mRowWidth );
	}
	else
	{
		if ( pRow->mHasUpperHandle )

			mpPane->DrawVertHandle( dc, bounds.x-1, 
									bounds.y, pRow->mRowWidth );
	}
}

void cbPaneDrawPlugin::DrawLowerRowHandle( cbRowInfo* pRow, wxDC& dc )
{
	wxRect& bounds = pRow->mBoundsInParent;

	// check if iter-row handles present

	if ( mpPane->IsHorizontal() )
	{
		if ( pRow->mHasLowerHandle )
		
			mpPane->DrawHorizHandle( dc, bounds.x, bounds.y + bounds.height - mpPane->mProps.mResizeHandleSize - 1, 
							 		 pRow->mRowWidth );
	}
	else
	{
		if ( pRow->mHasLowerHandle )

			mpPane->DrawVertHandle( dc, bounds.x + bounds.width - mpPane->mProps.mResizeHandleSize - 1, 
							 		bounds.y, pRow->mRowWidth );
	}
}

void cbPaneDrawPlugin::OnDrawRowHandles( cbDrawRowHandlesEvent& event )
{
	// short-cuts
	cbRowInfo* pRow = event.mpRow;
	wxDC&   dc      = *event.mpDc;
	mpPane          = event.mpPane;

	// draw handles of surrounding rows first

	if ( pRow->mpPrev && pRow->mpPrev->mHasLowerHandle )

			DrawLowerRowHandle( pRow->mpPrev, dc );

	if ( pRow->mpNext && pRow->mpNext->mHasUpperHandle )

		DrawUpperRowHandle( pRow->mpNext, dc );

	// draw handles of the given row

	if ( pRow->mHasUpperHandle )
	
		DrawUpperRowHandle( pRow, dc );

	if ( pRow->mHasLowerHandle )

		DrawLowerRowHandle( pRow, dc );

	event.Skip(); // pass event to the next plugin
}

void cbPaneDrawPlugin::OnDrawPaneBackground ( cbDrawPaneBkGroundEvent& event )
{
	wxDC& dc = *event.mpDc;
	mpPane   = event.mpPane;

	// FOR NOW:: hard-coded
	wxBrush bkBrush( mpLayout->mBorderPen.GetColour(), wxSOLID );

	dc.SetBrush( bkBrush );
	dc.SetPen( mpLayout->mNullPen );

	wxRect& bounds = mpPane->mBoundsInParent;

	if ( mpPane->mTopMargin >= 1 )
	
		dc.DrawRectangle( bounds.x, bounds.y,
						  bounds.width+1,
						  mpPane->mTopMargin + 1);


	if ( mpPane->mBottomMargin >= 1 )
	
		dc.DrawRectangle( bounds.x, 
						  bounds.y + bounds.height - mpPane->mBottomMargin,
						  bounds.width + 1,
						  mpPane->mBottomMargin + 1);


	if ( mpPane->mLeftMargin >= 1 )
	
		dc.DrawRectangle( bounds.x, 
						  bounds.y + mpPane->mTopMargin - 1,
						  mpPane->mLeftMargin + 1,
						  bounds.height - mpPane->mTopMargin - mpPane->mBottomMargin + 2);


	if ( mpPane->mRightMargin >= 1 )
	
		dc.DrawRectangle( bounds.x + bounds.width - mpPane->mRightMargin,
						  bounds.y + mpPane->mTopMargin - 1,
						  mpPane->mRightMargin + 1,
						  bounds.height - mpPane->mTopMargin - mpPane->mBottomMargin + 2);

	event.Skip(); // pass event to the next plugin
}

void cbPaneDrawPlugin::OnDrawRowBackground ( cbDrawRowBkGroundEvent& event )
{
	// short-cuts
	cbRowInfo* pRow = event.mpRow;
	wxDC&   dc      = *event.mpDc;
	mpPane          = event.mpPane;

	// get ready
	wxRect     rowBounds    = pRow->mBoundsInParent;
	bool       isHorizontal	= event.mpPane->IsHorizontal();
	
	int prevPos;

	if ( isHorizontal )
	{
		prevPos = rowBounds.x;
		// include one line obove and below the row
		--rowBounds.y;
		rowBounds.height +=2;

		--rowBounds.x;
		rowBounds.width += 2;
	}
	else
	{
		prevPos = rowBounds.y;
		// include one line obove and below the row
		--rowBounds.x;
		rowBounds.width += 2;

		--rowBounds.y;
		rowBounds.height +=2;
	}

//#define TEST_BK_ERASING

#ifdef TEST_BK_ERASING

	// DBG::
	wxBrush br0( wxColour(0,160,160), wxSOLID );
	dc.SetBrush(br0);
	dc.SetPen  ( mpLayout->mNullPen );
	dc.DrawRectangle( rowBounds.x, rowBounds.y,
			          rowBounds.width  + 1, 
					  rowBounds.height + 1  );
#endif

	wxBrush bkBrush( mpLayout->mGrayPen.GetColour(), wxSOLID );

	dc.SetPen  ( mpLayout->mNullPen );
	dc.SetBrush( bkBrush );

	// fill background-recatangle of entire row area
	dc.DrawRectangle( rowBounds.x, rowBounds.y,
			          rowBounds.width  + 1, 
					  rowBounds.height + 1  );

	dc.SetBrush( wxNullBrush );

	// draw "shaded-side-bars" for each bar
	for( size_t i = 0; i != pRow->mBars.Count(); ++i )
	{
		wxRect& bounds = pRow->mBars[i]->mBoundsInParent;

		if ( isHorizontal )
		{
			DrawShade( 1, bounds, FL_ALIGN_LEFT, dc );
			DrawShade( 1, bounds, FL_ALIGN_RIGHT, dc );
		}
		else
		{
			DrawShade( 1, bounds, FL_ALIGN_TOP, dc );
			DrawShade( 1, bounds, FL_ALIGN_BOTTOM, dc );
		}
	}

	// draw extra shades to simulate "glued-bricks" effect

	// TBD:: reduce exessive drawing of shades, when the
	//       row handle is present, and shades will be overr-drawn anyway

	DrawUpperRowShades( pRow, dc, 1 ); // outer shade

	if ( pRow->mpPrev )
	{
		DrawLowerRowShades( pRow->mpPrev, dc, 1 ); // outter shade
		DrawLowerRowShades( pRow->mpPrev, dc, 0 ); // inner shade
	}

	DrawLowerRowShades( pRow, dc, 1 );

	if ( pRow->mpNext )
	{
		DrawUpperRowShades( pRow->mpNext, dc, 1 ); 
		DrawUpperRowShades( pRow->mpNext, dc, 0 );
	}

	event.Skip(); // pass event to the next plugin
}

void cbPaneDrawPlugin::DrawUpperRowShades( cbRowInfo* pRow, wxDC& dc, int level )
{
	for( size_t i = 0; i != pRow->mBars.Count(); ++i )
	{
		wxRect& bounds = pRow->mBars[i]->mBoundsInParent;

		if ( mpPane->IsHorizontal() )
		{		
			DrawShade( level, bounds, FL_ALIGN_TOP, dc );
			if ( level == 1 )
			{
				dc.SetPen( mpLayout->mDarkPen );
				dc.DrawPoint( bounds.x - 1, bounds.y );
				dc.SetPen( mpLayout->mLightPen );
				dc.DrawPoint( bounds.x + bounds.width , bounds.y );
			}
		}
		else
		{
			DrawShade( level, bounds, FL_ALIGN_LEFT, dc );
			if ( level == 1 )
			{
				dc.SetPen( mpLayout->mDarkPen );
				dc.DrawPoint( bounds.x, bounds.y -1 );
				dc.SetPen( mpLayout->mLightPen );
				dc.DrawPoint( bounds.x, bounds.y + bounds.height );
			}
		}
	}
}

void cbPaneDrawPlugin::DrawLowerRowShades( cbRowInfo* pRow, wxDC& dc, int level )
{
	int prevX = 0;

	for( size_t i = 0; i != pRow->mBars.Count(); ++i )
	{
		wxRect& bounds = pRow->mBars[i]->mBoundsInParent;

		if ( mpPane->IsHorizontal() )
		{
			DrawShade( level, bounds, FL_ALIGN_BOTTOM, dc );
			if ( level == 1 )
			{
				dc.SetPen( mpLayout->mDarkPen );
				dc.DrawPoint( bounds.x - 1, bounds.y + bounds.height -1 );
				dc.SetPen( mpLayout->mLightPen );
				dc.DrawPoint( bounds.x + bounds.width , bounds.y + bounds.height -1 );
			}
		}
		else
		{
			DrawShade( level, bounds, FL_ALIGN_RIGHT, dc );
			if ( level == 1 )
			{
				dc.SetPen( mpLayout->mDarkPen );
				dc.DrawPoint( bounds.x + bounds.width - 1, bounds.y -1 );
				dc.SetPen( mpLayout->mLightPen );
				dc.DrawPoint( bounds.x + bounds.width - 1, bounds.y + bounds.height );
			}
		}
	}
}

void cbPaneDrawPlugin::DrawBarInnerShadeRect( cbBarInfo* pBar, wxDC& dc )
{
	wxRect& bounds = pBar->mBoundsInParent;

	dc.SetPen( mpLayout->mDarkPen );
	
	dc.DrawLine( bounds.x + bounds.width - 1,
		         bounds.y,
				 bounds.x + bounds.width - 1,
				 bounds.y + bounds.height );

	dc.DrawLine( bounds.x,
				 bounds.y + bounds.height - 1,
				 bounds.x + bounds.width,
				 bounds.y + bounds.height -1  );

	dc.SetPen( mpLayout->mLightPen );

	dc.DrawLine( bounds.x,
				 bounds.y,
				 bounds.x + bounds.width - 1,
				 bounds.y );

	dc.DrawLine( bounds.x,
		         bounds.y,
				 bounds.x,
				 bounds.y + bounds.height - 1 );
}

void cbPaneDrawPlugin::DrawShade( int level, wxRect& rect, int alignment, wxDC& dc )
{
	// simulates "guled-bricks" appearence of control bars

	if ( ( alignment == FL_ALIGN_TOP    && level == 1 ) ||
		 ( alignment == FL_ALIGN_BOTTOM && level == 0 ) ||
		 ( alignment == FL_ALIGN_LEFT   && level == 1 ) ||
		 ( alignment == FL_ALIGN_RIGHT  && level == 0 )
	   )
	
		dc.SetPen( mpLayout->mDarkPen  );
	else
		dc.SetPen( mpLayout->mLightPen );

	if ( alignment == FL_ALIGN_TOP )
	{
		if ( level == 0 )
		
			dc.DrawLine( rect.x, 
						 rect.y,
			             rect.x + rect.width - 1,
						 rect.y );
		else
			dc.DrawLine( rect.x - 1,
			             rect.y - 1,
						 rect.x + rect.width + 0,
						 rect.y - 1 );
	}
	else
	if ( alignment == FL_ALIGN_BOTTOM )
	{
		if ( level == 0 )
		
			dc.DrawLine( rect.x, 
						 rect.y + rect.height - 1,
			             rect.x + rect.width,
						 rect.y + rect.height - 1 );
		else
			dc.DrawLine( rect.x - 1,
			             rect.y + rect.height,
						 rect.x + rect.width + 1,
						 rect.y + rect.height );
	}
	else
	if ( alignment == FL_ALIGN_LEFT )
	{
		if ( level == 0 )
		
			dc.DrawLine( rect.x, 
						 rect.y,
			             rect.x,
						 rect.y + rect.height - 1 );
		else
			dc.DrawLine( rect.x - 1,
			             rect.y - 1,
						 rect.x - 1,
						 rect.y + rect.height );
	}
	else
	if ( alignment == FL_ALIGN_RIGHT )
	{
		if ( level == 0 )
		
			dc.DrawLine( rect.x + rect.width - 1, 
						 rect.y,
			             rect.x + rect.width - 1,
						 rect.y + rect.height );
		else
		{					 
			dc.DrawLine( rect.x + rect.width,
			             rect.y - 1,
						 rect.x + rect.width,
						 rect.y + rect.height + 1  );
		}
	}
}

void cbPaneDrawPlugin::DrawShade1( int level, wxRect& rect, int alignment, wxDC& dc )
{
	// simulates "guled-bricks" appearence of control bars

	if ( ( alignment == FL_ALIGN_TOP    && level == 1 ) ||
		 ( alignment == FL_ALIGN_BOTTOM && level == 0 ) ||
		 ( alignment == FL_ALIGN_LEFT   && level == 1 ) ||
		 ( alignment == FL_ALIGN_RIGHT  && level == 0 )
	   )
	
		dc.SetPen( mpLayout->mDarkPen  );
	else
		dc.SetPen( mpLayout->mLightPen );

	if ( alignment == FL_ALIGN_TOP )
	{
		if ( level == 0 )
		
			dc.DrawLine( rect.x, 
						 rect.y,
			             rect.x + rect.width,
						 rect.y );
		else
			dc.DrawLine( rect.x,
			             rect.y - 1,
						 rect.x + rect.width,
						 rect.y - 1 );
	}
	else
	if ( alignment == FL_ALIGN_BOTTOM )
	{
		if ( level == 0 )
		
			dc.DrawLine( rect.x, 
						 rect.y + rect.height - 1,
			             rect.x + rect.width,
						 rect.y + rect.height - 1 );
		else
			dc.DrawLine( rect.x,
			             rect.y + rect.height,
						 rect.x + rect.width,
						 rect.y + rect.height );
	}
	else
	if ( alignment == FL_ALIGN_LEFT )
	{
		if ( level == 0 )
		
			dc.DrawLine( rect.x, 
						 rect.y,
			             rect.x,
						 rect.y + rect.height );
		else
			dc.DrawLine( rect.x - 1,
			             rect.y,
						 rect.x - 1,
						 rect.y + rect.height );
	}
	else
	if ( alignment == FL_ALIGN_RIGHT )
	{
		if ( level == 0 )
		
			dc.DrawLine( rect.x + rect.width - 1, 
						 rect.y,
			             rect.x + rect.width - 1,
						 rect.y + rect.height );
		else
		{					 
			dc.DrawLine( rect.x + rect.width,
			             rect.y ,
						 rect.x + rect.width,
						 rect.y + rect.height );
		}
	}
}

void cbPaneDrawPlugin::DrawPaneShade( wxDC& dc, int alignment )
{
	if ( !mpPane->mProps.mShow3DPaneBorderOn ) return;

	wxRect bounds = mpPane->mBoundsInParent;

	bounds.x      += mpPane->mLeftMargin;
	bounds.y      += mpPane->mTopMargin;
	bounds.width  -= ( mpPane->mLeftMargin + mpPane->mRightMargin  );
	bounds.height -= ( mpPane->mTopMargin  + mpPane->mBottomMargin );

	DrawShade( 0, bounds, alignment, dc );
	DrawShade( 1, bounds, alignment, dc );
}

void cbPaneDrawPlugin::DrawPaneShadeForRow( cbRowInfo* pRow, wxDC& dc )
{
	if ( !mpPane->mProps.mShow3DPaneBorderOn ) return;

	// do not draw decoration, if pane has "vainished"
	if ( mpPane->mPaneWidth  < 0 ||
		 mpPane->mPaneHeight < 0 )

		 return;

	wxRect bounds = pRow->mBoundsInParent;

	if ( mpPane->mAlignment == FL_ALIGN_TOP ||
		 mpPane->mAlignment == FL_ALIGN_BOTTOM )
	{
		--bounds.y;
		bounds.height += 2;

		DrawShade1( 0, bounds, FL_ALIGN_LEFT, dc );
		DrawShade1( 1, bounds, FL_ALIGN_LEFT, dc );
		DrawShade1( 0, bounds, FL_ALIGN_RIGHT, dc );
		DrawShade1( 1, bounds, FL_ALIGN_RIGHT, dc );

		if ( !pRow->mpNext )
			DrawPaneShade( dc, FL_ALIGN_BOTTOM );

		if ( !pRow->mpPrev )
			DrawPaneShade( dc, FL_ALIGN_TOP );
	}
	else
	{
		--bounds.x;
		bounds.width += 2;

		DrawShade1( 0, bounds, FL_ALIGN_TOP, dc );
		DrawShade1( 1, bounds, FL_ALIGN_TOP, dc );
		DrawShade1( 0, bounds, FL_ALIGN_BOTTOM, dc );
		DrawShade1( 1, bounds, FL_ALIGN_BOTTOM, dc );

		if ( !pRow->mpNext )
			DrawPaneShade( dc, FL_ALIGN_RIGHT );

		if ( !pRow->mpPrev )
			DrawPaneShade( dc, FL_ALIGN_LEFT );
	}
}

void cbPaneDrawPlugin::OnDrawPaneDecorations( cbDrawPaneDecorEvent& event )
{
	wxDC& dc = *event.mpDc;

	cbDockPane* pPane = event.mpPane;

	RowArrayT& lst = pPane->GetRowList();
	
	// FIXME:: this is a workaround for some glitches

	if ( lst.Count() )
	{
		cbRowInfo* pLastRow = lst[ lst.Count() - 1 ];

		pPane->PaintRowBackground( pLastRow,  dc );
		pPane->PaintRowDecorations( pLastRow, dc );
		pPane->PaintRowHandles( pLastRow, dc );
	}

	if ( !pPane->mProps.mShow3DPaneBorderOn ) return;

	// do not draw decoration, if pane is completely hidden
	if ( event.mpPane->mPaneWidth  < 0 ||
		 event.mpPane->mPaneHeight < 0 )

		 return;

	DrawPaneShade( dc, FL_ALIGN_TOP    );
	DrawPaneShade( dc, FL_ALIGN_BOTTOM );
	DrawPaneShade( dc, FL_ALIGN_LEFT   );
	DrawPaneShade( dc, FL_ALIGN_RIGHT  );

	event.Skip(); // pass event to the next plugin
}

// bar decoration/sizing handlers

void cbPaneDrawPlugin::OnDrawBarDecorations( cbDrawBarDecorEvent& event )
{
	cbBarInfo* pBar = event.mpBar;
	wxDC&   dc      = *event.mpDc;

	// draw brick borders

	wxRect& rect = event.mBoundsInParent;

	dc.SetPen( mpLayout->mLightPen );

	// horiz
	dc.DrawLine( rect.x, rect.y, 
		         rect.x + rect.width-1, rect.y );

	// vert
	dc.DrawLine( rect.x, rect.y,
		         rect.x, rect.y + rect.height-1 );


	dc.SetPen( mpLayout->mDarkPen );

	// vert
	dc.DrawLine( rect.x + rect.width-1, rect.y,
		         rect.x + rect.width-1, rect.y + rect.height-1 );

	// horiz
	dc.DrawLine( rect.x, rect.y + rect.height-1,
				 rect.x + rect.width, rect.y + rect.height-1 );

	event.Skip(); // pass event to the next plugin
}

void cbPaneDrawPlugin::OnDrawBarHandles( cbDrawBarHandlesEvent& event )
{
	// short-cuts
	cbBarInfo* pBar = event.mpBar;
	wxDC&   dc      = *event.mpDc;
	mpPane          = event.mpPane;

	// draw handles around the bar if present

	if ( pBar->mHasLeftHandle ||
		 pBar->mHasRightHandle )
	{
		wxRect& bounds = pBar->mBoundsInParent;

		if ( mpPane->IsHorizontal() )
		{
			if ( pBar->mHasLeftHandle )

				mpPane->DrawVertHandle( dc, bounds.x - mpPane->mProps.mResizeHandleSize -1, 
									    bounds.y, bounds.height );

			if ( pBar->mHasRightHandle )

				mpPane->DrawVertHandle( dc, 
									    bounds.x + bounds.width -1, 
									    bounds.y, bounds.height );
		}
		else
		{
			if ( pBar->mHasLeftHandle )

				mpPane->DrawHorizHandle( dc, bounds.x, 
									     bounds.y  - mpPane->mProps.mResizeHandleSize - 1, 
										 bounds.width );

			if ( pBar->mHasRightHandle )

				mpPane->DrawHorizHandle( dc, bounds.x, 
										 bounds.y + bounds.height - 1,
										 bounds.width );
		}
	}

	event.Skip(); // pass event to the next plugin
}

void cbPaneDrawPlugin::OnStartDrawInArea( cbStartDrawInAreaEvent& event )
{
	// DBG::
	wxASSERT( mpClntDc == NULL );

	// FOR NOW:: create/destroy client-dc upon each drawing
	mpClntDc = new wxClientDC( &mpLayout->GetParentFrame() );

	(*event.mppDc) = mpClntDc;

	mpClntDc->SetClippingRegion( event.mArea.x,     event.mArea.y,
		                         event.mArea.width, event.mArea.height );
}

void cbPaneDrawPlugin::OnFinishDrawInArea( cbFinishDrawInAreaEvent& event )
{
	// DBG::
	wxASSERT( mpClntDc );

	delete mpClntDc;

	mpClntDc = NULL;
}
