/*!	\brief			Constructor of CategoryPreferencesView
 *	\details		It's a descendant of BView.
 *	\param[in]	frame	The frame rectangle of the view.
 */
CategoryPreferencesView::CategoryPreferencesView( BRect frame )
	:
	BView( frame,
		   "Category Preferences",
		   B_FOLLOW_ALL_SIDES,
		   B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE )
{
	BLayoutItem* layoutItem = NULL;
	BMessage* toSend = NULL;
	menuField = NULL;
	
	this->SetViewColor( ui_color( B_PANEL_BACKGROUND_COLOR ) );
	
	/*!	\note	Layout of the view
	 *			The view has a grid layout. It's arranged in the following way:
	 *			1)	Left column - list of Categories (CategoryList) that
	 *				contains all categories currently available.
	 *			2) 	Right column - three buttons, from top to bottom:
	 *				2a)	Edit currently selected category - guess what it's doing
	 *				2b) Add a new category
	 *				2c)	Merge a current directory into another one + menu with
	 *					all categories. The category selected in the list is disabled.
	 * \note	Restrictions:
	 *			a) The list of categories is scrolled.
	 *			b) If no category is selected, then
	 *				i) 	"Edit" button is disabled
	 *				ii)	"Merge to" field is disabled
	 *			
	 */
	BGridLayout* gridLayout = new BGridLayout();
	if ( !gridLayout )
	{
		/* Panic! */
		exit( 1 );
	}
	// Margins from the sides of the view and spacing between the elements
	gridLayout->SetInsets( 5, 5, 5, 5 );
	gridLayout->SetHorizontalSpacing( 10 );
	gridLayout->SetVerticalSpacing( 10 );
	this->SetLayout( gridLayout );
	gridLayout->SetExplicitAlignment( BAlignment( B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT ) );
	gridLayout->SetExplicitMinSize( BSize( (this->Bounds()).Width(), (this->Bounds()).Height() ) );
	

	BRect rect = gridLayout->Frame();
	printf ( "The frame is %d pixels wide and %d pixels high.\n",
			 (int )rect.Width(),
			 (int )rect.Height() );
	
	
	/* Creating the CategoryListView with its scroller */
	BRect r( this->Bounds() );
	r.InsetBySelf( 5, 10 );	// Margins near the border of the view
	r.right = (int)(r.right / 2) - B_V_SCROLL_BAR_WIDTH;
	r.bottom -= 0;
	
	listView = new CategoryListView( r, "List View" );
	if ( ! listView ) {
		/* Panic! */
		exit( 1 );
	}
	BLooper* looper = this->Looper();
	if ( looper && looper->LockLooper() )
	{
		looper->AddHandler( ( BHandler* )this );
		looper->UnlockLooper();	
	}
	
	scroller = new BScrollView( "Scroller",
								listView,
								B_FOLLOW_LEFT | B_FOLLOW_TOP,
								0, 	// Flags
								true,
								true );
	if ( !scroller )
	{
		/* Panic! */
		exit( 1 );
	}
	layoutItem = gridLayout->AddView( scroller, 0, 0, 1, 3 );
	if ( !layoutItem ) {
		/* Panic! */
		exit( 1 );
	}
	layoutItem->SetExplicitAlignment( BAlignment( B_ALIGN_LEFT,
												  B_ALIGN_USE_FULL_HEIGHT ) );
	toSend = new BMessage( kCategoryInvoked );
	if ( !toSend )
	{
		/* Panic! */
		exit( 1 );
	}
	listView->SetInvocationMessage( toSend );
	toSend = new BMessage( kCategorySelected );
	if ( !toSend )
	{
		/* Panic! */
		exit( 1 );
	}
	listView->SetSelectionMessage( toSend );
	
	r = listView->Bounds();	
	r.bottom += B_H_SCROLL_BAR_HEIGHT + 3;
	r.right -= ( B_V_SCROLL_BAR_WIDTH + 10 );
	
	layoutItem->SetExplicitMinSize( BSize( ( B_V_SCROLL_BAR_WIDTH * 2 ), r.Height() ) );
	gridLayout->SetMaxColumnWidth( 0, r.Width()-70 );
	gridLayout->SetMaxColumnWidth( 1, r.Width()-66 );
	
	// Add categories to the list
	PopulateCategoriesView();
	
	/* Creating the buttons */
	// Add new category button
	toSend = new BMessage( kAddNewCategory );
	addButton = new BButton( BRect( 0, 0, 1, 1), 
							 "Add category",
							 "Add category",
							 toSend,
							 B_FOLLOW_H_CENTER | B_FOLLOW_V_CENTER );
	if ( !toSend || !addButton ) {
		/* Panic! */
		exit( 1 );
	}
	addButton->ResizeToPreferred();
	addButton->SetTarget( this );
	layoutItem = gridLayout->AddView( addButton, 1, 0, 1, 1 );
	layoutItem->SetExplicitAlignment( BAlignment( B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER ) );
	
	// Edit old category button
	toSend = new BMessage( kCategoryInvoked );
	editButton = new BButton( BRect( 0, 0, 1, 1), 
							 "Edit category",
							 "Edit category",
							 toSend,
							 B_FOLLOW_H_CENTER | B_FOLLOW_V_CENTER );
	if ( !toSend || !editButton ) {
		/* Panic! */
		exit( 1 );
	}
	editButton->ResizeToPreferred();
	editButton->SetTarget( this );
	layoutItem = gridLayout->AddView( editButton, 1, 1, 1, 1 );
	layoutItem->SetExplicitAlignment( BAlignment( B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER ) );
	// Edit category button is disabled by default; 
	// it's enabled when user chooses a category in the list.
	editButton->SetEnabled( false );
	
	/* Creating the menu of merging a category */
	// Create a label
	BGroupLayout* groupLayout = new BGroupLayout( B_VERTICAL );
	if ( !groupLayout )
	{
		/* Panic! */
		exit( 1 );
	}
	gridLayout->AddItem( groupLayout, 1, 2, 1, 1 );
	groupLayout->SetExplicitAlignment( BAlignment( B_ALIGN_LEFT, B_ALIGN_TOP ) );
	
	mergeToLabel = new BStringView( BRect( 0, 0, 1, 1 ),
								"Merge to label",
								"Merge selected category into:" );
	if ( !mergeToLabel ) {
		/* Panic! */
		exit( 1 );
	}
	mergeToLabel->ResizeToPreferred();
	layoutItem = groupLayout->AddView( mergeToLabel );
	layoutItem->SetExplicitAlignment( BAlignment( B_ALIGN_LEFT, B_ALIGN_TOP ) );
	
	// Create the menu
	BMessage templateMessage( kMergeIntoCategory );
	listMenu = new CategoryMenu( "Select category to merge to:", true, &templateMessage );
	if ( !listMenu )
	{
		/* Panic! */
		exit( 1 );
	}	
	
	menuField = new BMenuField( mergeToLabel->Bounds(),
								"Merge to field",
								NULL,
								listMenu );
	if ( !menuField ) {
		/* Panic! */
		exit( 1 );
	}
	menuField->SetDivider( 0 );
	// Just like the "Edit" button above, the menu is initially disabled.
	menuField->SetEnabled( false );
	
	layoutItem = groupLayout->AddView( menuField );
	layoutItem->SetExplicitAlignment( BAlignment( B_ALIGN_USE_FULL_WIDTH, B_ALIGN_TOP ) );
	
	for ( int index = 0; index < gridLayout->CountColumns(); ++index )
	{
		gridLayout->SetColumnWeight( index, 1 );
	}
	for ( int index = 0; index < gridLayout->CountRows(); ++index )
	{
		gridLayout->SetRowWeight( index, 1 );	
	}
	gridLayout->InvalidateLayout();
	
}	// <-- end of constructor of CategoryPreferencesView
/*!	
 *	\brief			The constructor of CalendarControl.
 *	\param[in]	frame			The outer bounds of the control.
 *	\param[in]	name			Name of the control.
 *	\param[in]	labelInForCalendar		The label of the calendar part of control.
 *	\param[in]	calModuleName				ID of the calendar module used.
 *	\param[in]	initialTime					The initial time to be set, passed in seconds.
 *													If 0, current time is set.
 *	\param[in]	toSend						Message sent when the control's value is changed.
 *	\attention		It's assumed that calendar modules were already initialized prior
 *						to calling this function.
 */
CalendarControl::CalendarControl(BRect frame,
								 const char* name,
								 const BString& labelInForCalendar,
								 const BString& calModuleName,
								 time_t	initialTime,
								 BMessage* toSend )
	:
	BControl( frame, name, labelInForCalendar.String(), toSend,
			 B_FOLLOW_ALL_SIDES,
			 B_NAVIGABLE | B_WILL_DRAW | B_FRAME_EVENTS ),
	fLabel(NULL),
	fDateLabel(NULL),
	fDateSelector( NULL ),
	fLastError( B_OK )
{
	// Firstly, set the calendar module
	this->fCalModule = utl_FindCalendarModule( calModuleName );
	if ( this->fCalModule == NULL ) {
		fLastError = B_BAD_VALUE;
		return;
	}
	
	// Load the preferences of this calendar module
	ParsePreferences();
	
	// Init the internal data to submitted time representation
	InitTimeRepresentation( initialTime );
	
	// Create the UI elements
	fLabel = new BStringView( BRect( 0, 0, 1, 1 ), 
									  "label", 
									  labelInForCalendar.String() );
	if ( !fLabel) {
		/* Panic! */
		fLastError = B_NO_MEMORY;
		return;
	}
	fLabel->ResizeToPreferred();
	
	fDateLabel = new BStringView( BRect( 0, 0, 1, 1 ), 
											"dateLabel", 
											NULL );
	if ( !fDateLabel ) {
		/* Panic! */
		fLastError = B_NO_MEMORY;
		return;
	}
	
	
	fCalendarModuleLabel = new BStringView( BRect( 0, 0, 1, 1 ),
														 "label for the calendar module selector",
														 "Calendar:" );
	if ( !fCalendarModuleLabel ) {
		/* Panic! */
		fLastError = B_NO_MEMORY;
		return;
	}
	fCalendarModuleLabel->ResizeToPreferred();
	
	fCalendarsMenu = CreateMenuOfCalendarModules();
	if ( !fCalendarsMenu ) {
		/* Panic! */
		fLastError = B_NO_MEMORY;
		return;
	}
	
	fCalendarModuleSelector = new BMenuField( BRect( 0, 0, 1, 1 ),
															"calendar module selector",
															NULL,		// Label is created separately
															fCalendarsMenu );
	if ( !fCalendarModuleSelector ) {
		/* Panic! */
		fLastError = B_NO_MEMORY;
		return;
	}
	fCalendarModuleSelector->ResizeToPreferred();
	
	// Create the menu
	CreateMenu();
	
	BRect stringViewFrame = fLabel->Frame();
	
	BPoint topLeftCorner = stringViewFrame.RightTop();
	BSize size( BUTTON_WIDTH, stringViewFrame.Height() + SPACING );
	
	
	fMenuBar = new BMenuBar( BRect(topLeftCorner, size),
								"menuBar",
								B_FOLLOW_RIGHT | B_FOLLOW_TOP,
								B_ITEMS_IN_ROW,
								false);
	if (! fMenuBar) {
		// Panic!
		fLastError = B_NO_MEMORY;
		return;
	}
	fMenuBar->SetBorder( B_BORDER_EACH_ITEM );
	fMenuBar->AddItem( fDateSelector );

	// Update the selected date label to currently selected moment of time
	UpdateText();
	
	// Initializing the layout
	BGridLayout* lay = new BGridLayout( );
	
	if (!lay) { 
		// Panic! 
		fLastError = B_NO_MEMORY;
		return; 
	}
	lay->SetInsets(0, 5, 0, 0);
	lay->SetSpacing( 10, 5 );
	lay->SetExplicitAlignment( BAlignment( B_ALIGN_USE_FULL_WIDTH, B_ALIGN_MIDDLE ) );
	this->SetLayout(lay);
	
	BLayoutItem* layoutItem;
	
	layoutItem = lay->AddView( fLabel, 0, 0 );
	layoutItem->SetExplicitAlignment( BAlignment( B_ALIGN_LEFT, B_ALIGN_TOP ) );
	layoutItem = lay->AddView( fDateLabel, 1, 0 );
	layoutItem->SetExplicitAlignment( BAlignment( B_ALIGN_USE_FULL_WIDTH, B_ALIGN_TOP ) );
	layoutItem = lay->AddView( fMenuBar, 2, 0 );	
	layoutItem->SetExplicitAlignment( BAlignment( B_ALIGN_RIGHT, B_ALIGN_TOP ) );
	layoutItem->SetExplicitMaxSize( size );
	
	layoutItem = lay->AddView( fCalendarModuleLabel, 0, 1 );
	layoutItem->SetExplicitAlignment( BAlignment( B_ALIGN_LEFT, B_ALIGN_MIDDLE ) );
	
	layoutItem = lay->AddView( fCalendarModuleSelector, 1, 1 );
	layoutItem->SetExplicitAlignment( BAlignment( B_ALIGN_USE_FULL_WIDTH, B_ALIGN_TOP ) );

	lay->SetColumnWeight( 0, 0 );
	lay->SetColumnWeight( 1, 1000 );
	lay->SetColumnWeight( 2, 0 );
	
	lay->SetMaxColumnWidth( 2, BUTTON_WIDTH );
	
	this->InvalidateLayout();
	this->Relayout();
	this->Invalidate();
	
	fLastError = B_OK;
}