{*********************************************************}
{*              VPCONTACTBUTTONS.PAS 1.03                *}
{*********************************************************}

{* ***** BEGIN LICENSE BLOCK *****                                            *}
{* Version: MPL 1.1                                                           *}
{*                                                                            *}
{* The contents of this file are subject to the Mozilla Public License        *}
{* Version 1.1 (the "License"); you may not use this file except in           *}
{* compliance with the License. You may obtain a copy of the License at       *}
{* http://www.mozilla.org/MPL/                                                *}
{*                                                                            *}
{* Software distributed under the License is distributed on an "AS IS" basis, *}
{* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License   *}
{* for the specific language governing rights and limitations under the       *}
{* License.                                                                   *}
{*                                                                            *}
{* The Original Code is TurboPower Visual PlanIt                              *}
{*                                                                            *}
{* The Initial Developer of the Original Code is TurboPower Software          *}
{*                                                                            *}
{* Portions created by TurboPower Software Inc. are Copyright (C) 2002        *}
{* TurboPower Software Inc. All Rights Reserved.                              *}
{*                                                                            *}
{* Contributor(s):                                                            *}
{*        Steve Forbes                                                        *}
{*                                                                            *}
{* ***** END LICENSE BLOCK *****                                              *}

{ TurboPower Software Company wishes to thank Steve Forbes for }
{ providing this component, and allowing us to include it.     }
{ Thanks Steve!                                                }

{$I Vp.INC}

unit VpContactButtons;

interface

uses
  {$IFDEF LCL}
  LMessages,LCLProc,LCLType,LCLIntf,
  {$ELSE}
  Windows,
  {$ENDIF}
  Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  VpBase, VpContactGrid, VpMisc;

const
        VP_MIN_BUTTONS = 2;
  VP_LETTERS_IN_ALPHABET = 26;
  VP_MAX_BUTTONS = VP_LETTERS_IN_ALPHABET + 1;
        VP_LETTER_A = Ord('a');

type
  TVpButtonRec = packed record
    Rect: TRect;
    Caption: String;
  end;

  TVpButtonArray = array[0..VP_MAX_BUTTONS - 1] of TVpButtonRec;

  TVpButtonBarOrientation = (baHorizontal, baVertical);

  TVpButtonBarClickEvent = procedure(Sender: TObject; ButtonIndex: Integer;
    SearchString: String) of object;

  TVpContactButtonBar = class(TVPCustomControl)
  protected {private}
    FBarOrientation: TVpButtonBarOrientation;
    FBorderWidth: Integer;
    FButtonPressed: Integer;
    FButtonColor: TColor;
    FButtonCount: Integer;
    FButtonHeight: Integer;
    FButtonsArray: TVpButtonArray;
    FButtonWidth: Integer;
    FContactGrid: TVpContactGrid;
    FDrawingStyle: TVpDrawingStyle;
    FOnButtonClick: TVpButtonBarClickEvent;
    FShowNumberButton: Boolean;
    FRadioStyle: Boolean;
    {internal variables}
    bbSearchString: string;

    {internal methods}
    procedure bbPopulateSearchString;
    procedure CreateButtons;
    procedure DrawButton(Index: Integer; Pressed: Boolean);
    procedure SelectContact;

    { Property setter methods }
    procedure SetBarOrientation(const Value: TVpButtonBarOrientation);
    procedure SetBorderWidth(const Value: Integer);
    procedure SetButtonColor(const Value: TColor);
    procedure SetButtonHeight(const Value: Integer);
    procedure SetButtonWidth(const Value: Integer);
    procedure SetContactGrid(Value: TVpContactGrid);
    procedure SetDrawingStyle(const Value: TVpDrawingStyle);
    procedure SetShowNumberButton(const Value: Boolean);

    { Overridden methods }
    procedure Loaded; override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
    procedure Notification(AComponent: TComponent;
      Operation: TOperation); override;
    procedure Paint; override;
    procedure Resize; override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property BarOrientation: TVpButtonBarOrientation
      read FBarOrientation write SetBarOrientation default baVertical;
    property BorderWidth: Integer
      read FBorderWidth write SetBorderWidth default 2;
    property ButtonColor: TColor
      read FButtonColor write SetButtonColor default clBtnFace;
    property ButtonHeight: Integer
      read FButtonHeight write SetButtonHeight default 18;
    property ButtonWidth: Integer
      read FButtonWidth write SetButtonWidth default 34;
    property ContactGrid: TVpContactGrid
      read FContactGrid write SetContactGrid;
    property DrawingStyle: TVpDrawingStyle
      read FDrawingStyle write SetDrawingStyle default ds3d;
    property ShowNumberButton: Boolean
      read FShowNumberButton write SetShowNumberButton default True;
    property OnButtonClick: TVpButtonBarClickEvent
      read FOnButtonClick write FOnButtonClick;
    property RadioStyle: Boolean
      read FRadioStyle write FRadioStyle;

    property Align;
    property Anchors;
    property BiDiMode;
    property Color;
    property Constraints;
    property Cursor;
    property DragCursor;
    property DragKind;
    property DragMode;
    property Enabled;
    property Font;
    property ParentBiDiMode;
    property ParentColor;
    property ParentFont;
    property ParentShowHint;
    property PopupMenu;
    property ShowHint;
    property Visible;
    {events}
    {$IFNDEF LCL}
    property OnCanResize;
    {$ENDIF}
    property OnClick;
    property OnConstrainedResize;
    property OnDblClick;
    property OnDockDrop;
    property OnDockOver;
    property OnDragDrop;
    property OnDragOver;
    property OnEndDock;
    property OnEndDrag;
    property OnGetSiteInfo;
    property OnMouseDown;
    property OnMouseMove;
    property OnMouseUp;
    property OnResize;
    property OnStartDock;
    property OnStartDrag;
    property OnUnDock;
  end;

implementation
{ TVpContactButtonBar }

constructor TVpContactButtonBar.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  Width := 40;
  Height := 280;

  {$IFDEF VERSION4}
  DoubleBuffered := True;
  {$ENDIF}

  FBarOrientation := baVertical;
  FBorderWidth := 2;
  FButtonColor := clBtnFace;
  FButtonHeight := 18;
  FButtonWidth := 34;
  FDrawingStyle := ds3d;
  FShowNumberButton := True;
end;
{=====}

destructor TVpContactButtonBar.Destroy;
begin
  inherited Destroy;
end;
{=====}

procedure TVpContactButtonBar.CreateButtons;
var
  I: Integer;
  TotalXY: Integer;
  StartLetter, EndLetter: Char;
  ButtonLetters: Single;
  ButtonCaption: String;
  Offset: Integer;
  MaxButtons: Integer;
        MinButtons: Integer;
begin
  I := 0;

  if FShowNumberButton then begin
    MaxButtons := VP_MAX_BUTTONS;
                MinButtons := VP_MIN_BUTTONS + 1;
        end else begin
    MaxButtons := VP_LETTERS_IN_ALPHABET;
                MinButtons := VP_MIN_BUTTONS;
        end;

  if FBarOrientation = baVertical then begin
    TotalXY := FBorderWidth;

    while ((TotalXY + FButtonHeight + FBorderWidth < ClientHeight)
           and (I < MaxButtons))
    or (I < MinButtons)
    do begin
      FButtonsArray[I].Rect := Rect(FBorderWidth, TotalXY,
        ClientWidth - FBorderWidth, TotalXY + FButtonHeight);
      Inc(I);
      TotalXY := TotalXY + FButtonHeight + FBorderWidth;
    end;

    FButtonCount := I;
  end else begin
    TotalXY := FBorderWidth;

    while ((TotalXY + FButtonWidth + FBorderWidth < ClientWidth)
           and (I < MaxButtons))
    or (I < MinButtons)
    do begin
      FButtonsArray[i].Rect := Rect(TotalXY, FBorderWidth,
        TotalXY + FButtonWidth, ClientHeight - FBorderWidth);
      Inc(I);
      TotalXY := TotalXY + FButtonWidth + FBorderWidth;
    end;

    FButtonCount := I;
  end;

  Offset := 0;

  if FShowNumberButton then begin
    ButtonLetters := VP_LETTERS_IN_ALPHABET / (FButtonCount - 1);
    FButtonsArray[0].Caption := '123';
    Offset := 1;
  end else
    ButtonLetters := VP_LETTERS_IN_ALPHABET / FButtonCount;

  for i := 0 to FButtonCount - Offset - 1 do begin
    StartLetter := Chr(Round(VP_LETTER_A + ButtonLetters * I));
    EndLetter := Chr(Round(VP_LETTER_A + ButtonLetters * (I + 1)) - 1);

    if Ord(EndLetter) = Ord(StartLetter) then
      ButtonCaption := StartLetter
    else begin
      if Ord(EndLetter) = Ord(StartLetter) + 1 then
        ButtonCaption := StartLetter + EndLetter
      else begin
        if Ord(EndLetter) = Ord(StartLetter) + 2 then
          ButtonCaption := StartLetter + Succ(StartLetter) + EndLetter
        else
          ButtonCaption := StartLetter + '-' + EndLetter;
      end;
    end;

    FButtonsArray[I + Offset].Caption := ButtonCaption;
  end;
end;
{=====}

procedure TVpContactButtonBar.DrawButton(Index: Integer; Pressed: Boolean);
var
  ButtonRect: TRect;
begin
  with Canvas do begin
    Font := Self.Font;
    ButtonRect := FButtonsArray[Index].Rect;
    Brush.Color := FButtonColor;
    FillRect(ButtonRect);
    if (FDrawingStyle = dsFlat) then begin

      if Pressed then
        Pen.Color := clBtnShadow
      else
        Pen.Color := clBtnHighlight;

      PolyLine([Point(ButtonRect.Right - 1, ButtonRect.Top),
        Point(ButtonRect.Left, ButtonRect.Top),
        Point(ButtonRect.Left, ButtonRect.Bottom - 1)]);

      if Pressed then
        Pen.Color := clBtnHighlight
      else
        Pen.Color := clBtnShadow;

      PolyLine([Point(ButtonRect.Left, ButtonRect.Bottom - 1),
        Point(ButtonRect.Right - 1, ButtonRect.Bottom - 1),
        Point(ButtonRect.Right - 1, ButtonRect.Top)]);

      InflateRect(ButtonRect, -2, -2);
    end else begin
      if Pressed then
        DrawFrameControl(Handle, ButtonRect,
          DFC_BUTTON, DFCS_BUTTONPUSH or DFCS_PUSHED)
      else
        DrawFrameControl(Handle, ButtonRect, DFC_BUTTON, DFCS_BUTTONPUSH);

      InflateRect(ButtonRect, -2, -2);
      FillRect(ButtonRect);
    end;

    if Pressed then begin
      ButtonRect.Left := ButtonRect.Left + 2;
      ButtonRect.Top := ButtonRect.Top + 2;
    end;

    DrawText(Handle, PChar(FButtonsArray[Index].Caption),
      Length(FButtonsArray[Index].Caption), ButtonRect,
      {DrawTextBiDiModeFlagsReadingOnly or }DT_SINGLELINE or DT_CENTER
      or DT_VCENTER);
  end;
end;
{=====}

procedure TVpContactButtonBar.SelectContact;
var
  I: Integer;
begin
  if FContactGrid <> nil then begin
    FContactGrid.SetFocus;
    for I := 1 to Length(bbSearchString) do
      if FContactGrid.SelectContactByName(bbSearchString[I]) then
        Break;
  end;
end;
{=====}

procedure TVpContactButtonBar.Loaded;
begin
  inherited Loaded;
  CreateButtons;
end;
{=====}

procedure TVpContactButtonBar.MouseDown(Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  I: Integer;
  P: TPoint;
  R: TRect;
begin
  inherited MouseDown(Button, Shift, X, Y);

  if Button = mbLeft then begin
    P := Point(X, Y);
    for I := 0 to pred(FButtonCount) do begin
      R := FButtonsArray[I].Rect;
      if PointInRect(P, R) then begin
        { if RadioStyle then un-press the last clicked button. }
        if RadioStyle then
          DrawButton(FButtonPressed, False);
        FButtonPressed := I;
        bbPopulateSearchString;
        DrawButton(I, True);
        Break;
      end;
    end;

    if Assigned(FOnButtonClick) then
      FOnButtonClick(Self, FButtonPressed, bbSearchString)
    else
      SelectContact;
  end;
end;
{=====}

procedure TVpContactButtonBar.MouseUp(Button: TMouseButton; Shift: TShiftState;
  X, Y: Integer);
begin
  inherited MouseUp(Button, Shift, X, Y);

  if not RadioStyle then
    DrawButton(FButtonPressed, False);
end;
{=====}

procedure TVpContactButtonBar.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited Notification(AComponent, Operation);
  if (AComponent = FContactGrid) and (Operation = opRemove) then
    FContactGrid := nil;
end;
{=====}

procedure TVpContactButtonBar.Paint;
var
  I: Integer;
begin
  for I := 0 to FButtonCount - 1 do begin
    if RadioStyle and (FButtonPressed = I) then
      DrawButton(I, True)
    else
      DrawButton(I, False);
  end;
end;
{=====}

procedure TVpContactButtonBar.Resize;
begin
  inherited Resize;
  CreateButtons;
end;
{=====}

procedure TVpContactButtonBar.SetDrawingStyle(const Value: TVpDrawingStyle);
begin
  if FDrawingStyle <> Value then begin
    FDrawingStyle := Value;
    Repaint;
  end;
end;
{=====}

procedure TVpContactButtonBar.SetContactGrid(Value: TVpContactGrid);
begin
  if (FContactGrid <> Value) then begin
    FContactGrid := Value;
    if FContactGrid <> nil then begin
      Height := FContactGrid.Height;
      Repaint;
    end;
  end;
end;
{=====}

procedure TVpContactButtonBar.bbPopulateSearchString;
var
  BC: string; // button caption
  I: integer;
begin
  bc := FButtonsArray[FButtonPressed].Caption;
  if FButtonPressed = 0 then
    bbSearchString := '0123456789'
  else if (pos('-', BC) > 0) then begin
    bbSearchString := '';
    for I := ord(BC[1]) to ord(BC[Length(BC)]) do
      bbSearchString := bbSearchString + chr(I);
  end else
    bbSearchString := FButtonsArray[FButtonPressed].Caption;
end;
{=====}

procedure TVpContactButtonBar.SetBarOrientation(const Value: TVpButtonBarOrientation);
begin
  if (FBarOrientation <> Value) then begin
    FBarOrientation := Value;
    CreateButtons;
    Repaint;
  end;
end;
{=====}

procedure TVpContactButtonBar.SetBorderWidth(const Value: Integer);
begin
  if (FBorderWidth <> Value) then begin
    FBorderWidth := Value;
    if FBorderWidth < 0 then
      FBorderWidth := 0;
    CreateButtons;
    Repaint;
  end;
end;
{=====}

procedure TVpContactButtonBar.SetButtonColor(const Value: TColor);
begin
  if (FButtonColor <> Value) then begin
    FButtonColor := Value;
    Repaint;
  end;
end;
{=====}

procedure TVpContactButtonBar.SetButtonHeight(const Value: Integer);
begin
  if (FButtonHeight <> Value) then begin
    FButtonHeight := Value;
    if FButtonHeight < 18 then
      FButtonHeight := 18;
    CreateButtons;
    Repaint;
  end;
end;
{=====}

procedure TVpContactButtonBar.SetButtonWidth(const Value: Integer);
begin
  if (FButtonWidth <> Value) then begin
    FButtonWidth := Value;
    if FButtonWidth < 34 then
      FButtonWidth := 34;
    CreateButtons;
    Repaint;
  end;
end;
{=====}

procedure TVpContactButtonBar.SetShowNumberButton(const Value: Boolean);
begin
  if (FShowNumberButton <> Value) then begin
    FShowNumberButton := Value;
    CreateButtons;
    Repaint;
  end;
end;
{=====}

end.
