home *** CD-ROM | disk | FTP | other *** search
/ PC Plus SuperCD (UK) 2000 March / pcp161b.iso / full / delphi / RUNIMAGE / DELPHI30 / SOURCE / VCL / TABNOTBK.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1997-08-03  |  18.1 KB  |  669 lines

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