(***********************************************************************
   This file is part of GTKpas, an OOP wrapper for GTK+ written
   for Pascal.

   Copyright (C) 2000 by Johann Glaser <Johann.Glaser@gmx.at>

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; either version 2
   of the License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program in the file COPYING; if not, write to the 
   Free Software Foundation, Inc., 
   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
***********************************************************************)
Program CalendarDemo;
{$MODE OBJFPC}
Uses SysUtils,GLib,GDK,GTK,GTKpas;

Const DEF_PAD = 10;
      DEF_PAD_SMALL = 5;
      
      TM_YEAR_BASE = 1900;


Type PCalendarData = ^TCalendarData;
     TCalendarData = record
                       FlagCheckBoxes : Array[0..4] of CCheckButton;
                       Settings        : Array[0..4] of GBoolean;
                       Font            : PGChar;
                       FontDialog      : CFontSelectionDialog;
                       Window          : CCalendar;
                       Prev2Sig        : CLabel;
                       PrevSig         : CLabel;
                       LastSig         : CLabel;
                       Month           : CWidget;
                     End;

Type CCalendarDemo = Class(CWindow)
                VBox, VBox2, VBox3 : CVBox;
                HBox : CHBox;
                HBBox : CHButtonBox;
                Calendar : CCalendar;
                Check : CCheckButton;
                Button : CButton;
                Frame : CFrame;
                Separator : CSeparator;
                TheLabel : CLabel;
                BBox : CButtonBox;
                CalendarData : TCalendarData;
                Constructor Create;
                Procedure CalendarDateToString(Data:PCalendarData;Buffer:PChar;BuffLen:GInt);
                Procedure CalendarSetSignalStrings(SigStr:PChar;Data:PCalendarData);
                Procedure CalendarSetFlags(Data:PCalendarData);
                Function OnCalendarMonthChanged(Sender:CCalendar;Data:GPointer):GBoolean;
                Function OnCalendarDaySelected(Sender:CCalendar;Data:GPointer):GBoolean;
                Function OnCalendarDaySelectedDoubleClick(Sender:CCalendar;Data:GPointer):GBoolean;
                Function OnCalendarPrevMonth(Sender:CCalendar;Data:GPointer):GBoolean;
                Function OnCalendarNextMonth(Sender:CCalendar;Data:GPointer):GBoolean;
                Function OnCalendarPrevYear(Sender:CCalendar;Data:GPointer):GBoolean;
                Function OnCalendarNextYear(Sender:CCalendar;Data:GPointer):GBoolean;
                Function OnCalendarToggleFlag(Sender:CToggleButton;Data:GPointer):GBoolean;
                Function OnCalendarSelectFont(Sender:CButton;Data:GPointer):GBoolean;
                Function OnCalendarFontSelectionOk(Sender:CButton;Data:GPointer):GBoolean;
                Function DeleteEventImpl(aEvent:PGdkEventAny):Gint; virtual; override;
                Procedure Destroy_; virtual; override;
                Function OnCloseButtonClicked(Sender:CButton;Data:GPointer):GBoolean;
              End;

Var Kit    : CMain;
    Window : CWindow;
    Button : CButton;

Constructor CCalendarDemo.Create;
Var I            : GInt;
    Flags        : Array[0..4] of PGChar;
Begin
  Inherited Create(GTK_WINDOW_TOPLEVEL);
  Flags[0] := 'Show Heading';
  Flags[1] := 'Show Day Names';
  Flags[2] := 'No Month Change';
  Flags[3] := 'Show Week Numbers';
  Flags[4] := 'Week Start Monday';

  CalendarData.Window := Nil;
  CalendarData.Font := Nil;
  CalendarData.FontDialog := Nil;

  For I := 0 to 4 do
    CalendarData.Settings[I] := False;

  Title := 'GtkCalendar Example';
  BorderWidth := 5;

  SetPolicy(0{False}, 0{False}, 1{True});

  VBox := CVBox.Create(False, DEF_PAD);
  Add(VBox);

  (*
   * The top part of the Window, Calendar, Flags and Fontsel.
   *)

  HBox := CHBox.Create(False, DEF_PAD);
  VBox.PackStart(HBox, True, True, DEF_PAD);
  HBBox := CHButtonBox.Create;
  HBox.PackStart(HBBox, False, False, DEF_PAD);
  HBBox.Layout  := GTK_ButtonBOX_SPREAD;
  HBBox.Spacing := 5;

  (* Calendar widget *)
  Frame := CFrame.Create('Calendar');
  HBBox.PackStart(Frame, False, True, DEF_PAD);
  Calendar := CCalendar.Create;
  CalendarData.Window := Calendar;
  CalendarSetFlags(@CalendarData);
  Calendar.MarkDay(19);
  Frame.Add(Calendar);
  Calendar.OnMonthChanged               := @OnCalendarMonthChanged;
  Calendar.OnMonthChangedData           := @CalendarData;
  Calendar.OnDaySelected                := @OnCalendarDaySelected;
  Calendar.OnDaySelectedData            := @CalendarData;
  Calendar.OnDaySelectedDoubleClick     := @OnCalendarDaySelectedDoubleClick;
  Calendar.OnDaySelectedDoubleClickData := @CalendarData;
  Calendar.OnPrevMonth                  := @OnCalendarPrevMonth;
  Calendar.OnPrevMonthData              := @CalendarData;
  Calendar.OnNextMonth                  := @OnCalendarNextMonth;
  Calendar.OnNextMonthData              := @CalendarData;
  Calendar.OnPrevYear                   := @OnCalendarPrevYear;
  Calendar.OnPrevYearData               := @CalendarData;
  Calendar.OnNextYear                   := @OnCalendarNextYear;
  Calendar.OnNextYearData               := @CalendarData;

  Separator := CVSeparator.Create;
  HBox.PackStart(Separator, False, True, 0);

  VBox2 := CVBox.Create(False, DEF_PAD);
  HBox.PackStart(VBox2, False, False, DEF_PAD);
  
  (* Build the Right Frame with the Flags in *) 

  Frame := CFrame.Create('Flags');
  VBox2.PackStart(Frame, True, True, DEF_PAD);
  VBox3 := CVBox.Create(True, DEF_PAD_SMALL);
  Frame.Add(VBox3);

  For I := 0 to 4 do
    Begin
      Check := CCheckButton.Create(Flags[I]);
      Check.OnToggled     := @OnCalendarToggleFlag;
      Check.OnToggledData := @CalendarData;
      VBox3.PackStart(Check, True, True, 0);
      CalendarData.FlagCheckBoxes[i] := Check;
    End;

  (* Build the right Font-Button *)
  
  Button := CButton.Create('Font...');
  Button.OnClicked     := @OnCalendarSelectFont;
  Button.OnClickedData := @CalendarData;
  VBox2.PackStart(Button, False, False, 0);

  (*
   *  Build the Signal-event part.
   *)

  Frame := CFrame.Create('Signal events');
  VBox.PackStart(Frame, True, True, DEF_PAD);

  VBox2 := CVBox.Create(True, DEF_PAD_SMALL);
  Frame.Add(VBox2);
  
  HBox := CHBox.Create(False, 3);
  VBox2.PackStart(HBox, False, True, 0);
  TheLabel := CLabel.Create('Signal:');
  HBox.PackStart(TheLabel, False, True, 0);
  CalendarData.LastSig := CLabel.Create('');
  HBox.PackStart(CalendarData.LastSig, False, True, 0);

  HBox := CHBox.Create(False, 3);
  VBox2.PackStart(HBox, False, True, 0);
  TheLabel := CLabel.Create('Previous signal:');
  HBox.PackStart(TheLabel, False, True, 0);
  CalendarData.PrevSig := CLabel.Create('');
  HBox.PackStart(CalendarData.PrevSig, False, True, 0);

  HBox := CHBox.Create(False, 3);
  VBox2.PackStart(HBox, False, True, 0);
  TheLabel := CLabel.Create('Second previous signal:');
  HBox.PackStart(TheLabel, False, True, 0);
  CalendarData.Prev2Sig := CLabel.Create('');
  HBox.PackStart(CalendarData.Prev2Sig, False, True, 0);

  BBox := CHButtonBox.Create;
  VBox.PackStart(BBox, False, False, 0);
  BBox.Layout := GTK_BUTTONBOX_END;

  Button := CButton.Create('Close');
  Button.OnClicked := @OnCloseButtonClicked;
  BBox.Add(Button);
  GTK_WIDGET_SET_FLAGS (PGtkWidget(Button.GtkObject), GTK_CAN_DEFAULT);
  Button.GrabDefault;

  gtk_widget_destroy(PGtkWidget(Button.GtkObject));

  ShowAll;
End;

(*
 * GtkCalendar
 *)

Procedure CCalendarDemo.CalendarDateToString(Data:PCalendarData;Buffer:PChar;BuffLen:GInt);
Var Year,Mon,MDay : GUInt;
    St : AnsiString;
Begin
  Data^.Window.GetDate(@Year,@Mon,@MDay);
  { This is german date format. Try to translate the commented C code }
  { at the bottom of this Procedure to get internationalized date }
  { format }
  FmtStr(St,'%d.%d.%d',[MDay,Mon+1,Year]);
  if BuffLen > Length(St)+1 then BuffLen := Length(St)+1;
  Move(St[1],Buffer^,BuffLen);

(*
  struct tm tm;
  time_t time;

  memset (@tm, 0, sizeof (tm));
  gtk_Calendar_get_date (GTK_Calendar(Data^.Window),
			 @tm.tm_year, @tm.tm_mon, @tm.tm_mday);
  tm.tm_year -:= TM_YEAR_BASE;
  time := mktime(@tm);
  strftime (Buffer, buff_len-1, '%x', gmtime(@time));*)
End;

Procedure CCalendarDemo.CalendarSetSignalStrings(SigStr:PChar;Data:PCalendarData);
Var PrevSig : PGChar;
Begin
  Data^.PrevSig.Get(@PrevSig);
  Data^.Prev2Sig.SetText(PrevSig);

  Data^.LastSig.Get(@PrevSig);
  Data^.PrevSig.SetText(PrevSig);
  Data^.LastSig.SetText(SigStr);
End;

Function CCalendarDemo.OnCalendarMonthChanged(Sender:CCalendar;Data:GPointer):GBoolean;
Var Buffer : Array[0..255] of Char;
Begin
  Buffer := 'month_changed: ';
  CalendarDateToString(Data, @Buffer+15, 256-15);
  CalendarSetSignalStrings(Buffer, Data);
  OnCalendarMonthChanged := False;
End;

Function CCalendarDemo.OnCalendarDaySelected(Sender:CCalendar;Data:GPointer):GBoolean;
Var Buffer : Array[0..255] of Char;
Begin
  Buffer := 'day_selected: ';
  CalendarDateToString (Data, @Buffer+14, 256-14);
  CalendarSetSignalStrings(Buffer, Data);
  OnCalendarDaySelected := False;
End;

Function CCalendarDemo.OnCalendarDaySelectedDoubleClick(Sender:CCalendar;Data:GPointer):GBoolean;
Var Buffer : Array[0..255] of Char;
    Year,Mon,MDay : GUInt;
Begin
  Buffer := 'day_selected_double_click: ';

  CalendarDateToString (Data, @Buffer+27, 256-27);
  CalendarSetSignalStrings(Buffer, Data);

  PCalendarData(Data)^.Window.GetDate(@Year,@Mon,@MDay);

  if PGtkCalendar(PCalendarData(Data)^.Window.GtkObject)^.marked_date[MDay-1] = 0 then
    PCalendarData(Data)^.Window.MarkDay(MDay)
  else
    PCalendarData(Data)^.Window.UnmarkDay(MDay);
  OnCalendarDaySelectedDoubleClick := False;
End;

Function CCalendarDemo.OnCalendarPrevMonth(Sender:CCalendar;Data:GPointer):GBoolean;
Var Buffer : Array[0..255] of Char;
Begin
  Buffer := 'prev_month: ';
  CalendarDateToString (Data, @Buffer+12, 256-12);
  CalendarSetSignalStrings(Buffer, Data);
  OnCalendarPrevMonth := False;
End;

Function CCalendarDemo.OnCalendarNextMonth(Sender:CCalendar;Data:GPointer):GBoolean;
Var Buffer : Array[0..255] of Char;
Begin
  Buffer := 'next_month: ';
  CalendarDateToString (Data, @Buffer+12, 256-12);
  CalendarSetSignalStrings(Buffer, Data);
  OnCalendarNextMonth := False;
End;

Function CCalendarDemo.OnCalendarPrevYear(Sender:CCalendar;Data:GPointer):GBoolean;
Var Buffer : Array[0..255] of Char;
Begin
  Buffer := 'prev_year: ';
  CalendarDateToString (Data, @Buffer+11, 256-11);
  CalendarSetSignalStrings(Buffer, Data);
  OnCalendarPrevYear := False;
End;

Function CCalendarDemo.OnCalendarNextYear(Sender:CCalendar;Data:GPointer):GBoolean;
Var Buffer : Array[0..255] of Char;
Begin
  Buffer := 'next_year: ';
  CalendarDateToString (Data, @Buffer+11, 256-11);
  CalendarSetSignalStrings(Buffer, Data);
  OnCalendarNextYear := False;
End;

Procedure CCalendarDemo.CalendarSetFlags(Data:PCalendarData);
Var I : GInt;
    Options : LongInt;
Begin
  Options := 0;
  For I := 0 to 4 do
    if Data^.Settings[I] then
      Options := Options + (1 shl I);
  if Data^.Window <> Nil then
    Data^.Window.DisplayOptions(TGtkCalendarDisplayOptions(Options));
End;

Function CCalendarDemo.OnCalendarToggleFlag(Sender:CToggleButton;Data:GPointer):GBoolean;
Var I,J : GInt;
Begin
  J := 0;
  For I := 0 to 4 do
    if PCalendarData(Data)^.FlagCheckBoxes[i] = Sender then
      J := I;
  PCalendarData(Data)^.Settings[J] := Not PCalendarData(Data)^.Settings[J];
  CalendarSetFlags(Data);
  OnCalendarToggleFlag := False;
End;

Function CCalendarDemo.OnCalendarFontSelectionOk(Sender:CButton;Data:GPointer):GBoolean;
Var aStyle : PGtkStyle;
    aFont  : PGdkFont;
Begin
  PCalendarData(Data)^.Font := PCalendarData(Data)^.FontDialog.FontName;
  if PCalendarData(Data)^.Window <> Nil then
    Begin
      aFont := PCalendarData(Data)^.FontDialog.Font;
      if aFont <> Nil then
        Begin
	  aStyle := gtk_style_copy (PCalendarData(Data)^.Window.Style);
	  gdk_font_unref (aStyle^.Font);
	  aStyle^.Font := aFont;
	  gdk_font_ref (aStyle^.Font);
          PCalendarData(Data)^.Window.Style := aStyle;
        End;
    End;
  OnCalendarFontSelectionOk := False;
End;

Function CCalendarDemo.OnCalendarSelectFont(Sender:CButton;Data:GPointer):GBoolean;
Var Window    : CFontSelectionDialog;
    aCalendar : PCalendarData;
    aButton   : CButton;
Begin
  aCalendar := PCalendarData(Data);

  if aCalendar^.FontDialog = Nil then
    Begin
      Window := CFontSelectionDialog.Create('Font Selection Dialog');
      if Window = Nil then Exit;
      aCalendar^.FontDialog := Window;
      Window.Position := GTK_WIN_POS_MOUSE;
      
(*      gtk_signal_connect (GTK_OBJECT (Window), 'destroy',
  			GTK_SIGNAL_FUNC (gtk_widget_destroyed),
  			@Calendar^.FontDialog);*)
      
      aButton := CButton(Wrap(PGtkObject(PGtkFontSelectionDialog(Window.GtkObject)^.ok_button)));

      { do this in C<Object>WrapNew!!! }
      PGtkObject(PGtkFontSelectionDialog(Window.GtkObject)^.ok_button)^.Klass := gtk_type_class(GTK_OBJECT_TYPE(PGtkObject(Button.GtkObject)));

{
  ok, some things are working now. At least when replacing the "klass" field of an Gtk<Object> the signals
  are transmitted properly! :-))))
  This should be done inside the CObject.Create(CastItem:PGtkObject);. Move the upper line to this constructor,
  when everything works well.

  But one problem consists: The Destructor "Destroy" is never called. This is due to the next problem:
  the DestroyNotify_ Procedure on object.pas, which is installed within
    gtk_object_set_data_by_id_full(GtkObject,GTKpasQuark,GPointer(Self),@DestroyNotify_)
  is never executed. Why not? Whats happening here?

  This is important, because the ok_button is destroyed within the dialog, so no CButton.Destroy can be
  called directly. Hmm, where is the notification left? :-(

}

      aButton.OnClicked := @OnCalendarFontSelectionOk;
      aButton.OnClickedData := aCalendar;

(*      aButton := Wrap(PGtkObject(PGtkFontSelectionDialog(Window.GtkObject)^.cancel_button));
      aButton.OnClicked :=
      aButton.OnClickedData := Calendar^.Fontdialog;
      
      gtk_signal_connect_object (GTK_OBJECT (GTK_Font_SELECTION_DIALOG (Window)^.cancel_Button),
  			       'clicked',
  			       GTK_SIGNAL_FUNC (gtk_widget_destroy), 
  			       GTK_OBJECT (Calendar^.FontDialog));*)
    End;
  WriteLn('MemAvail = ',MemAvail);
  Window := aCalendar^.FontDialog;
  if not GTK_WIDGET_VISIBLE(PGtkWidget(Window.GtkObject)) then
    Window.Show
  else
    Begin
      Window.Destroy;

      aCalendar^.FontDialog := Nil;
    End;
  OnCalendarSelectFont := False;
End;


Function CCalendarDemo.DeleteEventImpl(aEvent:PGdkEventAny):Gint;
Begin
  inherited DeleteEventImpl(aEvent);
  DeleteEventImpl := 0;
End;
Procedure CCalendarDemo.Destroy_;
Begin
  inherited Destroy_;
  Kit.Quit;
End;
Function  CCalendarDemo.OnCloseButtonClicked(Sender:CButton;Data:GPointer):GBoolean;
Begin
  OnCloseButtonClicked := False;
  Destroy_;
End;

Begin
  gtk_set_locale;
  Kit := CMain.Create(@ArgC,@ArgV,False);

  Window := CCalendarDemo.Create;

  Kit.Run;
End.

enum Begin
  Calendar_show_header,
  Calendar_show_days,
  Calendar_month_change, 
  Calendar_show_week,
  Calendar_monday_first
End;;


