home *** CD-ROM | disk | FTP | other *** search
/ Delphi Anthology / aDELPHI.iso / Runimage / Delphi50 / Source / Vcl / tabnotbk.pas < prev    next >
Pascal/Delphi Source File  |  1999-08-11  |  19KB  |  673 lines

  1.  
  2. {*******************************************************}
  3. {                                                       }
  4. {       Borland Delphi Visual Component Library         }
  5. {                                                       }
  6. {       Copyright (c) 1995,99 Inprise Corporation       }
  7. {                                                       }
  8. {*******************************************************}
  9.  
  10. { This unit defines the TTabbedNotebook Component. }
  11.  
  12. unit Tabnotbk;
  13.  
  14. {$H+,X+}
  15.  
  16. interface
  17.  
  18. uses Windows, Classes,  Stdctrls, Forms, Messages, Graphics, Controls,
  19.   ComCtrls;
  20.  
  21. const
  22.   CM_TABFONTCHANGED = CM_BASE + 100;
  23.  
  24. type
  25.  
  26.   TPageChangeEvent = procedure(Sender: TObject; NewTab: Integer;
  27.     var AllowChange: Boolean) of object;
  28.  
  29.   { Class       : TTabPage
  30.     Description : This class implements the individual tab page behavior.
  31.                   Each instance of this class will hold controls to be
  32.                   displayed when it is the active page of a TTabbedNotebook
  33.                   component. }
  34.   TTabPage = class(TWinControl)
  35.   protected
  36.     procedure ReadState(Reader: TReader); override;
  37.   public
  38.     constructor Create(AOwner: TComponent); override;
  39.   published
  40.     property Caption;
  41.     property Height stored False;
  42.     property TabOrder stored False;
  43.     property Visible stored False;
  44.     property Width stored False;
  45.     property Enabled stored False;
  46.   end;
  47.  
  48.   { Class       : TTabbedNotebook
  49.     Description : This class implements Tabbed notebook component.
  50.                   It holds a collection of TTabPages onto which
  51.                   users can drop controls.  It uses MS-Word style
  52.                   tab buttons to allow the user to control which
  53.                   page is currently active. }
  54.   TTabbedNotebook = class(TCustomTabControl)
  55.   private
  56.     FPageList: TList;
  57.     FAccess: TStrings;
  58.     FPageIndex: Integer;
  59.     FTabFont: TFont;
  60.     FTabsPerRow: Integer;
  61.     FOnClick: TNotifyEvent;
  62.     FOnChange: TPageChangeEvent;
  63.     function GetActivePage: string;
  64.     procedure SetPages(Value: TStrings);
  65.     procedure SetActivePage(const Value: string);
  66.     procedure SetTabFont(Value: TFont);
  67.     procedure SetTabsPerRow(NewTabCount: Integer);
  68.     procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
  69.     procedure CMDialogChar(var Message: TCMDialogChar); message CM_DIALOGCHAR;
  70.     procedure WMPaint(var Message: TWMPaint); message wm_Paint;
  71.   protected
  72.     procedure AlignControls(AControl: TControl; var Rect: TRect); override;
  73.     procedure Change; override;
  74.     procedure Click; override;
  75.     procedure CreateHandle; override;
  76.     procedure CreateParams(var Params: TCreateParams); override;
  77.     function GetChildOwner: TComponent; override;
  78.     procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
  79.     procedure Loaded; override;
  80.     procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  81.     procedure ReadState(Reader: TReader); override;
  82.     procedure SetPageIndex(Value: Integer);
  83.     procedure ShowControl(AControl: TControl); override;
  84.     procedure CMTabFontChanged(var Message: TMessage); message CM_TABFONTCHANGED;
  85.   public
  86.     constructor Create(AOwner: TComponent); override;
  87.     destructor  Destroy; override;
  88.     function GetIndexForPage(const PageName: string): Integer;
  89.     property TopFont: TFont read FTabFont;
  90.     procedure TabFontChanged(Sender: TObject);
  91.   published
  92.     property ActivePage: string read GetActivePage write SetActivePage
  93.       stored False;
  94.     property Align;
  95.     property Anchors;
  96.     property Constraints;
  97.     property Enabled;
  98.     property PageIndex: Integer read FPageIndex write SetPageIndex default 0;
  99.     property Pages: TStrings read FAccess write SetPages stored False;
  100.     property Font;
  101.     property TabsPerRow: Integer read FTabsPerRow write SetTabsPerRow default 3;
  102.     property TabFont: TFont read FTabFont write SetTabFont;
  103.     property ParentShowHint;
  104.     property PopupMenu;
  105.     property ShowHint;
  106.     property TabOrder;
  107.     property TabStop default True;
  108.     property Visible;
  109.     property OnClick: TNotifyEvent read FOnClick write FOnClick;
  110.     property OnChange: TPageChangeEvent read FOnChange write FOnChange;
  111.     property OnContextPopup;
  112.     property OnEnter;
  113.     property OnExit;
  114.   end;
  115.  
  116. implementation
  117.  
  118. uses SysUtils, Consts;
  119.  
  120. const
  121.   TabTopBorder = 4;
  122.   PageLeftBorder = 2;
  123.   PageBevelWidth = 3;
  124.   BorderWidth  = 8;
  125.  
  126. type
  127.   { Class       : TTabPageAccess
  128.     Description : Maintains the list of TTabPages for a
  129.                   TTabbedNotebook component. }
  130.   TTabPageAccess = class(TStrings)
  131.   private
  132.     PageList: TList;
  133.     Notebook: TTabbedNotebook;
  134.   protected
  135.     function GetCount: Integer; override;
  136.     function Get(Index: Integer): string; override;
  137.     procedure Put(Index: Integer; const S: string); override;
  138.     function GetObject(Index: Integer): TObject; override;
  139.     procedure SetUpdateState(Updating: Boolean); override;
  140.   public
  141.     constructor Create(APageList: TList; ANotebook: TTabbedNotebook);
  142.     procedure Clear; override;
  143.     procedure Delete(Index: Integer); override;
  144.     procedure Insert(Index: Integer; const S: string); override;
  145.     procedure Move(CurIndex, NewIndex: Integer); override;
  146.   end;
  147.  
  148. { TTabPageAccess }
  149.  
  150. { Method      : Create
  151.   Description : Keeps track of the pages for the notebook. }
  152. constructor TTabPageAccess.Create(APageList: TList; ANotebook: TTabbedNotebook);
  153. begin
  154.   inherited Create;
  155.   PageList := APageList;
  156.   Notebook := ANotebook;
  157. end;
  158.  
  159. { Method      : GetCount
  160.   Description : Return the number of pages in the notebook. }
  161. function TTabPageAccess.GetCount: Integer;
  162. begin
  163.   Result := PageList.Count;
  164. end;
  165.  
  166. { Method      : Get
  167.   Description : Return the name of the indexed page, which should match
  168.                 the name of the corresponding button. }
  169. function TTabPageAccess.Get(Index: Integer): string;
  170. begin
  171.   Result := TTabPage(PageList[Index]).Caption;
  172. end;
  173.  
  174. { Method      : Put
  175.   Description : Put a name into a page.  The button for the page must have
  176.                 the same name. }
  177. procedure TTabPageAccess.Put(Index: Integer; const S: string);
  178. begin
  179.   TTabPage(PageList[Index]).Caption := S;
  180.   if Notebook.HandleAllocated then
  181.     Notebook.Tabs[Index] := S;
  182. end;
  183.  
  184. { Method      : GetObject
  185.   Description : Return the page indexed. }
  186. function TTabPageAccess.GetObject(Index: Integer): TObject;
  187. begin
  188.   Result := PageList[Index];
  189. end;
  190.  
  191. { Method      : SetUpdateState
  192.   Description : We don't want to do this. }
  193. procedure TTabPageAccess.SetUpdateState(Updating: Boolean);
  194. begin
  195.   { do nothing }
  196. end;
  197.  
  198. { Method      : Clear
  199.   Description : Remove the pages and buttons from the list. }
  200. procedure TTabPageAccess.Clear;
  201. var
  202.   Index: Integer;
  203. begin
  204.   for Index := 0 to PageList.Count - 1 do
  205.     (TObject(PageList[Index]) as TTabPage).Free;
  206.   PageList.Clear;
  207.  
  208.   if Notebook.HandleAllocated then
  209.     Notebook.Tabs.Clear;
  210.  
  211.   Notebook.Realign;
  212. end;
  213.  
  214. { Method      : Delete
  215.   Description : Delete a page from the pagelist.  Take its button away too. }
  216. procedure TTabPageAccess.Delete(Index: Integer);
  217. begin
  218.   (TObject(PageList[Index]) as TTabPage).Free;
  219.   PageList.Delete(Index);
  220.  
  221.   if Notebook.HandleAllocated then
  222.     Notebook.Tabs.Delete(Index);
  223.  
  224.   { We need to make sure the active page index moves along with the pages. }
  225.   if index = Notebook.FPageIndex then
  226.     begin
  227.       Notebook.FpageIndex := -1;
  228.       Notebook.SetPageIndex(0);
  229.     end
  230.   else if index < Notebook.FPageIndex then
  231.     Dec(Notebook.FPageIndex);
  232.  
  233.   { Clean up the apperance. }
  234.   Notebook.Realign;
  235.   Notebook.Invalidate;
  236. end;
  237.  
  238. { Method      : Insert
  239.   Description : Add a page, along with its button, to the list. }
  240. procedure TTabPageAccess.Insert(Index: Integer; const S: string);
  241. var
  242.   Page: TTabPage;
  243. begin
  244.   Page := TTabPage.Create(Notebook);
  245.   with Page do
  246.   begin
  247.     Parent := Notebook;
  248.     Caption := S;
  249.   end;
  250.   PageList.Insert(Index, Page);
  251.   if Notebook.HandleAllocated then
  252.     Notebook.Tabs.Insert(Index, S);
  253.  
  254.   Notebook.SetPageIndex(Index);
  255.  
  256.   { Clean up the apperance. }
  257.   Notebook.Realign;
  258.   Notebook.Invalidate;
  259. end;
  260.  
  261. { Method      : Move
  262.   Description : Move a page, and its button, to a new index.  the object
  263.                 currently at the new location gets swapped to the old
  264.                 position. }
  265. procedure TTabPageAccess.Move(CurIndex, NewIndex: Integer);
  266. begin
  267.   if CurIndex <> NewIndex then
  268.   begin
  269.     PageList.Exchange(CurIndex, NewIndex);
  270.     with Notebook do
  271.     begin
  272.       if HandleAllocated then
  273.         Tabs.Exchange(CurIndex, NewIndex);
  274.       if PageIndex = CurIndex then
  275.         PageIndex := NewIndex
  276.       else if PageIndex = NewIndex then
  277.         PageIndex := CurIndex;
  278.       Realign;
  279.     end;
  280.   end;
  281. end;
  282.  
  283. { TTabPage }
  284.  
  285. { Method      : Create
  286.   Description : Since the border is drawn by the notebook, this should be
  287.                 invisible.  Don't waste time drawing pages you can't see. }
  288. constructor TTabPage.Create(AOwner: TComponent);
  289. begin
  290.   inherited Create(AOwner);
  291.   ControlStyle := ControlStyle + [csAcceptsControls];
  292.   Align := alClient;
  293.   TabStop := False;
  294.   Enabled := False;
  295.   Visible := False;
  296. end;
  297.  
  298. { Method      : ReadState
  299.   Description : Another procedure that shouldn't be messed with. }
  300. procedure TTabPage.ReadState(Reader: TReader);
  301. begin
  302.   if Reader.Parent is TTabbedNotebook then
  303.     TTabbedNotebook(Reader.Parent).FPageList.Add(Self);
  304.   inherited ReadState(Reader);
  305.   TabStop := False;
  306. end;
  307.  
  308. { TTabbedNotebook }
  309.  
  310. { Method      : Create
  311.   Description : Set all the notebook defaults and create the mandatory
  312.                 one page. }
  313. var
  314.   Registered: Boolean = False;  { static class data }
  315.  
  316. constructor TTabbedNotebook.Create(AOwner: TComponent);
  317. begin
  318.   inherited Create(AOwner);
  319.   Exclude(FComponentStyle, csInheritable);
  320.   ControlStyle := ControlStyle + [csClickEvents] - [csAcceptsControls];
  321.   Width := 300;
  322.   Height := 250;
  323.   TabStop := True;
  324.   FPageList := TList.Create;
  325.  
  326.   FTabFont := TFont.Create;
  327.   FTabFont.Color := clBtnText;
  328.   FTabFont.OnChange := TabFontChanged;
  329.  
  330.   FTabsPerRow := 3;
  331.   FAccess := TTabPageAccess.Create(FPageList, Self);
  332.   FPageIndex := -1;
  333.   FAccess.Add(SDefault);
  334.   PageIndex := 0;
  335.  
  336.   if not Registered then
  337.   begin
  338.     RegisterClasses([TTabPage]);
  339.     Registered := True;
  340.   end;
  341. end;
  342.  
  343. { Method      : Destroy
  344.   Description : Remove all the lists before removing self. }
  345. destructor  TTabbedNotebook.Destroy;
  346. begin
  347.   FAccess.Free;
  348.   FPageList.Free;
  349.   FTabFont.Free;
  350.   inherited  Destroy;
  351. end;
  352.  
  353. procedure TTabbedNotebook.CreateHandle;
  354. var
  355.   X: Integer;
  356. begin
  357.   inherited CreateHandle;
  358.   if not (csReading in ComponentState) and (Tabs.Count = 0) then
  359.   begin
  360.     { don't copy the objects into the Tabs list }
  361.     for X := 0 to FAccess.Count-1 do
  362.       Tabs.Add(FAccess[X]);
  363.     TabIndex := FPageIndex;
  364.   end;
  365. end;
  366.  
  367. { Method      : CreateParams
  368.   Description : Make sure ClipChildren is set. }
  369. procedure TTabbedNotebook.CreateParams(var Params: TCreateParams);
  370. begin
  371.   inherited CreateParams(Params);
  372.   Params.Style := Params.Style or WS_CLIPCHILDREN;
  373. end;
  374.  
  375. function TTabbedNotebook.GetChildOwner: TComponent;
  376. begin
  377.   Result := Self;
  378. end;
  379.  
  380. procedure TTabbedNotebook.GetChildren(Proc: TGetChildProc; Root: TComponent);
  381. var
  382.   I: Integer;
  383. begin
  384.   for I := 0 to FPageList.Count - 1 do Proc(TControl(FPageList[I]));
  385. end;
  386.  
  387. { Method      : Loaded
  388.   Description : Make sure only one page is visible, the one set as the
  389.                 default page. }
  390. procedure TTabbedNotebook.Loaded;
  391. var
  392.   Index: Integer;
  393. begin
  394.   inherited Loaded;
  395.   for Index := 0 to FPageList.Count - 1 do
  396.     if Index <> FPageIndex then
  397.     begin
  398.       (TObject(FPageList[Index]) as TTabPage).Enabled := False;
  399.       (TObject(FPageList[Index]) as TTabPage).Visible := False;
  400.     end
  401.     else
  402.     begin
  403.       (TObject(FPageList[Index]) as TTabPage).Enabled := True;
  404.       (TObject(FPageList[Index]) as TTabPage).Visible := True;
  405.     end;
  406.   if HandleAllocated then
  407.   begin
  408.     Tabs.Clear;
  409.     for Index := 0 to FAccess.Count-1 do
  410.       Tabs.Add(FAccess[Index]);
  411.     TabIndex := FPageIndex;
  412.   end;
  413.   Realign;
  414. end;
  415.  
  416. { Method      : ReadState
  417.   Description : Don't send the button information out since it is all the
  418.                 same anyway.}
  419. procedure TTabbedNotebook.ReadState(Reader: TReader);
  420. begin
  421.   FAccess.Clear;
  422.   inherited ReadState(Reader);
  423.   if (FPageIndex >= 0) and (FPageIndex < FPageList.Count) then
  424.   begin
  425.     with (TObject(FPageList[FPageIndex]) as TTabPage) do
  426.     begin
  427.       Enabled := True;
  428.       BringToFront;
  429.       Align := alClient;
  430.     end;
  431.   end
  432.   else
  433.     FPageIndex := -1;
  434. end;
  435.  
  436. { Method      : SetPages
  437.   Description : }
  438. procedure TTabbedNotebook.SetPages(Value: TStrings);
  439. begin
  440.   FAccess.Assign(Value);
  441.   if FAccess.Count > 0 then
  442.     FPageIndex := 0
  443.   else
  444.     FPageIndex := -1;
  445. end;
  446.  
  447. procedure TTabbedNotebook.ShowControl(AControl: TControl);
  448. var
  449.   I: Integer;
  450. begin
  451.   for I := 0 to FPageList.Count - 1 do
  452.     if FPageList[I] = AControl then
  453.     begin
  454.       SetPageIndex(I);
  455.       Exit;
  456.     end;
  457.   inherited ShowControl(AControl);
  458. end;
  459.  
  460. { Method      : SetPageIndex
  461.   Description : Set the active page to the one specified in Value. }
  462. procedure TTabbedNotebook.SetPageIndex(Value: Integer);
  463. var
  464.   AllowChange: Boolean;
  465.   ParentForm: TCustomForm;
  466. begin
  467.   if csLoading in ComponentState then
  468.   begin
  469.     FPageIndex := Value;
  470.     Exit;
  471.   end;
  472.  
  473.   if (Value <> FPageIndex) and (Value >= 0) and (Value < FPageList.Count) then
  474.   begin
  475.     if Assigned(FOnChange) then
  476.     begin
  477.       AllowChange := True;
  478.       FOnChange(Self, Value, AllowChange);
  479.       if not AllowChange then Exit;
  480.     end;
  481.  
  482.     ParentForm := GetParentForm(Self);
  483.     if ParentForm <> nil then
  484.       if ContainsControl(ParentForm.ActiveControl) then
  485.         ParentForm.ActiveControl := Self;
  486.  
  487.     if HandleAllocated then
  488.       TabIndex := Value;
  489.  
  490.     with TTabPage(FPageList[Value]) do
  491.     begin
  492.       BringToFront;
  493.       Visible := True;
  494.       Enabled := True;
  495.     end;
  496.  
  497.     if (FPageIndex >= 0) and (FPageIndex < FPageList.Count) then
  498.       with TTabPage(FPageList[FPageIndex]) do
  499.       begin
  500.         Visible := False;
  501.         Enabled := False;
  502.       end;
  503.  
  504.     if (FPageIndex div FTabsPerRow) <> (Value div FTabsPerRow) then
  505.     begin
  506.       FPageIndex := Value;
  507.       Realign;
  508.     end
  509.     else
  510.       FPageIndex := Value;
  511.   end;
  512. end;
  513.  
  514.  
  515. { Method      : SetActivePage
  516.   Description : Set the active page to the named page. }
  517. procedure TTabbedNotebook.SetActivePage(const Value: string);
  518. begin
  519.   SetPageIndex(FAccess.IndexOf(Value));
  520. end;
  521.  
  522. { Method      : GetActivePage
  523.   Description : Return the name of the currently active page. }
  524. function TTabbedNotebook.GetActivePage: string;
  525. begin
  526.   if (FAccess.Count > 0) and (FPageIndex >= 0) then
  527.     Result := FAccess[FPageIndex]
  528.   else
  529.     Result := '';
  530. end;
  531.  
  532. { Method      : WMGetDlgCode
  533.   Description : Get arrow keys to manage the tab focus rect }
  534. procedure TTabbedNotebook.WMGetDlgCode(var Message: TWMGetDlgCode);
  535. begin
  536.   Message.Result := DLGC_WANTARROWS;
  537. end;
  538.  
  539. { Method      : CMDialogChar
  540.   Description : Check for dialog keys in the tabs }
  541. procedure TTabbedNotebook.CMDialogChar(var Message: TCMDialogChar);
  542. var
  543.   Index: Integer;
  544. begin
  545.   with Message do
  546.     if FPageList <> nil then
  547.     begin
  548.       for Index := 0 to FPageList.Count - 1 do
  549.       begin
  550.         if IsAccel(CharCode, TTabPage(FPageList[Index]).Caption) then
  551.         begin
  552.           SetFocus;
  553.           if Focused then
  554.           begin
  555.             SetPageIndex(Index);
  556.             Click;
  557.           end;
  558.           Result := 1;
  559.           Exit;
  560.         end;
  561.       end;
  562.     end;
  563.     inherited;
  564. end;
  565.  
  566. { Method      : KeyDown
  567.   Description : Grab arrow keys to manage the active page. }
  568. procedure TTabbedNotebook.KeyDown(var Key: Word; Shift: TShiftState);
  569. begin
  570.   case Key of
  571.     VK_RIGHT, VK_DOWN:
  572.       begin
  573.         if FPageIndex >= (FPageList.Count-1) then SetPageIndex(0)
  574.         else SetPageIndex(FPageIndex + 1);
  575.         Click;
  576.       end;
  577.     VK_LEFT, VK_UP:
  578.       begin
  579.         if FPageIndex > 0 then SetPageIndex(FPageIndex - 1)
  580.         else SetPageIndex(FPageList.Count - 1);
  581.         Click;
  582.       end;
  583.   end;
  584. end;
  585.  
  586. { Method      : SetTabsPerRow
  587.   Description : Set the number of tabs in each row.  Don't allow less than
  588.                 three. }
  589. procedure TTabbedNotebook.SetTabsPerRow(NewTabCount: Integer);
  590. begin
  591.   if (NewTabCount >= 3) then
  592.   begin
  593.     FTabsPerRow := NewTabCount;
  594.     Realign;
  595.     Invalidate;
  596.   end;
  597. end;
  598.  
  599. { Mathod: GetIndexForPage
  600.   Description : Given a page name, return its index number. }
  601. function TTabbedNotebook.GetIndexForPage(const PageName: String): Integer;
  602. var
  603.   Index: Integer;
  604. begin
  605.   Result := -1;
  606.  
  607.   if FPageList <> nil then
  608.   begin
  609.     For Index := 0 to FPageList.Count-1 do
  610.     begin
  611.       if ((TObject(FPageList[Index]) as TTabPage).Caption = PageName) then
  612.       begin
  613.         Result := Index;
  614.         Exit;
  615.       end;
  616.     end;
  617.   end;
  618. end;
  619.  
  620. { Method      : SetTabFont
  621.   Description : Set the font for the tabs. }
  622. procedure TTabbedNotebook.SetTabFont(Value: TFont);
  623. begin
  624.   FTabFont.Assign(Value);
  625. end;
  626.  
  627. { Method      : CMTabFontChanged
  628.   Description : Fix the TopFont and redraw the buttons with the new font. }
  629. procedure TTabbedNotebook.CMTabFontChanged(var Message: TMessage);
  630. begin
  631.   Invalidate;
  632. end;
  633.  
  634. procedure TTabbedNotebook.AlignControls(AControl: TControl; var Rect: TRect);
  635. begin
  636.   If (FPageIndex >= 0) and (FPageIndex < FPageList.Count) then
  637.     inherited AlignControls(FPageList[FPageIndex], Rect);
  638. end;
  639.  
  640. { Method      : TabFontChanged
  641.   Description : Send out the proper message. }
  642. procedure TTabbedNotebook.TabFontChanged(Sender: TObject);
  643. begin
  644.   Perform(CM_TABFONTCHANGED, 0, 0);
  645. end;
  646.  
  647. { Method      : Click
  648.   Description : Call event procedure. }
  649. procedure TTabbedNotebook.Click;
  650. begin
  651.   if Assigned(FOnClick) then FOnClick(Self);
  652. end;
  653.  
  654. procedure TTabbedNotebook.Change;
  655. begin
  656.   if TabIndex >= 0 then
  657.     SetPageIndex(TabIndex);
  658.   if FPageIndex = TabIndex then
  659.     inherited Change
  660.   else
  661.     TabIndex := FPageIndex;
  662. end;
  663.  
  664. procedure TTabbedNotebook.WMPaint(var Message: TWMPaint);
  665. begin
  666.   SendMessage(Handle, wm_SetFont, TabFont.Handle, 0);
  667.   inherited;
  668. end;
  669.  
  670.  
  671. end.
  672.  
  673.