/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

// CToolbarPatternBevelView.cp

#include <LArrayIterator.h>

#include "CToolbarPatternBevelView.h"
#include "CToolbarButton.h"
#include "StRegionHandle.h"

#include <stddef.h>

// Bug #79175
// Change the update command status so that the "Show/Hide Component Bar" works
CToolbarPatternBevelView::CToolbarPatternBevelView(LStream* inStream)
:	CPatternBevelView(inStream)
{
	LCommander::SetUpdateCommandStatus(true);
}

// Bug #79175
// Change the update command status so that the "Show/Hide Component Bar" works
CToolbarPatternBevelView::~CToolbarPatternBevelView()
{
	LCommander::SetUpdateCommandStatus(true);
}

void CToolbarPatternBevelView::HandleModeChange( Int8 inNewMode, SDimension16& outSizeChange )
	{
		CalcArrangement(false, inNewMode, outSizeChange);
	}

void CToolbarPatternBevelView::RotateArrangement( SDimension16& outSizeChange )
	{
		CalcArrangement(true, Int8(), outSizeChange);
	}

void CToolbarPatternBevelView::CalcArrangement( Boolean inRotateArrangement, Int8 inNewMode, SDimension16& outSizeChange)
	/*
		Note: either rotates the arrangement _or else_ changes the mode, but not both.

		...treats |CToolbarButtons| sub-views as though they are layed out in a grid
		whose cell-size is the maximum height and width of any of them.  In a mode change,
		their sizes change, the grid must be recalculated, and the buttons moved
		appropriately.
	*/
{
	size_t				number_of_buttons = 0;
	SDimension16	old_cell_size={ 0, 0 }, new_cell_size={ 0, 0 };
	SDimension16	grid_origin;

		/*
			Iterate over the |CToolbarButtons| within me, _twice_...
		*/

		// ...once to calculate the old and new grid cell-size, and change the mode of each button;
	LPane* current_subview_ptr = 0;
	for ( LArrayIterator iter(mSubPanes); iter.Next(&current_subview_ptr); )
		if ( CToolbarButton* button = dynamic_cast<CToolbarButton*>(current_subview_ptr) )
			{
				++number_of_buttons;

				SDimension16 button_size;

					// Use the pre-ChangeMode size of this button to calculate |old_cell_size|
				button->GetFrameSize(button_size);


					/*
						The origin of the grid is the remainder when dividing the maximum sized
						cells offset by its size (independently for x and y).
					*/

				Boolean recalc_origin_x = button_size.width > old_cell_size.width;
				Boolean recalc_origin_y = button_size.height > old_cell_size.height;

				if ( recalc_origin_x || recalc_origin_y )
					{
						Rect button_rect;
						button->CalcLocalFrameRect(button_rect);

						if ( recalc_origin_x )
							{
								old_cell_size.width = button_size.width;
								grid_origin.width = button_rect.left - (button_size.width * (button_rect.left / button_size.width));
							}
						else // recalc_origin_y
							{
								old_cell_size.height = button_size.height;
								grid_origin.height = button_rect.top - (button_size.height * (button_rect.top / button_size.height));
							}
					}


				if ( !inRotateArrangement )
					{
						button->ChangeMode(inNewMode, SDimension16());

							// ...and the post-ChangeMode size for |new_cell_size|
						button->GetFrameSize(button_size);
						if ( new_cell_size.width < button_size.width )
							new_cell_size.width = button_size.width;
						if ( new_cell_size.height < button_size.height )
							new_cell_size.height = button_size.height;
					}
			}

	if ( inRotateArrangement )
		new_cell_size = old_cell_size;


	Int16 number_of_columns	= 0;
	Int16 number_of_rows		= 0;

		// ...and again (if buttons were found in the first pass) to actually move them.
	if ( number_of_buttons > 0 )
		{
			for ( LArrayIterator iter(mSubPanes); (number_of_buttons > 0) && iter.Next(&current_subview_ptr); )
				if ( CToolbarButton* button = dynamic_cast<CToolbarButton*>(current_subview_ptr) )
					{
						--number_of_buttons;

							// Determine which cell this button occupies.  It hasn't been moved yet, so use |old_cell_size|, and the buttons top-left.
						Rect button_rect;
						button->CalcLocalFrameRect(button_rect);
						Int16 column	= (button_rect.left - grid_origin.width) / old_cell_size.width;
						Int16 row			= (button_rect.top - grid_origin.height) / old_cell_size.height;

						if ( inRotateArrangement )
							{
								Int16 temp = column;
								column = row;
								row = temp;
							}

						if ( number_of_columns <= column )
							number_of_columns = column + 1;
						if ( number_of_rows <= row )
							number_of_rows = row + 1;

							// Calculate the padding needed to center this button within a (new-size) cell.
						SDimension16 button_size;
						button->GetFrameSize(button_size);
						Int16	horizontal_padding	= (new_cell_size.width - button_size.width) / 2;
						Int16 vertical_padding		= (new_cell_size.height - button_size.height) / 2;

							// Calculate the buttons new position within its superview (i.e., within |me|)
						Int32 x = grid_origin.width + (column * new_cell_size.width) + horizontal_padding;
						Int32 y = grid_origin.height + (row * new_cell_size.height) + vertical_padding;

						button->PlaceInSuperFrameAt(x, y, true);	// ...only moves if it has to
					}
		}

	Int16 old_number_of_columns	= inRotateArrangement ? number_of_rows : number_of_columns;
	Int16 old_number_of_rows		= inRotateArrangement ? number_of_columns : number_of_rows;

	outSizeChange.width		= (number_of_columns	* new_cell_size.width)	- (old_number_of_columns	* old_cell_size.width);
	outSizeChange.height	= (number_of_rows			* new_cell_size.height)	- (old_number_of_rows			* old_cell_size.height);
}

#pragma mark -

// ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ FocusDraw
// ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	Set up coordinates system and clipping region
//
//	Panes rely on their superview to focus them. When a Pane requests
//	focus for drawing, we set the clipping region to the revealed
//	portion of that Pane's Frame minus the Frames of all sibling Panes
//	that are in front of that Pane.

Boolean
CToolbarPatternBevelView::FocusDraw(
	LPane	*inSubPane)
{
	Boolean		revealed = true;
	
	if (inSubPane == nil) {			// Focus this view
		revealed = LView::FocusDraw();
		
	} else {						// Focus a SubPane
	
		if (EstablishPort()) {		// Set current Mac Port	
			sInFocusView = nil;		// Saved focus is now invalid
				
									// Set up local coordinate system
			::SetOrigin(mPortOrigin.h, mPortOrigin.v);
			
									// Build clipping region
									
									// Start with the intersection of the
									//   revealed rect of this View with the
									//   Frame of the SubPane
			Rect	subRect;
			inSubPane->CalcPortFrameRect(subRect);
			
			if (!::SectRect(&subRect, &mRevealedRect, &subRect)) {
									// No intersection, so subpane is
									//   not revealed. Set empty clip.
				::ClipRect(&subRect);
				return false;
			}
			
									// SubPane is revealed. Make region
									//   from the intersection.
			StRegionHandle	clipR;
			::RectRgn(clipR, &subRect);
			
				// Determine first sibling which is in front of this SubPane.
			ArrayIndexT		subIndex = 0;			
			LPane*			thePane = nil;
			
			{
				LArrayIterator	iterator(mSubPanes, LArrayIterator::from_Start);
				while (iterator.Next(&thePane))
				{	
					subIndex++;
					if (thePane == inSubPane)
						break;
				}
			}

				// Subtract Frames of all sibling Panes (which are visible)
				// that are in front of this SubPane (i.e., come after the
				// SubPane in this View's list of SubPanes.
			
			StRegionHandle	siblingR;
			LArrayIterator	iterator(mSubPanes, subIndex);
			Rect			siblingFrame;
			
			while (iterator.Next(&thePane))
			{					
				if (thePane->IsVisible() && thePane->CalcPortFrameRect(siblingFrame))
				{
									// Subtract sibling's Frame from
									//   the clipping region
					::RectRgn(siblingR, &siblingFrame);
					::DiffRgn(clipR, siblingR, clipR);
				}
			}

									// Convert Clip region from Port to
									//   Local coords and set it
			::OffsetRgn(clipR, mPortOrigin.h, mPortOrigin.v);
			::SetClip(clipR);
			
			revealed = !::EmptyRgn(clipR);

		} else {
			SignalPStr_("\pFocus View with no GrafPort");
			revealed = false;
		}
	
	}
	
	return revealed;
}
