• No results found

Creating forms and projects

In document Component Writer s Guide (Page 170-173)

Delphi comes with a number of form and project wizards already installed, and you can write your own. The Object Repository lets you create static templates that can be used in a project, but a wizard offers much more power because it is dynamic. The wizard can prompt the user and create different kinds of files depending on the user’s responses. This section describes how to write a form or project wizard.

Creating modules

A form or project wizard typically creates one or more new files. Instead of real files, however, it is best to create unnamed, unsaved modules. When the user saves them, the IDE prompts the user for a file name. A wizard uses a creator object to create such modules.

A creator class implements a creator interface, which inherits from IOTACreator. The wizard passes a creator object to the module service’s CreateModule method, and the IDE calls back to the creator object for the parameters it needs to create the module. For example, a form wizard that creates a new form typically implements GetExisting to return false and GetUnnamed to return true. This creates a module that has no name (so the user must pick a name before the file can be saved) and is not backed by an existing file (so the user must save the file even if the user does not make any changes). Other methods of the creator tell the IDE what kind of file is being created (e.g., project, unit, or form), provide the contents of the file, or return the form name, ancestor name, and other important information. Additional callbacks let a wizard add modules to a newly created project, or add components to a newly created form. To create a new file, which is often required in a form or project wizard, you usually need to provide the contents of the new file. To do so, write a new class that

implements the IOTAFile interface. If your wizard can make do with the default file contents, you can return nil from any function that returns IOTAFile.

For example, suppose your organization has a standard comment block that must appear at the top of each source file. You could do this with a static template in the Object Repository, but the comment block would need to be updated manually to reflect the author and creation date. Instead, you can use a creator to dynamically fill in the comment block when the file is created.

The first step is to write a wizard that creates new units and forms. Most of the creator’s functions return zero, empty strings, or other default values, which tells the Tools API to use its default behavior for creating a new unit or form. Override

GetCreatorType to inform the Tools API what kind of module to create: a unit or a

form. To create a unit, return sUnit. To create a form, return sForm. To simplify the code, use a single class that takes the creator type as an argument to the constructor.

C r e a t i n g f o r m s a n d p r o j e c t s

Save the creator type in a data member, so that GetCreatorType can return its value. Implement NewImplSource and NewIntfSource to return the desired file contents.

TCreator = class(TInterfacedObject, IOTAModuleCreator)

public

constructor Create(const CreatorType: string); { IOTAModuleCreator }

function GetAncestorName: string;

function GetImplFileName: string;

function GetIntfFileName: string;

function GetFormName: string;

function GetMainForm: Boolean;

function GetShowForm: Boolean;

function GetShowSource: Boolean;

function NewFormFile(const FormIdent, AncestorIdent: string): IOTAFile;

function NewImplSource(const ModuleIdent, FormIdent, AncestorIdent: string): IOTAFile;

function NewIntfSource(const ModuleIdent, FormIdent, AncestorIdent: string): IOTAFile;

procedure FormCreated(const FormEditor: IOTAFormEditor); { IOTACreator }

function GetCreatorType: string;

function GetExisting: Boolean;

function GetFileSystem: string;

function GetOwner: IOTAModule;

function GetUnnamed: Boolean;

private

FCreatorType: string;

end;

Most of the members of TCreator return zero, nil, or empty strings. The boolean methods return true, except GetExisting, which returns false. The most interesting method is GetOwner, which returns a pointer to the current project module, or nil if there is no project. There is no simple way to discover the current project or the current project group. Instead, GetOwner must iterate over all open modules. If a project group is found, it must be the only project group open, so GetOwner returns its current project. Otherwise, the function returns the first project module it finds, or

nil if no projects are open.

function TCreator.GetOwner: IOTAModule;

var I: Integer; Svc: IOTAModuleServices; Module: IOTAModule; Project: IOTAProject; Group: IOTAProjectGroup; begin

{ Return the current project. }

Supports(BorlandIDEServices, IOTAModuleServices, Svc); Result := nil;

begin

{ Remember the first project module}

if Result = nil then Result := Project;

end

else if Supports(Module, IOTAProjectGroup, Group) then

begin

{ Found the project group, so return its active project} Result := Group.ActiveProject;

Exit;

end; end; end;

The creator returns nil from NewFormSource, to generate a default form file. The interesting methods are NewImplSource and NewIntfSource, which create an IOTAFile instance that returns the file contents.

The TFile class implements the IOTAFile interface. It returns –1 as the file age (which means the file does not exist), and returns the file contents as a string. To keep the

TFile class simple, the creator generates the string, and the TFile class simply passes it

on.

TFile = class(TInterfacedObject, IOTAFile)

public

constructor Create(const Source: string);

function GetSource: string;

function GetAge: TDateTime;

private

FSource: string;

end;

constructor TFile.Create(const Source: string);

begin

FSource := Source;

end;

function TFile.GetSource: string;

begin

Result := FSource;

end;

function TFile.GetAge: TDateTime;

begin

Result := TDateTime(-1);

end;

You can store the text for the file contents in a resource to make it easier to modify, but for the sake of simplicity, this example hardcodes the source code in the wizard. The example below generates the source code, assuming there is a form. You can easily add the simpler case of a plain unit. Test FormIdent, and if it is empty, create a plain unit; otherwise create a form unit. The basic skeleton for the code is the same as

N o t i f y i n g a w i z a r d o f I D E e v e n t s

the IDE’s default (with the addition of the comments at the top, of course), but you can modify it any way you desire.

function TCreator.NewImplSource(

const ModuleIdent, FormIdent, AncestorIdent: string): IOTAFile;

var FormSource: string; begin FormSource := '{ --- ' + #13#10 + '%s - description'+ #13#10 +

'Copyright © %y Your company, inc.'+ #13#10 + 'Created on %d'+ #13#10 +

'By %u'+ #13#10 +

' --- }' + #13#10 + #13#10;

return TFile.Create(Format(FormSource, ModuleIdent, FormIdent, AncestorIdent));

}

The final step is to create two form wizards: one uses sUnit as the creator type, and the other uses sForm. As an added benefit for the user, you can use INTAServices to add a menu item to the File|New menu to invoke each wizard. The menu item’s

OnClick event handler can call the wizard’s Execute function.

Some wizards need to enable or disable the menu items, depending on what else is happening in the IDE. For example, a wizard that checks a project into a source code control system should disable its Check In menu item if no files are open in the IDE. You can add this capability to your wizard by using notifiers, the subject of the next section.

In document Component Writer s Guide (Page 170-173)

Related documents