// Calendar Control version 2.5
// by Al.V. Sarikov.
// Kherson, Ukraine, 2006.
// E-mail: avix@ukrpost.net.
// Home page: http://avix.pp.ru.

// Control which allows to work with dates:
// enter date to text field and choose it from calendar.

// Distributed under BSD license (see LICENSE file).

#include <Bitmap.h>
#include <View.h>
#include <time.h>
#include <ControlLook.h>
#include "MouseSenseStringView.cpp"


class MonthView : public BView
{
	public:
		MonthView(int day, int month, int year, int first_year, int last_year);
		~MonthView();
		virtual void AttachedToWindow(void);
		virtual void Draw(BRect r);
		virtual void KeyDown(const char *bytes, int32 numBytes);
		virtual void MessageReceived(BMessage *msg);
		virtual void MouseDown(BPoint p);
		virtual void MouseUp(BPoint p);
		void MouseMoved(BPoint pt, uint32 transit, const BMessage *msg);

	private:
		void DrawMonth();
		  
		BMessenger *msng;
		MouseSenseStringView *yearMStringView[2];
		MouseSenseStringView *monthMStringView[2];
		  
		MouseSenseStringView *todayStringView;
		BStringView *monthStringView;
		BStringView *yearStringView;
		char *weekdayNames[7];
		char *monthNames[12];
		int monthDays[12];
		  
		BBitmap *Bmp;
		BView *BmpView;
		  
		int cday, cmonth, cyear; // current date in window
		int cwday1; // day of week of 1st of month in window
		int cwday; // day of week of current day of month in window
		  
		int first_year, last_year;
		  
		int tday, tmonth, tyear; // today
		int twday; // is needed to calculate days of week
		  
		float h_cell, w_cell; // height and width of cell for one number of day of nonth
		  
		BRect cursor; // rectangle arounding chosen date
		  
		BRect cursorPressed; // rectangle arounding date where mouse was pressed
		bool MousePressed; // is mouse pressed on date
		int dayPressed; // date where mouse is pressed
		  
		bool NewMonth; // if true field must be cleared
		  
		int which_focused; // 0 - if month, 1 - if year,
				   // 2 - if days of month, 3 - if today
};


MonthView::MonthView(int day, int month, int year, int first_year, int last_year)
                :BView(BRect(0,0,100,100), "MonthViewAViX", B_FOLLOW_LEFT, B_WILL_DRAW)
{
	this->cday=day;
	this->cmonth=month;
	this->cyear=year;
	
	this->first_year=first_year;
	this->last_year=last_year;
	
	struct tm *dat;
	time_t tmp;
	time(&tmp);
	dat=localtime(&tmp);
	
	tday=dat->tm_mday;
	tmonth=dat->tm_mon+1;
	tyear=dat->tm_year+1900; // new day coming when window is opened is not handled
	                         // because it is very rear case
	twday=dat->tm_wday;
	
	weekdayNames[0]=(char*)"Mo";
	weekdayNames[1]=(char*)"Tu";
	weekdayNames[2]=(char*)"We";
	weekdayNames[3]=(char*)"Th";
	weekdayNames[4]=(char*)"Fr";
	weekdayNames[5]=(char*)"Sa";
	weekdayNames[6]=(char*)"Su";
	
	monthNames[0]=(char*)"January";
	monthNames[1]=(char*)"February";
	monthNames[2]=(char*)"March";
	monthNames[3]=(char*)"April";
	monthNames[4]=(char*)"May";
	monthNames[5]=(char*)"June";
	monthNames[6]=(char*)"July";
	monthNames[7]=(char*)"August";
	monthNames[8]=(char*)"September";
	monthNames[9]=(char*)"October";
	monthNames[10]=(char*)"November";
	monthNames[11]=(char*)"December";
	
	monthDays[0]=31;
	monthDays[1]=28;
	monthDays[2]=31;
	monthDays[3]=30;
	monthDays[4]=31;
	monthDays[5]=30;
	monthDays[6]=31;
	monthDays[7]=31;
	monthDays[8]=30;
	monthDays[9]=31;
	monthDays[10]=30;
	monthDays[11]=31;
	
	NewMonth=false; // first field is cleared and clearing is not needed
	
	MousePressed=false;
}


MonthView::~MonthView()
{
	delete todayStringView;
	delete monthStringView;
	delete monthMStringView[0];
	delete monthMStringView[1];
	delete yearStringView;
	delete yearMStringView[0];
	delete yearMStringView[1];
	delete msng;
	Bmp->RemoveChild(BmpView);
	delete BmpView;
	delete Bmp;
}


void MonthView::AttachedToWindow(void)
{
	SetFont(be_plain_font);
	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
	
	// Calculate size of cell needed for number of day of month
	font_height h;
	be_plain_font->GetHeight(&h);
	h_cell = (int)(h.ascent+h.descent+h.leading+6);
	w_cell = StringWidth("4")*4;

	// add today's date 
	BString s("");
	s << tday << " " << monthNames[tmonth-1] << " " << tyear;
	
	msng=new BMessenger(this);
	todayStringView=new MouseSenseStringView(new BMessage('TODA'), msng,
	                                         BRect(10,10,100,100),"todayMStringViewAViX", s.String());
	todayStringView->ResizeToPreferred();
	todayStringView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
	AddChild(todayStringView);

	// add month selection
	monthStringView=new BStringView(BRect(10,10,100,100),"monthStringViewAViX", monthNames[8]);
	monthStringView->SetAlignment(B_ALIGN_CENTER);
	monthStringView->ResizeToPreferred();
	monthStringView->SetText(monthNames[cmonth-1]);
	monthStringView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
	AddChild(monthStringView);

	// add year selection 
	s.SetTo("");
	if(cyear<10) s.Append("000");
	else if(cyear<100) s.Append("00");
	else if(cyear<1000) s.Append("0");
	s << cyear;
	yearStringView=new BStringView(BRect(10,10,100,100),"yearStringViewAViX",
	                               "0000");
	yearStringView->ResizeToPreferred();
	yearStringView->SetText(s.String());
	yearStringView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
	AddChild(yearStringView);
	
	ResizeTo(w_cell*7+1,h_cell*7+3+16+yearStringView->Bounds().bottom+todayStringView->Bounds().bottom);
	Window()->ResizeTo(Bounds().right, Bounds().bottom);

	// year to the left button	
	yearMStringView[0]=new MouseSenseStringView(new BMessage('YEA0'),msng,
	                                            BRect(10,10,100,100),
	                                            "yearMStringViewAViX0",
	                                            "<<");
	AddChild(yearMStringView[0]);
	yearMStringView[0]->ResizeToPreferred();
	yearMStringView[0]->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
	
	// year to the right button	
	yearMStringView[1]=new MouseSenseStringView(new BMessage('YEA1'),msng,
	                                            BRect(10,10,100,100),
	                                            "yearMStringViewAViX1",
	                                            ">>");
	AddChild(yearMStringView[1]);
	yearMStringView[1]->ResizeToPreferred();
	yearMStringView[1]->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
	
	// month to the left button	
	monthMStringView[0]=new MouseSenseStringView(new BMessage('MON0'),msng,
	                                             BRect(10,10,100,100),
	                                             "monthMStringViewAViX0",
	                                             "<<");
	AddChild(monthMStringView[0]);
	monthMStringView[0]->ResizeToPreferred();
	monthMStringView[0]->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
	
	// month to the right button	
	monthMStringView[1]=new MouseSenseStringView(new BMessage('MON1'),msng,
                                              BRect(10,10,100,100),
                                              "monthMStringViewAViX1",
                                              ">>");
	AddChild(monthMStringView[1]);
	monthMStringView[1]->ResizeToPreferred();
	monthMStringView[1]->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));

	// move all in correct position
	todayStringView->MoveTo((Bounds().right-todayStringView->Bounds().right)/2,
	                        Bounds().bottom-todayStringView->Bounds().bottom-2);
	
	if(tyear<first_year || tyear>last_year) todayStringView->SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_3_TINT));
	
	yearMStringView[1]->MoveTo(Bounds().right-yearMStringView[1]->Bounds().right,5);
	yearStringView->MoveTo(yearMStringView[1]->Frame().left-yearStringView->Bounds().right,5);
	yearMStringView[0]->MoveTo(yearStringView->Frame().left-yearMStringView[0]->Bounds().right,5);
	
	monthStringView->MoveTo((yearMStringView[0]->Frame().left-monthStringView->Bounds().right)/2,5);
	monthMStringView[0]->MoveTo(monthStringView->Frame().left-monthMStringView[0]->Bounds().right,5);
	monthMStringView[1]->MoveTo(monthStringView->Frame().right,5);
	
	which_focused=2; // days of month
 
	// Bitmap for the dates 
	Bmp=new BBitmap(Bounds(),B_RGB32,true);
	BmpView=new BView(Bmp->Bounds(),"BV",0,B_WILL_DRAW);
	Bmp->AddChild(BmpView);
	
	Bmp->Lock();
	BmpView->SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
	BmpView->FillRect(BmpView->Frame());
	BmpView->SetHighColor(255,255,255);
	BRect bmpRect(Bounds());
	bmpRect.top = yearStringView->Frame().bottom + 2;
	bmpRect.bottom = todayStringView->Frame().top - 5;
	BmpView->FillRect(bmpRect);
	
	BmpView->SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_1_TINT));
	BmpView->StrokeLine(BPoint(0,bmpRect.top), BPoint(BmpView->Bounds().right,bmpRect.top));
	BmpView->StrokeLine(BPoint(0,bmpRect.bottom), BPoint(BmpView->Bounds().right,bmpRect.bottom));
	BmpView->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
	
	float y=yearStringView->Frame().bottom+h_cell;
	float x=0;
	for(int i=0;i<7;i++)
	{
		BmpView->DrawString(weekdayNames[i],BPoint(x+(w_cell-StringWidth(weekdayNames[i]))/2,y));
		x+=w_cell;
	}
	
	BmpView->Sync();
	Bmp->Unlock();

	DrawMonth();
}


void MonthView::Draw(BRect r)
{
	Window()->Lock();
	DrawBitmap(Bmp, BPoint(0,0));
	Window()->Unlock();
}


void MonthView::DrawMonth()
{
	Bmp->Lock();
	
	float y=yearStringView->Frame().bottom+h_cell;
	float x=0;
	
	if(NewMonth)
	{
		BmpView->SetHighColor(255,255,255);
		BmpView->FillRect(BRect(0,y+1, BmpView->Bounds().right,todayStringView->Frame().top - 6));
		BmpView->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
		BmpView->SetLowColor(255,255,255);
		NewMonth=false;
	}
	
	int byear=cyear; // base year
	if(tyear<byear) byear=tyear;
	 
	int day1=0, m=0, k=byear;
	
	while(k<cyear)
	{
	 day1++;
	
	 if(k%4==0) // leap year?
	  if((k%100!=0) || (k%400==0)) day1++; // yes
  
	 k++;
	}
	while(++m<cmonth)
	{
	 day1+=(monthDays[m-1]-28);
	 if(m==2) if((cyear%4)==0) if((cyear%100!=0) || (cyear%400==0)) day1++;
	}
	day1++; // day1 is number of 1st day of chosen month in chosen year
	day1=day1%7;
	
	int day2=0;
	m=0;
	k=byear;
	while(k<tyear)
	{
	 day2++;
	 if((k%4)==0) if((k%100!=0) || (k%400==0)) day2++;
	 k++;
	}
	while(++m<tmonth)
	{
	 day2+=(monthDays[m-1]-28);
	 if(m==2) if((tyear%4)==0) if((tyear%100!=0) || (tyear%400==0)) day2++;
	}
	day2+=tday; // day2 - number of today's day in today's year
	day2=day2%7;
	
	k=(twday==0) ? 6 : twday-1;
	
	k=k-day2+day1;
	while(k<0) k+=7;
	k=k%7;
	cwday1=k;
	
	x=w_cell*k+1;
	y+=h_cell;
	
	int qu_days=monthDays[cmonth-1]; // quantity of days in month
	
	if(cmonth==2) if(cyear%4==0) if((cyear%100!=0) || (cyear%400==0)) qu_days=29;
	
	BString s;
	int t=0;
	while(t<qu_days)
	{
	 t++;
	 
	 s<<t;
	 
	 if(cyear==tyear) if(cmonth==tmonth) if(t==tday) BmpView->SetHighColor(236,0,0,0);
	 BmpView->DrawString(s.String(),BPoint(x+(w_cell-StringWidth(s.String()))/2,y));
	 if(cyear==tyear) if(cmonth==tmonth) if(t==tday) BmpView->SetHighColor(0,0,0,0);
	 
	 if(t==cday)
	 {
	  cwday=k;
	  if(which_focused==2) BmpView->SetHighColor(ui_color(B_NAVIGATION_BASE_COLOR));
	  else 
		// BmpView->SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_3_TINT));
		BmpView->SetHighColor(255,255,255);
	  cursor.Set(x,y-h_cell+5,x+w_cell-1,y+4);
	  BmpView->StrokeRect(cursor);
	  BmpView->SetHighColor(0,0,0,0);
	 }
	 
	 x+=w_cell;
	 k++;
	 s.SetTo("");
	 
	 if(k==7)
	 {
	  k=0;
	  y+=h_cell;
	  x=1;
	 }
	}
	
	BmpView->Sync();
	Bmp->Unlock();
	Draw(Bounds());
}


////////////////////////////////////////////////////////////////
void MonthView::KeyDown(const char *bytes, int32 numBytes)
{
	switch(bytes[0])
	{
	 case B_TAB:
	 {
	  Bmp->Lock();
	  switch(which_focused)
	  {
	   case 0: // months
	   {
	    BmpView->SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
	    BmpView->StrokeLine(BPoint(monthMStringView[0]->Frame().left,
	                               monthMStringView[0]->Frame().bottom+1),
	                        BPoint(monthMStringView[1]->Frame().right,
	                               monthMStringView[1]->Frame().bottom+1));
	    BmpView->SetHighColor(0,0,0);
	    break;
	   }
	   case 1: // years
	   {
	    BmpView->SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
	    BmpView->StrokeLine(BPoint(yearMStringView[0]->Frame().left,
	                               yearMStringView[0]->Frame().bottom+1),
	                        BPoint(yearMStringView[1]->Frame().right,
	                               yearMStringView[1]->Frame().bottom+1));
	    BmpView->SetHighColor(0,0,0);
	    break;
	   }
	   case 2: // days of month
	   {
	    BmpView->SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_3_TINT));
	    BmpView->StrokeRect(cursor);
	    BmpView->SetHighColor(0,0,0);
	    break;
	   }
	   case 3: // today
	   {
	    BmpView->SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
	    BmpView->StrokeLine(BPoint(todayStringView->Frame().left,
	                               todayStringView->Frame().bottom+1),
	                        BPoint(todayStringView->Frame().right,
	                               todayStringView->Frame().bottom+1));
	    BmpView->SetHighColor(0,0,0);
	    break;
	   }
	  }
	  
	  // changing which_focused
	  if(modifiers() & B_SHIFT_KEY)
	  {
	   if(which_focused==0)
	     which_focused=3;
	   else which_focused--;
	  }
	  else // simply Tab
	  {
	   if(which_focused==3)
	    which_focused=0;
	   else which_focused++;
	  }
	  
	  // drawing with new which_focused
	  switch(which_focused)
	  {
	   case 0: // months
	   {
	    BmpView->SetHighColor(ui_color(B_NAVIGATION_BASE_COLOR));
	    BmpView->StrokeLine(BPoint(monthMStringView[0]->Frame().left,
	                               monthMStringView[0]->Frame().bottom+1),
	                        BPoint(monthMStringView[1]->Frame().right,
	                               monthMStringView[1]->Frame().bottom+1));
	    BmpView->SetHighColor(0,0,0);
	    break;
	   }
	   case 1: // years
	   {
	    BmpView->SetHighColor(ui_color(B_NAVIGATION_BASE_COLOR));
	    BmpView->StrokeLine(BPoint(yearMStringView[0]->Frame().left,
	                               yearMStringView[0]->Frame().bottom+1),
	                        BPoint(yearMStringView[1]->Frame().right,
	                               yearMStringView[1]->Frame().bottom+1));
	    BmpView->SetHighColor(0,0,0);
	    break;
	   }
	   case 2: // days of month
	   {
	    BmpView->SetHighColor(ui_color(B_NAVIGATION_BASE_COLOR));
	    BmpView->StrokeRect(cursor);
	    BmpView->SetHighColor(0,0,0);
	    break;
	   }
	   case 3: // today
	   {
	    BmpView->SetHighColor(ui_color(B_NAVIGATION_BASE_COLOR));
	    BmpView->StrokeLine(BPoint(todayStringView->Frame().left,
	                               todayStringView->Frame().bottom+1),
	                        BPoint(todayStringView->Frame().right,
	                               todayStringView->Frame().bottom+1));
	    BmpView->SetHighColor(0,0,0);
	    break;
	   }
	  }
	  BmpView->Sync();
	  Bmp->Unlock();
	  Draw(Bounds());
	  break;
	 } // B_TAB
  
	 case B_DOWN_ARROW:
	 {
	  if(which_focused==0) // month
	  { 
	   BMessage msg('MON0');
	   MessageReceived(&msg);
	  }
	  else if(which_focused==1) // year
	  {
	   BMessage msg('YEA0');
	   MessageReceived(&msg);
	  }
	  else if(which_focused==2) // days of month
	  {
	   int qu_days=monthDays[cmonth-1];
	   if(cmonth==2) if(cyear%4==0) if((cyear%100!=0) || (cyear%400==0)) qu_days=29;
	   if((cday+7)<=qu_days)
	   {
	    Bmp->Lock();
	    BmpView->SetHighColor(255,255,255);
	    BmpView->StrokeRect(cursor);
	    cursor.OffsetBySelf(0, h_cell);
	    cday+=7;
	    BmpView->SetHighColor(ui_color(B_NAVIGATION_BASE_COLOR));
	    BmpView->StrokeRect(cursor);
	    BmpView->SetHighColor(0,0,0);
	    BmpView->Sync();
	    Bmp->Unlock();
	    Draw(Bounds());
	   }
	  }
	  break;
	 }
	 
	 case B_UP_ARROW:
	 {
	  // Test whether Ctrl+UpArrow is pressed
	  if(modifiers() & B_CONTROL_KEY)
	   Window()->PostMessage(B_QUIT_REQUESTED);
	  else
	  {
	   if(which_focused==0) // month
	   {
	    BMessage msg('MON1');
	    MessageReceived(&msg);
	   }
	   else if(which_focused==1) // year
	   {
	    BMessage msg('YEA1');
	    MessageReceived(&msg);
	   }
	   else if(which_focused==2) // days of month
	   {
	    if((cday-7)>=1)
	    {
	     Bmp->Lock();
	     BmpView->SetHighColor(255,255,255);
	     BmpView->StrokeRect(cursor);
	     cursor.OffsetBySelf(0, -h_cell);
	     cday-=7;
	     BmpView->SetHighColor(ui_color(B_NAVIGATION_BASE_COLOR));
	     BmpView->StrokeRect(cursor);
	     BmpView->SetHighColor(0,0,0);
	     BmpView->Sync();
	     Bmp->Unlock();
	     Draw(Bounds());
	    }
	   }
	  }
	  break;
	 }
	 
	 case B_LEFT_ARROW:
	 {
	  if(which_focused==0) // month
	  {
	   BMessage msg('MON0');
	   MessageReceived(&msg);
	  }
	  else if(which_focused==1) // year
	  {
	   BMessage msg('YEA0');
	   MessageReceived(&msg);
	  }
	  else if(which_focused==2) // days of month
	  {
	   if(cday>1)
	   {
	    Bmp->Lock();
	    BmpView->SetHighColor(255,255,255);
	    BmpView->StrokeRect(cursor);
	    if(cwday>0) // by dates no matter of day of week
	    {
	     cursor.OffsetBySelf(-w_cell,0);
	     cwday--;
	    }
	    else // cwday==0
	    {
	     cursor.OffsetBySelf(w_cell*6,-h_cell);
	     cwday=6;
	    }
	    cday--;
	    BmpView->SetHighColor(ui_color(B_NAVIGATION_BASE_COLOR));
	    BmpView->StrokeRect(cursor);
	    BmpView->SetHighColor(0,0,0);
	    BmpView->Sync();
	    Bmp->Unlock();
	    Draw(Bounds());
	   }
	   else // 1st of month, go month before
	   {
	    if(cyear>first_year || cmonth>1) // one can go
	    {
	     int cm=(cmonth==1) ? 12 : cmonth-1;
	     int qu_days=monthDays[cm-1];
	     if(cm==2) if(cyear%4==0) 
	      if((cyear%100!=0) || (cyear%400==0)) qu_days=29;
	      // it is correct not to pay attention to year changing
	      // because if we moved to february then year was not changed
	      // but if month is other then february 
	      // then it has the same number of days every year.
	     cday=qu_days;
	     
	     BMessage msg('MON0');
	     MessageReceived(&msg); // here month, cwday and year (if needed) will be changed
	    }
	   }
	  }
	  break;
	 }
	 
	 case B_RIGHT_ARROW:
	 {
	  if(which_focused==0) // month
	  {
	   BMessage msg('MON1');
	   MessageReceived(&msg);
	  }
	  else if(which_focused==1) // year
	  {
	   BMessage msg('YEA1');
	   MessageReceived(&msg);
	  }
	  else if(which_focused==2) // days of month
	  {
	   int qu_days=monthDays[cmonth-1];
	   if(cmonth==2) if(cyear%4==0) if((cyear%100!=0) || (cyear%400==0)) qu_days=29;
	   if(cday<qu_days)
	   {
	    Bmp->Lock();
	    BmpView->SetHighColor(255,255,255);
	    BmpView->StrokeRect(cursor);
	    if(cwday<6) // by dates no matter of day of week
	    {
	     cursor.OffsetBySelf(w_cell,0);
	     cwday++;
	    }
	    else // cwday==6
	    {
	     cursor.OffsetBySelf(-w_cell*6,h_cell);
	     cwday=0;
	    }
	    cday++;
	    BmpView->SetHighColor(ui_color(B_NAVIGATION_BASE_COLOR));
	    BmpView->StrokeRect(cursor);
	    BmpView->SetHighColor(0,0,0);
	    BmpView->Sync();
	    Bmp->Unlock();
	    Draw(Bounds());
	   }
	   else // last of month, go next month
	   {
	    if(cyear<last_year || cmonth<12) // one can go
	    {
	     cday=1;
	     BMessage msg('MON1');
	     MessageReceived(&msg); // here month, cwday and year (if needed) will be changed
	    }
	   }
	  }
	  break;
	 }
	 
	 case B_ESCAPE:
	 {
	  Window()->PostMessage(B_QUIT_REQUESTED);
	  break;
	 }
	 
	 case B_ENTER:
	 case B_SPACE:
	 {
	  if(which_focused==2)
	  {
	   // here it is needed to send dayPressed (==cwday), cyear, cmonth
	   // to window and leave
	   BMessage *msg=new BMessage('MVME');
	   msg->AddInt32("day",cday);
	   msg->AddInt32("month",cmonth);
	   msg->AddInt32("year",cyear);
	   Window()->PostMessage(msg);
	  }
	  else if(which_focused==3)
	  {
	   BMessage msg('TODA');
	   MessageReceived(&msg);
	  }
	  break;
	 }
	 
	 default: 
	  BView::KeyDown(bytes, numBytes);
	}
}


////////////////////////////////////////////////////
void MonthView::MessageReceived(BMessage *msg)
{
	switch(msg->what)
	{
	 case 'YEA0': // year before
	 {
	  if(cyear<=first_year) break;
	  cyear--;
	  NewMonth=true;
	  BString s("");
	  if(cyear<10) s.Append("000");
	  else if(cyear<100) s.Append("00");
	  else if(cyear<1000) s.Append("0");
	  s<<cyear;
	  yearStringView->SetText(s.String());
	  
	  if(cmonth==2) if(cday==29) // one can do simplier: if cday==29, then make
	                             // cday=28, because two leap years one after other can't be
	  {
	   if(cyear%4!=0) cday=28;
	   else if(cyear%100==0 && cyear%400!=0) cday=28;
	  }
	  
	  DrawMonth();
	  break;
	 }
	 case 'YEA1': // year after
	 {
	  if(cyear>=last_year) break;
	  cyear++;
	  NewMonth=true;
	  BString s("");
	  if(cyear<10) s.Append("000");
	  else if(cyear<100) s.Append("00");
	  else if(cyear<1000) s.Append("0");
	  s<<cyear;
	  yearStringView->SetText(s.String());
	  
	  if(cmonth==2) if(cday==29)
	  {
	   if(cyear%4!=0) cday=28;
	   else if(cyear%100==0 && cyear%400!=0) cday=28;
	  }
	  
	  DrawMonth();
	  break;
	 }
	 case 'MON0': // month before
	 {
	  if(cmonth>1) cmonth--;
	  else if(cyear>first_year)
	  {
	   cmonth=12;
	   cyear--;
	   
	   BString s("");
	   if(cyear<10) s.Append("000");
	   else if(cyear<100) s.Append("00");
	   else if(cyear<1000) s.Append("0");
	   s<<cyear;
	   yearStringView->SetText(s.String());
	  }
	  else break; // nothing is changed
	  
	  NewMonth=true;
	  monthStringView->SetText(monthNames[cmonth-1]);
	  
	  if(cmonth==2)
	  {
	   int tmpday=28;
	   if(cyear%4==0) if((cyear%100!=0) || (cyear%400==0)) tmpday=29;
	   if(cday>tmpday) cday=tmpday;
	  }
	  else if(cday>monthDays[cmonth-1]) cday=monthDays[cmonth-1];
	  
	  DrawMonth();
	  break;
	 }
	 case 'MON1': // month after
	 {
	  if(cmonth<12) cmonth++;
	  else if(cyear<last_year)
	  {
	   cmonth=1;
	   cyear++;
	   
	   BString s("");
	   if(cyear<10) s.Append("000");
	   else if(cyear<100) s.Append("00");
	   else if(cyear<1000) s.Append("0");
	   s<<cyear;
	   yearStringView->SetText(s.String());
	  }
	  else break; // nothing is changed
	  
	  NewMonth=true;
	  monthStringView->SetText(monthNames[cmonth-1]);
	  
	  if(cmonth==2)
	  {
	   int tmpday=28;
	   if(cyear%4==0) if((cyear%100!=0) || (cyear%400==0)) tmpday=29;
	   if(cday>tmpday) cday=tmpday;
	  }
	  else if(cday>monthDays[cmonth-1]) cday=monthDays[cmonth-1];
	  
	  DrawMonth();
	  break;
	 }
	 case 'TODA': // to place to today's date
	 {
	  if(tyear<first_year || tyear>last_year) break;
	  
	  NewMonth=true;
	  
	  cyear=tyear;
	  cmonth=tmonth;
	  cday=tday;
	  
	  BString s("");
	  if(cyear<10) s.Append("000");
	  else if(cyear<100) s.Append("00");
	  else if(cyear<1000) s.Append("0");
	  s<<cyear;
	  yearStringView->SetText(s.String());
	  monthStringView->SetText(monthNames[cmonth-1]);
	  
	  DrawMonth();
	  break;
	 }
	 default:
	  BView::MessageReceived(msg);
	}
}


/////////////////////////////////////////
void MonthView::MouseDown(BPoint p)
{
	// It's necessary to define whether mouse cursor is on one of dates
	BRect r;
	
	int k=cwday1;
	float x=w_cell*k+1;
	float y=yearStringView->Frame().bottom+h_cell+h_cell;
	
	int qu_days=monthDays[cmonth-1]; // quantity of days in month
	
	if(cmonth==2) if(cyear%4==0) if((cyear%100!=0) || (cyear%400==0)) qu_days=29;
	
	bool flag=false; // whether mouse is pressed just on date
	int t=0;
	while(t<qu_days)
	{
	 t++;
	 r.Set(x,y-h_cell+5,x+w_cell-1,y+4);
	 
	 if(r.Contains(p))
	 {
	  flag=true;
	  dayPressed=t;
	  cursorPressed.Set(r.left,r.top,r.right,r.bottom);
	  MousePressed=true;
	  Bmp->Lock();
	  BmpView->SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
	  BmpView->StrokeRect(cursor);
	  BmpView->SetHighColor(ui_color(B_NAVIGATION_BASE_COLOR));
	  BmpView->StrokeRect(cursorPressed);
	  Bmp->Unlock();
	  Invalidate();
	  break; // exit while
	 }
	 
	 x+=w_cell;
	 k++;
	 
	 if(k==7)
	 {
	  k=0;
	  y+=h_cell;
	  x=1;
	 }
	}
}


void MonthView::MouseUp(BPoint p)
{
	if(!MousePressed) return;
	
	if(cursorPressed.Contains(p))
	{
		// here it is needed to send dayPressed, cyear, cmonth
		// to window and leave
		BMessage *msg=new BMessage('MVME');
		msg->AddInt32("day",dayPressed);
		msg->AddInt32("month",cmonth);
		msg->AddInt32("year",cyear);
		Window()->PostMessage(msg);
	}
	else 
	{
		Bmp->Lock();
		BmpView->SetHighColor(255,255,255);
		BmpView->StrokeRect(cursorPressed);
	  	BmpView->SetHighColor(ui_color(B_NAVIGATION_BASE_COLOR));
	  	BmpView->StrokeRect(cursor);
		Bmp->Unlock();
		Invalidate();
		MousePressed=false;
	}
}


void MonthView::MouseMoved(BPoint pt, uint32 transit, const BMessage *msg)
{
	BPoint point;
	uint32 buttons;
	GetMouse(&point,&buttons);

        if(transit==B_ENTERED_VIEW)
        {
                if( (buttons & B_PRIMARY_MOUSE_BUTTON)==0 &&
                                (buttons & B_SECONDARY_MOUSE_BUTTON)==0 &&
                                (buttons & B_PRIMARY_MOUSE_BUTTON)==0 )
				MousePressed = false;
                else
                        	MousePressed = true;
        }

        if(transit==B_EXITED_VIEW || transit==B_OUTSIDE_VIEW)
			MouseUp(Bounds().LeftTop());
}