Files
Yab2Cpp/src/DateTextView.cpp
2015-04-13 13:40:27 -07:00

621 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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 <Clipboard.h>
#include <InterfaceDefs.h>
#include <Rect.h>
#include <String.h>
#include <TextView.h>
#include <stdlib.h>
#include <time.h>
#define LAST_FORMAT 1 // quantity of formats - 1
#define LAST_DIVIDER 2 // quantity of dividers - 1
class DateTextView: public BTextView
{
public:
DateTextView(int day, int month, int year, uint32 flags, uint32 look);
virtual void Cut(BClipboard *clip);
virtual void KeyDown(const char *bytes, int32 numBytes);
virtual void MakeFocus(bool focused);
virtual void Paste(BClipboard *clip);
virtual void SetEnabled(bool enabled);
void GetDate(int *day, int *month, int *year);
void SetDate(int day, int month, int year);
void SetDate(const char *tdate);
void GetYearRange(int *first_year, int *last_year);
uint32 GetDivider();
void SetDivider(uint32 dvder);
uint32 GetDateFlags();
void SetDateFlags(uint32 flags);
protected:
virtual void DeleteText(int32 start, int32 finish);
private:
virtual bool AcceptsDrop(const BMessage *message);
virtual bool AcceptsPaste(BClipboard *clip);
void DrawDate(int day, int month, int year);
bool VerifyDate(int *day, int *month, int *year);
bool is_ins; // is it necessar to insert symbol
uint32 flags;
char *div[LAST_DIVIDER+1]; // сstrings of dividers
uint32 divider;
bool enabled;
int first_year;
int last_year; // first and last year which control accepts
int textlen; // length of text string (10 or 8 symbols)
uint32 interface; // the same as control variable
};
//////////////////////////////////////////////////////////////////////////
DateTextView::DateTextView(int day, int month, int year,
uint32 flags, uint32 look)
:BTextView(BRect(0,0,110,15),"DateTextViewAViX",
BRect(0,0,110,15),B_FOLLOW_LEFT | B_FOLLOW_TOP,
B_WILL_DRAW | B_NAVIGABLE)
{
enabled=true;
is_ins=false;
interface=look;
div[0]=(char*)".";
div[1]=(char*)"/";
div[2]=(char*)"-";
divider=look & CC_ALL_DIVIDERS;
if(divider>LAST_DIVIDER || divider<0) divider=0; // index of divider
this->flags=(flags & (LAST_FORMAT | CC_SHORT_YEAR | CC_HALF_CENTURY));
if((this->flags & (CC_SHORT_YEAR | CC_HALF_CENTURY))==CC_HALF_CENTURY)
this->flags=this->flags^CC_HALF_CENTURY; // XOR; CC_FULL_YEAR and CC_HALF_CENTURY
// at the same time may not be used
first_year=1;
last_year=9999;
if((this->flags & CC_SHORT_YEAR)==CC_SHORT_YEAR)
{
textlen=8;
// Changing first and last acceptable years
// Working in range of century defined by year variable
if(year<first_year || year>last_year)
{
// Range is set relative to today's year
struct tm *dat;
time_t tmp;
time(&tmp);
dat=localtime(&tmp);
year=dat->tm_year+1900; // today's year
}
first_year=(year/100)*100+1; // dividing with loosing rest
last_year=first_year+99;
if(last_year==10000) last_year--; // full century
if((this->flags & CC_HALF_CENTURY)==CC_HALF_CENTURY)
{
if(year<51 || year>9950)
this->flags=this->flags^CC_HALF_CENTURY; // HALF_CENTURY may nor be set
else
{
if((year%100)>50)
{
first_year+=50;
last_year+=50;
}
else
{
first_year-=50;
last_year-=50;
}
}
}
}
else textlen=10; // length of text string
SetDate(day,month,year);
float width=0;
BString s("");
for(char i='0'; i<='9'; i++)
{
s<<i;
if(StringWidth(s.String())>width) width=StringWidth(s.String());
s.SetTo("");
}
ResizeTo(width*(textlen-2)+StringWidth("/")*2.5, LineHeight()-1);
SetWordWrap(false);
SetMaxBytes(textlen);
SetTextRect(Bounds());
SetDoesUndo(false);
for(int32 i=0;i<256;i++) DisallowChar(i);
for(int32 i='0';i<='9';i++) AllowChar(i);
}
///////////////////////////////////////////////////////
bool DateTextView::AcceptsDrop(const BMessage *message)
{
return false;
}
/////////////////////////////////////////////////
bool DateTextView::AcceptsPaste(BClipboard *clip)
{
return false;
}
////////////////////////////////////////
void DateTextView::Cut(BClipboard *clip)
{
Copy(clip);
}
////////////////////////////////////////////////////////
void DateTextView::DeleteText(int32 start, int32 finish)
{
BTextView::DeleteText(start,finish);
if(is_ins)
{
if(start==2 || start==5) InsertText(div[divider],1,start,NULL);
else InsertText("0",1,start,NULL);
}
}
/////////////////////////////////////////////////////////////
void DateTextView::KeyDown(const char *bytes, int32 numBytes)
{
if(!enabled) if(bytes[0]!=B_TAB) return;
int32 i1,i2;
GetSelection(&i1,&i2);
if(bytes[0]>='0' && bytes[0]<='9')
{
if(i1>(textlen-1)) return; // not to insert after end of string
Select(i1,i1);
if(i1==2 || i1==5)
{
i1++;
Select(i1,i1);
}
DeleteText(i1,i1+1);
BTextView::KeyDown(bytes, numBytes);
}
else if(bytes[0]==B_DELETE)
{
if(i1>(textlen-1)) return; // not to insert after end of string
Select(i1,i1);
is_ins=true; // symbol "0" or divider will be inserted
BTextView::KeyDown(bytes, numBytes);
is_ins=false;
}
else if(bytes[0]==B_BACKSPACE)
{
Select(i1,i1);
is_ins=true;
BTextView::KeyDown(bytes, numBytes);
is_ins=false;
}
else if(bytes[0]==B_TAB)
{
Parent()->KeyDown(bytes, numBytes);
}
else if(bytes[0]==B_DOWN_ARROW)
{
// Is Ctrl+DownArrow pressed?
if(modifiers() & B_CONTROL_KEY)
{
// yes
BMessage msg(myButtonMessage);
Parent()->MessageReceived(&msg);
}
else
BTextView::KeyDown(bytes, numBytes);
}
else
BTextView::KeyDown(bytes, numBytes);
}
///////////////////////////////////////////////
void DateTextView::MakeFocus(bool focused=true)
{
BTextView::MakeFocus(focused);
int day, month, year;
GetDate(&day, &month, &year);
Parent()->Draw(Parent()->Bounds());
}
//////////////////////////////////////////
void DateTextView::Paste(BClipboard *clip)
{
return;
}
///////////////////////////////////////////
void DateTextView::SetEnabled(bool enabled)
{
this->enabled=enabled;
SetFlags(Flags()^B_NAVIGABLE);
MakeEditable(enabled);
BFont font;
rgb_color color;
GetFontAndColor((int32) 0, &font, &color);
color.alpha=0;
if(enabled)
{
SetViewColor(255,255,255,255);
color.red=color.green=color.blue=0;
}
else
{
SetViewColor(239,239,239,255);
color.red=color.green=color.blue=128;
}
SetFontAndColor(&font,B_FONT_ALL,&color);
Invalidate();
}
/////////////////////////////////////////////////////////
void DateTextView::DrawDate(int day, int month, int year)
{
// It is assumed that date is correct
BString s;
s.SetTo("");
if(!((flags & CC_MM_DD_YYYY_FORMAT)==CC_MM_DD_YYYY_FORMAT))
{
if(day<10) s.Append("0");
s<<day;
s.Append(div[divider]);
if(month<10) s.Append("0");
s<<month;
}
else // CC_MM_DD_YYYY_FORMAT
{
if(month<10) s.Append("0");
s<<month;
s.Append(div[divider]);
if(day<10) s.Append("0");
s<<day;
}
s.Append(div[divider]);
if((flags & CC_SHORT_YEAR)==CC_SHORT_YEAR)
{
int year1=year%100;
if(year1<10) s.Append("0");
s<<year1;
}
else // FULL_YEAR
{
if(year<10) s.Append("000");
else if(year<100) s.Append("00");
else if(year<1000) s.Append("0");
s<<year;
}
SetText(s.String());
}
///////////////////////////////////////////////////////////
void DateTextView::GetDate(int *day, int *month, int *year)
{
int mday=*day;
int mmonth=*month;
int myear=*year;
BString s(Text());
char n1[11];
char n2[11];
char n3[11];
s.CopyInto(n1,0,2);
n1[2]='\0';
s.CopyInto(n2,3,2);
n2[2]='\0';
if((flags & CC_SHORT_YEAR)==CC_SHORT_YEAR)
{
s.CopyInto(n3,6,2);
n3[2]='\0';
}
else // FULL_YEAR
{
s.CopyInto(n3,6,4);
n3[4]='\0';
}
if(!((flags & CC_MM_DD_YYYY_FORMAT)==CC_MM_DD_YYYY_FORMAT))
{
mday=atoi(n1);
mmonth=atoi(n2);
}
else
{
mday=atoi(n2);
mmonth=atoi(n1);
}
myear=atoi(n3);
if((flags & CC_SHORT_YEAR)==CC_SHORT_YEAR)
{
if((flags & CC_HALF_CENTURY)==CC_HALF_CENTURY)
{
if(myear<51) myear+=50; else myear-=50;
}
else if(myear==0) myear=100;
myear+=(first_year-1);
}
if(!VerifyDate(&mday,&mmonth,&myear)) SetDate(mday,mmonth,myear);
*day=mday;
*month=mmonth;
*year=myear;
return;
}
////////////////////////////////////////////////////////
void DateTextView::SetDate(int day, int month, int year)
{
int mday=day;
int mmonth=month;
int myear=year;
VerifyDate(&mday, &mmonth, &myear);
DrawDate(mday, mmonth, myear);
}
/////////////////////////////////////////////
void DateTextView::SetDate(const char *tdate)
{
// Almost the same as GetDate. May be to combine them.
// Changes text using current settings of control.
int day;
int month;
int year;
int k;
bool short_year=false;
if((flags & CC_SHORT_YEAR)==CC_SHORT_YEAR) short_year=true;
char n1[3]="00";
char n2[3]="00";
char n3[5]="0000";
if(short_year) n3[2]='\0';
bool zero=false; // was the end of tdate string?
int c=0;
while (!(c==2 || zero))
{
if(tdate[c]=='\0') zero=true;
else n1[c]=tdate[c];
c++;
}
if(zero) goto L1;
if(tdate[2]=='\0') goto L1;
c=0;
while (!(c==2 || zero))
{
if(tdate[c+3]=='\0') zero=true;
else n2[c]=tdate[c+3];
c++;
}
if(zero) goto L1;
if(tdate[5]=='\0') goto L1;
k=short_year ? 2 : 4;
c=0;
while (!(c==k || zero))
{
if(tdate[c+6]=='\0') zero=true;
else n3[c]=tdate[c+6];
c++;
}
L1:
if(!((flags & CC_MM_DD_YYYY_FORMAT)==CC_MM_DD_YYYY_FORMAT))
{
day=atoi(n1);
month=atoi(n2);
}
else
{
day=atoi(n2);
month=atoi(n1);
}
year=atoi(n3);
if(short_year)
{
if((flags & CC_HALF_CENTURY)==CC_HALF_CENTURY)
{
if(year<51) year+=50; else year-=50;
}
else if(year==0) year=100;
year+=(first_year-1);
}
SetDate(day,month,year);
}
//////////////////////////////////////////////////////////////
bool DateTextView::VerifyDate(int *day, int *month, int *year)
{
// Function verifies date to be correct and changes it if it's needed
// Returns true if date was correct (and wasn't changed)
struct tm *dat;
time_t tmp;
time(&tmp);
dat=localtime(&tmp);
bool flag=true; // date is correct
if((flags & CC_SHORT_YEAR)==CC_SHORT_YEAR)
{
if(*year<first_year || *year>last_year)
{
int year1=*year%100;
if((flags & CC_HALF_CENTURY)==CC_HALF_CENTURY)
{
if(year1<51) year1+=50; else year1-=50;
}
else if(year1==0) year1=100;
*year=year1+first_year-1;
flag=false;
}
}
else // FULL_YEAR
{
if(*year<1 || *year>9999)
{
*year=dat->tm_year+1900;
flag=false;
}
}
if(*month<1 || *month>12)
{
*month=dat->tm_mon+1;
flag=false;
}
if(*day<1 || *day>31)
{
*day=dat->tm_mday;
flag=false;
}
if((*month==4 || *month==6 || *month==9 || *month==11) && *day>30)
{
if((*day=dat->tm_mday)>30) *day=30;
flag=false;
}
else if (*month==2)
{
int tmpday;
if((*year)%4==0) // leap year?
{
if((*year)%100==0 && (*year)%400!=0) tmpday=28; // no
else tmpday=29; // yes
}
else tmpday=28;
if(*day>tmpday)
{
if((*day=dat->tm_mday)>tmpday) *day=tmpday;
flag=false;
}
}
return flag;
}
////////////////////////////////////////////////////////////////
void DateTextView::GetYearRange(int *first_year, int *last_year)
{
*first_year=this->first_year;
*last_year=this->last_year;
}
/////////////////////////////////
uint32 DateTextView::GetDivider()
{
return divider;
}
///////////////////////////////////////////
void DateTextView::SetDivider(uint32 dvder)
{
if(dvder<0 || dvder>LAST_DIVIDER) dvder=0;
BString s(Text());
SetText((s.ReplaceAll(div[divider],div[dvder])).String());
this->divider=dvder;
}
///////////////////////////////////
uint32 DateTextView::GetDateFlags()
{
return flags;
}
///////////////////////////////////////////
void DateTextView::SetDateFlags(uint32 fmt)
{
int mday, mmonth, myear;
GetDate(&mday, &mmonth, &myear);
// Blocking changing of parameters of year
// (full/short year, full/half century)
fmt=fmt & 0xFFE7;
if(fmt<0 || fmt>LAST_FORMAT) fmt=0;
flags=fmt | (flags & 0xFFFE); // LAST_FORMAT==1, 1 bit
SetDate(mday, mmonth, myear);
}