home *** CD-ROM | disk | FTP | other *** search
/ Chip 1997 April / Chip_1997-04_cd.bin / prezent / cb / data.z / TABNOTBK.PAS < prev    next >
Pascal/Delphi Source File  |  1997-01-16  |  19KB  |  676 lines

  1.  
  2. {*******************************************************}
  3. {                                                       }
  4. {       Delphi Visual Component Library                 }
  5. {                                                       }
  6. {       Copyright (c) 1995,96 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,
  17.   Messages, Graphics, Controls, Dsgnintf, 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); 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.     function GetPageAt(Index: Integer): TTabPage;
  142.   end;
  143.  
  144. { TTabPageAccess }
  145.  
  146. { Method      : Create
  147.   Description : Keeps track of the pages for the notebook. }
  148. constructor TTabPageAccess.Create(APageList: TList; ANotebook: TTabbedNotebook);
  149. begin
  150.   inherited Create;
  151.   PageList := APageList;
  152.   Notebook := ANotebook;
  153. end;
  154.  
  155. { Method      : GetCount
  156.   Description : Return the number of pages in the notebook. }
  157. function TTabPageAccess.GetCount: Integer;
  158. begin
  159.   Result := PageList.Count;
  160. end;
  161.  
  162. { Method      : Get
  163.   Description : Return the name of the indexed page, which should match
  164.                 the name of the corresponding button. }
  165. function TTabPageAccess.Get(Index: Integer): string;
  166. begin
  167.   Result := TTabPage(PageList[Index]).Caption;
  168. end;
  169.  
  170. { Method      : Put
  171.   Description : Put a name into a page.  The button for the page must have
  172.                 the same name. }
  173. procedure TTabPageAccess.Put(Index: Integer; const S: string);
  174. begin
  175.   TTabPage(PageList[Index]).Caption := S;
  176.   if Notebook.HandleAllocated then
  177.     Notebook.Tabs[Index] := S;
  178. end;
  179.  
  180. { Method      : GetObject
  181.   Description : Return the page indexed. }
  182. function TTabPageAccess.GetObject(Index: Integer): TObject;
  183. begin
  184.   Result := PageList[Index];
  185. end;
  186.  
  187. { Method      : SetUpdateState
  188.   Description : We don't want to do this. }
  189. procedure TTabPageAccess.SetUpdateState(Updating: Boolean);
  190. begin
  191.   { do nothing }
  192. end;
  193.  
  194. { Method      : Clear
  195.   Description : Remove the pages and buttons from the list. }
  196. procedure TTabPageAccess.Clear;
  197. var
  198.   Index: Integer;
  199. begin
  200.   for Index := 0 to PageList.Count - 1 do
  201.     (TObject(PageList[Index]) as TTabPage).Free;
  202.   PageList.Clear;
  203.  
  204.   if Notebook.HandleAllocated then
  205.     Notebook.Tabs.Clear;
  206.  
  207.   Notebook.Realign;
  208. end;
  209.  
  210. { Method      : Delete
  211.   Description : Delete a page from the pagelist.  Take its button away too. }
  212. procedure TTabPageAccess.Delete(Index: Integer);
  213. begin
  214.   (TObject(PageList[Index]) as TTabPage).Free;
  215.   PageList.Delete(Index);
  216.  
  217.   if Notebook.HandleAllocated then
  218.     Notebook.Tabs.Delete(Index);
  219.  
  220.   { We need to make sure the active page index moves along with the pages. }
  221.   if index = Notebook.FPageIndex then
  222.     begin
  223.       Notebook.FpageIndex := -1;
  224.       Notebook.SetPageIndex(0);
  225.     end
  226.   else if index < Notebook.FPageIndex then
  227.     Dec(Notebook.FPageIndex);
  228.  
  229.   { Clean up the apperance. }
  230.   Notebook.Realign;
  231.   Notebook.Invalidate;
  232. end;
  233.  
  234. { Method      : Insert
  235.   Description : Add a page, along with its button, to the list. }
  236. procedure TTabPageAccess.Insert(Index: Integer; const S: string);
  237. var
  238.   Page: TTabPage;
  239. begin
  240.   Page := TTabPage.Create(Notebook);
  241.   with Page do
  242.   begin
  243.     Parent := Notebook;
  244.     Caption := S;
  245.   end;
  246.   PageList.Insert(Index, Page);
  247.   if Notebook.HandleAllocated then
  248.     Notebook.Tabs.Insert(Index, S);
  249.  
  250.   Notebook.SetPageIndex(Index);
  251.  
  252.   { Clean up the apperance. }
  253.   Notebook.Realign;
  254.   Notebook.Invalidate;
  255. end;
  256.  
  257. { Method      : Move
  258.   Description : Move a page, and its button, to a new index.  the object
  259.                 currently at the new location gets swapped to the old
  260.                 position. }
  261. procedure TTabPageAccess.Move(CurIndex, NewIndex: Integer);
  262. begin
  263.   if CurIndex <> NewIndex then
  264.   begin
  265.     PageList.Exchange(CurIndex, NewIndex);
  266.     with Notebook do
  267.     begin
  268.       if HandleAllocated then
  269.         Tabs.Exchange(CurIndex, NewIndex);
  270.       if PageIndex = CurIndex then
  271.         PageIndex := NewIndex
  272.       else if PageIndex = NewIndex then
  273.         PageIndex := CurIndex;
  274.       Realign;
  275.     end;
  276.   end;
  277. end;
  278.  
  279. { Method      : GetPageAt
  280.   Description : Access a page through GetObject. }
  281. function TTabPageAccess.GetPageAt(Index: Integer): TTabPage;
  282. begin
  283.   Result := (GetObject(Index) as TTabPage);
  284. end;
  285.  
  286. { TTabPage }
  287.  
  288. { Method      : Create
  289.   Description : Since the border is drawn by the notebook, this should be
  290.                 invisible.  Don't waste time drawing pages you can't see. }
  291. constructor TTabPage.Create(AOwner: TComponent);
  292. begin
  293.   inherited Create(AOwner);
  294.   ControlStyle := ControlStyle + [csAcceptsControls];
  295.   Align := alClient;
  296.   TabStop := False;
  297.   Enabled := False;
  298.   Visible := False;
  299. end;
  300.  
  301. { Method      : ReadState
  302.   Description : Another procedure that shouldn't be messed with. }
  303. procedure TTabPage.ReadState(Reader: TReader);
  304. begin
  305.   if Reader.Parent is TTabbedNotebook then
  306.     TTabbedNotebook(Reader.Parent).FPageList.Add(Self);
  307.   inherited ReadState(Reader);
  308.   TabStop := False;
  309. end;
  310.  
  311. { TTabbedNotebook }
  312.  
  313. { Method      : Create
  314.   Description : Set all the notebook defaults and create the mandatory
  315.                 one page. }
  316. var
  317.   Registered: Boolean = False;  { static class data }
  318.  
  319. constructor TTabbedNotebook.Create(AOwner: TComponent);
  320. begin
  321.   inherited Create(AOwner);
  322.   Exclude(FComponentStyle, csInheritable);
  323.   ControlStyle := ControlStyle + [csClickEvents] - [csAcceptsControls];
  324.   Width := 300;
  325.   Height := 250;
  326.   TabStop := True;
  327.   FPageList := TList.Create;
  328.  
  329.   FTabFont := TFont.Create;
  330.   FTabFont.Color := clBtnText;
  331.   FTabFont.Name := DefFontData.Name;
  332.   FTabFont.Height := DefFontData.Height;
  333.   FTabFont.OnChange := TabFontChanged;
  334.  
  335.   FTabsPerRow := 3;
  336.   FAccess := TTabPageAccess.Create(FPageList, Self);
  337.   FPageIndex := -1;
  338.   FAccess.Add(LoadStr(SDefault));
  339.   PageIndex := 0;
  340.  
  341.   if not Registered then
  342.   begin
  343.     RegisterClasses([TTabPage]);
  344.     Registered := True;
  345.   end;
  346. end;
  347.  
  348. { Method      : Destroy
  349.   Description : Remove all the lists before removing self. }
  350. destructor  TTabbedNotebook.Destroy;
  351. begin
  352.   FAccess.Free;
  353.   FPageList.Free;
  354.   FTabFont.Free;
  355.   inherited  Destroy;
  356. end;
  357.  
  358. procedure TTabbedNotebook.CreateHandle;
  359. var
  360.   X: Integer;
  361. begin
  362.   inherited CreateHandle;
  363.   if not (csReading in ComponentState) then
  364.   begin
  365.     { don't copy the objects into the Tabs list }
  366.     for X := 0 to FAccess.Count-1 do
  367.       Tabs.Add(FAccess[X]);
  368.     TabIndex := FPageIndex;
  369.   end;
  370. end;
  371.  
  372. { Method      : CreateParams
  373.   Description : Make sure ClipChildren is set. }
  374. procedure TTabbedNotebook.CreateParams(var Params: TCreateParams);
  375. begin
  376.   inherited CreateParams(Params);
  377.   Params.Style := Params.Style or WS_CLIPCHILDREN;
  378. end;
  379.  
  380. function TTabbedNotebook.GetChildOwner: TComponent;
  381. begin
  382.   Result := Self;
  383. end;
  384.  
  385. procedure TTabbedNotebook.GetChildren(Proc: TGetChildProc);
  386. var
  387.   I: Integer;
  388. begin
  389.   for I := 0 to FPageList.Count - 1 do Proc(TControl(FPageList[I]));
  390. end;
  391.  
  392. { Method      : Loaded
  393.   Description : Make sure only one page is visible, the one set as the
  394.                 default page. }
  395. procedure TTabbedNotebook.Loaded;
  396. var
  397.   Index: Integer;
  398. begin
  399.   inherited Loaded;
  400.   for Index := 0 to FPageList.Count - 1 do
  401.     if Index <> FPageIndex then
  402.     begin
  403.       (TObject(FPageList[Index]) as TTabPage).Enabled := False;
  404.       (TObject(FPageList[Index]) as TTabPage).Visible := False;
  405.     end
  406.     else
  407.     begin
  408.       (TObject(FPageList[Index]) as TTabPage).Enabled := True;
  409.       (TObject(FPageList[Index]) as TTabPage).Visible := True;
  410.     end;
  411.   if HandleAllocated then
  412.   begin
  413.     Tabs.Clear;
  414.     for Index := 0 to FAccess.Count-1 do
  415.       Tabs.Add(FAccess[Index]);
  416.     TabIndex := FPageIndex;
  417.   end;
  418.   Realign;
  419. end;
  420.  
  421. { Method      : ReadState
  422.   Description : Don't send the button information out since it is all the
  423.                 same anyway.}
  424. procedure TTabbedNotebook.ReadState(Reader: TReader);
  425. begin
  426.   FAccess.Clear;
  427.   inherited ReadState(Reader);
  428.   if (FPageIndex >= 0) and (FPageIndex < FPageList.Count) then
  429.   begin
  430.     with (TObject(FPageList[FPageIndex]) as TTabPage) do
  431.     begin
  432.       Enabled := True;
  433.       BringToFront;
  434.       Align := alClient;
  435.     end;
  436.   end
  437.   else
  438.     FPageIndex := -1;
  439. end;
  440.  
  441. { Method      : SetPages
  442.   Description : }
  443. procedure TTabbedNotebook.SetPages(Value: TStrings);
  444. begin
  445.   FAccess.Assign(Value);
  446.   if FAccess.Count > 0 then
  447.     FPageIndex := 0
  448.   else
  449.     FPageIndex := -1;
  450. end;
  451.  
  452. procedure TTabbedNotebook.ShowControl(AControl: TControl);
  453. var
  454.   I: Integer;
  455. begin
  456.   for I := 0 to FPageList.Count - 1 do
  457.     if FPageList[I] = AControl then
  458.     begin
  459.       SetPageIndex(I);
  460.       Exit;
  461.     end;
  462.   inherited ShowControl(AControl);
  463. end;
  464.  
  465. { Method      : SetPageIndex
  466.   Description : Set the active page to the one specified in Value. }
  467. procedure TTabbedNotebook.SetPageIndex(Value: Integer);
  468. var
  469.   AllowChange: Boolean;
  470.   ParentForm: TForm;
  471. begin
  472.   if csLoading in ComponentState then
  473.   begin
  474.     FPageIndex := Value;
  475.     Exit;
  476.   end;
  477.  
  478.   if (Value <> FPageIndex) and (Value >= 0) and (Value < FPageList.Count) then
  479.   begin
  480.     if Assigned(FOnChange) then
  481.     begin
  482.       AllowChange := True;
  483.       FOnChange(Self, Value, AllowChange);
  484.       if not AllowChange then Exit;
  485.     end;
  486.  
  487.     ParentForm := GetParentForm(Self);
  488.     if ParentForm <> nil then
  489.       if ContainsControl(ParentForm.ActiveControl) then
  490.         ParentForm.ActiveControl := Self;
  491.  
  492.     if HandleAllocated then
  493.       TabIndex := Value;
  494.  
  495.     with TTabPage(FPageList[Value]) do
  496.     begin
  497.       BringToFront;
  498.       Visible := True;
  499.       Enabled := True;
  500.     end;
  501.  
  502.     if (FPageIndex >= 0) and (FPageIndex < FPageList.Count) then
  503.       with TTabPage(FPageList[FPageIndex]) do
  504.       begin
  505.         Visible := False;
  506.         Enabled := False;
  507.       end;
  508.  
  509.     if (FPageIndex div FTabsPerRow) <> (Value div FTabsPerRow) then
  510.     begin
  511.       FPageIndex := Value;
  512.       Realign;
  513.     end
  514.     else
  515.       FPageIndex := Value;
  516.   end;
  517. end;
  518.  
  519. { Method      : SetActivePage
  520.   Description : Set the active page to the named page. }
  521. procedure TTabbedNotebook.SetActivePage(const Value: string);
  522. begin
  523.   SetPageIndex(FAccess.IndexOf(Value));
  524. end;
  525.  
  526. { Method      : GetActivePage
  527.   Description : Return the name of the currently active page. }
  528. function TTabbedNotebook.GetActivePage: string;
  529. begin
  530.   if (FAccess.Count > 0) and (FPageIndex >= 0) then
  531.     Result := FAccess[FPageIndex]
  532.   else
  533.     Result := '';
  534. end;
  535.  
  536. { Method      : WMGetDlgCode
  537.   Description : Get arrow keys to manage the tab focus rect }
  538. procedure TTabbedNotebook.WMGetDlgCode(var Message: TWMGetDlgCode);
  539. begin
  540.   Message.Result := DLGC_WANTARROWS;
  541. end;
  542.  
  543. { Method      : CMDialogChar
  544.   Description : Check for dialog keys in the tabs }
  545. procedure TTabbedNotebook.CMDialogChar(var Message: TCMDialogChar);
  546. var
  547.   Index: Integer;
  548. begin
  549.   with Message do
  550.     if FPageList <> nil then
  551.     begin
  552.       for Index := 0 to FPageList.Count - 1 do
  553.       begin
  554.         if IsAccel(CharCode, TTabPage(FPageList[Index]).Caption) then
  555.         begin
  556.           SetFocus;
  557.           if Focused then
  558.           begin
  559.             SetPageIndex(Index);
  560.             Click;
  561.           end;
  562.           Result := 1;
  563.           Exit;
  564.         end;
  565.       end;
  566.     end;
  567.     inherited;
  568. end;
  569.  
  570. { Method      : KeyDown
  571.   Description : Grab arrow keys to manage the active page. }
  572. procedure TTabbedNotebook.KeyDown(var Key: Word; Shift: TShiftState);
  573. begin
  574.   case Key of
  575.     VK_RIGHT, VK_DOWN:
  576.       begin
  577.         if FPageIndex >= (FPageList.Count-1) then SetPageIndex(0)
  578.         else SetPageIndex(FPageIndex + 1);
  579.         Click;
  580.       end;
  581.     VK_LEFT, VK_UP:
  582.       begin
  583.         if FPageIndex > 0 then SetPageIndex(FPageIndex - 1)
  584.         else SetPageIndex(FPageList.Count - 1);
  585.         Click;
  586.       end;
  587.   end;
  588. end;
  589.  
  590. { Method      : SetTabsPerRow
  591.   Description : Set the number of tabs in each row.  Don't allow less than
  592.                 three. }
  593. procedure TTabbedNotebook.SetTabsPerRow(NewTabCount: Integer);
  594. begin
  595.   if (NewTabCount >= 3) then
  596.   begin
  597.     FTabsPerRow := NewTabCount;
  598.     Realign;
  599.     Invalidate;
  600.   end;
  601. end;
  602.  
  603. { Mathod: GetIndexForPage
  604.   Description : Given a page name, return its index number. }
  605. function TTabbedNotebook.GetIndexForPage(const PageName: String): Integer;
  606. var
  607.   Index: Integer;
  608. begin
  609.   Result := -1;
  610.  
  611.   if FPageList <> nil then
  612.   begin
  613.     For Index := 0 to FPageList.Count-1 do
  614.     begin
  615.       if ((TObject(FPageList[Index]) as TTabPage).Caption = PageName) then
  616.       begin
  617.         Result := Index;
  618.         Exit;
  619.       end;
  620.     end;
  621.   end;
  622. end;
  623.  
  624. { Method      : SetTabFont
  625.   Description : Set the font for the tabs. }
  626. procedure TTabbedNotebook.SetTabFont(Value: TFont);
  627. begin
  628.   FTabFont.Assign(Value);
  629. end;
  630.  
  631. { Method      : CMTabFontChanged
  632.   Description : Fix the TopFont and redraw the buttons with the new font. }
  633. procedure TTabbedNotebook.CMTabFontChanged(var Message: TMessage);
  634. begin
  635.   Invalidate;
  636. end;
  637.  
  638. procedure TTabbedNotebook.AlignControls(AControl: TControl; var Rect: TRect);
  639. begin
  640.   If (FPageIndex >= 0) and (FPageIndex < FPageList.Count) then
  641.     inherited AlignControls(FPageList[FPageIndex], Rect);
  642. end;
  643.  
  644. { Method      : TabFontChanged
  645.   Description : Send out the proper message. }
  646. procedure TTabbedNotebook.TabFontChanged(Sender: TObject);
  647. begin
  648.   Perform(CM_TABFONTCHANGED, 0, 0);
  649. end;
  650.  
  651. { Method      : Click
  652.   Description : Call event procedure. }
  653. procedure TTabbedNotebook.Click;
  654. begin
  655.   if Assigned(FOnClick) then FOnClick(Self);
  656. end;
  657.  
  658. procedure TTabbedNotebook.Change;
  659. begin
  660.   if TabIndex >= 0 then
  661.     SetPageIndex(TabIndex);
  662.   if FPageIndex = TabIndex then
  663.     inherited Change
  664.   else
  665.     TabIndex := FPageIndex;
  666. end;
  667.  
  668. procedure TTabbedNotebook.WMPaint(var Message: TWMPaint);
  669. begin
  670.   SendMessage(Handle, wm_SetFont, TabFont.Handle, 0);
  671.   inherited;
  672. end;
  673.  
  674. end.
  675.  
  676.