//------------------------------------------------------------------------------
// OWL Extensions (OWLEXT) Class Library
// CALEDIT.CPP
//
// TCalendarEdit Class
// TDateValidator Class
// TPopupCalWin Class
//
// Original code by Daniel BERMAN (dberman@inge-com.fr)
// Written: 19th November 1995
//
//------------------------------------------------------------------------------
#include <owlext\pch.h>
#pragma hdrstop
 
#include <owl/appdict.h>
 
#include <owl/celarray.h>
#include <owl/gadgetwi.h>
 
#include <owlext/caledit.h>
#include <owlext/caledit.rh>
 
#include <algorithm>
 
namespace OwlExt {
 
using namespace owl;
 
 
// private function
TModule* FindResourceModule(TWindow* parent, TModule* module, TResId resId, LPCTSTR type);
 
class TPopupCalWin : public TWindow {
public:
 
  TFont*   Font;
  int     FontWidth;
  int     FontHeight;
 
  TDate   Date;
 
  int MonthRow;
  int WeekDaysRow;
  int DaysRow;
  int StartCol;
 
  bool   Dragging;
  TRect CurRect;
 
  TCelArray* BmpArray;
  TRect BtnPrevRect;
  TRect BtnNextRect;
 
  TCalendarEdit* CalEdit;
 
  enum {Prev, Next};
 
  TPopupCalWin(TWindow* parent, TCalendarEdit* edit, TModule* module = 0);
  ~TPopupCalWin();
 
  void SetupWindow() override;
 
  void BuildBmpArray();
  void DrawFocusRect(TDC& dc, int x, int y);
  void Paint(TDC&, bool erase, TRect&) override;
  void PaintBorders(TDC& dc, TRect& rect);
 
  void EvKeyDown(uint key, uint repeatCount, uint flags);
  void EvKillFocus(HWND hwndGetFocus);
  void EvLButtonDown(uint modKeys, const TPoint& point);
  void EvLButtonUp(uint modKeys, const TPoint& point);
  void EvMouseMove(uint modKeys, const TPoint& point);
 
  DECLARE_RESPONSE_TABLE(TPopupCalWin);
};
 
 
//
//-- TPopupCalWin --------------------------------------------------------------
//
 
DEFINE_RESPONSE_TABLE1(TPopupCalWin, TWindow)
EV_WM_KEYDOWN,
EV_WM_KILLFOCUS,
EV_WM_LBUTTONDOWN,
EV_WM_LBUTTONUP,
EV_WM_MOUSEMOVE,
END_RESPONSE_TABLE;
 
TPopupCalWin::TPopupCalWin(TWindow* parent, TCalendarEdit* edit, TModule* module)
:
TWindow(parent, 0, module)
{
  ModifyStyle(0,WS_POPUP|WS_BORDER|WS_VISIBLE);
 
  CalEdit = edit;
 
  Font = new TFont(_T("MS Shell Dlg"), -8);
  {
    TRect rc;
    TScreenDC dc;
    dc.SelectObject(*Font);
    dc.DrawText(_T("Abc"), -1, rc, DT_CENTER | DT_CALCRECT);
    FontWidth = rc.Width();
    FontHeight = rc.Height();
  }
 
  MonthRow = 0;
  WeekDaysRow = MonthRow + FontHeight;
  DaysRow = WeekDaysRow + FontHeight + 4;
  StartCol = 4;
 
  Attr.W = (7 * FontWidth) + ( 2 * StartCol);
  Attr.H = DaysRow + (6 * FontHeight) + 4;
 
  Dragging = false;
 
  BmpArray = 0;
  BuildBmpArray();
 
  BtnPrevRect = TRect(2, 2, BmpArray->CelSize().cx + 2, BmpArray->CelSize().cy + 2);
 
  BtnNextRect.left = Attr.W - BmpArray->CelSize().cx - 4;
  BtnNextRect.top = 2;
  BtnNextRect.right = BtnNextRect.left + BmpArray->CelSize().cx;
  BtnNextRect.bottom = BtnNextRect.top + BmpArray->CelSize().cy;
}
 
TPopupCalWin::~TPopupCalWin()
{
  delete Font;
  delete BmpArray;
}
 
void TPopupCalWin::BuildBmpArray()
{
  TBitmap bmp(*FindResourceModule(Parent,GetModule(),IDB_CALPREVNEXT,RT_BITMAP),
    IDB_CALPREVNEXT);
  int bmpWidth = bmp.Width();
  int bmpHeight = bmp.Height() / 2;
 
  if (!BmpArray){
    TBitmap* tmp = new TBitmap(TScreenDC(), bmpWidth, bmpHeight);
    BmpArray = new TCelArray(tmp, 2);
  }
 
  TMemoryDC bmpDC;
  {
    TBitmap tmp(TScreenDC(), bmpWidth, bmpHeight);
    bmpDC.SelectObject(tmp);
    bmpDC.TextRect(0, 0, bmpWidth, bmpHeight, TColor::Sys3dFace);
    DrawMaskedBmp(bmpDC, 0, 0, bmp, 0, bmpWidth);
  }
 
  TMemoryDC memDC;
  memDC.SelectObject(*BmpArray);
  memDC.BitBlt(0, 0, bmpWidth, bmpHeight, bmpDC, 0, 0);
}
 
//
//
//
void
TPopupCalWin::DrawFocusRect(TDC& dc, int x, int y)
{
  TRect rect = TRect(StartCol + x * FontWidth,
    DaysRow + y * FontHeight,
    StartCol + x * FontWidth + FontWidth,
    DaysRow + y * FontHeight + FontHeight);
 
  dc.SaveDC();
 
  if (!CurRect.IsEmpty()){
    dc.SelectObject(TBrush(TColor::Sys3dFace));
    dc.PatBlt(CurRect.left, CurRect.top, CurRect.Width() - 1, 1, PATCOPY);
    dc.PatBlt(CurRect.left, CurRect.top, 1, CurRect.Height() - 1, PATCOPY);
    dc.PatBlt(CurRect.right - 1, CurRect.top, 1, CurRect.Height(), PATCOPY);
    dc.PatBlt(CurRect.left, CurRect.bottom - 1, CurRect.Width(), 1, PATCOPY);
  }
 
  dc.SelectObject(TBrush(TColor::Sys3dShadow));
  dc.PatBlt(rect.left, rect.top, rect.Width() - 1, 1, PATCOPY);
  dc.PatBlt(rect.left, rect.top, 1, rect.Height() - 1, PATCOPY);
  dc.SelectObject(TBrush(TColor::Sys3dHilight));
  dc.PatBlt(rect.right - 1, rect.top, 1, rect.Height(), PATCOPY);
  dc.PatBlt(rect.left, rect.bottom - 1, rect.Width(), 1, PATCOPY);
 
  CurRect = rect;
 
  dc.RestoreDC();
}
 
void
TPopupCalWin::EvKeyDown(uint key, uint repeatCount, uint flags)
{
  TDate curDate = Date;
 
  switch (key){
case VK_DOWN:
  Date += 7;
  break;
case VK_END:
  if (GetKeyState(VK_CONTROL) < 0)
    if (Date < TDate(31, 12, Date.Year()))
      Date = TDate(31, 12, Date.Year());
    else
      Date = TDate(31, 12, Date.Year() + 1);
  else
    if ((int)Date.DayOfMonth() < Date.DaysInMonth())
      Date = TDate(Date.DaysInMonth(), Date.Month(), Date.Year());
  break;
case VK_ESCAPE:
  CalEdit->SetFocus();
  CloseWindow();
  return;
case VK_HOME:
  if (GetKeyState(VK_CONTROL) < 0)
    if (Date > TDate(1, 1, Date.Year()))
      Date = TDate(1, 1, Date.Year());
    else
      Date = TDate(1, 1, Date.Year() - 1);
  else
    if (Date.DayOfMonth() > 1)
      Date = TDate(1, Date.Month(), Date.Year());
  break;
case VK_LEFT:
  --Date;
  break;
case VK_MULTIPLY:
  Date = TDate();
  break;
case VK_NEXT:
  if (GetKeyState(VK_CONTROL) < 0){
    if (Date.Month() == 2){
      TDate date(1, 2, Date.Year() + 1);
      if ((int)Date.DayOfMonth() > date.DaysInMonth()){
        Date = TDate(date.DaysInMonth(), 2, date.Year());
        break;
      }
    }
    Date = TDate(Date.DayOfMonth(), Date.Month(), Date.Year() + 1);
  }
  else
  {
    int days = Date.DaysInMonth(Date.Month()+1 > 12 ? 1 : Date.Month()+1);
    if ((int)Date.DayOfMonth() <= days)
      Date += Date.DaysInMonth();
    else
      if (Date.Month() == 1)
        Date = TDate(Date.DaysInMonth(2), 2, Date.Year());
      else
        Date += std::min(days, Date.DaysInMonth());
  }
  break;
case VK_PRIOR:
  if (GetKeyState(VK_CONTROL) < 0){
    if (Date.Month() == 2){
      TDate date(1, 2, Date.Year() - 1);
      if ((int)Date.DayOfMonth() > date.DaysInMonth()){
        Date = TDate(date.DaysInMonth(), 2, date.Year());
        break;
      }
    }
    Date = TDate(Date.DayOfMonth(), Date.Month(), Date.Year() - 1);
  }
  else{
    int days = Date.DaysInMonth(Date.Month()-1 < 1 ? 12 : Date.Month()-1);
    if (days < (int)Date.DayOfMonth())
      Date -= Date.DayOfMonth();
    else
      Date -= days;
  }
  break;
case VK_RETURN:
case VK_SPACE:
  CalEdit->SetDate(Date);
  CalEdit->SetFocus();
  CloseWindow();
  return;
case VK_RIGHT:
  ++Date;
  break;
case VK_UP:
  Date -= 7;
  break;
default:
  TWindow::EvKeyDown(key, repeatCount, flags);
  return;
  };
 
  if (Date.Month() != curDate.Month() || Date.Year() != curDate.Year())
    Invalidate();
  else{
    TClientDC dc(*this);
    DrawFocusRect(dc, Date.WeekDay() - 1, (Date.DayOfMonth() + Date.WeekDay(1) - 2 ) / 7);
  }
}
 
void
TPopupCalWin::EvKillFocus(HWND /*hwndGetFocus*/)
{
  CloseWindow();
}
 
void
TPopupCalWin::EvLButtonDown(uint modKeys, const TPoint& point)
{
  SetCapture();
 
  if (BtnPrevRect.Contains(point) || BtnNextRect.Contains(point))
    return;
 
  Dragging = true;
  EvMouseMove(modKeys, point);
}
 
void
TPopupCalWin::EvLButtonUp(uint /*modKeys*/, const TPoint& point)
{
  ReleaseCapture();
 
  if (Dragging){
    Dragging = false;
 
    int x = ((point.x - StartCol) / FontWidth) + 1;
    int y = ((point.y - DaysRow) / FontHeight) + 1;
    TDate date((y*7) - (7-x) - (Date.WeekDay(1)-1), Date.Month(), Date.Year());
    if (!date.IsValid())
      return;
 
    CalEdit->SetDate(date);
    CalEdit->SetFocus();
 
    CloseWindow();
  }
  else if (BtnPrevRect.Contains(point)){
    int days = Date.DaysInMonth(Date.Month()-1 < 1 ? 12 : Date.Month()-1);
    if (days < (int)Date.DayOfMonth())
      Date -= Date.DayOfMonth();
    else
      Date -= days;
    Invalidate();
  }
  else if (BtnNextRect.Contains(point)){
    int days = Date.DaysInMonth(Date.Month()+1 > 12 ? 1 : Date.Month()+1);
    if ((int)Date.DayOfMonth() <= days)
      Date += Date.DaysInMonth();
    else
      if (Date.Month() == 1)
        Date = TDate(Date.DaysInMonth(2), 2, Date.Year());
      else
        Date += std::min(days, Date.DaysInMonth());
    Invalidate();
  }
}
 
void
TPopupCalWin::EvMouseMove(uint modKeys, const TPoint& point)
{
  if (Dragging){
    int x = ((point.x - StartCol) / FontWidth);
    int y = ((point.y - DaysRow) / FontHeight);
 
    if (TRect(StartCol, DaysRow, StartCol + 7 * FontWidth,
      DaysRow + 6 * FontHeight).Contains(point) &&
      TDate::DayWithinMonth(((y+1)*7) - (7-(x+1)) - (Date.WeekDay(1)-1),
      Date.Month(), Date.Year()))
    {
      TClientDC dc(*this);
      DrawFocusRect(dc, x,y);
    }
  }
  TWindow::EvMouseMove(modKeys, point);
}
 
void
TPopupCalWin::Paint(TDC& dc, bool /*erase*/, TRect& /*rect*/)
{
  int i;
  TRect r;
  TDate today;
  int weekday = Date.WeekDay(1);
  TRect clientRect = GetClientRect();
  TFont boldFont(_T("MS Shell Dlg"), -8, 0, 0, 0, FW_BOLD);
 
  PaintBorders(dc, clientRect);
 
  dc.SetBkMode(TRANSPARENT);
 
  TMemoryDC memDC(dc);
  memDC.SelectObject(*BmpArray);
  dc.BitBlt(BtnPrevRect, memDC, BmpArray->CelOffset(Prev));
  dc.BitBlt(BtnNextRect, memDC, BmpArray->CelOffset(Next));
 
  dc.SetTextColor(TColor::SysBtnText);
 
  _TCHAR buf[80];
  wsprintf(buf, _T("%s %d"), Date.NameOfMonth(), Date.Year());
  r = TRect(0, MonthRow, clientRect.right, MonthRow + FontHeight);
  dc.SelectObject(boldFont);
  dc.DrawText(buf, -1, r, DT_CENTER | DT_VCENTER);
  dc.SelectObject(*Font);
 
  r = TRect(StartCol, WeekDaysRow, StartCol + FontWidth, WeekDaysRow + FontHeight);
  for (i = 0; i < 7; i++){
    dc.DrawText(TDate::GetIntlName(TDate::dtWeekDayNames,i),2,r,DT_CENTER|DT_VCENTER);
    r.left += FontWidth;
    r.right += FontWidth;
  }
 
  dc.SaveDC();
  clientRect.top = WeekDaysRow + FontHeight;
  clientRect.bottom = clientRect.top + 2;
  dc.SelectObject(TBrush(TColor::Sys3dShadow));
  dc.PatBlt(clientRect.left, clientRect.top, clientRect.Width() - 1, 1, PATCOPY);
  dc.SelectObject(TBrush(TColor::Sys3dHilight));
  dc.PatBlt(clientRect.left, clientRect.bottom - 1, clientRect.Width(), 1, PATCOPY);
  dc.RestoreDC();
 
  r = TRect(StartCol + ((weekday -1) * FontWidth),
    DaysRow,
    StartCol + ((weekday -1) * FontWidth) + FontWidth,
    DaysRow + FontHeight);
  for (i = 1; i <= Date.DaysInMonth(); i++, weekday++){
    _TCHAR buf[3];
    if (today == TDate(i, Date.Month(), Date.Year()))
      dc.SelectObject(boldFont);
    dc.DrawText(_itot(i, buf, 10), -1, r, DT_CENTER | DT_VCENTER);
    if (today == TDate(i, Date.Month(), Date.Year()))
      dc.SelectObject(*Font);
    r.left += FontWidth;
    r.right += FontWidth;
    if (weekday % 7 == 0){
      r.left = StartCol;
      r.right = StartCol + FontWidth;
      r.top += FontHeight;
      r.bottom += FontHeight;
    }
  }
 
  DrawFocusRect(dc, Date.WeekDay()-1, (Date.DayOfMonth()+Date.WeekDay(1)-2)/7);
}
 
void TPopupCalWin::PaintBorders(TDC& dc, TRect& rect)
{
  dc.SaveDC();
  dc.SelectObject(TBrush(TColor::Sys3dHilight));
  dc.PatBlt(rect.left, rect.top, rect.Width() - 1, 1, PATCOPY);
  dc.PatBlt(rect.left, rect.top, 1, rect.Height() - 1, PATCOPY);
  dc.SelectObject(TBrush(TColor::Sys3dShadow));
  dc.PatBlt(rect.right - 1, rect.top, 1, rect.Height(), PATCOPY);
  dc.PatBlt(rect.left, rect.bottom - 1, rect.Width(), 1, PATCOPY);
  dc.RestoreDC();
}
 
void TPopupCalWin::SetupWindow()
{
  TWindow::SetupWindow();
 
  _TCHAR str[80];
  CalEdit->GetText(str, sizeof(str) / sizeof(_TCHAR));
 
  Date = TDate(str);
  if (!Date.IsValid())
    Date = TDate();
 
  TRect rc, rcThis = GetWindowRect();
  ::GetWindowRect(GetDesktopWindow(), &rc);
  if (rcThis.left < rc.left)
    rcThis.Offset(rc.left-rcThis.left, 0);
  if (rcThis.bottom > rc.bottom)
    rcThis.Offset(0, rc.bottom-rcThis.bottom);
  if (rcThis.right > rc.right)
    rcThis.Offset(rc.right-rcThis.right, 0);
  MoveWindow(rcThis);
 
  BuildBmpArray();
  SetBkgndColor(TColor::Sys3dFace);
}
 
 
//
//-- TCalendarEdit -------------------------------------------------------------
//
 
DEFINE_RESPONSE_TABLE1(TCalendarEdit, TPopupEdit)
EV_WM_CHAR,
EV_WM_ENABLE,
EV_WM_KEYDOWN,
EV_WM_KILLFOCUS,
EV_WM_SIZE,
END_RESPONSE_TABLE;
 
TCalendarEdit::TCalendarEdit(TWindow* parent, int id, LPCTSTR text,
               int x, int y, int w, int h, uint textLen,
               TModule* module)
               :
TPopupEdit(parent, id, text, x, y, w, h, IDB_CALBTN, textLen, module)
{
  Init();
}
 
TCalendarEdit::TCalendarEdit(TWindow* parent, int resourceId, uint textLen,
               TModule* module)
               :
TPopupEdit(parent, resourceId, IDB_CALBTN, textLen, module)
{
  Init();
}
 
TCalendarEdit::~TCalendarEdit()
{
}
 
void
TCalendarEdit::Clicked()
{
  if (*CalWin)
    return;
 
  TRect r = GetWindowRect();
  CalWin->GetWindowAttr().X = r.left;
  CalWin->GetWindowAttr().Y = r.bottom;
  CalWin->Create();
}
 
void TCalendarEdit::EvChar(uint key, uint repeatCount, uint flags)
{
  if (key != '+' && key != '-')
    TPopupEdit::EvChar(key, repeatCount, flags);
}
 
void TCalendarEdit::EvKeyDown(uint key, uint repeatCount, uint flags)
{
  switch (key){
case VK_ADD:
case VK_SUBTRACT:  {
  _TCHAR str[80];
  GetText(str, sizeof(str) / sizeof(_TCHAR));
  TDate date(str);
  if (!date.IsValid())
    date = TDate();
  key == VK_ADD ? ++date : --date;
  SetDate(date);
  break;
          }
default:
  TPopupEdit::EvKeyDown(key, repeatCount, flags);
  }
}
 
void TCalendarEdit::EvKillFocus(HWND hWndGetFocus)
{
  _TCHAR str[80];
  GetText(str, sizeof(str) / sizeof(_TCHAR));
  SetDate(str);
  TPopupEdit::EvKillFocus(hWndGetFocus);
}
 
const TDate& TCalendarEdit::GetDate()
{
  _TCHAR str[80];
  GetText(str, sizeof(str) / sizeof(_TCHAR));
  CalWin->Date = TDate(str);
  return CalWin->Date;
}
 
void TCalendarEdit::Init()
{
  CalWin = new TPopupCalWin(Parent, this, GetModule());
  CalWin->DisableAutoCreate();
}
 
void TCalendarEdit::SetDate(const TDate& date)
{
  if (date.IsValid()){
    CalWin->Date = date;
 
    _TCHAR str[80];
    GetText(str, sizeof(str) / sizeof(_TCHAR));
    if (_tcscmp(str, date.AsString().c_str()))
      TPopupEdit::SetText(date.AsString().c_str());
 
  }
  else
    Clear();
}
 
void TCalendarEdit::SetDate(LPCTSTR str)
{
  SetDate(TDate(str));
}
 
 
//
//-- TCalendarEditGadget -------------------------------------------------------
//
 
TCalendarEditGadget::TCalendarEditGadget(TWindow* gadgetWnd, int id, int width,
                     LPCTSTR text)
                     :
TPopupEditGadget(*new TCalendarEdit(gadgetWnd, id, text, 0, 0, width, 0))
{
}
 
 
//
//-- TDateValidator ------------------------------------------------------------
//
 
TDateValidator::TDateValidator()
:
TValidator()
{
}
 
void TDateValidator::Error(TWindow* wnd)
{
  TApplication* app;
 
  if(wnd)
    app  = wnd->GetApplication();
  else{
    app  = GetApplicationObject();
    wnd = GetWindowPtr(((TWindow*)app->GetMainWindow())->GetLastActivePopup());
  }
  owl::tstring msg;
  TModule* module = FindResourceModule(wnd,app,IDS_CAL_ERRMSG,RT_STRING);
  if(module)
    msg = module->LoadString(IDS_CAL_ERRMSG);
 
  if (wnd)
    wnd->MessageBox(msg.c_str(), app->GetName(), MB_ICONEXCLAMATION | MB_OK);
  else
    ::MessageBox(0, msg.c_str(), app->GetName(), MB_ICONEXCLAMATION | MB_OK | MB_TASKMODAL);
}
 
bool TDateValidator::IsValid(LPCTSTR str)
{
  if (!*str)
    return true;
 
  TDate date(str);
  bool isValid = date.IsValid();
 
  return isValid;
}
 
} // OwlExt namespace
//==============================================================================

V576 Incorrect format. Consider checking the fourth actual argument of the 'wsprintfA' function. The SIGNED integer type argument is expected.

V807 Decreased performance. Consider creating a reference to avoid using the 'BmpArray->CelSize()' expression repeatedly.