Rave - Criando componentes compativeis

Top  Previous  Next


        The Rave Application iNterface Technology (RANT) Specification


                         "How to Rant with Rave"



Table of Contents


1.0 - Introduction

  1.1 - Technical Support


2.0 - Creating a Rave Component

  2.1 - Basic Rave Component Classes

  2.2 - Other Important Rave Classes

  2.3 - Example Component

    2.3.1 - Designer-Only Library Files

  2.4 - Registering a Rave Component

    2.4.1 - Registering the component's group

    2.4.2 - Registering the component class

    2.4.3 - Registering the component's properties

    2.4.4 - Creating Component Icons

    2.4.5 - Putting It All Together

  2.5 - Distribution of Components and Editors

    2.5.1 - Unique Component, Editor and File Names

    2.5.2 - Library Files and Delphi/C++Builder Versions

    2.5.3 - Creating Packages for Custom Components or Editors

    2.5.4 - Directory Usage for Compiled Binaries

  2.6 - Using TRaveComponent.OverrideProperties to handle changes in properties

  2.7 - Using the Rave Listener interface

    2.7.1 - Creating a Rave speaker class

    2.7.2 - Creating a Rave listener class

    2.7.3 - Property editors for speaker classes

  2.8 - The TRaveComponent.Changing method

    2.8.1 - Using NotifyChanging procedure at runtime

  2.9 - Hiding and Moving Rave Components in the Visual Designer


3.0 - Interfacing with TRaveProjectManager

  3.1 - Public properties of TRaveProjectManager

  3.2 - Events of TRaveProjectManager

  3.3 - Methods of TRaveProjectManager


4.0 - Interfacing with TRaveDataSystem

  4.1 - Properties of TRaveDataSystem

  4.2 - Events of TRaveDataSystem

  4.3 - Methods of TRaveDataSystem

  4.4 - Global functions related to the Rave data system


5.0 - Interfacing with TRaveDesigner

  5.1 - Properties of TRaveDesigner

  5.2 - Methods of TRaveDesigner


6.0 - Creating a Rave Property Editor

  6.1 Types of property editors

  6.2 Steps for creating a property editor

  6.3 Properties of TRavePropertyEditor

  6.4 Methods of TRavePropertyEditor

  6.5 Virtual methods of TRavePropertyEditor


7.0 - Creating a Rave Component Editor

  7.1 Steps for creating a component editor


8.0 - Creating a Rave Project Editor

  8.1 Steps for creating a menu-based project editor

  8.2 Special notes for creating components at design-time

  8.3 Steps for creating an event-based project editor




1.0 - Introduction


This document is provided to enable programmers to interface with the RAVE

(Report Authoring Visual Environment) Report Designer at a very low level.

This document assumes that you are familiar with normal Delphi component

development. Many of the concepts and principles for interfacing with Rave are

very similar to the code interface of Delphi. If you are already familiar with

Delphi component development, you should still pay close attention to this

document as there are subtle yet important differences that will allow you to

take full advantage of RANT. After reading this document, you should be able

to create custom Rave components, property editors, component editors, project

editors (also know as wizards) and control the Rave environment in a wide

variety of ways.



1.1 - Technical Support


Technical support for items contained within this specification will be

limited to Rave specific features. We cannot provide free technical support

for programming questions which are common to all Delphi components and

classes. We recommend that if you encounter problems with your RANT components

or classes, you first refer to the Delphi Component Writer's Guide manual or

help file, books on Delphi programming or post a message on a public Delphi

forum. An excellent location to post these type of questions is on the Nevrona

Forum where you can communicate directly with other Rave users who may have

already encountered and solved the same problems you are having. For more

instructions on how to join the Nevrona Forum go to http://www.nevrona.com.



2.0 - Creating a Rave Component


One of the most powerful capabilities of Rave is the ability to create and

dynamically install custom reporting components. Every component shipped with

RAVE was created and registered in the same manner as what is available to the

RANT developer. The first step to creating a Rave component is to understand

the basic architecture of Rave's class library.



2.1 - Basic Rave Component Classes


The following three classes, TRaveComponent, TRaveControl and

TRaveContainerControl are the basic Rave classes that most Rave components

descend fromIf you are creating a custom Rave component you should either

descend from an existing rave component or one of these classes. The

relationship of these classes is as follows:




 +-> TRaveComponent


      +-> TRaveControl


           +-> TRaveContainerControl


TRaveComponent (RVClass.pas) - This is the base class for all Rave components

  and descends directly from Delphi's TComponent. TRaveComponent classes are

  non-visual in nature and thus should only be used for items which will not

  be displayed on the page designer or the report. However, the Rave user will

  still be able to select a TRaveComponent class and modify its properties

  through the Project Tree.


TRaveControl (RVClass.pas) - This class descends from TRaveComponent and is

  the base class for all visual components.  All TRaveControl classes have

  Left, Top, Width, Height, Anchor, Mirror and DisplayOn properties (although

  they may not be visible on all components).  TRaveControl classes display

  themselves through both the Paint method (for the visual designer) and the

  Print method (for the printed output).


TRaveContainerControl (RVClass.pas) - This class descends from TRaveControl

  and adds basic parent-child functionality for components that will contain

  other components. Examples of TRaveContainerControl classes are TRaveBand

  and TRaveSection. Note that the parent-child methods and properties are

  defined at the TRaveComponent level and may be overriden from any class

  level (TRaveContainerComponent for example), however, if you are creating a

  visual container component you normally will want to descend from this




2.2 - Other Important Rave Classes


These classes are used by many of Rave's editor and project management

components. You will not normally need to descend your components from these

classes, however, you will often interface with components of these class types

to perform the tasks that you need to.




 +-> TRaveComponent


      +-> TRaveDataField


      +-> TRaveContainerComponent


           +-> TRaveProjectItem


                +-> TRavePage


                +-> TRaveDataView


                +-> TRaveReport


                +-> TRaveProjectManager


TRaveContainerComponent (RVClass.pas) - This class descends from

  TRaveComponent and adds basic parent-child functionality similar to

  TRaveContainerControl. This class is used by Rave's TRaveProjectItem class.

  If you are creating a visual component that will contain other components,

  you should descend from the TRaveContainerControl class instead.


TRaveProjectItem (RVClass.pas) - This is the base class for all of Rave's

  project oriented classes including TRavePage, TRaveDataView, TRaveReport and

  TRaveProjectManager. This class descends from TRaveContainerComponent and

  adds a basic streaming and active interface.


TRavePage (RVClass.pas) - This class descends from TRaveProjectItem and is the

  class used to define each page of a report. All components on a page are

  owned by the page and can be accessed through the pages Components and

  ComponentCount properties. There are two types of TRavePage components in a

  Rave project, Report and Global. Report pages belong to a specific report

  while Global pages are available to the whole system. The Global boolean

  property of TRavePage can be used to determine which category a TRavePage

  class instance falls into.


TRaveDataView (RVData.pas) - This class descends from TRaveProjectItem and

  is the class used to define a data view. The TRaveDataView class is the

  owner and parent for all TRaveDataField components belonging to that data

  view. This class is discussed in further detail in the section "Interfacing

  with TRaveDataSystem".


TRaveDataField (RVData.pas) - This class descends from TRaveComponent and

  is the base class for all data field classes (TRaveIntegerField,

  TRaveStringField, ...). A TRaveDataField component will always be owned and

  parented by the TRaveDataView class that defines the data view that it is a

  part of.  This class and it's descendents is where your components will

  interface with the data that's available through the data views. This class

  is discussed in further detail in the section "Interfacing with



TRaveReport (RVProj.pas) - This class descends from TRaveProjectItem and

  is the class used to define all Rave reports. The TRaveReport class is the

  owner and parent of all report pages that belong to that report but does not

  own or parent any global pages even though they may be visible in the page

  designer for that report. The TRaveReport class is also the main starting

  point to print reports through the Execute method.


TRaveProjectManager (RVProj.pas) - This class descends from TRaveProjectItem

  and is the class that manages all components in a report project.

  TRaveProjectManager is the owner and parent of all TRaveReport, global

  TRavePage and TRaveDataView classes. This is the main interface to load,

  save, import to and export from a report project. This is also the main

  interface that you will use to find a specific report or other component.

  This class is discussed in further detail in the section "Interfacing with




2.3 - Example Component


For this sample we'll create a simple text component that presets the font

color to blue when it is created.  First you'll want to create a unit

containing your component class definition.  This is very similar to the steps

you would follow for creating a normal Delphi component.  Here's the source for

our text component that overrides the default color:


  unit NDCsBlue;





    Classes, Graphics, RVClass, RVCsStd;



    TNDBlueText = class(TRaveText)


      constructor Create(AOwner: TComponent); override;

    end; { TNDBlueText }




    constructor TNDBlueText.Create(AOwner: TComponent);


    begin { Create }

      inherited Create(AOwner);

      FFont.Color := clBlue;

    end;  { Create }





2.3.1 - Designer-Only Library Files


To save size in compiled applications, certain properties and methods are only

available when compiling for the visual designer.  This is accomplished with

the DESIGNER compiler directive and is used throughout the Rave classes. For

example, the Paint method is used by the visual designer to display a

component to the screen and so it must be defined within {$IFDEF

DESIGNER}...{$ENDIF} directives.  The Paint method, however, is not needed

by a normal Delphi application and so it's code is not included in the



These designer-only files are located in the C:\Rave4\RV directory and can be

recompiled with the C:\Rave4\Source\RaveD5.BAT file.  When compiling a

package for inclusion in the Rave designer, the file in the C:\Rave4\RV

directory should be referenced as the Rave library file.



2.4 - Registering a Rave Component


After you've created your Rave component, it's time to register it with the

Rave system.  The first step is to create a global procedure called

RaveRegister.  The spelling and capitialization must match exactly or it will

not work.  The RaveRegister procedure will be called when your component

package is added to the Rave system to register your components or other

editors.  Inside of the RaveRegister procedure you will want to call

RegisterRaveGroup, RegisterRaveComponents and RegisterRaveProperties.



2.4.1 - Registering the component's group


The RegisterRaveGroup procedure creates a component group for your component

and must be called before you register the component class.  The first

parameter is the name of the group and the second parameter is the full name.

Currently in the Rave designer, each group is stored in a dockable toolbar and

the full name is used as the caption for the toolbar.  The following source is

an example of RegisterRaveGroup being called to create a group called ABC with

a caption of "ABC Components".


  RegisterRaveGroup('ND','ND Components');



2.4.2 - Registering the component class


The RegisterRaveComponents procedure actually registers your component class

with the Rave system, loads the component icons and creates a component button

on the group toolbar.  The first parameter is the name (not the full name) of

the group that you want to add this component to and the second is an array of

component classes that you want to register.  All classes that are registered

must descend from TRaveComponent or one of it's descendents.  The following

source is an example of RegisterRaveComponents being called to create two

components, TNDHeaderText and TNDFooterText.





2.4.3 - Registering the component's properties


The RegisterRaveProperties procedure registers the published properties for

your components and must be called after the component class is registered.

With this procedure you can determine which properties are visible to Beginner

or Intermediate level users, which properties are Developer only, and which

properties are hidden.  The first parameter is the component class you are

registering the properties for.  The second and third parameters are a list of

properties that are available to Beginner and Intermediate level properties.

By default, all published properties are available to Advanced level users.

Any properties registered in the Intermediate properties are available to

Intermediate and Advanced level users while any properties registered in the

Beginner properties are available to all level users.  Developer level

properties are set with the fourth parameter and will be visible in the

programmer version of the Rave designer and not visible in the end-user

version of the Rave designer.  The last parameter defines properties that are

to be hidden to all users and can be used to hide properties that were visible

in a ancestor class.  The following source is an example of

RegisterRaveProperties being called for the TRaveDataText component.



     {Beginner}     'DataField;DataView',

     {Intermediate} 'LookupDataView;LookupField;LookupDisplay',

     {Developer}    '',

     {Hidden}       'Text');



2.4.4 - Creating Component Icons


Each component has a 24x24 pixel icon that is used to represent it.  In the

visual desinger, this icon is placed on a button that is used to create the

component.  There is also a 16x16 pixel icon that is used to represent the

component in the Project Tree.  Lastly, there is a 24x24 pixel icon that each

component group (see section 2.4.1 below) uses to represent itself.  In the

visual designer, this icon is used on a button on the Toolbar toolbar to show

or hide the component group toolbar.  The name of the large component icon

must match the name of the component class.  The small component icon must

match the name of the component class plus the characters '16' following it.

The component group icon must match the name (not the full name) of the

component group.  So for our example with TNDBlueText in the ND component

group, we would create bitmap resources named TABCBlueText (24x24 pixel

component icon), TNDBlueText16 (16x16 pixel component icon) and ND (24x24

pixel component group icon).  These bitmap resources will normally be included

in a resource file (*.res) with the same name as the unit containing the

component class.  To register the icons with the unit, include the following

source immediately after the implementation keyword in the unit containing the

component class.



  {$R *.RES}




2.4.5 - Putting It All Together


Certainly there are plenty of examples of components in the Rave source. There

is also a special web page available on the Nevrona Designs web site that

contains other useful components (available shortly after release).  The

following is a complete example of a component and the RaveRegister procedure.


unit NDCsBlue;





  RVClass, RVCsStd;



  TNDBlueText = class(TRaveText)


    constructor Create(AOwner: TComponent); override;

  end{ TNDBlueText }


  procedure RaveRegister;





{$R *.RES}



  procedure RaveRegister;


  begin { RaveRegister }

    RegisterRaveGroup('ND','ND Components');





     {Beginner}     '',

     {Intermediate} '',

     {Developer}    '',

     {Hidden}       '');

  end;  { RaveRegister }


  constructor TNDBlueText.Create(AOwner: TComponent);


  begin { Create }

    inherited Create(AOwner);

    FFont.Color := clBlue;

  end;  { Create }





2.5 - Distribution of Components and Editors


Now that you've created the source for your component, you will need to

compile it into a package and other formats depending upon how it is to be




2.5.1 - Unique Component, Editor and File Names


As with any library file, you must uniquely name your items to avoid conflicts

with items distributed from other authors.  Any Rave components or editors

distributed with Rave will be located in files beginning with "RP" or "RV".

It is recommended that you select a unique prefix of at least 2 characters

that will uniquely identify any files that are yours.


You should also apply this uniqueness when creating component and editor

classes.  All Rave components or editors distributed with Rave will be named

with the prefix "TRave".  It is recommended that you select a unique prefix

that will uniquely identify any classes that are yours.


The following are guidelines that we have found to be a succesful:


- Avoid long file names.  Even though Rave is currently only 32-bit, Delphi

1.0 support is planned for the near future and this will cause problems if you

are creating a Rave component.  Also, certian DOS based compression software

and even some network operating systems do not handle long file names very



- Select a 2 character unique identifier (e.g. your initials) and use the

following naming scheme for the type of item contained in each file (assume ND

is the identifier you have chosen and Xxxx is a unique identifier of the item):


  * Components: NDCsXxxx.pas

  * Wizards (Project Editors): NDWzXxxx.pas

  * Property Editor: NDPEXxxx.pas

  * Component Editors: NDCEXxxx.pas

  * Packages: ND_Xxxxx.dpk


These naming conventions keep file names at or under 8 characters and will

help users of your Rave add-ons to understand what is in each file.



2.5.2 - Library Files and Delphi/C++Builder Versions


The 3.0 version of the Rave designer requires that all packages to be

installed within it, are compiled with Delphi 4.0.  For designer only items

such as property editors or report wizards, this is typically the only form

that will need to be compiled.


For components, however, this is only one of the forms that the source needs

to be compiled for.  When an application prints a report that contains a

custom component, the component code itself needs to be compiled in the

application.  So, for example, if a Delphi 2.0 user uses a component that you

have written, you will need to provide either .PAS or .DCU files to allow them

to compile the component code into their application.



2.5.3 - Creating Packages for Custom Components or Editors


To compile a package containing custom Rave components or editors you will

either need to use Delphi 4.0 IDE to create a new package (Component| Install

Componet...) or the recommended method of compiling the package with the

command line compiler.  If you decide to use the Delphi 4.0 IDE, you need to

make sure that you only include your own custom units.


The recommended method to compile packages for the Rave designer is to create

a .DPK file and use the command line compiler.  This is a cleaner method than

using the IDE package tools since Delphi does several things that are geared

more for it's own packages.  The following an example of the .DPK file for our

example component.  Notice that we include any packages containing units

required by our components in the Requires clause.  The Contains clause lists

the units containing the custom components or editors and the Description

compiler directive defines the name of the package that will be displayed in

the designer's Edit|Preferences...|Packages dialog.


  package ND_Tools;



    VCL40, RVCL30, RVCLS30;





It is also useful to create a batch file to compile your custom package.  The

following is a sample of the batch file that can be used to compile our

example component.  This batch file is designed to be used from a directory

under C:\Rave4 such as C:\Rave4\Custom.  Notice the DESIGNER compiler

directive that is added to compile any code that is for the visual designer.

Also notice the inclusion of the designer-only units in the RV directory

(/U..\RV compiler option).



Contents of RANTCOMP.BAT


  @echo off

  if "%NDD4%"=="" goto endsetup

  REM ********************

  REM Compile Rave Package

  REM ********************


  REM Usage: RANTCOMP packagename


  REM Note: The packagename parameter should not include the .dpk extension


  REM Desc: This batch file will compile a package containing a RAVE add-on

  REM       and place the .BPL in the parent directory (normally C:\Rave4).

  REM       It is meant to be run from a directory under Rave4 such as

  REM       C:\Rave4\RANT. The Rave library files must reside in C:\Rave4\RV.


  REM ***************************************************

  %NDD4%\bin\dcc32 %1.dpk /b /h /w /LE.. /U..\RV -$d-l-n+p+r-s-t-w- /DDESIGNER

  if errorlevel 1 goto enderror

  del *.dcu >nul

  del %1.dcp >nul

  goto endok


  echo Error!

  goto endok


  echo You will need to define a variable in your AUTOEXEC.BAT file

  echo called NDD4 that points to your Delphi 4.0 directory and reboot

  echo before using this batch file.  Example:

  echo   SET NDD4=C:\Program Files\Borland\Delphi4




2.5.4 - Directory Usage for Compiled Binaries


There are several binary directories under the C:\Rave4 main directory.  The

following is a list of these directories and how to rebuild them.



Directory         | Description


C:\Rave4\D4       | Delphi 4.0 binary files.  Run

                  | C:\Rave4\Source\FullD4.BAT to rebuild.


C:\Rave4\D5       | Delphi 5.0 binary files.  Run

                  | C:\Rave4\Source\FullD5.BAT to rebuild.


C:\Rave4\D6       | Delphi 6.0 binary files.  Run

                  | C:\Rave4\Source\FullD6.BAT to rebuild.


C:\Rave4\C4       | C++Builder 4.0 binary files.  Run

                  | C:\Rave4\Source\FullC4.BAT to rebuild.


C:\Rave4\C5       | C++Builder 5.0 binary files.  Run

                  | C:\Rave4\Source\FullC5.BAT to rebuild.


C:\Rave4\C6       | C++Builder 6.0 binary files.  Run

                  | C:\Rave4\Source\FullC6.BAT to rebuild.


C:\Rave4\RV       | Designer-only Rave files.  Run C:\Rave4\Source\RaveD5.BAT

                  | to rebuild.  These files should be used instead of the

                  | other binary files when compiling a package for inclusion

                  | into the Rave designer.




2.6 - Using TRaveComponent.OverrideProperties to handle changes in properties


The OverrideProperties method is an easy way to intercept the normal reading

and writing of your components properties. This is normally done to maintain

backwards compatibility with existing project files that contain property data

in a older format or under a different name. It may also be used to write data

that does not fit into the normal types such as graphics files or record



The first thing you will want to do is override the OverrideProperties method

in your component class and call the Filer.OverrideProperty method for each

property that you want to override.


  procedure TMyRaveComponent.OverrideProperties(Filer: TRaveFiler);


  begin { OverrideProperties }


  end;  { OverrideProperties }


You will also need to define a read and write method that will be called when

a property of name, 'MyProperty', is encountered for your component type.


  procedure TMyRaveComponent.ReadMyProperty(Reader: TRaveReader);


  begin { ReadMyProperty }

  // code to read MyProperty from Reader.StreamHandler

  end;  { ReadMyProperty }


  procedure TMyRaveComponent.WriteMyProperty(Writer: TRaveWriter);


  begin { WriteMyProperty }

  // code to write MyProperty to Writer.StreamHandler

  end;  { WriteMyProperty }


There are many different methods of the TRaveWrite and TRaveReader classes

(defined in RVCLASS.PAS) and you should become familiar with their operation

before continuing.


Here's an example of an override that was needed because a property changed

from a TStrings type to a string type:


  procedure TMyRaveComponent.ReadDescription(Reader: TRaveReader);



    TempStrings: TStrings;

    ValueKind: TValueKind;


  begin { ReadDescription }

    With Reader do begin

      ValueKind := TValueKind(StreamHelper.ReadByte);

      Case ValueKind of

        vkPropList: begin { Read a TStrings object }

          TempStrings := TStrings.Create;



            Description := TempStrings.Text;



          end{ tryf }

        vkString: begin { Read a normal string }

          Description := StreamHelper.ReadString;


      end{ case }

    end{ with }

  end;  { ReadDescription }


  procedure TMyRaveComponent.OverrideProperties(Filer: TRaveFiler);


  begin { OverrideProperties }


  end;  { OverrideProperties }


Notice that we didn't define a write method for this property override. That

is because we want the normal output format to be written so there is no need.


Rave also supports Delphi's TPersistent.DefineProperties interface for writing

custom data to the stream but this is more limiting than Rave's

OverrideProperties interface and should be used if you only want to read and

write a custom format for your data.



2.7 - Using the Rave Listener Interface


A special interface has been built into all Rave components to allow them to

communicate with each other.  You do this by defining methods in a component

class to make it a speaker of a specific conversation and then registering

another class as the listener of that class.  Note that it is the listener

object's responsibility to add itself to the conversation of a speaker object

at runtime.  An example of where this is used in Rave is the TRaveCalcText

component (the listener class) and the TRaveDataBand component (the speaker

class).  The conversation name is 'CalcNewData' and allows DataBand components

to notify CalcText components when to perform calculations.  The CalcText

components adds itself as a listener to a specific DataBand defined by it's

Controller property.  Now that this conversation has been defined, it would be

possible to create other speakers (as is the case for the component,

TRaveCalcController).  You could also create other listeners (as is the case

for the component, TRaveCalcTotal).



2.7.1 - Creating a Rave speaker class


For this example we'll create a conversation type called ABC and create a

simple speaker class. At a minimum, the following items must be defined in the

speaker class:


    TRaveSpeakerClass = class(TRaveControl)


      ABCListenList: TRaveMethodList;


      procedure Changing(OldItem: TRaveComponent;

                         NewItem: TRaveComponent); override;


      procedure AddListener(Conversation: string;

                            ListenMethod: TRaveListenEvent); override;

      procedure RemoveListener(Conversation: string;

                               ListenMethod: TRaveListenEvent); override;

      function Habla(Conversation: string): boolean; override;

    end{ TRaveSpeakerClass }


1: ABCListenList field: A listener list, of type TRaveMethodList, to keep

   track of all listeners. There needs to be a seperate listener list for each

   conversation that the speaker class can speak.


2: AddListener method: Override this method to add a listener to a

   conversation (listener list).  Since the speaker class can be a speaker of

   several conversations, the Conversation parameter should be checked to add

   it to the proper listener list.  Even if the speaker class only speaks one

   conversation, the Conversation parameter should still be checked since

   there is nothing stopping a listener class from attempting it add itself to

   a conversation that doesn't exist.


    procedure TRaveSpeakerClass.AddListener(Conversation: string;

                                            ListenMethod: TRaveListenEvent);


    begin { AddListener }

      inherited AddListener(Conversation,ListenMethod);

      If CompareText(Conversation,'ABC') = 0 then begin

        If not Assigned(ABCListenList) then begin

          ABCListenList := TRaveMethodList.Create;

        end; { if }


      end; { if }

    end;  { AddListener }


3: RemoveListener method: Override this method to remove a listener from a

   conversation (listener list).  The same rules specified above for checking

   the Conversation parameter apply to RemoveListener as well.


    procedure TRaveSpeakerClass.RemoveListener(Conversation: string;

                                               ListenMethod: TRaveListenEvent);


    begin { RemoveListener }

      inherited RemoveListener(Conversation,ListenMethod);

      If (CompareText(Conversation,'ABC') = 0) and

       Assigned(ABCListenList) then begin


      end; { if }

    end;  { RemoveListener }


4: Habla method: Override this method to notify listener classes whether the

   speaker class can speak a particular conversation.  Conversation names

   should not be case-sensitive so it is best to use the CompareText function

   when determining a match.


    function TRaveSpeakerClass.Habla(Conversation: string): boolean;


    begin { Habla }

      Result := CompareText(Conversation,'ABC') = 0;

    end;  { Habla }


5: Changing method: Override this method to track when a listner component has

   been deleted or replaced.  See section 2.8 for more information in how to

   use the Changing method.


    procedure TRaveSpeakerClass.Changing(OldItem: TRaveComponent;

                                         NewItem: TRaveComponent);


    begin { Changing }

      inherited Changing(OldItem,NewItem);

      If Assigned(ABCListenList) and Assigned(OldItem) then begin

        If Assigned(NewItem) then begin


        end else begin


        end; { else }

      end; { if }

    end;  { Changing }


6: Speak method: Call this method whenever the speaker class wants to send a

   message to the listener objects.  The first parameter is the listener list

   and the second parameter is the message.  The structure of the Msg

   parameter is defined by the type of conversation being spoken.  For the

   CalcNewData conversation, no data is needed so the Msg parameter is nil

   instead of an actual object.  For more complicated conversations, actual

   objects could be sent to transfer more information.


    procedure Speak(List: TRaveMethodList; Msg: TObject);



2.7.2 - Creating a Rave listener class


Continuing with the previous example of the ABC conversation, we'll now

create a simple listener class. At a minimum, a listener method must be

defined in the listener class.  The format for this method must match as

follows (although the name of the method can and should be changed).


    TRaveListenerClass = class(TRaveControl)


      FSpeakerObj: TRaveComponent;


      procedure ABCListen(Speaker: TRaveComponent;

                          Msg: TObject);

      procedure SetSpeakerObj(Value: TRaveComponent);


      property SpeakerObj: TRaveComponent read FSpeakerObj write SetSpeakerObj;

    end{ TRaveListenerClass }


1: The listener object must add itself to a specific conversation by calling

   the AddListener() method of a speaker class object:




2: The listener object should make sure to remove itself from a conversation

   if it no longer wants to receive messages from a specific speaker:




3: Typically, both of these calls can be combined into a single method for

   setting the property containing the reference to the speaker object.


    procedure TRaveListenerClass.SetSpeakerObj(Value: TRaveComponent);


    begin { SetSpeakerObj }

      If Value = FSpeakerObj then Exit;

      If Assigned(FSpeakerObj) then begin


      end{ if }

      FSpeakerObj := Value;

      If Assigned(FSpeakerObj) then begin


      end{ if }

    end;  { SetSpeakerObj }


4: The code that is inside of the ABCListen method will be executed whenever

   the speaker object calls the Speak() method for the 'ABC' conversation. The

   speaker object will be passed as the first parameter and the message will

   be passed as the second parameter.  The structure of the Msg parameter is

   defined by the type of conversation being spoken.  For the CalcNewData

   conversation, no data is needed so the Msg parameter is nil instead of an

   actual object.  For more complicated conversations, actual objects could be

   sent to transfer more information.



2.7.3 - Property editors for speaker classes


Since several different classes can qualify as speakers for a conversation,

the listener component should use the Habla method to determine if a component

is a valid speaker rather than checking for specific class types.  The

following is a sample property editor for our sample speaker and listener

classes.  By using the Include function of the TRaveComponentPropertyEditor

class, only component that speak the ABC conversation will be listed in the

drop-down list for the TRaveListenerClass.SpeakerObj property.



  TRaveABCSpeakerPropertyEditor = class(TRaveComponentPropertyEditor)


    function Include(Value: TComponent;

                     Data: longint): boolean; override;

  end{ TRaveABCSpeakerPropertyEditor }



  procedure RaveRegister;


  begin { RaveRegister }



  end;  { RaveRegister }


  function TRaveABCSpeakerPropertyEditor.Include(Value: TComponent;

                                                 Data: longint): boolean;


  begin { Include }

    Result := (Value is TRaveComponent) and TRaveComponent(Value).Habla('ABC');

  end;  { Include }



2.8 - The TRaveComponent.Changing method


The Changing method allows components to respond to components that are being

added, deleted or replaced during design-time.  This is similar to Delphi's

Notification method but is done a little differently.  For component

additions, OldItem will be nil and NewItem will be the component being added.

For component deletions, OldItem will be the component being deleted and

NewItem will be nil.  For component replacements (ususally done during

imports), OldItem will be the component being replaced and NewItem will be the

component that should replace OldItem.  If the component class keeps

references to other components, it should check to see if those references

match deleted or replaced components through the Changing method and take

appropriate actions so that invalid component references are not called.


  procedure TSampleClass.Changing(OldItem: TRaveComponent;

                                  NewItem: TRaveComponent);


  begin { Changing }

    inherited Changing(OldItem,NewItem);

    If FComponentReference = OldItem then begin

      FComponentReference := NewItem; { Handles deletes and replaces }

    end; { if }

  end;  { Changing }


2.8.1 - Using NotifyChanging procedure at runtime


If you are deleting a Rave component at runtime, you will need to notify the

other components in the same report project of this change or they attempt to

reference an object that no longer exists.  This is done through the

NotifyChanging procedure.


  procedure NotifyChanging(OldItem: TRaveComponent;

                           NewItem: TRaveComponent);


For example, if you are going to delete a dataview you would call:


  DataView1 := RaveProject1.ProjMan.FindRaveComponent('ObseleteDataView',nil);


  RaveProject1.ProjMan.DeleteItem(DataView1); // Delete after notifying


If you are going to delete a normal component you would call:


  Page1 := RaveProject1.ProjMan.FindRaveComponent('CustReport.Page1',nil);

  Text1 := RaveProject1.ProjMan.FindRaveComponent('Text1',Page1);


  Text1.Free; // Free after notifying


What this is doing above is telling all components that the component

referenced in OldItem is going to be changed to NewItem (which is nil in these

examples).  Not that within Rave at design-time, this notification is normally

handled automatically.


If you want to replace a component with another, you can also use

NotifyChanging.  This will automatically change all references from the

old to the new dataview and keep the links intact.  If you simply

deleted the old dataview and then added a new one, any components such

as databands or datatexts would no longer be pointing to a dataview.  Here's an

example of replacing one dataview with another:


  OldDataView := RaveProject1.ProjMan.FindRaveComponent('ObseleteDataView',nil);

  NewDataView := CreateDataView(CreateDataCon(RPDataSetConnection1));


  RaveProject1.ProjMan.DeleteItem(OldDataView); // Delete after notifying



2.9 - Hiding and Moving Rave Components in the Visual Designer


If you want to hide or move certain Rave components to another toolbar there

are several ways to do this.  The most difficult is to actually modify the

RaveRegister procedure to either remove the component reference or assign it

to a different component group (toolbar).  However, a simpler method exists

that will allow you to customize the components contained in the Rave designer

for yourself or your end-users.  This is done through the Windows registry.

To hide a component, set the following string value to '1':


HKEY_CURRENT_USER\Software\Nevrona Designs\Rave\Components\TRaveBitmap\Hidden


Obviously this string is for the TRaveBitmap component.  If you want to hide

another component, simply replace TRaveBitmap with the full class name of the

component.  Setting Hidden to any value other than '1' or not having a Hidden

string value will cause the component to appear in the visual designer.  Note

that reports containing hidden components can still be loaded, edited and

printed.  The resourceful user could create additional, 'hidden' components

simply by copying and pasting any that already exist in the report.


To move a component to another group (toolbar), set the following string value

to the name of the destination group (i.e. Report, Standard,...).


HKEY_CURRENT_USER\Software\Nevrona Designs\Rave\Components\TRaveBitmap\Group


Once again, this is an example of the string value for TRaveBitmap.  If you

want to move another component, simply replace TRaveBitmap with the full class

name of the component.



3.0 - Interfacing with TRaveProjectManager


The Rave project manager class is the owner of all report, global page and

data view components in a reporting project.  It provides many methods and

properties to interface with the reporting project.


If you need to interface with the project manager from within a component

or editor, the global variable, ProjectManager of type TRaveProjectManager,

provides what you need. To access the ProjectManager variable you must include

the unit RVProj in your uses clause.


If you are interfacing with the project manager from within a Delphi or

C++Builder application you will want to access the TRaveProject.ProjMan

property instead of the global variable, ProjectManager.  This is because

multiple projects may be open at the same time.



3.1 - Public properties of TRaveProjectManager


  property ReportList: TList (read only)

  property GlobalPageList: TList (read only)

  property DataViewList: TList (read only)


  - These properties provide access to the reports, global pages and

    dataviews of the report project. Since ProjectManager owns all of

    these components, you can also access these components through the

    Components property just like any other Delphi component.


  property ActiveReport: TRaveReport (read only)


  - This property returns the currently active report. To activate a new

    report use the ActivateReport method.


  property DataChanged: boolean (read/write*)


  - DataChanged should be set to true whenever a change is made to the

    report project. Note that once DataChanged is set to true, it will only

    be set to false again when the project is saved or by calling the

    ClearChanged method.


  property Printing: boolean (read only)


  - Printing will be true if a report is currently being printed.


  property Version: integer (read only)


  - This property is the version of the currently loaded project. Once the

    project is saved it will be automatically upgraded to the current version.


  property FileName: string (read/write)


  - This property is the name of the file used by the Save and Load methods.


  property StreamParamValues: boolean (read/write)


  - This property should not normally be used.  It is used internally to

    transfer parameter values to the end-user Rave designer.


  property Saved: boolean (read/write)


  - This property can be used to tell if the report project has been saved to

    the disk or not.  When a project is loaded from disk or after is has been

    saved to disk, Saved will be true.


  property Categories: TStrings (read/write/pub)


  - This property defines the available report categories for the report

    project. Each line defines a name for a separate report category.


  property Parameters: TStrings (read/write/pub)


  - This property defines the project parameters for the report project.  Each

    line defines a parameter name and values can be set or retrieved using

    the SetParam and GetParam methods.


  property Units: TPrintUnits (read/write/pub)


  - This property defines the units that will be used across the reporting

    project. Valid values include unInch, unMM, unCM, unPoint and unUser. When

    changing Units to values other than unUser, UnitsFactor will be set to the

    appropriate value.  unUser allows custom values of the UnitsFactor

    property to handle any kind of units conversion.


  property UnitsFactor: TRaveFloat (read/write/pub)


  - This property defines the relationship between the units used across the

    report project and inches.  For example, when using a Units value of unMM

    (millimeters), UnitsFactor would be 25.4 (25.4 mm per inch).  Custom unit

    conversions can be set by setting this property to any value desired.



3.2 - Events of TRaveProjectManager


  TImportConflictEvent = procedure(    CurrentItem: TRaveProjectItem;

                                   var ImportName: string) of object;


  property OnImportConflict: TImportConflictEvent;


  - This event will be called whenever a naming conflict is encountered

    during an import.  This will allow you to change the name of the

    imported component to a unique name.



3.3 - Methods of TRaveProjectManager


  function NewReport: TRaveReport;

  function NewGlobalPage: TRavePage;

  function NewDataView: TRaveDataView;


  - These methods can be used to create a new report, global page or data

    view to the report project. The new item will be returned as the result of

    the functionFor creating dataviews dynamically at runtime you should use

    the CreateDataView function which is covered in section 4.4.


  procedure New;


  - New will create a new report project.


  procedure Save;


  - Save will save the report project to the file specified by the FileName



  procedure Load;


  - Load will load the report project from the file specified by the FileName



  procedure Unload;


  - Unloads all items from current project.


  procedure LoadFromStream(Stream: TStream);


  - Loads a report project from Stream.  The Unload method should be called

    before LoadFromStream is called.


  procedure SaveToStream(Stream: TStream);


  - Save the report project out to Stream.


  procedure ExportProject(ExportFileName: string;

                          Items: TList);


  - Creates an export report project containing the items listed in Items.

    Only Reports, Global Page and Data Views should be referenced in Items.


  function ImportProject(ImportFileName: string;

                         AutoReplace: boolean): boolean;


  - Imports an exported report project into the current project.  AutoReplace

    will determine if all duplicate items are automatically replaced instead

    of using the OnImportConflict event.


  procedure DeactivateReport;


  - Deactivates the current report.


  procedure ActivateReport(Report: TRaveReport);


  - Activates Report as the current report.


  function FindRaveComponent(Name: string;

                             DefRoot: TRaveComponent): TRaveComponent;


  - This function can be used to find a component of a given name.  The

    Name parameter can include the owner name as well as the component name

    (e.g. "Report1.Page2") with the DefRoot parameter set to nil.  The other

    method of calling FindRaveComponent is to pass the component name in the

    Name parameter and the owner component in the DefRoot parameter.


    NOTE: The ProjectManager component owns all reports, global pages and

    dataviews.  Report components own their report pages and all page

    components are the owner for all components contained within.


  function GetUniqueName(BaseName: string;

                         NameOwner: TRaveComponent;

                         UseCurrent: boolean): string;


  - This function will calculate a unique name for a component.  This can be

    useful when creating components within a report wizard.  The BaseName

    property is the starting point used for the component name.  NameOwner is

    the owner component that will be used to search for naming conflicts.

    UseCurrent tells the function whether to attempt to use BaseName first

    before appending unique suffixes.  When UseCurrent is true, the BaseName

    parameter should consist of the class base name appended to the desired

    name separated by a '|' character.  So for a text component that you

    want to name 'MyText' you would pass the string 'MyText|Text' as the

    BaseName parameter and UserCurrent set to true.




    TxtComp.Name := ProjectManager.GetUniqueName('Text',PageComp,false);


    TxtComp.Name := ProjectManager.GetUniqueName('ReportTitle|Text',PageComp,true);


  procedure DeleteItem(Item: TRaveProjectItem;

                       Notify: boolean);


  - This method is used to delete TRaveProjectItem components such as reports,

    global pages and dataviews from the report project.  In most cases,

    Notify should be set to true to notify the designer of the deleted item.


  procedure SetParam(Param: string;

                     Value: string);


  - This procedure set the project parameter, Param, to the text specified in



  function GetParam(Param: string): string;


  - This function will return the value of the project parameter, Param.


  procedure ClearChanged;


  - The procedure should be called to set the DataChanged property to false.

    This procedure should be called with caution since it may cause changes

    to the report project to not be saved.



4.0 - Interfacing with TRaveDataSystem


The Rave data system is controlled by the TRaveDataSystem class and the

RaveDataSystem global variable. Through the RaveDataSystem object you can

interface with data connection components to request actions or retrieve data.

The RaveDataSystem interface provides more flexibility but is also more

complicated. For most cases, the functionality provided by the TRaveDataView

component is sufficient and is much simpler.



4.1 - Properties of TRaveDataSystem


  property RTConnectList: TStringList (read only)


  - This is a list of all runtime data connection names.  A runtime data

    connection is one that exists in a running application.  The Object array

    property of RTConnectList contains the TRaveDataConnection object that

    contains additional information about the runtime data connections.


  property DTConnectList: TStringList (read only)


  - This is a list of all design-time data connection names.  A design-time

    data connection is one that exists on a form currently loaded in Delphi

    or C++Builder.  The Object array property of DTConnectList contains the

    TRaveDataConnection object that contains additional information about

    the design-time data connections.



4.2 - Events of TRaveDataSystem


  TRaveDataResult = (drContinue,drAbort,drPause);

  TTimeoutEvent = procedure(    DataSystem: TRaveDataSystem;

                                Counter: integer;

                                Timeout: integer;

                                EventType: integer;

                                Connection: string;

                                First: boolean;

                            var DataResult: TRaveDataResult) of object;


  property OnSmallTimeout: TTimeoutEvent

  property OnLargeTimeout: TTimeoutEvent


  - These events are called when a timeout occurs while communicating with

    a data connection.  The small timeout event will be encountered first

    and should serve as a warning that communication has stalled.  This may

    occur because of the data connection performing some type of action

    (i.e. opening a table).  The large timeout event will be called after

    the delay is longer than the configured maximum and the user should then

    be prompted for action.  The DataSystem parameter is the current

    TRaveDataSystem object that is being used.  Counter is the current timing

    count (in 1/100ths of a second).  Timeout is the maximum time that is

    alotted for this event to execute.  EventType is the type of event that

    is being executed.  Connection is the name of the data connection and

    First is set to true if this is the first time the small or large timeout

    has occured for this timeout.  The timeout events will be continually

    called every 1/100th of a second so processing should be brief.  The

    DataResult parameter allows the event to control the timeout processing.

    The default is drContinue which allows continued attempts to perform the

    data event.  drAbort will abort the data event while drPause will suspend

    attempted retries until a drContinue is passed back.


  TRaveDataAction = (daOpen,daClose);

  TDataActionEvent = procedure(DataSystem: TRaveDataSystem;

                               DataAction: TRaveDataAction) of object;

  property OnDataAction: TDataActionEvent


  - This event can be used to keep track of when data connections are opened

    and closed by the Rave data system.  The Rave designer uses this event to

    update the Data connection status LED during report execution.



4.3 - Methods of TRaveDataSystem


  function GainControl: boolean;


  - This function should be called at the beginning of a Rave data session.

    Only one application can have control of the Rave data system at one time.

    If control cannot be gained, the result of the function will be false.

    Otherwise if control is successfully gained, the result will be true.


  procedure ReleaseControl;


  - This procedure must be called at the end of a Rave data session to release

    control of the Rave data system.


  function IsUnique(Name: string): boolean;


  - This function will check for duplicate data connections of the same

    connection name.  This function will not normally need to be called.


  procedure UpdateConnections;


  - This procedure will query the Windows environment for active data

    connections and update the RTConnectList and DTConnectList properties.


  function ReadStr: string;

  function ReadInt: integer;

  function ReadBool: boolean;

  function ReadFloat: extended;

  function ReadCurr: currency;

  function ReadDateTime: TDateTime;

  procedure ReadBuf(var Buffer;

                        Len: integer);

  function ReadPtr(Len: integer): pointer;


  - These functions will read data from the Rave communication buffers.  These

    functions are not normally needed as the TRaveDataView class handles the

    reading of the communication buffers.


  procedure WriteStr(Value: string);

  procedure WriteInt(Value: integer);

  procedure WriteBool(Value: boolean);

  procedure WriteFloat(Value: extended);

  procedure WriteCurr(Value: currency);

  procedure WriteDateTime(Value: TDateTime);

  procedure WriteBuf(var Buffer;

                         Len: integer);


  - These functions will write data to the Rave communication buffers. These

    functions are not normally needed as the TRaveDataView class or the

    CallEvent method handles writing to the communication buffers.


  procedure ClearBuffer;


  - This procedure will clear the contents of the communication buffer and

    reset the buffer pointer to the beginning.  As with the ReadXxxx and

    WriteXxxx methods, ClearBuffer should not normally be called as

    TRaveDataView or the CallEvent method handles when the buffer needs to be



  function OpenDataEvent(AName: string;

                         DataCon: TRaveDataConnection): boolean;


  - This function will open a data connection for a given connection name.  If

    the connection is opened successfully, the result of the function will be

    true.  This function is normally called by the data view object itself and

    should not be called directly.


  procedure CloseDataEvent(DataCon: TRaveDataConnection);


  - This function will close a data connection that was previously opened by a

    call to OpenDataEvent.  This function is normally called by the data view

    object itself and should not be called directly.


  function CallEvent(EventType: integer;

                     DataCon: TRaveDataConnection): boolean;


  - This function will execute a specific data event for the given data

    connection.  The data connection must already have been opened with a call

    to OpenDataEvent.  EventType must be one of the valid data event



    DATAACKNOWLEDGE, DATAFREEALTBUF.  The format of the data in the

    communication buffer differs depending upon the type of data event. This

    function is normally called by the data view object itself and should not

    be called directly.


  procedure PrepareEvent(EventType: integer);


  - This method will prepare the communication buffer for the given data

    event. Any additional data values that are required must still be written

    using the WriteXxxx methods.


  procedure CreateAltFileMap(BufIdx: integer);


  - This method will create an alternate communication buffer.  This method is

    used internally by the data connection components and should not be called



  procedure FreeAltFileMap(DataCon: TRaveDataConnection);


  - This method will free an alternate communication buffer.  This method is

    used internally by the data connection components and should not be called




4.4 - Global functions related to the Rave data system


There are several global functions defined in the RVData unit that are useful

for dealing with data related the Rave data system.


  function ProcessDataStr(DefaultDataView: TRaveDataView;

                          Value: string): string;


  - This function will process a DataView and DataText combination and return

    the text that it evaluates to.


  function CreateDataCon(RPConnection: TRPCustomConnection): TRaveDataConnection;


  - This function will create a TRaveDataConnection object for a given data

    connection component (TRPCustomConnection or one of its descendents). The

    result of this function will normally be used in conjunction with the

    CreateDataView function.


  function CreateDataView(DataCon: TRaveDataConnection): TRaveDataView;


  - This function will create a DataView object for the given

    TRaveDataConnection object (normally created through a call to the

    CreateDataCon function). Do not free the DataCon object that is passed

    into this function since it will belong to the DataView and will be freed

    by the DataView itself. The following code is how a dataview would

    normally be created dynamically at runtime:


      MyDataView := CreateDataView(CreateDataCon(TableConnection1));


  function CreateFieldName(DataViewName: string;

                           FieldName: string): string;


  - This function will create a valid field name for a given data view.  This

    function does not normally need to be called since it is called from

    within the CreateFields procedure.


  procedure CreateFields(DataView: TRaveDataView;

                         DeletedFields: TStringList;

                         ReplacedFields: TStringList;

                         DoCreate: boolean);


  - This function will create field components for a specific data viewIf

    non-nil values are passed in for the DeletedFields and ReplacedFields

    parameters, the CreateFields procedure will return existing field

    components that will be deleted or replaced if the DoCreate parameter is

    set to true.  In the Rave designer, these parameters are used to warn the

    user that fields are about to be deleted or replaced.  This function does

    not normally need to be called since it is called from within the

    CreateDataView function.


  function PerformLookup(LookupDataView: TRaveDataView;

                         LookupValue: string;

                         LookupValueField: TRaveDataField;

                         LookupField: TRaveFieldName;

                         LookupDisplay: TRaveFieldName;

                         LookupInvalid: string): string;


  - This function will perform a lookup for the given parameters and return

    the text that it evaluates to.


  procedure DataViewFirst(DataView: TRaveDataView;

                          DetailKey: TRaveFieldName;

                          MasterDataView: TRaveDataView;

                          MasterKey: TRaveFieldName;

                          SortKey: string);


  - This function will initialize a data view for the given filter values

    (DetailKey, MasterDataView and MasterKey) and sort order (SortKey).



5.0 - Interfacing with TRaveDesigner


The TRaveDesigner class allows you to view and control the settings of the

Rave design environment.  Access to the designer object is done through the

TRavePage.Designer property.  Note that only projects that are loaded in the

Rave visual designer will have designers assigned to the TRavePage.Designer

property so it is not possible to interface with the TRaveDesigner class from

within a Delphi or C++Builder application.  If generic access to the currently

displayed designer is needed, a global variable, CurrentDesigner, is provided

in the RVClass unit.



5.1 - Properties of TRaveDesigner


  property GridPen: TPen (read/write)


  - This property defines the pen used to draw the grid of the current page.


  property MinimumBorder: TRaveFloat (read/write)


  - This property defines the border around the page that will be used by the

    ZoomPage and ZoomPageWidth methods.  The default is 1.0 inches.


  property Page: TRavePage (read only)


  - This property defines the page component that this designer is currently

    being used to edit.


  property Selections: integer (read only)


  - This property will return the number of components that are in the current



  property Selection[Index: integer]: TRaveComponent (read only)


  - This property will allow access to specific selected component.  The Index

    property is 0 based.


  property ZoomFactor: TRaveFloat (read/write)


  - This property sets or returns the current zoom factor.



5.2 - Methods of TRaveDesigner


  procedure AddPip(Index: byte;

                   Control: TRaveControl;

                   Cursor: TCursor;

                   X: TRaveUnits;

                   Y: TRaveUnits);


  - This method will create a selection/sizing pip at the current position

    X,Y.  When creating most components, the default pip locations will be

    sufficient, however the pip methods will allow for custom pip placement

    and functionality.  Each pip for a specific component must have a unique

    Index value.  A component can respond to pip movements by overriding

    the TRaveControl.PipSize method.


  procedure UpdatePip(Index: byte;

                      Control: TRaveControl;

                      X: TRaveUnits;

                      Y: TRaveUnits);


  - This method will update the position of the pip defined by Index.


  procedure RemovePips(Control: TRaveControl);


  - This method removes all pips for the component defined by Control.


  procedure SwitchPips(RavePip: TRavePip;

                       SwitchIdx: byte);


  - This method will switch the pips defined by RavePip and the pip for the

    same component at index SwitchIdx.  This is normally done when a pip of a

    component is dragged past another pip during resizing.


  procedure Modified;


  - This method should be called whenever modifications have been made to the

    components in the current designer.  This will signal the project manager

    that the changes need to be saved and will also allow the designer to

    update it's various displays.


{ Selection methods }

  procedure DeselectControl(Control: TRaveComponent);


  - This method removes the component defined by Control from the current



  procedure ClearSelection;


  - This method removes all components from the current selection.


  procedure SelectControl(Control: TRaveComponent);


  - This method adds the component defined by Control to the current



  procedure ToggleControl(Control: TRaveComponent);


  - This method toggles the selected status of the component defined by



  function IsSelected(Control: TRaveComponent): boolean;


  - This method will return whether the component defined by Control is in the

    current selection.


  procedure DeleteSelection;


  - This method will delete all components in the current selection.


  procedure CopySelection;


  - This method will copy the selected components to the Windows clipboard.


  procedure PasteSelection;


  - This method will paste the components previously copied to the Windows

    clipboard into the current page.


  procedure SelectChildren(Control: TRaveComponent);


  - This method will add all children components of the component defined by

    Control to the current selection.


  procedure SelectType(ProjectItem: TRaveProjectItem;

                       RaveClass: TClass);


  - This method will add all components on the current page that are the same

    type as RaveClass to the current selection.


  procedure MoveSelection(X,Y: TRaveUnits);


  - This method will move the top, left corner of the selected component(s) to

    the position defined by X,Y.


{ Find methods }

  function FindControl(Name: string): TRaveComponent;


  - This method will search the current page for the component of the given



  function FindControlAt(X,Y: TRaveUnits): TRaveControl;


  - This method returns the name of the Control at X,Y. If no Control is found

    at that location, then this will return the current Page component.


  function FindContainerAt(X,Y: TRaveUnits;

                           NewChild: TClass): TRaveControl;


  - This method returns the name of a valid parent component at location X,Y

    for a component of type NewChild.  If no valid parent is found, the page

    will be the default container.


{ Position methods }

  function XI2D(Value: TRaveUnits): longint;

  function YI2D(Value: TRaveUnits): longint;


  - These methods convert units measurements (inches) to printer canvas

    measurements (dots).


  function XD2I(Value: longint): TRaveUnits;

  function YD2I(Value: longint): TRaveUnits;


  - These methods convert the printer canvas measurement (dots) to

    unit measurements (inches).


  function SnapX(Value: TRaveUnits): TRaveUnits;

  function SnapY(Value: TRaveUnits): TRaveUnits;


  - These methods will take a Value which could be the result of some

    calculation and converts it to the nearest grid value. For example, if grid

    spacing is set for 0.1 inches and you pass the function a value of 1.12,

    then it will return a value of 1.1.


{ Zooming methods }

  function ZoomToRect(X1,Y1,X2,Y2: TRaveUnits): TRaveFloat;


  - This method changes both the page position and zoom factor so that the

    area indicated by X1,Y1,X2,Y2 will occupy the page design. This function

    examines both the vertical and horizontal sizes when determining what zoom

    factor should be used so that the full rectangle is shown.


  procedure ZoomPage;


  - This method changes the zoom factor so that the full page plus the

    Minimum border fills the page designer screen.


  procedure ZoomPageWidth;


  - This method changes the zoom factor so that the form width plus the

    minimum border fills the page designer screen.


  procedure ZoomSelected;


  - This method changes the zoom factor so that all selected items will fill

    the page designer screen. If only one item is selected, then the zoom

    factor will be changed so that single item is full screen.


  procedure ZoomIn(X,Y: TRaveUnits);


  - This method increases the zoom factor by the zoom increment amount set

    by the preferences.


  procedure ZoomOut;


  - This method decreases the zoom factor by the zoom increment amount set

    by the preferences.


  procedure CenterWindow(X,Y: TRaveUnits);


  - This method changes the page layout to center at the point indicated by the

    position, X,Y. This procedure does NOT change the zoom factor.


  procedure AlignSelection(AlignType: integer);


  - This method will perform the alignment action defined by AlignType.  Valid

    values for AlignType include: RaveAlignLeft, RaveAlignHCenter,

    RaveAlignRight, RaveAlignHCenterInParent, RaveAlignHSpace,

    RaveAlignEquateWidths, RaveAlignTop, RaveAlignVCenter, RaveAlignBottom,

    RaveAlignVCenterInParent, RaveAlignVSpace, RaveAlignEquateHeights,

    RaveAlignMoveForward, RaveAlignMoveBehind, RaveAlignBringToFront,

    RaveAlignSendToBack, RaveAlignTapLeft, RaveAlignTapRight, RaveAlignTapUp,

    RaveAlignTapDown, RaveAlignTapHSizeDown, RaveAlignTapHSizeUp,

    RaveAlignTapVSizeDown and RaveAlignTapVSizeUp.



6.0 - Creating a Rave Property Editor


The property editor interface of Rave allows you to create and respond to

property editors through the TRavePropertyEditor class (RVTool.pas). The

property editor can apply to a specific property of a specific component type,

a specific property across all components or all properties of a specific type

for a specific component or across all components.



6.1 Types of property editors


- Simple - This property editor only displays an edit box allowing the user

  to type in a string of data (e.g. TRavePage.Name).


- Listing - Like a Simple property editor, but also displays a drop down list

  of values to select from (e.g. TRectangle.Color).


- Editor - Like a Simple property editor, but also displays an editor button

  that brings up a custom dialog to edit the property (e.g. TRaveText.Font).


- Editor/Listing - This is a combination of the Listing and Editor property

  editors.  It allows both a drop down list of values to select from and

  an editor button that brings up a custom dialog to edit the property

  (e.g. TRaveDataText.DataField)



6.2 Steps for creating a property editor


1: Create a class descending from TRavePropertyEditor (or one of its

   descendents). Below is a list of the methods that will normally need to

   be overriden for the different types of property editors (these methods

   are described in detail below).


   Simple Property Editor - GetOptions, GetValue and SetValue

   Listing Property Editor - GetOptions, GetValue, SetValue and GetValues

   Editor Property Editor - GetOptions, GetValue and Edit

   Editor/Listing Property Editor - GetOptions, GetValue, SetValue, GetValues

    and Edit



       TMyPropertyEditor = class(TRavePropertyEditor)


         function GetOptions: TPropertyOptionsSet; override;

         function GetValue: string; override;

         procedure SetValue(Value: string); override;

       end; { TMyPropertyEditor }


2: Create methods for GetOptions, GetValue, SetValue, GetValues and/or

   Edit as necessary. For examples, there are many property editors defined

   in this unit and other units in the Rave library.


3: Register the property editor by calling RegisterRavePropertyEditor from

   within a global scope procedure named RaveRegister (exact case required).

   The PropType parameter should be the type info structure for the property

   type you are registering. The function TypeInfo() will return the value

   you need. The ControlClass parameter defines the class of components

   that this property editor applies to. If this parameter is nil, then the

   property editor applies to all component classes. The PropName parameter

   allows you to limit the property editor to only properties of a specific

   name.  If this field is blank then the property editor applies to all

   properties with a type info of PropType. The EditorClass property should

   be the class type of the property editor you are registering. See the

   RaveRegister procedure in this unit for examples.


     procedure RaveRegister;


     begin { RaveRegister }



     end;  { RaveRegister }


4: Include the unit containing the property editor in a Delphi package

   and register that with RAVE through Edit|Preferences|Components.



6.3 Properties of TRavePropertyEditor


  property Instance[Index: integer]: TRaveComponent (read only)


  - This property returns the Index'th component being edited. See also



  property InstCount: integer (read only)


  - This property returns the numbers of components that are currently being

    edited by this property editor (i.e. multiple components are selected).

    If poMultiSelect is not in the property editor options, this value will

    always be 1. See also Instance property.


  property Name: string (read only)


  - This property returns the name of the property field being edited.


  property Options: TPropertyOptionsSet (read only)


  - This property returns the current options for this property editor. See

    also TPropertyOptions and GetOptions.


  property PropInfo[Index: integer]: PPropInfo (read only)


  - This property will return the PPropInfo structure for the instance

    specified by Index. See Delphi's TypInfo unit for a description of



  property Value: string (read/write)


  - This property sets or returns the value of the property as a string.  See

    also GetValue, SetValue.



6.4 Methods of TRavePropertyEditor


  procedure Modified;


  - This method should be called whenever a property value is modified so that

    the Property Panel can refresh its display.


  function GetOrdValue(Index: integer): integer;

  function GetFloatValue(Index: integer): extended;

  function GetStrValue(Index: integer): string;

  function GetVariantValue(Index: integer): variant;


  - These methods should be called to get the property value of the instance

    specified by Index. If the property is a set, class or pointer type, use

    GetOrdValue and typecast it appropriately.


  procedure SetOrdValue(Value: integer);

  procedure SetFloatValue(Value: extended);

  procedure SetStrValue(Value: string);

  procedure SetVariantValue(Value: variant);


  - These methods should be called to set the property value of all instances

    connected to this property editor. If the property is a set, class or

    pointer type, use SetOrdValue and typecast it appropriately. Note however,

    if the property is a class type and the object is owned by the component

    (i.e. Font), you should iterate through the Instance property assigning

    the data instead of overwriting the object pointer which is what

    SetOrdValue will do.


  function SameValue(TestComp: TRaveComponent;

                     TestValue: string): boolean;


  - This method is used internally by the property panel to determine whether

    the current value is different than the default (Highlight Changes

    option).  You will should not need to call this method directly.



6.5 Virtual methods of TRavePropertyEditor


  function GetOptions: TPropertyOptionsSet;


  - This virtual method should be overriden to return the set of options for

    this property editor.  See also Options property and TPropertyOption.


      poEditor - This property has a dialog editor

      poListing - This property returns a listing of values

      poNoSort - The listing returned by this property should not be sorted

      poMultiSelect - This property can be edited across a multiple selection

      poLiveUpdate - This property is updated whenever any change is made

      poReadOnly - This property can only be edited through the list or editor

      poPassword - Edit/Display this property with *'s

      poRefreshAll - When this property is changed, all windows will be

                     refreshed.  Used with properties such as Locked and

                     Mirror that drastically change the contents of the entire

                     Property Panel.


  function GetValue: string;

  procedure SetValue(Value: string);


  - These two virtual methods should be overriden to set and return the value

    of the property as a string. If poLiveUpdate is in the property editors

    options, SetValue will be called for each keypress the user makes in the

    properly editor's edit box. See also Value property.


  procedure GetValues(ValueList: TStrings);


  - This virtual method should be overriden to return the list of available

    options in the TStrings object, ValueList. If poNoSort is not in the

    Options property, the items will be sorted alphabetically. This method

    will only be called if poListing is in the property editors options.


  procedure Edit;


  - This virtual method will be called whenever the user presses the editor

    button on the property panel. Normally you will want to display a dialog

    allowing the user to edit a property that is too complicated for a simple

    text string.  This method will only be called if poEditor is in the

    property editors options.


  procedure PaintValue(Canvas: TCanvas;

                       Rect: TRect;

                       DefaultValue: string);


  - This virtual method allows a property editor to customize the contents of

    the property panel.  You will not normally need to override this method

    but it can be used to provide more visual information on a property value.

    The color, line style and fill style property editors are examples of

    where this can be useful.



7.0 - Creating a Rave Component Editor


The component editor interface of Rave allows you to create and respond to

menu items that you create on a components popup menu (from a right click).

This is done through the TRaveComponentEditor class (RVTool.pas).

This will allow you to perform actions such as opening a dialog to allow the

user to modify the component's properties. Creating a component editor

requires the following steps:



7.1 Steps for creating a component editor


1Create a class descending from TRaveComponentEditor and override

   the AddToMenu and RunFromMenu procedures.



       TMyComponentEditor = class(TRaveComponentEditor)


         procedure AddToMenu(AddMenuItem: TAddMenuItemProc); override;

         procedure RunFromMenu(ID: integer); override;

       end{ TMyComponentEditor }


2Create a method for AddToMenu and call AddMenuItem for each line you

   want to create in the component's popup menu (see TAddMenuItemProc

   definition below). ID should be unique for this component editor and

   greater than 0 for each menu item. ParentID should be 0 to place the

   new menu item on the first level. To make a child menu from an existing

   menu item, pass the parent's ID value as ParentID.


     procedure TMyComponentEditor.AddToMenu(AddMenuItem: TAddMenuItemProc);


     begin { AddToMenu }

       AddMenuItem('MenuItem 1',1,0);

       AddMenuItem('MenuItem 2',2,0);

       AddMenuItem('SubMenuItem 2a',21,2);

       AddMenuItem('SubMenuItem 2b',22,2);

       AddMenuItem('-',0,0); // Create a divider

       AddMenuItem('MenuItem 3',3,0);

       AddMenuItem('MenuItem 4',4,0);

     end;  { AddToMenu }


3Create a method for RunFromMenu which will be called whenever a user

   selects a menu item from a components popup menu. The ID value will be

   equal to a value given in the previous calls to AddMenuItem from the

   AddToMenu procedureIf an ID of -1 is given, the default action should

   be taken (i.e. User double-clicked the component. You can get access to

   the component currently being edited through the Instance property.

   NOTE: Menu Items which are parents to other menu items will not cause a

   RunFromMenu call.


     procedure TMyComponentEditor.RunFromMenu(ID: integer);


     begin { RunFromMenu }

       Case ID of


         // MenuItem1 action (double-click default)



         // MenuItem 2a action



         // MenuItem 2b action



         // MenuItem 3 action



         // MenuItem 4 action


       end{ case }

     end;  { RunFromMenu }


4: Register the component editor by calling RegisterRaveComponentEditor

   from within a global scope procedure named RaveRegister (exact case

   required). The ControlClass parameter should be the class type of the

   component you are defining an editor for and the EditorClass parameter

   should be the class type of the component editor you are registering.


     procedure RaveRegister;


     begin { RaveRegister }


     end;  { RaveRegister }


5: Include the unit containing the component editor in a Delphi package

   and register that with RAVE through Edit|Preferences|Components.



8.0 - Creating a Rave Project Editor


The project editor interface of Rave allows you to create two types of experts.


The first type of project editor can respond to menu items that you create on

the Rave Tools menu through the TRaveProjectEditor class (RVTool.pas). This

will allow you to perform actions such as starting a report wizard that will

create or modify a report and all of the pages and components within.  See

section 8.1 for more details on menu-based project editors.


The second type of project editor can respond to project events.  Examples of

project events includes when a new project is opened, after a report is

printed or when a data connection is opened.  See section 8.3 for more details

on event-based project editors.


There is no limitation preventing an event-based project editor from using a

menu-based interface as well. In fact, you will find that most of the

event-based project editors such as the Rave Data Recorder or Rave Data

Randomizer create a menu interface to configure the project editor.



8.1 Steps for creating a menu-based project editor


1Create a class descending from TRaveProjectEditor and override

   the AddToMenu and RunFromMenu procedures.



       TMyProjectEditor = class(TRaveProjectEditor)


         procedure AddToMenu(AddMenuItem: TAddMenuItemProc); override;

         procedure RunFromMenu(ID: integer); override;

       end{ TMyProjectEditor }


2Create a method for AddToMenu and call AddMenuItem for each line you want

   to create in the Tools menu (see TAddMenuItemProc definition above).

   ID should be unique for this project editor and greater than 0 for each

   menu item. ParentID should be 0 to place the new menu item on the first

   levelTo make a child menu from an existing menu item, pass the parent's

   ID value as ParentID.


     procedure TMyProjectEditor.AddToMenu(AddMenuItem: TAddMenuItemProc);


     begin { AddToMenu }

       AddMenuItem('MenuItem 1',1,0);

       AddMenuItem('MenuItem 2',2,0);

       AddMenuItem('SubMenuItem 2a',21,2);

       AddMenuItem('SubMenuItem 2b',22,2);

       AddMenuItem('-',0,0); // Create a divider

       AddMenuItem('MenuItem 3',3,0);

       AddMenuItem('MenuItem 4',4,0);

     end;  { AddToMenu }


3: Create a method for RunFromMenu which will be called whenever a user

   selects a menu item from the Tools menu. The ID value will be equal to a

   value given in the previous calls to AddMenuItem from the AddToMenu

   procedure. NOTE: Menu Items which are parents to other menu items will

   not cause a RunFromMenu call.


     procedure TMyProjectEditor.RunFromMenu(ID: integer);


     begin { RunFromMenu }

       Case ID of

         -1,1: begin

         // MenuItem1 action (double-click default)


         21: begin

         // MenuItem 2a action


         22: begin

         // MenuItem 2b action


         3: begin

         // MenuItem 3 action


         4: begin

         // MenuItem 4 action


       end; { case }

     end;  { RunFromMenu }


4: Register the project editor by calling RegisterRaveProjectEditor from

   within a global scope procedure named RaveRegister (exact case required).

   The EditorClass parameter should be the class type of the project editor

   you are registering.


     procedure RaveRegister;


     begin { RaveRegister }


     end;  { RaveRegister }


5: Include the unit containing the project editor in a Delphi package

   and register that with RAVE through Edit|Preferences|Components.



8.2 Special notes for creating components at design-time


(1) Most of the interface to the components of a project are through the

    ProjectManager component. ProjectManager is of type TRaveProjectManager

    and is located in the unit RVProj.pas. To create a new report call

    ProjectManager.NewReport. To create a new GlobalPage call

    ProjectManager.NewGlobalPage and to create a new data view call

    ProjectManager.NewDataView. To create a new report page, call

    ProjectManager.ActiveReport.NewPage. To create other types of components

    follow the directions below.


(2) You will need to assign the owner for the component using the AOwner

    parameter of the Create constructor. Any TRaveField components should

    have the appropriate TRaveDataView component as their owner. Any other

    components that are placed on a Global Page or Report Page should use the

    TRavePage object as their owner.


(3) You will need to assign the Parent property for all components. For

    components being placed on a Global Page or Report Page, the parent should

    be the graphical owner of the component (e.g. If you want to place a

    component in a TRaveSection, you need to set the Parent property to the

    TRaveSection component). For all other components, the Parent property

    should be set to the same as the Owner property.


(4) You will need to set the Name property for all components.  Call

    ProjectManager.GetUniqueName to make sure you are using a unique name.

    The BaseName parameter is the base part of the name that you want to use

    (i.e. "Section"). The NameOwner parameter is the Owner component whose

    namespace you will be checking against (normally the TRavePage object).

    The UseCurrent parameter tells whether you want to try the current

    BaseName before appending digits on the end (i.e. Try "CustomerName"

    before trying "CustomerName1", "CustomerName2",..).


(5) For visual components (anything deriving from TRaveControl) you will need

    to initialize the Top, Left, Width and Height properties. These should be

    the location relative to the upper left corner of the component's Parent.


(6For all components, you will need to call AddComponent(Component) to

    signal the visual designer that the component has been added to the

    visual designer. If your wizard is deleting component that have already

    been added to the report project, your will need to call DeleteComponent(

    Component) before calling the components Free method.



8.3 Steps for creating an event-based project editor


There are several project events that can be handled and a single project

editor can handle as many project events that it needs to. The following is a

list of project events currently supported (Item and Param values list the

type of data that is passed in for each project event type). Before events

such as peBeforeAppClose will use the return value of HandleEvent to determine

whether to continue with (true) or cancel (false) the specific action.


  peAfterAppOpen - Called after the Rave visual designer is opened

   (Item = ProjectManager, Param = nil)


  peBeforeAppClose - Called before the Rave visual designer is closed

   (Item = ProjectManager, Param = nil)


  peAfterProjectOpen - Called after a project is opened

   (Item = ProjectManager, Param = nil)


  peBeforeProjectClose - Called before a project is closed

   (Item = ProjectManager, Param = nil)


  peBeforeNewProject - Called before a new project is created

   (Item = ProjectManager, Param = nil)


  peAfterNewProject - Called after a new project is created

   (Item = ProjectManager, Param = nil)


  peBeforeNewReport - Called before a new report is created

   (Item = nil, Param = nil)


  peAfterNewReport - Called after a new report is created

   (Item = the new TRaveReport object, Param = nil)


  peBeforeNewReportPage - Called before a new report page is created

   (Item = nil, Param = nil)


  peAfterNewReportPage - Called after a new report page is created

   (Item = the new TRavePage object, Param = nil)


  peBeforeNewGlobalPage - Called before a new global page is created

   (Item = nil, Param = nil)


  peAfterNewGlobalPage - Called after a new global page is created

   (Item = the new TRavePage object, Param = nil)


  peBeforeNewDataView - Called before a new data view is created

   (Item = nil, Param = nil)


  peAfterNewDataView - Called after a new data view is created

   (Item = the new TRaveDataView object, Param = nil)


  peBeforeProjectSave - Called before a project is saved

   (Item = ProjectManager, Param = nil)


  peAfterProjectSave - Called after a project is saved

   (Item = ProjectManager, Param = nil)


  peBeforeReportPrint - Called before a report is printed

   (Item = ProjectManager, Param = nil)


  peAfterReportPrint - Called after a report is printed

   (Item = ProjectManager, Param = nil)


  peDataConOpen - Called when a data connection is opened

   (Item = nil, Param = the TRaveDataConnection object)


  peDataConClose - Called when a data connection is closed

   (Item = nil, Param = the TRaveDataConnection object)


  peDataConConnect - Called when a data connection is made

   (Item = nil, Param = the TRaveDataConnection object)


  peDataSystemOpen - Called when the Rave data system is opened

   (Item = nil, Param = the TRaveDataSystem object)


  peDataSystemClose - Called when the Rave data system is closed

   (Item = nil, Param = the TRaveDataSystem object)


  peDataSystemCallEvent - Called before a Rave data event is executed

   (Item = nil, Param = pointer to a TDataSystemEventData record)


  peDataSystemEventCalled - Called after a Rave data event is executed

   (Item = nil, Param = pointer to a TDataSystemEventData record)


  peShowAboutDialog - Called before the About dialog is shown.  This will

   allow an alternate About Box dialog to be displayed.  If the result of

   HandleEvent is false, the normal Rave about box will not be shown.  NOTE:

   Any replacement About Box must clearly display the Nevrona Designs

   copyright as follows "Copyright © 1995-2000, Nevrona Designs".  Failure to

   do so will be a violation of the software license.

   (Item = nil, Param = nil)


  peGetAppTitle - Called when the designer is initializing to retrieve an

   alternate title bar text.  The RaveTitle string variable (defined in

   RVDefine.pas) should be set to the desired text.

   (Item = nil, Param = nil)


  peBeforePageChange - Called before the page designer is changed from

   one page to another.  Refer to the CurrentDesigner variable (defined in

   RVClass.pas) for information about the designer that is being moved


   (Item = nil, Param = nil)


  peAfterPageChange - Called after the page designer is changed from one

   page to another or when the Page Designer tab is selected.  Refer to the

   CurrentDesigner variable (defined in RVClass.pas) for information about the

   newly selected designer.

   (Item = nil, Param = nil)


  peAddShortCuts - This event is called when the designer is building the list

   of shortcuts that it will support.  By defaultall project editors

   that create tool menu items will have short cut entries created, but this

   peoject event allows you to create additional shortcuts.  Call

   RaveCreateShortCut (defined in RVClass.pas) to add a short cut to the list

   as follows:


     procedure RaveCreateShortCut(Desc: string; // Description used in IDE

                                  Name: string = ''// Registry Key

                                  Item: TComponent = nil; // Action component

                                  Key1: TShortCut = 0// Short-cut key

                                  Key2: TShortCut) = 0// Second key (if any)


   If Name is blank, the value of ('Rave@' + Desc) will be used.  Name is the

   value used to identify the shortcut in the Windows registry.  Once Name is

   defined, you should not change it or the link will be broken for existing

   settings.  The action component defined in Item is the component that will

   be "executed" when the short cut is performed.  TAction.OnExecute,

   TMenuItem.OnClick and TControl.OnClick are the only recognized actions.  If

   Item is nil, a title bar will be created within the ShortCut Editor.  Rave

   supports 2 key short cuts (i.e. ^K-H). To define one, define the first key

   in Key1 and the second key in Key2.

   (Item = nil, Param = nil)


  peAfterZoomChange - This event is called whenever the zoom factor is

   changed.  You can access the current zoom factor through the

   CurrentDesigner.ZoomFact property.

   (Item = nil, Param = nil)


  peAfterZoomToolChange - This event is called when the Zoom Tool state of the

   current designer is changed.  You can access the current state of the zoom

   tool with the Zooming boolean variable (defined in RVDefine.pas).

   (Item = nil, Param = nil)


  peAfterSelectionChange - This event is called whenever the selection of

   components is changed.  You can access information about the currently

   selected components through the CurrentDesigner.Selections and Selection


   (Item = nil, Param = nil)


  peLoadState - This event is called when the current state of the designer is

   being loaded from the registry.

   (Item = nil, Param = nil)


  peSaveState - This event is called when the current state of the designer is

   being saved to the registry.

   (Item = nil, Param = nil)


  peAfterPropertiesModified - This event is called whenever a property is

   changed in the Property Panel.

   (Item = nil, Param = nil)


  peInitialize - This event is called when the IDE is being initialized.  Use

   this event instead of FormCreate or a similar event.

   (Item = nil, Param = nil)




  peShowPage - This event is called whenever a new page needs to be shown in

   the Page Designer.  The Item can contain either the page to be shown or a

   component (in which case, that's component's page will be shown).

  (Item = Page or Component, Param = nil)


  peBeforeReportActivate -


  peAfterReportActivate -








The following steps will show how to create an event-based project editor.


1Create a class descending from TRaveProjectEditor and override the

   HandleEvent function.



       TMyProjectEditor = class(TRaveProjectEditor)


         function HandleEvent(ProjectEvent: TProjectEvent;

                              Item: TRaveComponent;

                              Param: pointer): boolean; override;

       end{ TMyProjectEditor }


2Create a method for HandleEvent and check the ProjectEvent parameter for

   the specific project event(s) that you want to handle.


     function TMyProjectEditor.HandleEvent(ProjectEvent: TProjectEvent;

                                           Item: TRaveComponent;

                                           Param: pointer): boolean;



       Report: TRaveReport;


     begin { HandleEvent }

       Result := true;

       Case ProjectEvent of

         peAfterNewReport: begin

           Report := Item as TRaveReport;

         { Insert code to get a description and assign to Report.Description }


       end{ case }

     end;  { HandleEvent }


3: Register the project editor by calling RegisterRaveProjectEditor from

   within a global scope procedure named RaveRegister (exact case required).

   The EditorClass parameter should be the class type of the project editor

   you are registering.


     procedure RaveRegister;


     begin { RaveRegister }


     end;  { RaveRegister }


4: Include the unit containing the project editor in a Delphi package

   and register that with RAVE through Edit|Preferences|Components.



8.3.1 - New Rave 4 Event System


The following steps will show how to create an event-based project editor.


1Create a class descending from TRaveProjectEditor and override the

   Handles function.  This function defines which events this project editor

   defines methods for.  If there are more than one event type that are

   defined, separate with semicolons (e.g. 'DataEvents;ToolMenuEvents').


     TMyProjectEditor = class(TRaveProjectEditor)


       function Handles: string; override;

     end{ TMyProjectEditor }


     function TMyProjectEditor.Handles: string;


       Result := 'DataEvents';



2: Define the event methods that you want to handle.  Use the same name and

   structure as the methods defined in the event handler interface structure.

   It is not necessary to define all methods of the interface.


   Data event handler interface structure in RVData.pas (this is already

   created, no need to define it yourself):


     IRaveDataEventHandler = interface


       procedure DataConOpen(DataCon: TRaveDataConnection);

       procedure DataConClose(DataCon: TRaveDataConnection);

       procedure DataConConnect(DataCon: TRaveDataConnection);

       procedure DataSystemOpen(DataSystem: TRaveDataSystem);

       procedure DataSystemClose(DataSystem: TRaveDataSystem);

       procedure DataSystemCallEvent(DataSystem: TRaveDataSystem;

                                     EventData: TDataSystemEventData);

       procedure DataSystemEventCalled(DataSystem: TRaveDataSystem;

                                       EventData: TDataSystemEventData);

     end{ IRaveDataEventHandler }


   New project editor source:


     TMyProjectEditor = class(TRaveProjectEditor)


       function Handles: string; override;

       procedure DataConOpen(DataCon: TRaveDataConnection);

     end{ TMyProjectEditor }


     procedure TMyProjectEditor.DataConOpen(DataCon: TRaveDataConnection);


     // Do whatever you want to do when a data connection is opened



3: Register the project editor by calling RegisterRaveProjectEditor from

   within a global scope procedure named RaveRegister (exact case required).

   The EditorClass parameter should be the class type of the project editor

   you are registering.


     procedure RaveRegister;


     begin { RaveRegister }


     end;  { RaveRegister }


8.3.2 - Creating and calling an event handler


In 8.3.1 we showed how to create a project editor to handle existing event

handler system.  This section will show how to create and call your own event

handler systems.


1: Define an event handler interface structure.  This is the methods or

   events that a project editor can define.  The GUID that is on the second

   line can be generated by pressing Shift-Ctrl-G within the Delphi IDE.


     IMyEventHandler = interface


       procedure MyProcA(ID: integer);

       procedure MyProcB(Value: string);

     end{ IMyEventHandler }


2: Define an event handler class.


   // Event methods type

     TMyEventMethod = (meMyProcA,meMyProcB);


   // Event handler class

     TMyEventHandler = class(TRaveEventHandler, IMyEventHandler)


     // Parameter fields

       FID: integer;

       FValue: string;

       FMyEventMethod: TMyEventMethod;

     // Overrides

       procedure Process; override;

       function Handles: string; override;

     // Interface property

       property ProjectEditor: TRaveProjectEditor read FProjectEditor implements



     // Shell Interface methods

       procedure MyProcA(ID: integer);

       procedure MyProcB(Value: string);

     // Broadcast methods

       procedure DoMyProcA(ID: integer);

       procedure DoMyProcB(Value: string);

     end{ TMyEventHandler }


   // Overrides

     procedure TMyEventHandler.Process;



       IItem: IMyEventHandler;


     begin { Process }

       IItem := self;

       Case FMyEventMethod of

         meMyProcA: IItem.MyProcA(FID);

         meMyProcB: IItem.MyProcB(FValue);

       end{ case }

     end;  { Process }


     function TMyEventHandler.Handles: string;


     begin { Handles }

       Result := 'MyEvents';

     end;  { Handles }


   // Shell Interface methods

     procedure TMyEventHandler.MyProcA(ID: integer); begin end;

     procedure TMyEventHandler.MyProcB(Value: string); begin end;


   // Broadcast methods

     procedure TMyEventHandler.DoMyProcA(ID: integer);


     begin { DoMyProcA }

       FID := ID; // Assign parameter(s) to field variables

       FMyEventMethod := meMyProcA; // Determine which method to execute

       Broadcast; // Broadcast event to all project editors

     end;  { DoMyProcA }


     procedure TMyEventHandler.DoMyProcB(Value: string);


     begin { DoMyProcB }

       FValue := Value; // Assign parameter(s) to field variables

       FMyEventMethod := meMyProcB; // Determine which method to execute

       Broadcast; // Broadcast event to all project editors

     end;  { DoMyProcB }


3: Define a variable of the same type as your event handler.



       MyEventHander: TMyEventHandler;


4: Register the event handler by calling RegisterRaveEventHandler from

   within a global scope procedure named RaveRegister (exact case required).

   The EventHandlerClass parameter should be the class type of the project

   editor you are registering.  The function will return an instance of your

   event handler class that you should store.


     procedure RaveRegister;


     begin { RaveRegister }

       MyEventHandler := RegisterRaveProjectEditor(TMyEventHandler);

     end;  { RaveRegister }


5To execute an event for all project editors simply call the broadcast

   method for the event handler such as:


     MyEventHandler.DoMyProcB('This is a test');


   This would call the MyProcB method for all project editors that handle the

   MyEvents event type and define a method called MyProcB.


6To execute an event for a single project editor instance (e.g.

   AProjectEditor) assign the project editor using the SetProjectEditor

   method, define an interface variable and assign your event handler to it

   then call the interface method (not the DoXxxxx broadcast method) of the

   interface variable (not the event handler).



     IEventHandler: IMyEventHandler;



     IEventHandler := MyEventHandler;

     IEventHandler.MyProcB('This is a test');




1Create a directory under C:\Rave4 that is the same name as the component

package that you will be creating (for this example: C:\Rave4\ND_AbtBx).


2In C:\Rave4\ND_AbtBx create a form (.PAS and .DFM) called NDCsAbt and

place the following code in there (place whatever controls on the form that

you want for the about box):


    unit NDCsAbt;





      Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

      StdCtrls, RVClass, RVTool;



      TNDAboutBoxForm = class(TForm)

        Label1: TLabel;

        Label2: TLabel;

        Button1: TButton;


        { Private declarations }


        { Public declarations }



      TNDAboutBoxProjectEditor = class(TRaveProjectEditor)


        function HandleEvent(ProjectEvent: TProjectEvent;

                             Item: TRaveComponent;

                             Param: pointer): boolean; override;

      end{ TNDAboutBoxProjectEditor }


      procedure RaveRegister;



      NDAboutBoxForm: TNDAboutBoxForm;




    {$R *.DFM}


      procedure RaveRegister;


      begin { RaveRegister }


      end{ RaveRegister }



    ( class TNDAboutBoxProjectEditor



      function TNDAboutBoxProjectEditor.HandleEvent(ProjectEvent: TProjectEvent;

                                                    Item: TRaveComponent;

                                                    Param: pointer): boolean;


      begin { HandleEvent }

        Result := true;

        Case ProjectEvent of

          peShowAboutDialog: begin

            With TNDAboutBoxForm.Create(Application) do try


              Result := false; // Signal not to show original about box



            end{ with }


        end{ case }

      end;  { HandleEvent }




3: Also in C:\Rave4\ND_AbtBx create a package unit called ND_AbtBx.dpk and

place the following code in there:


    package ND_AbtBx;


    {$DESCRIPTION 'About Box Replacement Property Editor (ver 1.0)'}












4Create a batch file in C:\Rave4 called RANTCOMP.BAT and place the

following commands in there:


    @echo off

    if exist setenv.bat call setenv.bat

    ..\source\computil SetupD4

    if exist setenv.bat call setenv.bat

    if "%1%"=="" goto showhelp

    goto docomp


    echo ***************************************************

    echo Compile RANT Package (for the Rave Visual Designer)

    echo ***************************************************

    echo :

    echo Usage: RANTCOMP packagename

    echo :

    echo Note: The packagename parameter should not include the .dpk extension

    echo :

    echo Desc: This batch file will compile a package containing a RAVE add-on

    echo       and place the .BPL in the parent directory (normally C:\Rave4).

    echo       It is meant to be run from a directory under Rave4 containing

    echo       the source for the add-on.  The Rave library files must reside

    echo       in C:\Rave4\RV.

    echo ***************************************************

    goto endok


    %NDD4%\bin\dcc32 %1.dpk /b /h /w /z /LE.. /U..\RV -$d-l-n+p+r-s-t-w- /DDESIGNER %2 %3 %4

    if errorlevel 1 goto enderror

    goto endok


    echo Error!



5: Change to the C:\Rave4\ND_AbtBx directory and run "..\RANTCOMP ND_AbtBx"


6: Start Rave and install the new package file.  The BPL will be located in



7If the RANT package defines any components, place the .PAS and any other

relavent files in the library C:\Rave4\D4 (or C4 for C++Builder 4.0, D5 for

Delphi 5.0, ...)