Skip to main navigation Skip to main content Skip to page footer
unit TL.Components;

interface

uses
  System.Classes, System.SysUtils, System.Variants, System.UITypes, Vcl.DBGrids, Vcl.Forms, Vcl.Graphics, Vcl.GraphUtil,
  Vcl.Grids, Vcl.Themes, Win.Registry, Data.DB, Winapi.ShlObj, Winapi.ActiveX, Winapi.Windows, Winapi.KnownFolders,
  FinAdmColors, FlatDesignColorsFull; // MaterialColorsFull,

const
  DesignPPI = 240;

type
  TLFolder = class(TObject)
  protected
    function GetKnownFolderPath(const folderID: TGUID): string;
    function GetDocuments: string;
    function GetDesktop: string;
    function GetProgramData: string;
    function GetLocalAppData: string;
    function GetPictures: string;
    function GetMusic: string;
    function GetVideos: string;
    function GetDownLoads: string;
    function GetVersion: string;
  public
    property Documents: string read GetDocuments;
    property Desktop: string read GetDesktop;
    property ProgramData: string read GetProgramData;
    property LocalAppData: string read GetLocalAppData;
    property Pictures: string read GetPictures;
    property Music: string read GetMusic;
    property Videos: string read GetVideos;
    property DownLoads: string read GetDownLoads;
    property AppVersion: string read GetVersion;
  end;

var
  Folder: TLFolder;

type
  TLDBGrid = class(TCustomDBGrid)
  private
    { Private declarations }
    FTotalRow: boolean;
    FSubTotalRow: boolean;
  protected
    { Protected declarations }
    procedure DrawColumnCell(
      const Rect: TRect;
      DataCol: Integer;
      Column: TColumn;
      State: TGridDrawState); override;
    procedure KeyDown(
      var Key: Word;
      Shift: TShiftState); override;
    procedure ColEnter; override;
    procedure ColExit; override;
    procedure CellClick(Column: TColumn); override;
  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
    procedure SetColumnAttr;
    procedure SetStyle(
      DataCol: Integer;
      Grid: TLDBGrid;
      Column: TColumn;
      State: TGridDrawState;
      SoortID: Integer;
      ID: Integer);
    property TotalRow: boolean read FTotalRow write FTotalRow default False;
    property SubTotalRow: boolean read FSubTotalRow write FSubTotalRow default False;
    property Canvas;
    property SelectedRows;
    // property DefaultRowHeight;
  published
    { Published declarations }
    property Align;
    property Anchors;
    property BiDiMode;
    property BorderStyle;
    property Color;
    [stored(False)]
    property Columns stored False; // StoreColumns;
    property Constraints;
    property Ctl3D;
    property DataSource;
    property DefaultDrawing;
    property DragCursor;
    property DragKind;
    property DragMode;
    property DrawingStyle;
    property Enabled;
    property FixedColor;
    property FixedCols;
    property GradientEndColor default clSilver_400;
    property GradientStartColor default clSilver_50;
    property Font;
    property ImeMode;
    property ImeName;
    property Options;
    property ParentBiDiMode;
    property ParentColor;
    property ParentCtl3D;
    property ParentFont;
    property ParentShowHint;
    property PopupMenu;
    property readonly;
    property ShowHint;
    property TabOrder;
    property TabStop;
    property TitleFont;
    property Touch;
    property Visible;
    property StyleElements;
    property StyleName;
    property ScrollBars;
    property OnCellClick;
    property OnColEnter;
    property OnColExit;
    property OnColumnMoved;
    property OnDrawColumnCell;
    property OnDblClick;
    property OnDragDrop;
    property OnDragOver;
    property OnEditButtonClick;
    property OnEndDock;
    property OnEndDrag;
    property OnEnter;
    property OnExit;
    property OnGesture;
    property OnKeyDown;
    property OnKeyPress;
    property OnKeyUp;
    property OnMouseActivate;
    property OnMouseDown;
    property OnMouseEnter;
    property OnMouseLeave;
    property OnMouseMove;
    property OnMouseUp;
    property OnMouseWheel;
    property OnMouseWheelDown;
    property OnMouseWheelUp;
    property OnStartDock;
    property OnStartDrag;
    property OnTitleClick;
  end;

type
  TLForm = class(TForm)
    FRegVars: TStringList;
    procedure SaveConfig;
    procedure LoadConfig;
  protected
    procedure DoCreate; override;
    procedure DoDestroy; override;
  private

  public
    property RegVars: TStringList read FRegVars write FRegVars;
  end;

procedure Register;

implementation

{ TLFolders }

procedure TLDBGrid.SetStyle(
  DataCol: Integer;
  Grid: TLDBGrid;
  Column: TColumn;
  State: TGridDrawState;
  SoortID: Integer;
  ID: Integer);
// ID bevat het categorie of subcategorienummer
// bij totalen of subtotalen bevat ID -1
begin
  // Style for descriptive column
  Grid.Canvas.Font.Style:= []; // Start met normale fontstyle

  if ID = -1 then // Totalen en Subtotalen rijen
    case SoortID of
      0: // Totalen en Subtotalen
        begin
          Grid.Canvas.Brush.Color:= FinAdmColors.clFooter;
          Grid.Canvas.Font.Color:= clWhite;
          Grid.Canvas.Font.Style:= [fsBold];
        end;
      1: // Inkomsten
        begin
          Grid.Canvas.Brush.Color:= FinAdmColors.clIncomeTotal;
          Grid.Canvas.Font.Color:= clWhite;
          Grid.Canvas.Font.Style:= [fsBold];
        end;
      2: // Vaste kosten
        begin
          Grid.Canvas.Brush.Color:= FinAdmColors.clFixedCostsTotal;
          Grid.Canvas.Font.Color:= clWhite;
          Grid.Canvas.Font.Style:= [fsBold];
        end;
      3: // Variabele kosten
        begin
          Grid.Canvas.Brush.Color:= FinAdmColors.clVariableCostsTotal;
          Grid.Canvas.Font.Color:= clWhite;
          Grid.Canvas.Font.Style:= [fsBold];
        end;
      4: // Aftrekbare kosten voor IB
        begin
          Grid.Canvas.Brush.Color:= FinAdmColors.clBelastingdienst;
          Grid.Canvas.Font.Color:= FinAdmColors.clBelastingdienstText;
          Grid.Canvas.Font.Style:= [fsBold];
        end;
    end // if ID = -1

  else // alle overige rijen
    begin

      if DataCol = 0 then // 1e kolom met omschrijvingen
        begin
          if not(gdSelected in State) then
            case SoortID of
              1:
                Grid.Canvas.Brush.Color:= FinAdmColors.clIncome;
              2:
                Grid.Canvas.Brush.Color:= FinAdmColors.clFixedCosts;
              3:
                Grid.Canvas.Brush.Color:= FinAdmColors.clVariableCosts;
            end // case SoortID
          else // if (gdSelected in State)
            case SoortID of
              0:
                begin
                  Grid.Canvas.Brush.Color:= FinAdmColors.clFooter;
                  Grid.Canvas.Font.Color:= clWhite;
                  Grid.Canvas.Font.Style:= [fsBold];
                end;
              1:
                begin
                  Grid.Canvas.Brush.Color:= FinAdmColors.clIncomeTotal;
                  Grid.Canvas.Font.Color:= clWhite;
                  Grid.Canvas.Font.Style:= [fsBold];
                end;
              2:
                begin
                  Grid.Canvas.Brush.Color:= FinAdmColors.clFixedCostsTotal;
                  Grid.Canvas.Font.Color:= clWhite;
                  Grid.Canvas.Font.Style:= [fsBold];
                end;
              3:
                begin
                  Grid.Canvas.Brush.Color:= FinAdmColors.clVariableCostsTotal;
                  Grid.Canvas.Font.Color:= clWhite;
                  Grid.Canvas.Font.Style:= [fsBold];
                end;
            end; // case SoortID
        end // if DataCol = 0

      else // if DataCol <> 0 alle overige kolommen met bedragen

        begin // default style actual values
          if not(gdSelected in State) then
            begin
              Grid.Canvas.Brush.Color:= FinAdmColors.clEmpty;
              Grid.Canvas.Font.Color:= FinAdmColors.clEmptyText;
            end // if not(gdSelected in State)
          else // if (gdSelected in State)
            begin
              Grid.Canvas.Brush.Color:= FinAdmColors.clSelected;
              Grid.Canvas.Font.Color:= clBlack;
            end; // if (gdSelected in State)
        end; // if DataCol <> 0
    end; // alle overige rijen
end;

function TLFolder.GetKnownFolderPath(const folderID: TGUID): string;
var
  pch: PChar;
begin
  if Succeeded(SHGetKnownFolderPath(folderID, 0, 0, pch)) then
    begin
      Result:= pch;
      CoTaskMemFree(pch);
    end
  else
    Result:= 'Error retrieving folder path';
end;

function TLFolder.GetDocuments: string;
begin
  Result:= GetKnownFolderPath(FOLDERID_Documents);
end;

function TLFolder.GetDesktop: string;
begin
  Result:= GetKnownFolderPath(FOLDERID_Desktop);
end;

function TLFolder.GetProgramData: string;
begin
  Result:= GetKnownFolderPath(FOLDERID_ProgramData);
end;

function TLFolder.GetLocalAppData: string;
begin
  Result:= GetKnownFolderPath(FOLDERID_LocalAppData);
end;

function TLFolder.GetPictures: string;
begin
  Result:= GetKnownFolderPath(FOLDERID_Pictures);
end;

function TLFolder.GetMusic: string;
begin
  Result:= GetKnownFolderPath(FOLDERID_Music);
end;

function TLFolder.GetVideos: string;
begin
  Result:= GetKnownFolderPath(FOLDERID_Videos);
end;

function TLFolder.GetDownLoads: string;
begin
  Result:= GetKnownFolderPath(FOLDERID_Downloads);
end;

function TLFolder.GetVersion: string;
var
  VerInfoSize: Cardinal;
  VerValueSize: Cardinal;
  Dummy: Cardinal;
  PVerInfo: Pointer;
  PVerValue: PVSFixedFileInfo;
begin
  Result:= ''; // Initialize the result

  // Get the size of the version information
  VerInfoSize:= GetFileVersionInfoSize(
    PChar(Application.ExeName),
    Dummy);
  if VerInfoSize > 0 then
    begin
      // Allocate memory for the version information
      GetMem(
        PVerInfo,
        VerInfoSize);
      try
        // Retrieve the version information
        if GetFileVersionInfo(PChar(Application.ExeName), 0, VerInfoSize, PVerInfo) then
          begin
            // Get the fixed file info
            if VerQueryValue(PVerInfo, '\', Pointer(PVerValue), VerValueSize) then
              begin
                Result:= IntToStr(DWORD(PVerValue.dwFileVersionMS) div $10000);
                Result:= Result + '.' + IntToStr(DWORD(PVerValue.dwFileVersionMS) mod $10000);
                Result:= Result + '.' + IntToStr(DWORD(PVerValue.dwFileVersionLS) div $10000);
                Result:= Result + '.' + IntToStr(DWORD(PVerValue.dwFileVersionLS) mod $10000);
              end;
          end;
      finally
        FreeMem(PVerInfo);
      end;
    end;
end;

{ TLDBGrid }

constructor TLDBGrid.Create(AOwner: TComponent);
begin
  inherited;
  Options:= [dgTitles, dgColLines, dgRowLines, dgRowSelect, dgAlwaysShowSelection];
end;

procedure TLDBGrid.CellClick(Column: TColumn);
begin
  if (Column.Field.DataType = ftSmallInt) then
    begin
      DataSource.DataSet.Edit;
      case Column.Field.Value of
        0:
          Column.Field.Value:= 1;
        1:
          Column.Field.Value:= 0;
      end;
      // DataSource.DataSet.Post;
      // Refresh;
    end;
  inherited;
end;

procedure TLDBGrid.ColEnter;
begin
  inherited;
  if SelectedField.DataType = ftSmallInt then
    Options:= Options - [dgEditing]
  else
    Options:= Options + [dgEditing]
end;

procedure TLDBGrid.ColExit;
begin
  inherited;
end;

procedure TLDBGrid.KeyDown(
  var Key: Word;
  Shift: TShiftState);
begin
  inherited;
  if Assigned(DataSource) and (SelectedField.DataType = ftSmallInt) and (Key = VK_SPACE) then
    begin
      DataSource.DataSet.Edit;
      case SelectedField.Value of
        0:
          SelectedField.AsInteger:= 1;
        1:
          SelectedField.AsInteger:= 0;
      end;
      // DataSource.DataSet.Post;
    end;
end;

procedure TLDBGrid.SetColumnAttr;
(* TFieldType = (ftUnknown, ftString, ftSmallint, ftInteger, ftWord, // 0..4
  ftBoolean, ftFloat, ftCurrency, ftBCD, ftDate, ftTime, ftDateTime, // 5..11
  ftBytes, ftVarBytes, ftAutoInc, ftBlob, ftMemo, ftGraphic, ftFmtMemo, // 12..18
  ftParadoxOle, ftDBaseOle, ftTypedBinary, ftCursor, ftFixedChar, ftWideString, // 19..24
  ftLargeint, ftADT, ftArray, ftReference, ftDataSet, ftOraBlob, ftOraClob, // 25..31
  ftVariant, ftInterface, ftIDispatch, ftGuid, ftTimeStamp, ftFMTBcd, // 32..37
  ftFixedWideChar, ftWideMemo, ftOraTimeStamp, ftOraInterval, // 38..41
  ftLongWord, ftShortint, ftByte, ftExtended, ftConnection, ftParams, ftStream, //42..48
  ftTimeStampOffset, ftObject, ftSingle); //49..51 *)
const
  MaxColumnWidth = 450;
var
  ColumnWidth, FieldNameWidth, FieldSizeWidth: Integer;
  FieldName: string;
  FieldSize: Integer;
begin
  if Assigned(DataSource) then
    begin
      for var i:= 0 to Columns.Count - 1 do
        begin
          Columns[i].Title.Font.Style:= [fsBold];
          Columns[i].Title.Alignment:= taCenter;

          FieldName:= DataSource.DataSet.FieldByName(Columns[i].FieldName).FieldName;
          FieldNameWidth:= Canvas.TextWidth(FieldName);
          FieldSize:= DataSource.DataSet.FieldByName(Columns[i].FieldName).Size;
          FieldSizeWidth:= FieldSize * Canvas.TextWidth('9');

          if FieldNameWidth > FieldSizeWidth then
            ColumnWidth:= FieldNameWidth
          else
            ColumnWidth:= FieldSizeWidth;
          if ColumnWidth > MaxColumnWidth then
            ColumnWidth:= MaxColumnWidth;

          case Columns[i].Field.DataType of
            ftFMTBcd, ftCurrency, ftBCD:
              begin
                TFloatField(DataSource.DataSet.FieldByName(Columns[i].FieldName)).DisplayFormat:=
                  '#,###,##0.00;-#,###,##0.00;-';
                Columns[i].Width:= MulDiv(
                  200,
                  PixelsPerInch,
                  DesignPPI);
              end;
            ftDate, ftTime:
              begin
                Columns[i].Alignment:= taRightJustify;
                Columns[i].Width:= MulDiv(
                  200,
                  PixelsPerInch,
                  DesignPPI);
              end;
            ftDateTime, ftTimeStamp:
              begin
                Columns[i].Alignment:= taRightJustify;
                Columns[i].Width:= MulDiv(
                  300,
                  PixelsPerInch,
                  DesignPPI);
              end;
            ftInteger, ftWord:
              begin
                TIntegerField(DataSource.DataSet.FieldByName(Columns[i].FieldName)).DisplayFormat:= '####0';
                if ColumnWidth < MulDiv(150, PixelsPerInch, DesignPPI) then
                  ColumnWidth:= MulDiv(
                    150,
                    PixelsPerInch,
                    DesignPPI);
                Columns[i].Width:= ColumnWidth;
              end;
            ftAutoInc:
              begin
                TIntegerField(DataSource.DataSet.FieldByName(Columns[i].FieldName)).DisplayFormat:= '####0';
                Columns[i].Width:= MulDiv(
                  150,
                  PixelsPerInch,
                  DesignPPI);
              end
            else
              Columns[i].Width:= ColumnWidth;
          end;
        end;
    end;
end;

procedure TLDBGrid.DrawColumnCell(
  const Rect: TRect;
  DataCol: Integer;
  Column: TColumn;
  State: TGridDrawState);
const
  CtrlState: array [boolean] of Integer = (DFCS_BUTTONCHECK, DFCS_BUTTONCHECK or DFCS_CHECKED);
  CtrlStateXP: array [boolean] of TThemedButton = (tbCheckBoxUncheckedNormal, tbCheckBoxCheckedNormal);
var
  R: TRect;
  Details: TThemedElementDetails;
begin
  if Assigned(OnDrawColumnCell) then
    OnDrawColumnCell(
      Self,
      Rect,

      DataCol,
      Column,
      State);

  // Cell padding
  R:= Rect;
  InflateRect(
    R,
    MulDiv(-10, PixelsPerInch, DesignPPI),
    0);

  Canvas.FillRect(Rect);

  case Column.Field.DataType of
    ftSmallInt:
      begin
        R:= Rect;
        InflateRect(
          R,
          MulDiv(-10, PixelsPerInch, DesignPPI),
          MulDiv(-5, PixelsPerInch, DesignPPI));

        if (VarIsNull(Column.Field.Value)) then
          Column.Field.AsInteger:= 0;

        if StyleServices.Enabled then
          begin
            case Column.Field.AsInteger of
              0:
                begin
                  if Column.Field.FieldName = 'IB Aftrekbaar' then
                    begin
                      if (gdSelected in State) then
                        Canvas.Brush.Color:= clPeter_river_200
                      else
                        Canvas.Brush.Color:= clPeter_river_50
                    end
                  else
                    begin
                      if (gdSelected in State) then
                        Canvas.Brush.Color:= clOrange_200
                      else
                        Canvas.Brush.Color:= clOrange_50;
                    end;
                  Canvas.FillRect(Rect);
                  Details:= StyleServices.GetElementDetails(CtrlStateXP[False]);
                  StyleServices.DrawElement(
                    Canvas.Handle,
                    Details,
                    R,
                    nil,
                    FCurrentPPI);
                end;
              1:
                begin
                  if Column.Field.FieldName = 'IB Aftrekbaar' then
                    begin
                      if (gdSelected in State) then
                        Canvas.Brush.Color:= clPeter_river_200
                      else
                        Canvas.Brush.Color:= clPeter_river_100
                    end
                  else
                    begin
                      if (gdSelected in State) then
                        Canvas.Brush.Color:= clOrange_200
                      else
                        Canvas.Brush.Color:= clOrange_100;
                    end;
                  Canvas.FillRect(Rect);
                  Details:= StyleServices.GetElementDetails(CtrlStateXP[TRUE]);
                  StyleServices.DrawElement(
                    Canvas.Handle,
                    Details,
                    R,
                    nil,
                    FCurrentPPI);
                end;
            end;
          end

        else

          begin
            case Column.Field.AsInteger of
              0:
                begin
                  Canvas.Brush.Color:= clBtnFace;
                  Canvas.FillRect(R);
                  DrawFrameControl(
                    Canvas.Handle,
                    Rect,
                    DFC_BUTTON,
                    CtrlState[False]);
                end;
              1:
                begin
                  if Column.Field.FieldName = 'IB Aftrekbaar' then
                    Canvas.Brush.Color:= clGradientInactiveCaption
                  else
                    Canvas.Brush.Color:= $CCCCFF;
                  Canvas.FillRect(R);
                  DrawFrameControl(
                    Canvas.Handle,
                    Rect,
                    DFC_BUTTON,
                    CtrlState[TRUE]);
                end;
            end;

          end;
      end;

    ftFMTBcd, ftFloat:
      begin
        if (Column.Field.Value > 0) then
          if gdSelected in State then
            begin
              Canvas.Font.Color:= clSelectedPositiveText;
              Canvas.Brush.Color:= clSelectedPositive;
              Canvas.Font.Style:= [fsBold];
            end
          else
            begin
              Canvas.Font.Color:= FinAdmColors.clPositiveText;
              Canvas.Brush.Color:= FinAdmColors.clPositive;
            end;
        Canvas.FillRect(Rect);
        DefaultDrawColumnCell(
          R,
          DataCol,
          Column,
          State);
      end;

    else
      DefaultDrawColumnCell(R, DataCol, Column, State);
  end;

  begin

    // Draw the custom gridline
    Canvas.Pen.Color:= clWhite;
    Canvas.MoveTo(
      Rect.Left,
      Rect.Bottom - 1);
    Canvas.LineTo(
      Rect.Right,
      Rect.Bottom - 1);
    Canvas.MoveTo(
      Rect.Right - 1,
      Rect.Top);
    Canvas.LineTo(
      Rect.Right - 1,
      Rect.Bottom);
  end;
end;

{ TLForm }

procedure TLForm.DoCreate;
begin
  Self.FRegVars:= TStringList.Create;
  inherited;
  Self.LoadConfig;
end;

procedure TLForm.DoDestroy;
begin
  inherited;
  Self.SaveConfig;
  Self.FRegVars.Free;
end;

procedure TLForm.SaveConfig;
var
  reg: TRegistry;
  Key, Value: string;
  i: Integer;
begin
  reg:= TRegistry.Create(KEY_WRITE);
  try
    reg.RootKey:= HKEY_CURRENT_USER;
    if reg.OpenKey('Software\Langstraat\' + Application.Title + '\' + Self.Name, TRUE) then
      begin
        // Write standard values of TLForm to registry
        if WindowState <> wsMaximized then
          begin
            reg.WriteInteger(
              'Top',
              Round(Self.Top / (PixelsPerInch / 96)));
            reg.WriteInteger(
              'Left',
              Round(Self.Left / (PixelsPerInch / 96)));
            reg.WriteInteger(
              'Height',
              Round(Self.Height / (PixelsPerInch / 96)));
            reg.WriteInteger(
              'Width',
              Round(Self.Width / (PixelsPerInch / 96)));
          end;
        reg.WriteBool(
          'WindowState',
          Self.WindowState = wsMaximized);
        // Write custom values to registry
        for i:= 0 to FRegVars.Count - 1 do
          begin
            Key:= FRegVars.Names[i];
            Value:= FRegVars.Values[Key];
            reg.WriteString(
              Key,
              Value);
          end;
        reg.CloseKey;
      end;
  finally
    reg.Free;
  end;
end;

procedure TLForm.LoadConfig;
var
  reg: TRegistry;
  Key, Value: string;
  i: Integer;
begin
  reg:= TRegistry.Create(KEY_READ);
  try
    reg.RootKey:= HKEY_CURRENT_USER;
    if reg.OpenKeyReadOnly('Software\Langstraat\' + Application.Title + '\' + Self.Name) then
      begin
        // Read standard values of TLForm from registry
        if reg.ValueExists('Top') then
          Self.Top:= MulDiv(
            reg.ReadInteger('Top'),
            PixelsPerInch,
            96);
        if reg.ValueExists('Left') then
          Self.Left:= MulDiv(
            reg.ReadInteger('Left'),
            PixelsPerInch,
            96);
        if reg.ValueExists('Height') then
          Self.Height:= MulDiv(
            reg.ReadInteger('Height'),
            PixelsPerInch,
            96);
        if reg.ValueExists('Width') then
          Self.Width:= MulDiv(
            reg.ReadInteger('Width'),
            PixelsPerInch,
            96);
        if reg.ValueExists('WindowState') then
          case reg.ReadBool('WindowState') of
            TRUE:
              Self.WindowState:= wsMaximized;
            False:
              Self.WindowState:= wsNormal;
          end;
        // Read custom values from registry
        for i:= 0 to FRegVars.Count - 1 do
          begin
            Key:= FRegVars.Names[i];
            Value:= FRegVars.Values[Key];
            if reg.ValueExists(Key) then
              begin
                Value:= reg.ReadString(Key);
                FRegVars.Values[Key]:= Value;
              end;
          end;
        reg.CloseKey;
      end;
  finally
    reg.Free;
  end;
end;

procedure Register;
begin
  RegisterComponents(
    'TL',
    [TLDBGrid]);
end;

initialization

Folder:= TLFolder.Create;

finalization

FreeAndNil(Folder);

end.