home *** CD-ROM | disk | FTP | other *** search
/ Boston 2 / boston-2.iso / DOS / PROGRAM / PASCAL / CALUNIT / CALUNIT.PAS
Pascal/Delphi Source File  |  1993-12-01  |  21KB  |  583 lines

  1. { Turbo Calendar }
  2. { Copyright (c) 1990 by Borland International, Inc. }
  3.  
  4. {$F+,O+,X+}
  5. Unit CalUnit;
  6.  
  7. { This unit provide a objects for creating a simple calendar.  Common
  8.   usage in a aplication would be:
  9.  
  10.   Procedure ShowCalendar;
  11.     Var
  12.       CalendarWindow: PCalendarWindow;
  13.     Begin
  14.       CalendarWindow := New(PCalendarWindow, Init(New(PDayDialog,Init)));
  15.       DeskTop^.Insert(CalendarWindow);
  16.     End;
  17.  
  18.  The DayDialog object has four virtual methods that me wish to be change
  19. to fit the needs of a specific users application.
  20.  
  21.     Function IsSpecial(Day, Month, Year: Word): Boolean;
  22.     Procedure GetActiveDays;
  23.     Function ReadDay: PCollection;
  24.     Procedure WriteDay(Collection: PCollection);
  25.  
  26. }
  27.  Interface
  28.    uses Drivers, Objects, App, Views, Dos, Dialogs;
  29.    Const
  30. { Default file name use to hold appointments.                       }
  31.      CalendarFile = 'Calendar.Dat';
  32.      DaysInMonth:Array[1..12] of byte =
  33.        (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
  34.      MonthStr:Array[1..12] of String =
  35.        ('January   ',
  36.         'February  ',
  37.         'March     ',
  38.         'April     ',
  39.         'May       ',
  40.         'June      ',
  41.         'July      ',
  42.         'August    ',
  43.         'September ',
  44.         'October   ',
  45.         'November  ',
  46.         'December  ');
  47.      BaseCommand      = 150;
  48.      brHourFocused    = 1 + BaseCommand;
  49.      brMessageSelect  = 2 + BaseCommand;
  50.    Type
  51.    St = String[80];
  52.    TString = ^St;
  53.    RDate = Record
  54.      KeyDate: String[10];
  55.      Message: Array[1..24] of String[80];
  56.    End;
  57.  
  58.    PMessageInputLine = ^TMessageInputLine;
  59.    TMessageInputLine = Object(TInputLine)
  60.      Constructor Init(var R: Trect; AMaxLen: Integer; Parent: PDialog);
  61.      Procedure HandleEvent(var Event: TEvent); Virtual;
  62.    End;
  63.  
  64.    PHourCollection = ^THourCollection;
  65.    THourCollection = Object(TCollection)
  66.      Constructor Init;
  67.      Procedure FreeItem(Item: Pointer); Virtual;
  68.    End;
  69.  
  70.    PHourListBox = ^THourListBox;
  71.    THourListBox = Object(TListBox)
  72.      Procedure FocusItem(Item: Integer); Virtual;
  73.      Procedure HandleEvent(var Event: TEvent); Virtual;
  74.      Destructor Done; Virtual;
  75.    End;
  76.  
  77.    PDayDialog = ^TDayDialog;
  78.    TDayDialog = Object(TDialog)
  79. { ActiveCollection hold all dates that have appointments.                }
  80.      ActiveCollection: PStringCollection;
  81.  
  82.      HourListBox: PHourListBox;
  83.      MessageInputLine: PMessageInputLine;
  84.      Constructor Init;
  85.      Destructor Done; Virtual;
  86.  
  87. { IsSpecial returns true if there is a appointment associated with       }
  88. {  the date.                                                             }
  89.      Function IsSpecial(Day, Month, Year: Word): Boolean; Virtual;
  90.  
  91. { GetActiveDays enters all active days into ActiveCollections.           }
  92.      Procedure GetActiveDays; Virtual;
  93.  
  94. { ReadDay reads all appointments for one day into a Collection.          }
  95. { ReadDay is called by SetNewDay.                                        }
  96.      Function ReadDay: PCollection; Virtual;
  97.  
  98.      Procedure SetNewDay(Day, Month, Year: Word);
  99.  
  100. { WriteDay saves all appointments for one day to Calendar.Dat}
  101.       Procedure WriteDay(Collection: PCollection); Virtual;
  102.  
  103.      Private
  104.      Function FStr(I: Integer): String;
  105.    End;
  106.  
  107.    PCalendarView = ^TCalendarView;
  108.    TCalendarView = object(TView)
  109.      Year, Month, Days : Word;
  110.      CurYear, CurMonth, CurDay : Word;
  111.      DayDialog: PDayDialog;
  112.      Constructor Init(Bounds: TRect; D: PDayDialog);
  113.      Destructor Done; Virtual;
  114.      procedure HandleEvent(var Event: TEvent); Virtual;
  115.      Procedure Draw; Virtual;
  116.    end;
  117.  
  118.    PCalendarWindow = ^TCalendarWindow;
  119.    TCalendarWindow = object(TWindow)
  120.      CalendarView: PCalendarView;
  121.      Constructor Init(DayDialog:PDayDialog);
  122.    End;
  123.  
  124.  Implementation
  125.  
  126. {-------------- TMessageInputLine ------}
  127.       Constructor TMessageInputLine.Init(var R: Trect; AMaxLen: Integer; Parent: PDialog);
  128.         Begin
  129.           Inc(R.A.X,5);
  130.           TInputLine.Init(R, AMaxLen);
  131.           GrowMode := gfGrowHiX;
  132.           EventMask := EventMask or evBroadcast;
  133.         End;
  134.  
  135.       Procedure TMessageInputLine.HandleEvent(var Event: TEvent);
  136.         Const
  137.           UpOrDown = [80, 81, 72, 73];  {A set of UP, PgUp, Down, PgDn}
  138.         Begin
  139.           if ((Event.What = evBroadcast) and (Event.Command = brHourFocused)) or
  140.              ((Event.What = evKeyDown) and ((Event.KeyCode = kbEnter) or (Event.ScanCode in UpOrDown))) then
  141.             Begin
  142.               Message(Owner, evBroadcast, brMessageSelect, Data);
  143.               Owner^.Delete(@Self);  {Remove Self from the Dialog Box}
  144.               If (Event.What = evKeyDown) and (Event.ScanCode in UpOrDown) then
  145.                 Message(TopView, Event.What, Event.Command, Event.InfoPtr);
  146.             End;
  147.           TInputLine.HandleEvent(Event);
  148.         End;
  149.  
  150. {-------------- THourCollection --------}
  151.       Constructor THourCollection.Init;
  152.         Begin
  153.           TCollection.Init(24,1);
  154.         End;
  155.  
  156.       Procedure THourCollection.FreeItem(Item: Pointer);
  157.         Begin
  158.           Dispose(TString(Item));  {This procedure must be used if using a }
  159.         End;                       {collection on non Objects.             }
  160.  
  161. {-------------- THourListBox -----------}
  162.       Destructor THourListBox.Done;
  163.         Begin
  164.           If List<>Nil then
  165.             Dispose(List, Done);
  166.           TListBox.Done;
  167.         End;
  168.  
  169.       Procedure THourListBox.FocusItem(Item: Integer);
  170.         Begin
  171.          { Send message to notify TMessageInputLine that an item has been }
  172.          { focused. (see TMessageInputLine.HandleEvent)                   }
  173.           Message(Owner, evBroadcast, brHourFocused, List^.At(Item));
  174.           TListBox.FocusItem(Item);
  175.         End;
  176.  
  177.      Procedure THourListBox.HandleEvent(var Event: TEvent);
  178.        Const
  179.          LeftOrRight = [75, 77]; {Set of Left and Right arrow keys }
  180.        Var
  181.          R, R1: TRect;
  182.        Begin
  183.          If (Event.What=evKeyDown) and ((Event.CharCode<>#0) or (Event.ScanCode in LeftOrRight)) then
  184.            Begin
  185.              GetBounds(R);
  186.              With PDayDialog(Owner)^.MessageInputLine^ do
  187.                Begin
  188.                  { Assign the inputline with the data in the collection of }
  189.                  { the listbox.  The copy is used to ignore the first 6    }
  190.                  { charaters.                                              }
  191.                  Data^ := Copy(TString(List^.At(Focused))^, 6, 80);
  192.                  { Change the location of the inputline to be at the same  }
  193.                  { position as the highlight bar of the listbox.           }
  194.                  R.A.Y:=Focused-TopItem+1;
  195.                  R.B.Y:=R.A.Y+1;
  196.                  R.A.X:=R.A.X+5;
  197.                  ChangeBounds(R);
  198.                end;
  199.              If (Event.ScanCode in LeftOrRight) or (Event.KeyCode=KbEnter) then
  200.                Event.KeyCode:=kbHome;
  201.              { Display the inputline by inserting it into the dialog box   }
  202.              Owner^.Insert(PDayDialog(Owner)^.MessageInputLine);
  203.              { Send the message whether to inputline                       }
  204.              Message(TopView, Event.What, Event.Command, Event.InfoPtr);
  205.              Exit;
  206.            End;
  207.          TListBox.HandleEvent(Event);
  208.          If (Event.What=evBroadCast) and (Event.Command=brMessageSelect) then
  209.            Begin
  210.              { If the inputline is finish replace the currently focused    }
  211.              { item with the newly enter information from the inputline.   }
  212.              { see TMessageInputLine.HandleEvent for the sending of this   }
  213.              { message.                                                    }
  214.              TString(List^.At(Focused))^:=Copy(TString(List^.At(Focused))^, 1, 5)+TString(Event.InfoPtr)^;
  215.              { Redraw the view after the change.                           }
  216.              DrawView;
  217.            End;
  218.        End;
  219.  
  220. {-------------- TDayDialog -------------}
  221.       Constructor TDayDialog.Init;
  222.         Var
  223.            R:TRect;
  224.            ScrollBar: PScrollBar;
  225.         Begin
  226.           R.Assign(10,10,43,21);
  227.           TDialog.Init(R, ' ');
  228.           Flags := Flags or (wfZoom+wfGrow); {Allow dialog to be resizeable}
  229.           GetExtent(R);
  230.           R.A.X:=1;
  231.           R.A.Y:=1;
  232.           R.B.Y:=2;
  233.           Dec(R.B.X,2);
  234.           MessageInputLine:=New(PMessageInputLine, Init(R, 80, @Self));
  235.           GetExtent(R);
  236.           Inc(R.A.Y);
  237.           Dec(R.B.X);
  238.           Dec(R.B.Y);
  239.           R.A.X:=R.B.X-1;
  240.           Scrollbar := New(PScrollBar, Init(R));
  241.           Insert(Scrollbar);
  242.           GetExtent(R);
  243.           Inc(R.A.X);
  244.           Inc(R.A.Y);
  245.           Dec(R.B.X,2);
  246.           Dec(R.B.Y);
  247.           HourListBox := New(PHourListBox, Init(R, 1, ScrollBar));
  248.           HourListBox^.GrowMode:=gfGrowHiX+gfGrowHiY;
  249.           Insert(HourListBox);
  250.           GetActiveDays;
  251.         End;
  252.  
  253.       Destructor TDayDialog.Done;
  254.         Begin
  255.           Dispose(ActiveCollection, Done);
  256.           TDialog.Done;
  257.         End;
  258.  
  259.       Function TDayDialog.IsSpecial(Day, Month, Year: Word): Boolean;
  260.         Var
  261.           DateStr: String;
  262.           Index: Integer;
  263.         Begin
  264.           DateStr:=FStr(Month)+'/'+FStr(Day)+'/'+FStr(Year);
  265.           IsSpecial:= ActiveCollection^.Search(@DateStr, Index);
  266.         End;
  267.  
  268.        Function TDayDialog.FStr(I: Integer): String;
  269.          Var
  270.            S: String;
  271.          Begin
  272.            Str(I:2, S);
  273.            FStr:=S;
  274.          End;
  275.  
  276.       Procedure TDayDialog.SetNewDay(Day, Month, Year: Word);
  277.          Var
  278.            DateTitle: String;
  279.            Hour, Minute, Second, hSecond: Word;
  280.          Begin
  281.            DateTitle:='Day - '+FStr(Month)+'/'+FStr(Day)+'/'+FStr(Year);
  282.            DisposeStr(Title);        { DisposeStr and NewStr must be used  }
  283.            Title:=NewStr(DateTitle); { when changing the Title of a dialog.}
  284.            GetTime(Hour, Minute, Second, hSecond);
  285.            HourListBox^.NewList(ReadDay);
  286.            HourListBox^.FocusItem(Hour-1); { Focus on current time.        }
  287.            DeskTop^.ExecView(@Self);
  288.            WriteDay(HourListBox^.List);    { Save the appointments.        }
  289.            DeskTop^.Delete(@Self);         { Hide the dialog box.          }
  290.          End;
  291.  
  292.       Procedure TDayDialog.GetActiveDays;
  293.         Var
  294.           F: File Of RDate;
  295.           Date: RDate;
  296.         Begin
  297.           ActiveCollection:=New(PStringCollection, Init(50,10));
  298.           Assign(F,CalendarFile);
  299.           {$I-}Reset(F);{$I+}
  300.           If IOResult=0 then
  301.             Begin
  302.               While Not Eof(F) do
  303.                 Begin
  304.                   Read(F, Date);
  305.                   ActiveCollection^.Insert(NewStr(Date.KeyDate));
  306.                 End;
  307.               System.Close(F);
  308.             End;
  309.         End;
  310.  
  311.       Function TDayDialog.ReadDay: PCollection;
  312.         Var
  313.           F: File Of RDate;
  314.           Date: RDate;
  315.           HourCollection: PHourCollection;
  316.           I: Integer;
  317.           S: TString;
  318.           FileDate: String;
  319.         Begin
  320.           HourCollection:=New(PHourCollection, Init);
  321.           Assign(F,CalendarFile);
  322.           {$I-}Reset(F);{$I+}
  323.           FileDate:=Copy(Title^,7,10);
  324.           { If not dos errors and the data is found in ActiveCollection   }
  325.           If (IOResult=0) and (ActiveCollection^.Search(@FileDate, I)) then
  326.             Begin
  327.               Read(F, Date);
  328.               While Date.KeyDate<>FileDate do
  329.                 Read(F, Date);
  330.               For I:=1 to 24 do { Insert each hour into the Collections }
  331.                 Begin
  332.                   New(S);
  333.                   S^:=Date.Message[I];
  334.                   HourCollection^.Insert(S);
  335.                 End;
  336.             End
  337.            Else
  338.              Begin
  339.                For I:=1 to 24 do
  340.                  Begin
  341.                    New(S);
  342.                    FillChar(S^, SizeOf(S^), ' ');
  343.                    If I<13 then
  344.                      Begin
  345.                        Str(I, S^);
  346.                        S^:=S^+'am ';
  347.                      End
  348.                    Else
  349.                      Begin
  350.                        Str(I-12, S^);
  351.                        S^:=S^+'pm ';
  352.                      End;
  353.                    S^[0]:=#5;
  354.                    HourCollection^.Insert(S);
  355.                  End;
  356.              End;
  357.           ReadDay:= HourCollection;
  358.         End;
  359.  
  360.       Procedure TDayDialog.WriteDay(Collection: PCollection);
  361.         Var
  362.           F:File of RDate;
  363.           Date, SearchDate: RDate;
  364.           I: Integer;
  365.           S: TString;
  366.         Begin
  367.           Assign(F,CalendarFile);
  368.           {$I-}Reset(F);{$I+}
  369.           If IOResult<>0 then
  370.             ReWrite(F);
  371.           For I:=1 to 24 do  { Copy all appointments into a record.        }
  372.              Date.Message[I]:=TString(Collection^.At(I-1))^;
  373.           Date.KeyDate:=Copy(Title^,7,10);
  374.           { If the day already existed                                     }
  375.           If ActiveCollection^.Search(@Date.KeyDate, I) then
  376.             Begin
  377.               Read(F, SearchDate);
  378.               { Find entry in file.                                        }
  379.               While SearchDate.KeyDate<>Date.KeyDate do
  380.                 Read(F, SearchDate);
  381.               Seek(F,FilePos(F)-1);
  382.               Write(F, Date);
  383.             End
  384.           Else
  385.             Begin
  386.               { Add new entry to file.                                     }
  387.               Seek(F, FileSize(F));
  388.               Write(F, Date);
  389.               New(S);
  390.               S^:=Date.KeyDate;
  391.               ActiveCollection^.Insert(S);
  392.             End;
  393.           System.Close(F);
  394.         End;
  395. {-------------- TCalendarWindow --------}
  396.       Constructor TCalendarWindow.Init(DayDialog: PDayDialog);
  397.          Var
  398.            R:TRect;
  399.          Begin
  400.            R.Assign(1,1,23,11);
  401.            TWindow.Init(R, 'Calendar', 0);
  402.            Flags := Flags and Not(wfZoom+wfGrow); { Do not allow window to }
  403.            GrowMode :=0;                          { be resizeable.         }
  404.            GetExtent(R);
  405.            Inc(R.A.X);
  406.            Inc(R.A.Y);
  407.            Dec(R.B.X);
  408.            Dec(R.B.Y);
  409.            CalendarView := New(PCalendarView,Init(R, DayDialog));
  410.            Insert(CalendarView);
  411.          End;
  412.  
  413. {-------------- TCalendarView ----------}
  414.       Constructor TCalendarView.Init(Bounds: TRect; D: PDayDialog);
  415.          Var
  416.            H: Word;
  417.          Begin
  418.            TView.Init(Bounds);
  419.            DayDialog:=D;
  420.            Options := Options or ofSelectable;
  421.            GetDate(CurYear, CurMonth, CurDay, H);
  422.            Year:=CurYear;
  423.            Month:=CurMonth;
  424.            DrawView;
  425.          End;
  426.  
  427.        Destructor TCalendarView.Done;
  428.          Begin
  429.            Dispose(DayDialog, Done);
  430.            TView.Done;
  431.          End;
  432.  
  433.        Function DayOfWeek (day, month, year: integer) : integer;
  434.            var
  435.              century, yr, dw: integer;
  436.            begin
  437.              if month < 3 then
  438.                begin
  439.                  Inc(month, 10);
  440.                  Dec(year);
  441.                end
  442.              else
  443.                 Dec(month, 2);
  444.              century := year div 100;
  445.              yr := year mod 100;
  446.              dw := (((26*month - 2) div 10)+day+yr+(yr div 4)+(century div 4) - (2*century)) mod 7;
  447.              if dw < 0 then
  448.                DayOfWeek := dw + 7
  449.              else
  450.                DayOfWeek := dw;
  451.            end;
  452.  
  453.      Procedure TCalendarView.Draw;
  454.        Const
  455.          Width = 20;
  456.        Var
  457.          i,j,DayOf,CurDays:Integer;
  458.          S: String;
  459.          B: Array[0..Width] of Word;
  460.          Color, BoldColor, SpecialColor: Byte;
  461.  
  462.          Function Num2Str(I :integer): String;
  463.            Var
  464.              S:String;
  465.            Begin
  466.              Str(i:2,S);
  467.              Num2Str := S+' ';
  468.            End;
  469.  
  470.        Begin
  471.          Color:= GetColor($01);
  472.          BoldColor:= GetColor($02);
  473.          SpecialColor:= GetColor($03);
  474.          DayOf:=DayOfWeek(1, Month, Year);
  475.          Days := DaysInMonth[Month] + Byte((Year mod 4=0) and (Month=2));
  476.          Str(Year:4,S);
  477.          MoveChar(B, ' ', Color, Width);
  478.          MoveStr(B, MonthStr[Month]+S+' '#30'  '#31,Color);
  479.          WriteLine(0,0,Width,1,B);
  480.          MoveChar(B, ' ', Color, Width);
  481.          MoveStr(B, 'Su Mo Tu We Th Fr Sa', Color);
  482.          WriteLine(0,1,Width,1,B);
  483.          CurDays := 1-DayOf;
  484.          For i:=1 to 6 do
  485.            Begin
  486.              MoveChar(B, ' ', Color, Width);
  487.              For j:=0 to 6 do
  488.                Begin
  489.                  If (CurDays<1) or (CurDays>Days) then
  490.                     MoveStr(B[J*3],'   ',Color)
  491.                  else
  492.                     { If it is the current day.                           }
  493.                     If (Year=CurYear) and (Month=CurMonth) and (CurDays=CurDay) Then
  494.                       MoveStr(B[J*3],Num2Str(CurDays),BoldColor)
  495.                     { If there is an appointment for this day.            }
  496.                     Else If DayDialog^.IsSpecial(CurDays, Month, Year) Then
  497.                       MoveStr(B[J*3],Num2Str(CurDays),SpecialColor)
  498.                     Else
  499.                       MoveStr(B[J*3],Num2Str(CurDays),Color);
  500.                  Inc(CurDays);
  501.                End;
  502.            WriteLine(0,i+1, Width,1,B);
  503.            End;
  504.        End;
  505.  
  506.        procedure TCalendarView.HandleEvent(var Event: TEvent);
  507.          Var
  508.            Point:TPoint;
  509.            SelectDay: Word;
  510.          begin
  511.            TView.HandleEvent(Event);
  512.            if (State and sfSelected <> 0) then
  513.              Begin
  514.                if (Event.What=evMouseDown) then
  515.                  If Event.Double then
  516.                    Begin
  517.                      MakeLocal(Event.Where,Point);
  518.                      If (Point.Y>1) then
  519.                        Begin
  520.                          SelectDay :=((Point.X div 3)+1)-DayOfWeek(1, Month, Year)+(Point.Y-2)*7;
  521.                          If (SelectDay>0) and (SelectDay-1<Days) then
  522.                            Begin
  523.                              { Display the dialog box.                    }
  524.                              DayDialog ^.SetNewDay(SelectDay, Month, Year);
  525.                              DrawView;
  526.                            End;
  527.                        End;
  528.                    End
  529.                  Else
  530.                  begin
  531.                    MakeLocal(Event.Where,Point);
  532.                    If ((Point.X=15) and (Point.Y = 0)) Then
  533.                      Begin
  534.                        inc(Month);
  535.                        If Month>12 then
  536.                          Begin
  537.                            inc(Year);
  538.                            Month :=1;
  539.                          End;
  540.                          DrawView;
  541.                        End;
  542.                    If ((Point.X=18) and (Point.Y=0)) Then
  543.                      Begin
  544.                        dec(Month);
  545.                        If Month<1 then
  546.                          Begin
  547.                            dec(Year);
  548.                            Month :=12;
  549.                          End;
  550.                          DrawView;
  551.                        End;
  552.                  end
  553.                else if Event.What=evKeyDown then
  554.                    Begin
  555.                      If (lo(Event.KeyCode) = byte('+')) or
  556.                         (Event.KeyCode = kbDown)
  557.                        Then
  558.                           Begin
  559.                             inc(Month);
  560.                             If Month>12 then
  561.                               Begin
  562.                                 inc(Year);
  563.                                 Month :=1;
  564.                               End;
  565.                           End;
  566.                      If (lo(Event.KeyCode) = byte('-')) or
  567.                         (Event.KeyCode = kbUp)
  568.                        Then
  569.                           Begin
  570.                             dec(Month);
  571.                             If Month<1 then
  572.                               Begin
  573.                                 dec(Year);
  574.                                 Month :=12;
  575.                               End;
  576.                           End;
  577.                       DrawView;
  578.                    End;
  579.              End;
  580.          end;
  581.  End.
  582.  
  583.