home *** CD-ROM | disk | FTP | other *** search
- unit bs2;
- {$DEFINE DEBUG}
-
- { PC Plus sample Delphi program.
- Illustrates the basic techniques for declaring a class and
- constructing and destroying objects.
- Also shows how to create a simple object hierarchy including
- list-owning objects, which can be saved to and loaded from disk
- }
-
- interface
-
- uses
- SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
- Forms, Dialogs, StdCtrls, ExtCtrls, ComCtrls;
-
-
- type
- TForm1 = class(TForm)
- Panel1: TPanel;
- Label1: TLabel;
- Label2: TLabel;
- ExtraLabel: TLabel;
- BookEd: TEdit;
- AuthorEd: TEdit;
- ComboBox1: TComboBox;
- ExtraEdit: TEdit;
- NumberCombo: TComboBox;
- AddBtn: TButton;
- ClearBtn: TButton;
- ExitBtn: TButton;
- ShowBtn: TButton;
- SaveBtn: TButton;
- LoadBtn: TButton;
- Workspace: TRichEdit;
- Button1: TButton;
- procedure ExitBtnClick(Sender: TObject);
- procedure FormCreate(Sender: TObject);
- procedure FormClose(Sender: TObject; var Action: TCloseAction);
- procedure AddBtnClick(Sender: TObject);
- procedure ShowBtnClick(Sender: TObject);
- procedure ClearBtnClick(Sender: TObject);
- procedure ComboBox1Change(Sender: TObject);
- procedure SaveBtnClick(Sender: TObject);
- procedure LoadBtnClick(Sender: TObject);
- procedure Button1Click(Sender: TObject);
- private
- { Private declarations }
- public
- { --- My declarations --- }
- procedure AddBookOb;
- procedure AddHorrorBookOb;
- procedure AddRefBookOb;
- procedure AddSeriesBookOb;
- procedure ShowObList;
- end;
-
- { declare a fixed-length string }
- bkstr = string[255];
- cnstr = string[15]; // to store the class name
- { declare a gorerating for HorrorBookOb }
- gorerating = 0..10;
-
- { BookObList is a special BookOb-managing TList }
- BookObList = class(TList)
- constructor Create;
- destructor Destroy; override;
- procedure FreeObs;
- procedure SaveObList( fname : string );
- procedure LoadObList( fname: string);
- procedure WriteToStream( fs : TFileStream );
- procedure ReadFromStream( fs: TFileStream);
- end;
-
- { declare a BookOb class }
- BookOb = class(TObject)
- name : bkstr;
- author : bkstr;
- cn : cnstr; // name of the class
- constructor Create( aName, anAuthor : bkstr );
- constructor CreateFromStream( cname : cnstr; fs : TFileStream );
- destructor Destroy; override;
- function Describe : bkstr; virtual;
- procedure WriteToStream( fs : TFileStream ); virtual;
- procedure ReadFromStream( fs: TFileStream); virtual;
- end;
-
- { declare a HorrorBookOb class }
- HorrorBookOb = class(BookOb)
- gorescore : integer;
- constructor Create( aName, anAuthor : bkstr; aGorescore : gorerating );
- destructor Destroy; override;
- function Describe : bkstr; override;
- procedure WriteToStream( fs : TFileStream ); override;
- procedure ReadFromStream( fs: TFileStream); override;
- end;
-
- { declare a RefBookOb class }
- RefBookOb = class(BookOb)
- reftype : bkstr;
- constructor Create( aName, anAuthor, aReftype : bkstr);
- destructor Destroy; override;
- function Describe : bkstr; override;
- procedure WriteToStream( fs : TFileStream ); override;
- procedure ReadFromStream( fs: TFileStream); override;
- end;
-
- {XXX New SeriesBookOb class XXX}
- { This maintains a list of books, called Volumes, of the BookObList class }
- SeriesBookOb = class(BookOb)
- volumes : BookObList; { the volumes in this series }
- constructor Create( aName, anAuthor : bkstr );
- destructor Destroy; override;
- function Describe : bkstr; override;
- procedure AddVolumes( name, author : bkstr; vols: integer );
- procedure WriteToStream( fs : TFileStream ); override;
- procedure ReadFromStream( fs: TFileStream); override;
-
- end;
-
-
- const
- SAVEFILE = 'Library.sav';
- var
- Form1: TForm1;
- ObList : BookObList; { declare a TList to hold our objects }
-
- implementation
-
- {$R *.DFM}
-
- // ===========================================
- // TFORM1 Start...
- // ===========================================
-
- procedure TForm1.ShowObList;
- var
- i :integer;
- begin
- if ObList.Count = 0 then
- Workspace.Lines.Add( 'No books in the list!' )
- else
- for i := 0 to ObList.Count - 1 do
- Workspace.Lines.Add( Format('>> BOOK %d: %s',
- [i,BookOb(ObList.Items[i]).Describe]) );
- end;
-
- procedure TForm1.Button1Click(Sender: TObject);
- begin
- WorkSpace.Clear;
- end;
-
- procedure TForm1.SaveBtnClick(Sender: TObject);
- begin
- ObList.SaveObList( SAVEFILE );
- Workspace.Lines.Add( 'Saved' );
- end;
-
- procedure TForm1.LoadBtnClick(Sender: TObject);
- begin
- if not FileExists( SAVEFILE ) then
- ShowMessage( SAVEFILE + ' not found!' )
- else
- begin
- ObList.LoadObList( SAVEFILE );
- ShowObList;
- end;
- end;
-
- procedure TForm1.ExitBtnClick(Sender: TObject);
- begin
- Close;
- end;
-
- procedure TForm1.FormCreate(Sender: TObject);
- begin
- { Create a BookObList called ObList when the main form is created }
- ObList := BookObList.Create;
- end;
-
- procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
- begin
- ObList.FreeObs; { Free the objects in the ObList, then... }
- ObList.Free; { Free the ObList when the main form is closed }
- end;
-
- { ----- add specific types of objects ----- }
- procedure TForm1.AddBookOb;
- { add an ordinary BookOb to the ObList and display its data on the List box }
- var
- book : BookOb;
- begin
- book := BookOb.Create( BookEd.Text, AuthorEd.Text );
- ObList.Add( book );
- Workspace.Lines.Add( 'Book added' );
- end;
-
- procedure TForm1.AddRefBookOb;
- { add a RefBookOb to the ObList and display its data on the List box }
- var
- book : RefBookOb;
- begin
- if (ExtraEdit.Text = '') then
- MessageDlg('You must enter a book type (e.g. "Physics" or "Art") ',
- mtInformation, [mbOk], 0)
- else
- begin
- book := RefBookOb.Create( BookEd.Text, AuthorEd.Text, ExtraEdit.Text );
- ObList.Add( book );
- Workspace.Lines.Add( 'Reference Book added' );
- end;
- end;
-
- procedure TForm1.AddHorrorBookOb;
- { add a HorrorBookOb to the ObList and display its data on the List box }
- var
- book : HorrorBookOb;
- score : integer;
- begin
- score := StrToIntDef( NumberCombo.Text, -1 );//XXX Use StrToIntDef and you
- if score = -1 then //won't need to handle exceptions
- MessageDlg('Invalid gorescore entry!', mtInformation,[mbOk], 0)
- else
- if (score < 0) or (score > 10) then
- MessageDlg('The score must be a number from 0 to 10!', mtInformation,
- [mbOk], 0)
- else
- begin { if all is ok, create the object }
- book := HorrorBookOb.Create( BookEd.Text, AuthorEd.Text, score );
- ObList.Add( book );
- Workspace.Lines.Add( 'Horror Book added' );
- end;
- end;
-
- procedure TForm1.AddSeriesBookOb;
- { add a SeriesBookOb to the ObList and display its data on the List box
- For simplicity (and ease of displaying data!), a series is here restricted
- from 2 to 4 volumes. This is an arbitrary restriction and it can easily
- be removed or extended }
- var
- book : SeriesBookOb;
- vols : integer;
- begin
- vols := StrToIntDef( NumberCombo.Text, -1 );
- if vols = -1 then
- MessageDlg('Invalid number of volumes!', mtInformation,[mbOk], 0)
- else
- if (vols < 2) or (vols > 4 ) then
- MessageDlg('A series must have from 2 to 4 volumes!', mtInformation,
- [mbOk], 0)
- else
- begin
- book := SeriesBookOb.Create( BookEd.Text, AuthorEd.Text );
- { XXX Just for testing, add some volumes. You could write an interactive
- procedure here to let the user add volumes with different authors or even of
- different book types }
- book.AddVolumes( BookEd.Text, AuthorEd.Text, vols );
- ObList.Add( book );
- {$IFDEF DEBUG}
- Caption := 'Adding book of class: ' + book.ClassName;
- {$ENDIF }
- Workspace.Lines.Add( 'Series Book added' );
- end;
- end;
-
- procedure TForm1.AddBtnClick(Sender: TObject);
- { User wants to add a book object. Determine the type of object }
- { by the Combo box item that's been selected, then call an appropriate }
- { method. }
- begin
- if ((BookEd.Text = '') or (AuthorEd.Text = '')) then
- MessageDlg('You must enter a book and an author!', mtInformation,
- [mbOk], 0)
- else
- if ComboBox1.Text = 'Horror' then
- AddHorrorBookOb
- else
- if ComboBox1.Text = 'Reference' then
- AddRefBookOb
- else
- if ComboBox1.Text = 'Series' then
- AddSeriesBookOb
- else
- AddBookOb;
- end;
-
- procedure TForm1.ShowBtnClick(Sender: TObject);
- begin
- ShowObList;
- end;
-
- procedure TForm1.ClearBtnClick(Sender: TObject);
- begin
- ObList.FreeObs;
- Workspace.Lines.Add('OK');
- end;
-
-
- procedure TForm1.ComboBox1Change(Sender: TObject);
- { When a change (normally a selection) is made in the ComboBox, see if the }
- { selected item indicates a special type of book. If so, display a label }
- { and text entry field to allow the user to enter the additional data needed }
- { to create an object of the specified type. }
- begin
- if ComboBox1.Text = 'Horror' then
- begin
- ExtraLabel.Caption := 'Enter Gore score [1 to 10]';
- ExtraLabel.Show;
- ExtraEdit.Hide;
- NumberCombo.Show;
- NumberCombo.SetFocus;
- end
- else if ComboBox1.Text = 'Reference' then
- begin
- ExtraLabel.Caption := 'Enter the type of reference book';
- ExtraEdit.Text := 'General';
- ExtraLabel.Show;
- ExtraEdit.Show;
- NumberCombo.Hide;
- ExtraEdit.SetFocus;
- end
- else if ComboBox1.Text = 'Series' then
- begin
- ExtraLabel.Caption := 'Enter number of volumes in series';
- ExtraLabel.Show;
- ExtraEdit.Hide;
- NumberCombo.Show;
- end
- else
- begin { if it's an ordinary book, don't display the extra edit box }
- ExtraLabel.Hide;
- ExtraEdit.Hide;
- NumberCombo.Hide;
- end;
- end;
-
-
- // ===========================================
- // BOOKOBLIST Start...
- // ===========================================
- // constructor
- constructor BookObList.Create;
- begin
- inherited Create;
- end;
-
- // destructor
- destructor BookObList.Destroy;
- begin
- FreeObs;
- inherited Destroy;
- end;
-
- procedure BookObList.SaveObList( fname : string );
- { Create a file stream then ask BookObList to write itself to that stream }
- var
- fs : TFileStream;
- begin
- fs := TFileStream.Create(fname, fmCreate );
- try
- self.WriteToStream( fs );
- finally;
- fs.Free;
- end;
- end;
-
- procedure BookObList.LoadObList( fname : string );
- { Create a file stream then ask BookObList to read itself from that stream }
- var
- fs : TFileStream;
- begin
- fs := TFileStream.Create(fname, fmOpenRead );
- self.Clear;
- try
- self.ReadFromStream(fs);
- finally
- fs.Free;
- end;
- end;
-
- procedure BookObList.FreeObs;
- { Free the objects in the ObList }
- var
- i : integer;
- begin
- for i := 0 to self.Count - 1 do
- if self.Items[i] <> nil then
- begin
- {$IFDEF DEBUG}
- Form1.Workspace.Lines.Add( 'Call to FREE Ob: ' +
- BookOb(self.Items[i]).Describe );
- {$ENDIF}
- BookOb(self.Items[i]).Free; // each BookOb knows how to Free itself
- end;
- self.Clear;
- end;
-
- //--- BookObList Streams ---
- procedure BookObList.ReadFromStream(fs: TFileStream);
- var
- cn : cnstr;
- i, obcount : integer;
- begin
- {$IFDEF DEBUG}
- Form1.Workspace.Lines.Add( 'BookObList.ReadFromStream' );
- {$ENDIF}
- { First read a count of objects }
- fs.ReadBuffer(obcount, sizeof(obcount));
- {$IFDEF DEBUG}
- Form1.Workspace.Lines.Add( 'BookObList.ReadFromStream, obcount = '+ IntToStr(obcount) );
- {$ENDIF}
- for i := 0 to obcount - 1 do
- begin { read classname and call appropriate constructor }
- fs.ReadBuffer(cn, sizeof(cn));
- {$IFDEF DEBUG}
- Form1.Workspace.Lines.Add('ClassName read in is: ' + cn );
- {$ENDIF}
- if cn = 'HorrorBookOb' then
- self.Add(HorrorBookOb.CreateFromStream(cn,fs))
- else if cn = 'RefBookOb' then
- self.Add(RefBookOb.CreateFromStream(cn,fs))
- else if cn = 'SeriesBookOb' then
- begin
- {$IFDEF DEBUG}
- Form1.Workspace.Lines.Add( 'SeriesBookOb read from Stream!' ) ;
- {$ENDIF}
- self.Add(SeriesBookOb.CreateFromStream(cn,fs));
- end
- else
- self.Add(BookOb.CreateFromStream(cn,fs));
- end;
- end;
-
- procedure BookObList.WriteToStream(fs: TFileStream);
- var
- i : integer;
- begin
- fs.WriteBuffer( self.Count, sizeof(self.Count ) );
- for i := 0 to self.Count - 1 do
- BookOb(self[i]).WriteToStream( fs ); { call VIRTUAL method }
- end;
-
- // ...BOOKOBLIST End
-
-
-
- // ===========================================
- // BOOKOB FAMILY TREE Start...
- // ===========================================
-
- // BOOKOB
-
- constructor BookOb.Create( aName, anAuthor : bkstr );
- { a standard BookOb. Call default initialisation, then init the 2 fields }
- begin
- inherited Create;
- cn := self.className; //XXX this is the class name of *actual* bookob type
- {$IFDEF DEBUG}
- Form1.Workspace.Lines.Add( Format('CONSTRUCTOR: BookOb.Create: %s',
- [ cn ]) );
- {$ENDIF}
- name := aName;
- author := anAuthor;
- end;
-
- // alternative constructor.
- // creates itself using data read from stream
- constructor BookOb.CreateFromStream( cname : cnstr; fs: TFileStream);
- begin
- inherited Create;
- {$IFDEF DEBUG}
- Form1.Workspace.Lines.Add( Format('CONSTRUCTOR: BookOb.CreateFromStream: %s',
- [ cname ]) );
- {$ENDIF}
- cn := cname;
- ReadFromStream( fs );
- end;
-
- destructor BookOb.Destroy;
- begin
- {$IFDEF DEBUG}
- Form1.Workspace.Lines.Add( 'DESTRUCTOR: BookOb.Destroy' );
- {$ENDIF}
- name := '';
- author := '';
- cn := '';
- inherited Destroy;
- end;
-
- procedure BookOb.WriteToStream( fs : TFileStream );
- begin
- fs.WriteBuffer(cn, sizeof(cn));
- fs.WriteBuffer(author, sizeof(author) );
- fs.WriteBuffer(name, sizeof(name ) );
- {$IFDEF DEBUG}
- Form1.Workspace.Lines.Add(Format('BookOb.WriteToStream. %s by %s, class= %s',
- [name,author,cn] ));
- {$ENDIF}
- end;
-
- procedure BookOb.ReadFromStream( fs : TFileStream );
- begin
- fs.ReadBuffer(author, sizeof(author) );
- fs.ReadBuffer(name, sizeof(name ) );
- {$IFDEF DEBUG}
- Form1.Workspace.Lines.Add( Format(
- 'In BookOb.ReadFromStream, Book author=%s,name=%s.', [author,name] ));
- {$ENDIF}
- end;
-
- function BookOb.Describe : bkstr;
- begin
- result := Format( '[General Book] %s by %s', [name,author]);
- end;
-
- // HORRORBOOKOB
- constructor HorrorBookOb.Create( aName, anAuthor : bkstr; aGorescore : gorerating );
- { a HorrorBookOb. Call its ancestor (BookOb) constructor, then init gorescore }
- begin
- inherited Create(aName, anAuthor);
- {$IFDEF DEBUG}
- Form1.Workspace.Lines.Add( 'CONSTRUCTOR: HorrorBookOb.Create' );
- {$ENDIF}
- gorescore := aGorescore;
- end;
-
- destructor HorrorBookOb.Destroy;
- begin
- {$IFDEF DEBUG}
- Form1.Workspace.Lines.Add( 'DESTRUCTOR: HorrorBookOb.Destroy' );
- {$ENDIF}
- gorescore := 0;
- inherited Destroy;
- end;
-
- procedure HorrorBookOb.WriteToStream( fs : TFileStream );
- begin
- fs.WriteBuffer(cn, sizeof(cn));
- fs.WriteBuffer(author, sizeof(author) );
- fs.WriteBuffer(name, sizeof(name ) );
- fs.WriteBuffer(gorescore, sizeof(gorescore ) );
- {$IFDEF DEBUG}
- Form1.Workspace.Lines.Add(Format('HorrorBookOb.WriteToStream. %s by %s, class= %s',
- [name,author,cn] ));
- {$ENDIF}
-
- end;
-
- procedure HorrorBookOb.ReadFromStream( fs : TFileStream );
- begin
- fs.ReadBuffer(author, sizeof(author) );
- fs.ReadBuffer(name, sizeof(name ) );
- fs.ReadBuffer(gorescore, sizeof(gorescore ) );
- end;
-
- function HorrorBookOb.Describe : bkstr;
- begin
- result := Format( '[Horror Book] %s by %s. Gore Score: %d',
- [name,author,gorescore]);
- end;
-
- // REFBOOKOB
- constructor RefBookOb.Create( aName, anAuthor, aReftype : bkstr );
- { a RefBookOb. Call its ancestor (BookOb) constructor, then init reftype }
- begin
- inherited Create(aName, anAuthor);
- {$IFDEF DEBUG}
- Form1.Workspace.Lines.Add( 'CONSTRUCTOR: RefBookOb.Create' );
- {$ENDIF}
- reftype := aReftype;
- end;
-
- destructor RefBookOb.Destroy;
- begin
- {$IFDEF DEBUG}
- Form1.Workspace.Lines.Add( 'DESTRUCTOR: RefBookOb.Destroy' );
- {$ENDIF}
- reftype := '';
- inherited Destroy;
- end;
-
- procedure RefBookOb.WriteToStream( fs : TFileStream );
- begin
- fs.WriteBuffer(cn, sizeof(cn));
- fs.WriteBuffer(author, sizeof(author) );
- fs.WriteBuffer(name, sizeof(name ) );
- fs.WriteBuffer(reftype, sizeof(reftype ) );
- {$IFDEF DEBUG}
- Form1.Workspace.Lines.Add(Format('RefBookOb.WriteToStream. %s by %s, class= %s',
- [name,author,cn] ));
- {$ENDIF}
-
- end;
-
- procedure RefBookOb.ReadFromStream( fs : TFileStream );
- begin
- fs.ReadBuffer(author, sizeof(author) );
- fs.ReadBuffer(name, sizeof(name ) );
- fs.ReadBuffer(reftype, sizeof(reftype ) );
- end;
-
- function RefBookOb.Describe : bkstr;
- begin
- result := Format( '[Reference Book] %s by %s. Book type: %s',
- [name,author,reftype]);
- end;
-
-
- // SERIESBOOKOB
- constructor SeriesBookOb.Create(aName, anAuthor: bkstr);
- begin
- inherited Create(aName, anAuthor);
- {$IFDEF DEBUG}
- Form1.Workspace.Lines.Add( 'CONSTRUCTOR: SeriesBookOb.Create' );
- {$ENDIF}
- Volumes := BookObList.Create;
- end;
-
- destructor SeriesBookOb.Destroy;
- begin
- Volumes.FreeObs;
- Volumes.Free;
- inherited Destroy;
- end;
-
- procedure SeriesBookOb.AddVolumes(name, author: bkstr; vols : integer );
- // simply initialise vols number of volumes owned by SeriesBookOb
- var
- i : integer;
- book : BookOb;
- begin
- for i := 1 to vols do
- begin
- book := BookOb.Create( 'Vol: [' + IntToStr(i) + ']' , author );
- Volumes.Add( book );
- end;
- end;
-
- procedure SeriesBookOb.WriteToStream(fs: TFileStream);
- begin
- fs.WriteBuffer(cn, sizeof(cn));
- fs.WriteBuffer(author, sizeof(author) );
- fs.WriteBuffer(name, sizeof(name ) );
- {$IFDEF DEBUG}
- Form1.Workspace.Lines.Add(Format('SeriesBookOb.WriteToStream. %s by %s, class= %s',
- [name,author,cn] ));
- {$ENDIF}
- // write Volumes of BookOb objects
- Volumes.WriteToStream( fs );
- end;
-
-
-
- procedure SeriesBookOb.ReadFromStream(fs: TFileStream);
- begin
- fs.ReadBuffer(author, sizeof(author) );
- fs.ReadBuffer(name, sizeof(name ) );
- {$IFDEF DEBUG}
- Form1.Workspace.Lines.Add( Format('SeriesBook author=%s,name=%s.', [author,name] ));
- {$ENDIF}
- // read Volumes of BookOb objects
- Volumes := BookObList.Create; // first, remember to Create Volumes
- Volumes.ReadFromStream( fs );
- end;
-
-
- function SeriesBookOb.Describe: bkstr;
- var
- vols : string;
- i : integer;
- begin
- vols := 'Volumes = ';
- for i := 0 to Volumes.Count - 1 do
- vols := vols + BookOb(Volumes[i]).Describe+'. ';
- result := Format( '[Series of %d volumes] %s by %s. %s',
- [i, name,author,vols]);
- end;
-
- // ...BOOKOB FAMILY TREE End
-
- end.
-
-
-
-
-