Copyright: Actuate Software Corporation


  Programming with Actuate

Programming with Actuate provides information for using the Actuate Basic programming language and the Actuate foundation classes to go beyond what you can accomplish with the Actuate Developer Workbench interface.

Programming with Actuate includes the following sections.

•	Introduction to programming with Actuate Basic. This section provides an overview of the Actuate Basic language, the basic code elements, and coding conventions.

•	Variables and data types. This section describes the Actuate Basic data types and how to work with them.

•	Writing and using procedures. This section describes how to write and call procedures.

•	Overview of the Actuate Foundation Classes. This section explains the architecture of the Actuate foundation class library, and provides an overview of the different categories of classes.

•	Understanding classes. This section explains what classes are and how to use them.

•	Working with objects. This section explains what objects are and how to work with them.

•	Using the Component Editor. This section describes how to use the Component Editor to modify classes.

•	The report generation process. This section explains how the Factory builds reports and describes the class protocols that determine how objects in a report fit together.

•	Customization examples. This section shows several examples of customizing reports through code.

•	Programming for report viewing events. This section describes the Viewer events you can control, hyperlinks, and provides examples.

•	Using stored procedures in SQL databases. This section describes how to create and call stored procedures.

•	Debugging. This section describes the features of the Actuate debugger.

•	Using objects from other applications. This section describes how to insert and use OLE (Object Linking and Embedding) objects in reports.

•	Programming other applications' objects. This section describes how to use OLE Automation to integrate other applications in reports.

•	Calling external C functions. This section describes how to use C functions in your report application.


Introduction to programming with Actuate Basic

The following topics are available. To view the topics in order, use the Browse button (>>) in the Help window's toolbar.

•	About Actuate Basic

•	Programming with Actuate Basic

•	Code elements

•	Coding conventions

•	Using the code examples


About Actuate Basic

Actuate Basic is a high-level, object-oriented programming language designed for creating sophisticated reports. It consists of standard Basic functions and statements, and object-oriented language extensions.

Actuate Basic is similar to other structured programming languages. It provides built-in functions and control structures like If...Then...Else constructs, For and While loops, and lets you use variables and create your own procedures. In fact, Actuate Basic is modeled after Microsoft Visual Basic, Version 3.0, so if you have programmed in that language, you already know a lot about Actuate Basic programming. The difference between the two languages is that Actuate Basic is designed for programming reports and has none of Visual Basic's form-related syntax.

This chapter introduces Actuate Basic: the elements of the language, syntax, coding conventions, and how to work with various types of data and data structures.

See also

Language features


Language features

The following table lists some of Actuate Basic's features:

Feature	Lets you ...	For information, see
Strong data typing	Detect mismatched type errors at compile time instead of at run time.	Variables and data types
User-defined data types	Combine variables of different types into a single type.	Variables and data types
OLE automation support	Exchange information with other applications that support OLE automation.	Programming other applications' objects
Calls to external C functions	Use C functions stored in external libraries. For example, you can call the standard functions in the Windows API.	Calling external C functions
Portability	Run report applications on NT, Windows, or UNIX servers.	Actuate Basic Language Reference


Programming with Actuate Basic

The Actuate Developer Workbench provides powerful design tools that let you create many reports without writing a line of code. Using the Design Editor, you create reports by placing components in the report design and setting properties to customize their appearance. Actuate translates your report design into corresponding Actuate Basic code.

You can write code in Actuate Basic if you need to take control of the normal report-generation process, you need to add application-specific logic, or you want your report to respond to user-triggered events when users view it. To accomplish these tasks, you typically override (augment or replace) predefined methods in the Actuate foundation classes, or create new methods. Methods are procedures that are declared within class declarations. They perform actions on the objects of a class. The following is an example of a method:

Sub AddMenuCommands( menu As AcPopupMenu )

Super::AddMenuCommands( menu )

menu.AddSeparator

    menu.AddItem( "Evaluate Discount", me, "OnEvaluateDiscount" )

End Sub

For information about classes and methods, see Understanding classes. For information about overriding methods, see Overriding inherited methods. For information about creating methods, see Creating methods.

See also

Procedures and modules


Procedures and modules

Programming in Actuate involves writing procedures. The procedures you write can be methods or general procedures that are available to the entire report application. You store general procedures in Basic source (.BAS) files. When you include the source files with your report design, you can use the general procedures in them the same way you use Actuate Basic's built-in statements and functions. For more information about procedures, see  Writing and using procedures.


Code elements

To program in Actuate Basic, you need to familiarize yourself with its syntax, naming conventions, control structures, and so on. The following topics provide overviews of the standard Actuate Basic code elements:

•	Statements

•	Expressions

•	Operators

See also

Statements
Expressions
Operators


Statements

A procedure typically contains a series of Actuate Basic statements that collectively perform an operation or calculate a value. A statement is a complete set of instructions directing Actuate Basic to execute a specific task.

See also

Assignment statements


Assignment statements

The simplest and most common statement in a procedure is the assignment statement. It consists of a variable name, followed by the equal sign, followed by an expression:

<variable> = <expression>

The expression can be as simple or complex as necessary, but it must evaluate to a valid data type. All of the following are valid assignment statements.

Area = PI * Radius ^ 2

StartTime = StopTime - Now

Net_Worth = Assets - Liabilities

Label.Text = "Page " & Str$(curPageNum) & " of" & Str$(NumPages)

In these examples, the assignment statement stores information. The value of an expression, which appears to the right of the assignment operator (=) is computed, and the result is stored in the variable to the left. The data type of the variable should be the same as that returned by the computed expression, or you may get unexpected results. For instance, assigning a double value to an integer variable will round the double to the nearest integer. If necessary, you can use functions to convert elements of different data types to one common type. The last example used the Str$ function to convert numbers to character values. You can use variants, too, but you should do so sparingly.


Expressions

An expression consists of values and operators combined in such a way that it evaluates to one of the Actuate Basic data types. The type of an expression is determined by the type of the operands within it.

For an expression that contains an operation, Actuate Basic considers the types of all the operands to determine the type of the result. For example, if you add two integers, the result of the expression is an integer. But if you mix data types in an expression, Actuate Basic makes the result the type of the widest range. For example, the expression 3 * 4.55 results in a double.

To avoid mixing types in an expression, you can use type-declaration characters with numeric constants. These characters force a constant to take on a specific type. For example, 10 is an integer, but 10@ is a currency value. Thus 10@ + 20@ yields a currency value of thirty dollars. For more information about type-declaration characters, see Using a type-declaration character.


Operators

An operator is a symbol or keyword that performs an operation on expressions. Actuate Basic provides the following types of operators:

•	Arithmetic

•	Comparison

•	Logical

•	Concatenation

See also

Arithmetic operators
Comparison operators
Logical operators
Concatenation operator
Operator precedence


Arithmetic operators

Use arithmetic operators with numeric expressions. You can also use arithmetic operators with date expressions to subtract one date from another, and to add or subtract a number from a date.

Operator	Description	Syntax
^	Exponentiation	number ^ exponent
*	Multiplication	expression1 * expression2
/	Floating-point division	expression1 / expression2
\	Integer division	expression1 \ expression2
Mod	Modulus (remainder)	expression1 Mod expression2
BAnd	Bitwise And	expression1 BAnd expression2
BOr	Bitwise Or	expression1 BOr expression2
+	Addition	expression1 + expression2
-	Subtraction	expression1 - expression2
Exponentiation

The exponentiation operator allows you to compute powers and roots. Powers are computed using a positive exponent and roots are computed using a negative exponent. For example 10 ^ 3 evaluates to 1000 (10 cubed). Conversely, 1000 ^ -3 evaluates to 10 (cube root of 10).

Floating-point vs. integer division

The floating-point division operator (/) performs standard division and returns a floating-point number. For example 3/2 returns 1.5 as a floating point, even though both operands are integers. The integer division operator (\) returns just the integer portion of the division. For example, 10\2 evaluates to 5, which is probably what you want. However, 3\2 evaluates to 1.


Comparison operators

Use comparison operators to compare two expressions of the same type and return True or False. Note that while the Equal to operator is the same as the assignment operator (=), Actuate Basic can determine by its use which one you intended. The assignment operator is valid only when it immediately follows a variable in an assignment statement. For all other contexts, the = operator is a comparison operator.

Operator	Description	Syntax
>	Greater than	expression1 > expression2
<	Less than	expression1 < expression2
>=	Greater than or equal to	expression1>= expression2
<=	Less than or equal to	expression1<= expression2
=	Equal to	expression1 = expression2
<>	Not equal to	expression1 <> experssion2
Like	String comparison	expression Like pattern
Is	Object reference variable comparison	objectref1 Is objectRef2


Logical operators

Use logical operators to compare two logical expressions and return True or False.

Operator	Description	Syntax
Not	Performs a logical negation on an expression. Returns False if expression is True.	Not expression
And	Performs a logical conjunction on two expressions. Returns True if both expressions are True. Returns False if either expression is False.	expression1 And expression2
Or	Performs a logical disjunction on two expressions. Returns True if one or both expressions are True.	expression1 Or expression2
Xor	Performs a logical exclusion on two expressions. Returns True if exactly one of the expressions is True, but not both.	expression1 Xor expression2
Eqv	Performs a logical equivalence on two expressions. Returns True if both expressions evaluate to the same logical value.	expression1 Eqv expression2
Imp	Performs a logical implication on two expressions.	expression1 Imp expression2


Concatenation operator

The string concatenation operator (&) combines two strings to produce a new string that contains both original strings. The original strings themselves are not changed. The operands must be strings. If you need to concatenate a number with a string, you must convert the number to a string first. For example:

"The cube root of 27 is " & Str$(27 ^ (1/3))


Operator precedence

When several operations occur in an expression, Actuate Basic evaluates and resolves each part in a predetermined order. This order is called operator precedence. You can use parentheses to override the order of precedence and force parts of an expression to be evaluated before others. Operations within parentheses are always performed before those outside.

When an expression contains operators from more than one category, Actuate Basic evaluates arithmetic operators first, comparison operators next, and logical operators last. Within individual categories, operators are evaluated in the order in which they are presented in the tables in the previous sections. However, all comparison operators have equal precedence. They are evaluated in the left-to-right order in which they appear.


Coding conventions

When writing code, there are several style conventions you can use to make your code easy to read and understand. For example, comment your code to explain its purpose, use a consistent style to name language elements, indent lines of code for nested statements, and break up long statements at logical places. The following topics describe in greater depth these common conventions you should follow.

•	Commenting code

•	Breaking up long statements

•	Placing short, related statements on a line

•	Indenting code

•	Using a consistent naming and style convention

•	Naming rules

See also

Commenting code
Breaking up long statements
Placing short, related statements on a line
Indenting code
Using a consistent naming and style convention
Naming rules


Commenting code

Code comments makes procedures easy to understand and maintain, especially if other programmers will be working with your code. Actuate Basic recognizes two comment markers:

•	The apostrophe (')

•	Rem

Actuate Basic treats characters to the right of a ' character or Rem characters as comments, and does not execute those lines. Comments can follow a statement, or occupy an entire line. However, a comment cannot follow a line continuation character.

Rem This is a comment that takes up an entire line.

' This is a comment that takes up an entire line.

Text1.BackGroundColor = Red	' print negative numbers in red.


Breaking up long statements

Most code is written with one Actuate Basic statement to a line with no terminator at the end of the line. Sometimes, however, you need to break a long statement over several lines. To do so, use a '+' (continuation) character at the beginning of lines after the first one. For example:

Data1.RecordSource = "SELECT * FROM Titles, Publishers"
+ & "WHERE Publishers.PubId = Titles.PubID AND
+ Publishers.State = 'CA'"


Placing short, related statements on a line

Sometimes it makes the code more readable to put several related and short statements on the same line. You can place two or more statements on a single line by using the colon separator. For example:

Text1.Text = "Loss" : Red = 255: Text1.BackgroundColor = Red


Indenting code

Indenting lines of code gives a visual clue to the hierarchy of statements, especially if the procedure contains nested procedures and statements. Indentation also makes it easy to see constructs that have unbalanced beginning and ending statements.


Using a consistent naming and style convention

A good naming scheme can help in describing and differentiating named elements. For example, you can develop notation to identify the element (whether it is a variable, method, or class), its scope, or data type.

Hungarian notation is one popular naming scheme used by many programmers. In Hungarian notation, a currency (cur) variable of global scope (g) might be called gcurPrimeRate.This kind of name can tell you a lot at a glance.

You might also want to use capitalization to improve the legibility of variable, method, and class names. For example, Actuate uses mixed capitalization, which makes it easy to read long, descriptive names such as GetFirstContent( ) and OnRButtonDblClk( ). Actuate Basic, however, is not case sensitive, so ThisFunction( ) is exactly the same as thisfunction( ).


Naming rules

When naming elements such as a variable, constant, or procedure, you must adhere to the following rules:

•	The name can contain up to 40 characters.

•	The first character of the variable name must be a letter. A letter is any upper- or lowercase character in the US ASCII (A-Z and a-z) character set, and does not include foreign characters such as å or ô.

•	Subsequent characters in a name can be letters, digits, or the underscore (_) character.

•	Keywords, such as Function, Type, Sub, If, and End, cannot be used. For a list of reserved keywords, see Appendix B, "Keywords," in Actuate Basic Language Reference.

•	Operators, such as *, ^, and %, cannot be used.

•	The name cannot contain spaces.


Using the code examples

Example code is included with the Actuate products to help you learn how to program specific types of functionality into your reports. For more information about using the example code, see Chapter 2, "Statements and functions," in Actuate Basic Language Reference.


Overview of the Actuate Foundation Classes

The following topics are available. To view the topics in order, use the Browse button (>>) in the Help window's toolbar.

•	About the Actuate Foundation Class architecture

•	Classes by functional categories

•	Report object structure classes

•	Connection classes

•	Data stream classes

•	Section classes

•	Page layout classes

•	Control classes

•	Internal tools classes

•	Collection classes

•	HTML classes


About the Actuate Foundation Class architecture

Use the Actuate Foundation Class library (AFC library) to build a wide range of reports, from tabular reports to crosstab reports. Since all AFC classes follow a defined class protocol, you can combine them in a variety of ways to address varied reporting requirements.

The class protocol is the set of methods and the rules governing their use. Graphs, integer controls, and sections may seem like disparate items at first glance, but they actually share a core protocol. The features that differentiate those classes are built on top of the core protocol.

You can create reports without understanding the class protocols most of the time. You need to understand the class protocols only if you create custom components that require programming, or if you want to change or extend the AFC class architecture.

See also

About abstract classes
About concrete classes
Actuate abstract base classes


About abstract classes

The higher up a class is in its class hierarchy, the more general is its protocol. Each successive generation of classes contains more and more specialized versions of the general protocol.

The top level, or core, protocol is defined in the abstract base classes. These classes are abstract because they are never instantiated and contain only general rules that other classes refine.

Many methods in the abstract base classes are empty. These empty methods are defined to enforce a protocol. The subclasses then add the necessary implementation details. If you derive a class directly from an abstract base class, you must add code to it to get it to do something. In general, do not derive your own classes from an abstract class, but from its specialized subclasses.


About concrete classes

Concrete classes, also called leaf classes, define the details that are specific implementations of the core protocol. Unlike abstract classes, concrete classes may be instantiated. You subclass a concrete class if you want to create a class that modifies or extends the functionality of the original class.


Actuate abstract base classes

The following illustration shows the abstract base classes in the Actuate Foundation Class library. Understanding these classes will give you an overview of the major report components.

The following table describes the base classes:

Base class	Description
AcComponent	Principal base class for all components that appear in the report structure pane. It defines the mechanism for creating objects contained within container objects.
AcConnection	Defines the logic for generic connections.
AcDBConnection	Defines the logic for connecting to relational databases. The database connection classes provide native support for the following database interfaces: DB2, Informix, Microsoft SQL server, ODBC, Oracle, Progress, and Sybase.
AcDataRow	Defines the general characteristics of a data row. A data row is a record structure that contains data retrieved from a single record which has been processed into a format that the report accepts.
AcDataAdapter	Defines the logic of data-related classes--data sources and data filters--that can be combined to form a data stream. The data stream manages data collection and processing.
AcDataSource	Defines the logic for retrieving data from an external source and creating a data row for each input record.
AcDataFilter	Defines the logic for processing data rows retrieved from another data adapter.
AcReportComponent	Defines the general structural characteristics of all classes--visual and non-visual--whose objects are stored persistently in a report.
AcSection	Defines the characteristics of all non-visual structural classes. Derived classes represent different ways of grouping data.
AcVisualComponent	Defines the characteristics of all visual classes. Derived classes display data or graphical elements.
AcFlow	Defines the logic for placing frames in a flow.
AcBaseFrame	Defines the general characteristics of frames and pages, and the logic for instantiating the content in frames and pages.
AcControl	Defines the general characteristics of all controls.
AcPageList	Defines the logic for building pages and managing displays.


Classes by functional categories

Actuate Foundation Classes fall into the following functional categories:

•	Report object structure classes

•	Connection classes

•	Data stream classes

•	Section classes

•	Page layout classes

•	Control classes

•	Internal tools classes

•	Collection classes

•	HTML classes

The topics provide an overview of each class category.  For detailed information about each class, see Chapter 3, "AFC classes," in Actuate Foundation Class Reference.


Report object structure classes

Classes in this category form the backbone of a report. They define the general structural characteristics of objects, how they are created, and how they fit together. Generally, you do not derive from these classes. When you design a report, the Design Editor automatically derives a report class from AcReport. The report is the container for all other objects in a report instance file.


Connection classes

Classes in this category provide communication links to SQL and ODBC-compliant databases.

Abstract base classes

AcConnection and AcDBConnection are abstract base classes; do not derive directly from them.

Concrete classes

AcInformixConnection,AcMSSQLConnection, AcODBCConnection, AcOracleConnection, AcProgressConnection, AcSybaseConnection, and AcSybaseDBLibConnection are concrete classes you use to connect to databases.

AcDBStatement and AcDBCursor provide the Actuate Basic interface for working with SQL statements and cursors. The framework creates and uses instances of these classes when your report accesses a SQL database.


Data stream classes

Classes in this category get and process data, create and send data rows to the report.

Abstract base classes

AcDataAdapter is an abstract base class and you generally do not derive from it. AcDataSource, AcDatabaseSource, and AcDataFilter are base classes you can subclass to create custom data filters and data sources.

Concrete classes

AcMemoryBuffer and AcMemoryDataSorter, AcSingleInputFilter and AcMultipleInputFilter are data filters that you can use in your report. AcSQLQuerySource is a data source that you use to retrieve data from a SQL database.


Section classes

Classes in this category manage the data from data streams and build the sections and frames that contain the data. Objects of this class are non-visual objects that form the report's structure.

Abstract base classes

AcSection andAcDataSection are abstract base classes and you generally do not derive from them.

Concrete classes

Use the AcReportSection, AcGroupSection, AcSequentialSection, AcConditionalSection, and AcParallelReport classes to organize data in a report.


Page layout classes

Classes in this category manage the creation and presentation of pages in the report.

Abstract base classes

AcBaseFrame, AcBasePage, AcDataFrame, AcFlow, and AcPageList are abstract base classes and you generally do not derive from them.

Concrete classes

Use the AcSubPage and AcPage classes to design page styles. Use AcTopDownFlow to determine how report objects are placed on the page. The AcSimplePageList, AcLeftRightPageList, and AcTitleBodyPageList classes represent specific page designs.

AcFrame is the container for all controls. You can place controls directly on a page, but you typically place them in frames.


Control classes

Classes in this category include data controls, graphs, and static graphical objects.

Abstract base classes

AcControl, AcDataControl, AcOleControl, and AcGraph are abstract base classes. Derive from these classes only when you want to create your own custom controls.

Concrete classes

Use the AcTextControl, AcIntegerControl, AcDoubleControl, AcCurrencyControl, and AcDateTimeControl controls to display various types of data from a data row. AcLabelControl is for displaying static text.

AcImageControl, AcLineControl, AcRectangleControl, and AcEllipseControl are drawing elements you add to your report for visual interest.

AcOleContainerControl lets you embed or link OLE objects from other applications into your report.

AcSummaryGraph, AcDetailGraph, and AcHLCGraph display data in various standard graph formats.


Internal tools classes

AcReportController provides the search mechanism for hyperlinks and other search operations. AcPopupMenu provides the services for controlling context menus.


Collection classes

Classes in this category define the way objects are stored and accessed in a linked list. The framework uses objects of these classes to keep track of objects in a list. For example, frames use lists to manage the controls contained within them. If you need to manipulate report contents in a list, for example, controls in a frame or flows on a page, you can create a collection and an iterator to access the contents.


HTML classes

Classes in this category provide HTML report generation capabilities, allowing creation of reports which can be viewed and distributed via the World Wide Web. Although you can generate HTML from any component, the HTML components provide additional HTML-specific properties and methods.

Abstract base classes

AcHtmlReport is the only base class in this category.

Concrete classes

Use AcHtmlFrame, AcHtmlRuleControl, and AcHtmlImageControl to manage and control the display of information in a web page.

Use AcHtmlAppletControl to include Java applets in an HTML report design.

Use AcHtmlPlugInControl to include Netscape plug-ins in an HTML report design.


Understanding classes

The following topics are available. To view the topics in order, use the Browse button (>>) in the Help window's toolbar.

•	Declaring a class

•	Class relationships

•	About class variables

•	Using variables

•	About methods

•	Using methods


Declaring a class

All classes are written in Actuate Basic. They are declared with the Class statement, as shown in the following syntax:

Class <subclass name> Subclass Of <superclass name>

[<variable declarations>]

[<nested class declarations>]

[<method declarations>]

End Class

When you create your report with the Design Editor, the Design Editor generates the class declaration code for each component you place on your report design.

See also

Contents of a class declaration
Example


Contents of a class declaration

The body of a class declaration consists of the following optional items:

•	Variable declarations, which declare variables associated with the class

•	Class declarations for classes nested in the current class

•	Method declarations, which consist of procedures and functions associated with the class


Example

The following is an example of class declaration code you see in a Basic source (.BAS) file, which Actuate generates when you compile a report design (.ROD) file:

Class ItemFrame Subclass Of AcFrame

' Variable declarations

Dim AlternateColor	As AcColor
Dim AlternateLines	As Integer
Static OriginalColor	As AcColor
Static RowNumber	As Integer

' Class declarations for nested classes

Class ItemCode Subclass Of AcTextControl

...

End Class

' Methods

Sub SetProperties( )

Super::SetProperties( )
AlternateColor = 15527148
AlternateLines = 2
BackgroundColor = 14408667

Size.Height = 12703

Size.Width = 400

End Sub

Sub OnStart( )

Super::OnStart( )

OriginalColor = BackgroundColor

RowNumber = RowNumber + 1

If RowNumber > AlternateLines * 2 Then

RowNumber = 1

End If

IF RowNumber > AlternateLines Then

BackgroundColor = AlternateColor

End If

End Sub

...

End Class


Class relationships

Classes do not exist in isolation. Rather, they co-exist to perform a variety of tasks. The following are the ways classes relate to each other.

Relationship	Description
Inheritance	When one class is derived from another, they are related through inheritance. In inheritance, the derived class (subclass) contains the variables and methods of the base class (superclass) plus any new variables or methods defined in the subclass.
Scope	Scope defines a relationship where the declaration of one class is nested within another class declaration. Scope determines the visibility of classes, variables and methods and, therefore, how you refer to those items in code.
Reference	When one class references another in code, they are related through that reference. In the structure pane of the Design Editor, classes are related by structure references, that is, how classes use or contain other classes.
Understanding the class relationships will help you work with classes, variables, and methods: how to name and refer to them, how to manage changes in classes to avoid causing unexpected effects in related classes, and so on.

See also

About inheritance
About scopes


About inheritance

In the Actuate class hierarchy, inheritance is used primarily to support a standard interface for report applications and less so for code reusability. The foundation classes at the top of the hierarchy typically contain empty method declarations or methods with a few lines of general instructions. The purpose of these methods is to enforce a protocol for how Actuate creates report applications. Subclasses redefine the methods to add implementation details.

When you derive classes from any of the Actuate foundation classes, those subclasses inherit the protocol, which ensures seamless integration with the Actuate framework.


About scopes

Scope is the part of an application in which a symbol exists or is visible. Symbols are names of items such as classes, methods, variables, and constants. Scope determines how you access or reference names, when classes are instantiated, when variables are initialized, and so on.

See also

Scope of classes
Class scopes and naming conventions
Default class scopes in a report design
Default scope of classes in a library
Benefits of the default class scopes


Scope of classes

A class in an application can be declared in global or class scope. A class not declared within another class has global scope. A class declared within another class has a class scope, that is, it has the scope of the enclosing class. Classes declared in class scope are called nested classes.

You cannot move the base class (superclass) into the scope of the subclass (derived class).

The following code snippet shows one class (SalesDetail) with global scope, and two nested classes (OfficeTitleFrame and CustomerTitleFrame) with class scopes.

Class SalesDetail Subclass Of AcReport

...

Class OfficeTitleFrame Subclass Of AcFrame

...

End Class

Class CustomerTitleFrame Subclass Of AcFrame

...

End Class

End Class

The following illustration shows a conceptual view of the global and class scopes.

To find out what scope a class has, look at the Scope information on the Class page of the Component Editor. For details on how to access this information, see Viewing general information about classes.


Class scopes and naming conventions

Scope determines how you name classes and how you reference classes in code. Classes with global scope must have unique names. Classes with nested scope need not have unique names because they are hidden within the scope of the enclosing class.

About the scope-resolution operator (::)

With the scope-resolution operator, you can refer to any name, even if it is hidden from the current scope. In the Design Editor, there can be up to three levels of scope. Using the scope-resolution operator, you can build a path of class scopes leading to the innermost scope. This convention is similar to specifying a path for a file on disk, where you specify each subdirectory leading to the file, separating each subdirectory with the backslash (\).

Just like files in directories, classes in scopes use the following rules:

•	The class names in one scope are independent of class names in other scopes. Just as you can have two files with the same name as long as they are in different directories, you can have two classes with the same name as long as they are in different scopes. For example, your report application can contain the following class names:

CustomerFrame::AddressControl
SalesRepFrame::AddressControl

•	To reference a class in a different scope, you must use a qualified name. This convention is similar to accessing a file in a different directory. For example, if you are writing code for CustomerFrame and you want to refer to AddressControl in SalesRepFrame, use the full name: SalesRepFrame::AddressControl.

•	To reference a class in the same scope, use just the class name. Again, this convention is similar to specifying another file in the current directory. For example, if you are writing code for a method in CustomerNameControl in the CustomerFrame and you want to refer to CustomerAddressControl also in CustomerFrame, use just the class name: CustomerAddressControl.


Default class scopes in a report design

In Actuate Basic, every class introduces its own scope. Actuate Basic allows an unlimited number of nesting levels, and any class can be nested within another--even if doing so makes little sense for a report application. For example, it makes no sense to nest a frame within a control.

The Design Editor applies common sense rules to set the scope of classes when you place components in your report design. The rules are:

•	The report (any subclass of AcReport) has a global scope

•	All other classes, except controls, are in the report's scope

•	All controls are in their container's scope. Controls, therefore, are typically in a frame's scope

The following illustration shows an example of how classes are scoped in a typical report design:

The following table summarizes the default scope of classes in the Design Editor:

Type of class	Default scope
Report (AcReport)	Global
Sections	Report
Page lists	Report
Pages	Report
Flows	Report
Connections	Report
Data sources	Report
Data filters	Report
Frames	Report
Controls	Frame or page


Default scope of classes in a library

Because classes in a library are for use in any report design, all these classes have global scope. If you publish a class that you created in a report design to a library, Actuate automatically changes the class scope to global scope. For example, a frame you created in a report design has the report's scope. If you publish that frame to a library, its scope becomes global.


Benefits of the default class scopes

The default scopes that the Design Editor defines for classes provides two key benefits: easy naming conventions and reusability.

Easy naming conventions

Nesting controls within frames makes it easy to manage control names. Imagine if controls had global scope. Every control would need a unique name. In typical reports where controls make up most of the elements in a report, assigning meaningful and unique names to controls would be a daunting task. Nesting controls within their container classes solves that problem.

Reusability

Nesting classes within the report object allows classes to be re-used in other report designs without name conflicts. Consider this example: You have a frame called CustomerFrame in your current report design. You want to use a frame, also called CustomerFrame, from a different report design. The Design Editor lets you create a reference to the frame in the second report design without changing either of the frame names. This is because the frames are in different scopes and, therefore, have unique qualified names. Actuate recognizes one name as <Report1>::CustomerFrame, and the other, as <Report2>::CustomerFrame.


About class variables

Class variables define the state and attributes of an object of a class. Their scope is within the class in which they are declared.

Actuate Basic supports two types of class variables:

•	Instance

•	Static

These types determine how the variables are stored.

See also

Instance variables
Static variables
Comparison of instance and static variables


Instance variables

As its name suggests, an instance variable applies to a particular instance of a class, that is, there is one copy of the variable in each instance. For example, each object of a text control class has instance variables, such as Position and Size, that define the object's appearance.

Declare instance variables with the Dim statement. For example:

Dim Position	As AcPoint

Dim Size	As AcSize


Static variables

When you declare a variable as static, only one copy of the variable is maintained for all instances of the class. A static variable is like a global variable, except that its scope is within a class. Use static variables if all instances need to share information, for example, a counter to track the number of times a particular class is instantiated.

Declare static variables with the Static statement. For example:

Static InstanceCount	As Integer

Static RowNumber	As Integer


Comparison of instance and static variables

The following illustration shows how instance and static variables differ in where they are stored.


Using variables

Actuate Basic declares two types of variables--instance and static. The Component Editor, however, lets you additionally specify variables by their usage and visibility in the Component Editor. The following topics describe the different types of variables.

•	Types of variables by usage

•	Visibility of variables in the Component Editor

See also

Types of variables by usage
Visibility of variables in the Component Editor
Using property variables
Using parameters
Using regular variables


Types of variables by usage

The following table describes the different functional types of variables.

Functional type	Description
Property	Defines the attributes for objects of the class. For example, all controls have property variables such as BackgroundColor, Font, Size, and Position that define their appearance. You supply property values at design time. Only instance variables can be properties.
Parameter	Stores values that you supply at run time to specify the data to include in the report. When you run the report, the Requester prompts for these values before report-generation begins. Only static variables can be parameters.
Regular	Stores values that the Actuate needs when generating the report. For example, the PageCount variable in the Book class stores the current page number, which Actuate updates continuously as it generates the report.


Visibility of variables in the Component Editor

The following table describes the types of visibility you can assign to a variable to determine if it appears only on the Variables page of the class where it is declared, or on the Variables page of subclasses as well.

Visibility type	Description
Private	Variable appears in the Variables page of the class in which it is declared. Specify a variable as private if you intend to use the variable only in the class where it is declared and do not want to clutter the Variables page of the subclasses.
Public	Variable appears in the Variables page of the class and its subclasses. Specify a variable as public if you intend to use the variable in the class and its subclasses.
The assignment of private or public to variables affects only what you see on the Variables page. Private and public variables are not a feature of Actuate Basic. Whether you specify a variable as private or public, you access it the same way in Actuate Basic code. In other words, through code, a variable specified as private is available to the class and its subclasses.


Using property variables

Properties are the most visible parts of a component because you can see and assign values to them at design time. They appear in the Properties page of the Component Editor, providing an intuitive interface to gather values for constructing objects. Many of the variables declared in Actuate foundation classes are presented as properties. These values you assign to the properties are then set in code by SetProperties( ), a method that Actuate Basic generates.

You can create your own variable properties through the Variables page of the Component Editor. For information on creating variables, see Creating variables.

The following illustrations show how property variables appear in the Component Editor, and the code that Actuate generates when you set properties.


Using parameters

Specify variables as parameters when you want to gather values when the report is run. Reports typically use parameters as a means to filter the data to retrieve and display. For example, a query might specify that the data stream retrieve all customer records from a Customer table. You might, however, want the flexibility to specify additional run-specific filter conditions when you run the report, for example, to get only records for customers in California, or to get only records in a specific date range.

You can also create parameters if you want the capability to set properties such as the font and color of objects when you run the report. Variables that you create and specify as parameters appear in the Requester when you run the report. At this time, you can specify the additional filter criteria or set property values. If you do not specify anything in the Requester, the report uses default values.

For more information about parameters and how to create them, see Designing report parameters in Building Reports.

The following illustration shows the parameter variables in the Component Editor and in the Requester.


Using regular variables

Variables that fall in the regular category are often used to store values that Actuate needs when generating the report. For example, a frame uses the Container variable to store a reference to its container object. You typically create regular variables when you need to store values that your methods need. For example, if you write a method that sets alternating rows to different colors, you will need a variable to store the current row number.


About methods

Methods are procedures defined within class declarations. They specify the actions an object performs. Most of the predefined methods defined in the Actuate foundation classes are called by the framework to generate a report. You can create new methods to add functionality not provided by a class. If the functionality you want is an extension or a version of an existing method, you can override the method.

For information about creating methods, see Creating methods. For information about overriding inherited methods, see Overriding inherited methods.


Using methods

The predefined methods in the Actuate Foundation Classes fall into three usage categories:

•	Methods you can override

•	Methods you can call

•	Private methods the framework calls

The categories indicate the purposes for which the methods are designed. You could override the private methods or override methods that are designed to be called. Taking any of these actions, however, is not recommended because they affect the framework and unless you understand the framework and the report generation process well, you might break the system.

The method descriptions in Actuate Foundation Class Reference explain what the methods do and how you typically use them. In addition, the Method Editor and the Methods page of the Component Editor provide a filter that lets you view methods in these usage categories. Use the manual and the Method Editor or Component Editor as guides to determining what methods you should call and override when you need to write code. The following illustration shows the filtering options.

See also

Methods you can override
Methods you can call
Private methods
Overloading methods


Methods you can override

The methods designated as overridables give you the flexibility to safely customize parts of the report generation or report viewing process. For example, methods that have an "On" prefix let you add code that executes after an event occurs. The methods that are part of a class' protocol, such as Start( ), Build( ), Fetch( ), Finish( ), are also overridable. Actuate guarantees these protocol methods will work the same way in future releases.


Methods you can call

The methods designed to be called typically provide a defined service or information about an object. For example, data adapter classes provide methods such as SeekTo( ), SeekBy( ), SeekToEnd( ), and Rewind( ) that you can call to access retrieved data. Report component classes provide methods such as IsContainer( ), IsLeaf( ), IsVisual( ) and HasContents( ) that you can call to get information about objects. Page list classes define methods such as GetPageCount( ), GetContents, GetCurrentPage( ), and GetFirstPage( ) that you can call to get a value your code might need.

You can, but typically do not, override these methods. If you need to accomplish a task and you cannot find a predefined method that does it, create a new method.


Private methods

Methods that fall in the private category are methods that the framework calls to perform complex internal tasks. Some of these methods call C functions. Overriding private methods is not recommended for two reasons: they require knowledge of internal implementation details and Actuate does not guarantee private methods will work the same way in future releases.


Overloading methods

Actuate Basic lets you overload methods. Overloading is the practice of supplying more than one definition for a given method name in the same scope. In other words, you can have multiple methods with the same name in a single class. You must, however, define different argument lists for the methods. In the following example, MyMethod( ) is an overloaded method.

Function Sum( num1 As Integer, num2 As Integer ) As Integer

Function Sum( num1 As Double, num2 As Double) As Double

Function Sum( num1 As Integer, num2 As Integer, num3 As Integer ) As    Integer

The compiler selects the appropriate version of the method based on the arguments with which it is called.

You typically create an overloaded method when you want to create a method that accomplishes a particular task, but which accepts different arguments.


Using the Component Editor

The following topics are available. To view the topics in order, use the Browse button (>>) in the Help window's toolbar.

•	About the Component Editor

•	Getting information about classes

•	Creating variables

•	Creating methods

•	Overriding inherited methods


About the Component Editor

When programming with Actuate, one of the main tools you use is the Component Editor. You use the Component Editor when you want to accomplish any of the following tasks:

•	Get information about an Actuate foundation class or any component in your report design

•	View and set the values of properties

•	Create or edit variables

•	Create new methods or override inherited methods


Getting information about classes

The Component Editor organizes information in a way that makes it easy to learn about a specific class. As the following illustration shows, the Component Editor consists of four pages, three of which correspond to the items that make up a component, and one that displays general information about the component.

See also

Opening the Component Editor
Viewing general information about classes
Inspecting variables
Inspecting methods


Opening the Component Editor

To inspect a class with the Component Editor, do one of the following:

•	In any view that displays classes, double-click the class

or

•	Select the class you want to inspect, then choose View→Component Editor


Viewing general information about classes

To see general information about a class, choose the Class tab in the Component Editor. The Class page identifies the class name, the class from which the selected class is derived, the class scope, the module in which the class resides, and any comments a programmer might have written.


Inspecting variables

To see the variables of a class, choose the Variables tab in the Component Editor. By default, the Variables page displays all public, inherited, and locally-declared variables in all three usage categories (regular, property, and parameter variables). You can, however, use the filter option to specify a smaller set of variables to display.

See also

Specifying the types of variables to display


Specifying the types of variables to display

You can use the filter option on the Variables page to specify the set of variables that interest you. The following illustration shows the filter options you can specify through the Variable Filtering dialog.

For information about the different types of variables, see Types of variables by usage and Visibility of variables in the Component Editor.


Inspecting methods

To see the methods of a class, choose the Methods tab in the Component Editor. By default, the Methods page displays inherited and locally-declared methods that are designed to be overridden. You can, however, use the filter option to specify a different set of methods to display.

See also

Specifying the types of methods to display


Specifying the types of methods to display

You can use the filter option on the Methods page to specify the set of methods that interest you. The following illustration shows the filter options you can specify through the Method Filtering dialog.

For information on how Actuate classifies methods in the overridable, callable, and private categories, see Using methods.


Creating variables

The Design Editor lets you create three types of variables: property, parameter, and regular. Each type of variable lets your application store values that the report uses in different ways.

You create property variables to store values that users supply at design time to manipulate the attributes of objects. You create parameter variables to store values that users supply when they run the report to specify run-specific conditions. And, you create regular variables typically to store values that methods need.

See also

How to add a variable to a class
Editing variables
Deleting variables


How to add a variable to a class

1	In any of the views that display classes, double-click the class to which you want to add a new variable.

The Component Editor appears.

2	Choose the Variables tab.

3	Choose New.

The Class Variable dialog box appears.

4	Specify the variable name. You can also specify the name of an array, for example, MyArray(10) or MultiArray(1 To 3, 1 To 3, 1 To 3).

5	Specify the variable data type.

You can use any of the following:

•	An Actuate Basic data type, such as integer or string, or a data type you defined.

For a list of the Basic data types, see Appendix A, "Actuate Basic data types," in Actuate Basic Language Reference.

•	An Actuate Foundation Class data type, such as AcColor or AcFont.

For a list of the Actuate Foundation Class data types, see Chapter 2, "AFC data types," in Actuate Foundation Class Reference.

•	The name of any declared class.

For a list of all classes in the Actuate Foundation Class library, open AFC.ROL by choosing View→Browse→Library. For a list of all the classes available to your report application, open the Globals Browser by choosing View→Browse→Globals.

6	Select either Instance or Static.

7	Select the visibility or usage of the variable.

The options differ depending on whether you selected Instance or Static in step 6. The following table describes the options.

	Visibility option	Description	Applies to
	Essential Property	Variable appears in the Properties page. Users must supply a value at design time.	Instance variable
	Parameter	Variable appears in the Requester when users run the report. Users can specify additional run-specific filter conditions.	Static variable
	Private	Variable appears in the Variables page of the class in which it is declared.	Instance and static variables
	Property	Variable appears in the Properties page. Users can optionally supply a value at design time.	Instance variable
	Public	Variable appears in the Variables page of the class and its subclasses.	Instance and static variables
8	Choose OK.

The variable appears on the Variables page. If you selected Property for an instance variable, it also appears on the Properties page.


Editing variables

You can edit only variables you created. In order to maintain the architecture of the framework, Actuate does not let you edit the variables it declares.

See also

How to edit a class variable you created


How to edit a class variable you created

1	In any of the views that display classes, double-click the class that contains the variable you want to edit.

2	Choose the Variables tab.

3	Select the variable you want to edit.

4	Choose Edit.

Tip

To revert a variable to its previous definition, choose Edit→Undo.


Deleting variables

You can delete only variables you created. In order to maintain the architecture of the framework, Actuate does not let you delete the variables it declares.

See also

How to delete a class variable you created


How to delete a class variable you created

1	In any of the views that display classes, double-click the class that contains the variable you want to delete.

2	Choose the Variables tab.

3	Select the variable you want to delete.

4	Choose Delete.

Tip

To recover a variable you just deleted, choose Edit→Undo.


Creating methods

You create new methods for a component to perform actions not provided by the component. If the functionality you want is an extension of an existing method, consider overriding the method instead.

For information about overriding methods, see Overriding inherited methods.

See also

Guidelines for creating methods
Editing methods
Deleting methods


Guidelines for creating methods

There are no restrictions on what you can do with the methods of a component. However, because methods affect immensely the behavior of objects, you should design, code, and test the methods carefully.

Use the same techniques for writing error-free procedures and functions plus the following guidelines, which are specific to creating methods:

•	Make sure the method needs to be a method.

Some features you implement as methods might be better encapsulated into properties. A rule of thumb is that method names have verbs in them. If you create methods that have no verbs in their names, consider if they should be properties instead.

•	Minimize conditions imposed on other programmers that might use the component.

For example, be aware of the complexities that can arise from creating the following kinds of methods:

•	Methods that users must call in order to use the component.

•	Methods that must execute in a strict order.

•	Methods that put the component into a state where certain events or methods could be invalid.

If you cannot avoid such conditions, write code that handles incorrect use of your methods. For example, if calling a method puts the component into a state where calling another method is invalid, program the other method to test the state before executing its main code. At a minimum, display warning messages and provide ways out of errors when they occur.

See also

Naming conventions for methods
How to create a new method


Naming conventions for methods

Actuate imposes no restrictions on what you name your methods. You can have duplicate method names within a report if the methods are in different scopes, or if the methods have different arguments lists (overloaded method). For information about class scopes and overloaded methods, see Scope of classes and Overloading methods.

The following list provides a few naming guidelines:

•	Begin names with verbs.

For example, GetHorizontalPosition is more understandable than XPosition, which sounds like a property.

•	Use descriptive names that reflect the method's purpose unambiguously.

For example, a name such as ReadDataRow is more informative than DoDataRow.


How to create a new method

1	In any of the views that display classes, double-click the class to which you want to add a new method.

The Component Editor appears.

2	Choose the Methods tab.

3	Choose New.

The Add Method dialog appears.

4	Type a name for the method.

5	Specify a return data type for the method, if needed.

6	Choose OK.

The Method Editor appears, displaying the method declaration.

7	Write code for the method. Follow the same rules for writing regular procedures.

8	To save the method, choose Update or Close.

The method is saved automatically when you select another method or choose a different tab in the Component Editor.


Editing methods

You can edit only methods you created or overrode. In order to maintain the architecture of the framework, Actuate does not let you edit the methods it declares.

See also

How to edit a method you created or overrode


How to edit a method you created or overrode

1	In any of the views that display classes, double-click the class whose method you want to edit.

The Component Editor appears.

2	Choose the Methods tab.

The Methods page displays inherited and locally-defined methods.

3	To simplify your search, use the filter options to display only methods defined and redefined in the class.

1	Choose Filter.

The Method Filtering dialog appears.

2	Choose Only local methods, then choose OK.

Only methods redefined or defined in the class appear in the Methods page.

4	Select the method you want to edit.

5	Choose Edit.


Deleting methods

You can delete only methods you created. In order to maintain the architecture of the framework, Actuate does not let you delete the methods it declares.

See also

How to delete a method you created


How to delete a method you created

1	In any of the views that display classes, double-click the class whose method you want to delete.

The Component Editor appears.

2	Choose the Methods tab.

The Methods page displays inherited and locally-defined methods.

3	To simplify your search, use the filter options to display only methods defined and redefined in the class.

1	Choose Filter.

The Method Filtering dialog appears.

2	Choose Only local methods, then choose OK.

Only methods redefined or defined in the class appear in the Methods page.

4	Select the method you want to delete.

5	Choose Delete.

Tip

To recover a method you just deleted, choose Edit→Undo. This command only works if you did not perform another task after deleting the method.


Overriding inherited methods

You typically override inherited methods when you want to extend or replace their functionality. Because most of the predefined methods in Actuate's classes perform specific operations in the report-generation process, you need to be careful when overriding these methods. You need to understand how they were implemented, their interactions with other methods, and so on.

When you override a method, the changes apply only to the selected class. For example, if you override the OnLButtonClick( ) method for a class derived from AcTextControl, other classes of that same type are not affected by the change.

See also

Guidelines for overriding methods
Restoring methods to their default behavior


Guidelines for overriding methods

•	Understand how the method works and the context in which it runs. The following are some of the questions you need to ask:

•	What does the method do?

•	What occurs before and after the method is called?

•	What are the rules for calling the superclass method?

•	What value does the method need to return, if any?

Actuate Foundation Class Reference is a good source for answering some of these questions.

•	Decide if you are replacing or extending the inherited method.

•	If you are extending the code, you must call the original method in the super class, as shown in the following code snippet:

Function Start( ) As Boolean

Start = Super::Start( )

' Your new code

End Function

Depending on the method and what you want to accomplish, you might call the superclass method before, in between, or after your code.

•	If you are replacing the code, do not call the method in the ancestor class. You must, however, ensure that the replacement code performs all the necessary tasks that the original method performs. For information on how a method is implemented in Basic, look at the source code. The source code (.BAS files) is in the AFC directory.

See also

How to override a method


How to override a method

1	In any of the views that display classes, double-click the class whose method you want to override.

The Component Editor appears.

2	Choose the Methods tab.

3	Select the method whose behavior you want to override.

4	Choose Override.

The Method Editor appears.

5	Add code to the method.

Follow the same rules for writing regular procedures.

Tip

If you want to retain the method's default behavior, keep the Super statement.


Restoring methods to their default behavior

If you override a method, then decide to revert to its default behavior, you can do so easily without having to delete your code manually.

See also

How to restore a method to its default behavior


How to restore a method to its default behavior

1	In the Methods page of the Component Editor, select the method.

2	Choose Default.

•	The method name reverts to grey.

•	Actuate removes all the code you added to the method.


Debugging

The following topics are available. To view the topics in order, use the Browse button (>>) in the Help window's toolbar.

•	About debugging

•	Types of bugs

•	Starting a debugging session

•	Controlling program execution

•	Examining variable values

•	Viewing the stack of method calls


About debugging

Debugging is the process of locating and eliminating errors (bugs) from a program. Essentially, debugging involves understanding what goes on when your code executes. You can debug two items in your report application:

•	Basic source files (.BAS) included in your report design

•	Methods

Actuate provides several tools to help you debug code you write. Using these tools, you can perform the following debugging tasks:

•	Set breakpoints to execute instructions up to a specified point, then stop and see what has happened

•	Inspect or watch variables to check that values are being set as you expected

•	Execute a method one line at a time, or step over methods that you know are bug-free

•	View the current execution stack


Types of bugs

The first step in fixing bugs is understanding the three kinds of errors your code can contain.

•	Compilation, or syntax, errors

•	Run-time errors

•	Logic errors

See also

Compilation (syntax) errors
Run-time errors
Logic errors


Compilation (syntax) errors

Compilation errors result from incorrectly constructed statements. You might have mistyped a reserved word, omitted necessary punctuation, or forgotten to balance pairs of statements such as If and End If. Compilation errors are easy to find and fix because the compiler detects these errors during compilation and generates a list of error descriptions in the Actuate Developer Workbench Output window. When you double-click an error description or press F4, Actuate displays the method containing the error and points to the line where the error occurred.


Run-time errors

Your code might be free of syntax errors and compile, but still not run because it contains run-time errors. These errors occur while the code is executing and result when an instruction tries to perform an impossible task, such as opening a file that does not exist, or trying to divide a number by zero. Like compilation errors, run-time errors are easy to find and fix. When Actuate Basic encounters a run-time error, it stops program execution, displays the method containing the error, and points to the line where the error occurred.


Logic errors

Your code could be syntactically correct and run without any run-time errors, but still produce incorrect results. For example, you might expect your code to calculate a particular value, but instead, it results in an erroneous value. These errors, called logic errors, occur if, for example, you use the wrong operator or function, forget to initialize a variable, or assign an incorrect value to a variable.

Actuate Basic cannot identify logic errors as it can with compile and run-time errors. Therefore, much of your debugging effort typically goes into solving logical errors. The debugging tools help you identify logic errors by letting you monitor the values of your program variables and objects as the program executes.


Starting a debugging session

You can debug methods and Basic source files. The following topics provide step-by-step instructions for starting a debugging session for each item.

How to start a debugging session for a method

How to start a debugging session for a Basic source file

See also

How to start a debugging session for a method


How to start a debugging session for a method

1	Select the method to debug and display it in the Method Editor. To do so:

1	In the structure pane or library view, double-click the class whose method you want to debug.

The Component Editor appears.

2	Choose the Method tab.

The list of methods--inherited and declared--associated with the class appears. You can debug only methods that are declared or overridden in the class. To debug any of the inherited methods that appear in this list, access it from the class in which it is declared.

3	Choose Filter.

The Method Filtering dialog appears.

4	Select Only local methods, then choose OK.

The Methods page displays only methods redefined or defined in the class.

5	Choose the method you want to debug.

The code for the method appears in the Method Editor.

2	Set a breakpoint at the line of code where you want to stop execution by choosing Debug→Toggle Breakpoint or pressing F9. For more information about using breakpoints, see Running to a breakpoint.

3	Choose Report→Run to start code execution.

When Actuate Basic reaches the breakpoint, it stops program execution. You can then use Actuate's debugging features to manipulate code execution or inspect variable values.

See also

How to start a debugging session for a Basic source file


How to start a debugging session for a Basic source file

1	Choose File→Open.

The Open dialog appears.

2	Type or select the Basic source file you want to debug.

The code in the file appears in the source file window.

3	Set a breakpoint at the line of code where you want to stop execution by choosing Debug→Toggle Breakpoint or pressing F9. For more information about using breakpoints, see Running to a breakpoint.

4	Choose Report→Run to start code execution.

When Actuate Basic reaches the breakpoint, it stops program execution. You can then use Actuate's debugging features to manipulate code execution or inspect variable values.


Controlling program execution

With Actuate's debugging tools, you can control the execution of your report application. You can control whether Actuate Basic executes a single line of code, an entire method, or a larger program block. By specifying when the program should run and when it should pause, you can move quickly over the areas you know work correctly and focus on the areas that are causing problems. The following topics describe the different ways you can control program execution.

Running to a breakpoint

Stepping through code

Resuming code execution

Stopping code execution

See also

Running to a breakpoint
Stepping through code
Resuming code execution
Stopping code execution


Running to a breakpoint

You use breakpoints to pause program execution at specific locations so you can see what is happening. For example, if you have a complex method that is not working and you do not want to trace through each line, set a breakpoint at the beginning of the method. When Actuate Basic encounters a breakpoint, it suspends execution just before the line with the breakpoint. You can then use the other debugging tools to inspect the state of the method or trace execution line by line from that statement.

See also

How to set and clear breakpoints


How to set and clear breakpoints

1	In the Method Editor or Basic source file window, place the insertion point on the line of code where you want to set or clear a breakpoint.

2	Do one of the following:

•	Press F9.

•	Choose Debug→Toggle Breakpoint.

•	Choose the Breakpoint button on the toolbar.

If setting a breakpoint, a red dot appears to the left of the line of code, indicating that a breakpoint has been set. Clearing a breakpoint removes the red dot.


Stepping through code

Setting breakpoints helps you get to and stop at the location where you suspect a problem occurs. Once there, you can step through your code and execute it line by line to monitor the effect of each statement. When you step through code, Actuate Basic executes the current statement, advances to the next statement, and suspends execution.

There are two ways to step through code. You can:

•	Step into each line of code, including code in called procedures.

•	Step over code in called procedures.

In a debugging session, you typically use both techniques, depending on which portions of code you want to analyze at any given time. For example, if you are stepping through method A and method A calls method B, you can step over method B if you have already determined that method B is bug-free. When you step over method B, Actuate Basic still executes it, but as one unit instead of stopping after each line in method B. Conversely, you step into a called procedure if you are not sure if the code is executing properly.

See also

Watching how Actuate generates a report
How to step into procedures
How to step over procedures


Watching how Actuate generates a report

By stepping through code, you can observe how the Factory generates a report. You can trace through the Basic source files that make up the Actuate foundation classes, and learn the sequence of method calls the Factory makes to create the report.


How to step into procedures

Do one of the following:

•	Press F8.

•	Choose Debug→Step Into.

•	 Choose the Step Into button on the toolbar.


How to step over procedures

Do one of the following:

•	Press F10.

•	Choose Debug→Step Over.

•	 Choose the Step Over button on the toolbar.


Resuming code execution

After suspending code execution with a breakpoint, you can step through each line of code as described in the previous section, or resume code execution at full speed until the next breakpoint or until the end of the program. If there are parts of the code you do not want to debug, it is more efficient to execute those portions normally.

See also

How to resume code execution


How to resume code execution

Do one of the following:

•	Press F5.

•	Choose Debug→Continue.

•	 Choose the Continue button on the toolbar.


Stopping code execution

You can stop code execution at any time. When you do so, Actuate Basic clears the program from memory. You cannot resume code execution or debugging once you stop code execution.

See also

How to stop code execution


How to stop code execution

Do one of the following:

•	Press the Shift and F5 keys.

•	Choose Debug→Halt.

•	 Choose the Halt button on the toolbar.


Examining variable values

The capability to examine the values of variables is a key part of debugging. For example, it might be helpful to track the values of variables in a data row as each Fetch( ) executes, or to inspect the property variables of components as they are instantiated to verify that variables are set as expected. With Actuate's debugging tools you can monitor the values of variables as they change during code execution or check the value of a specified variable.

See also

Monitoring variables as their values change
Checking the value of a specific variable


Monitoring variables as their values change

To monitor variables during code execution, use the Variables window. The Variables window shows the values of all current variables, organized by the following scopes:

•	Global

Global variables are variables that are accessible to the whole application. For example, the variables that store the names of the report executable and report instance files are global.

•	Local

Local variables are variables declared within the method currently executing.

•	Instance

Instance variables are variables that are part of an object. For example, if the current object is a frame, the instance variables displayed would include the BackgroundColor, BackgroundIsClear, and BorderStyle variables.

The set of variables that appears in the Variables window changes depending on the portion of code that is executing. As you step through the code, you will notice that the information in the Variables window is updated continuously.

See also

How to open the Variables window
Interpreting the icons in the Variables window
Tracing object reference variables in the Variables window


How to open the Variables window

Choose Debug→Variables.


Interpreting the icons in the Variables window

The icons in the Variables window indicate the different types of variables.

 Variable or array of a fundamental data type such as string or integer.

 Structure. For information about structures, see Structures.

 Object reference variable. For information about object reference variables, see Working with objects.


Tracing object reference variables in the Variables window

In Actuate reports, objects typically contain object reference variables that contain references to other objects. For example, a control has an object reference variable called Container that contains a reference to the object the control is contained within. A frame has several object reference variables, including Container, DisplayContainer, and ContentList, that contain references to the frame's container object in the content hierarchy, the frame's container in the display hierarchy, and the frame's contents, respectively.

A feature of the Variables windows is the capability to see the contents of object reference variables and see how these variables point to other objects as the report is being generated. The following illustration shows how you expand successive levels of object reference variables to trace through a control's container and the list of controls in the container.


Checking the value of a specific variable

While you can monitor the values of variables in the Variables window, you might find it more convenient to specify the variable whose value you want instead of looking through all the variables in the Variables window. To do so, use the Watch window.

If the variable you want to check is a local or instance variable, the code must be in that context. For example, if the code being traced is in Method A, you can only check variables associated with that method. You can, however, always check the values of global variables.

See also

How to check the value of a specific variable


How to check the value of a specific variable

There are two ways to specify the variable whose value you want to check, as described in the following procedures.

Method 1

1	Press F3 or choose Debug→Watch.

The Watch dialog appears.

2	In the first text box, type the name of the variable whose value you want to check.

3	Choose Show.

The value appears in the second text box. If you typed an invalid variable name, Actuate displays <variable name> = <Unrecognized Variable>.

Method 2

1	From the source code, select the variable whose value you want to check.

2	Press Ctrl+C to copy the selected text.

3	Press F3 or choose Debug→Watch.

The Watch dialog appears. The variable you copied in step 2 appears in the first text box, and the variable's value appears in the second text box.


Viewing the stack of method calls

As you step through code execution, Actuate Basic keeps track of the method calls up to the current line. You can view this information in the Stack window. Each line in the Stack window indicates the method name and line number of the method in the source file. You can double-click any of the lines in the Stack window to get to the method in the source code.

A line in the Stack window that is highlighted in yellow shows the location where code execution is suspended. A line highlighted in green shows the current context, that is, where you last double-clicked to display the method in the source code. Variables in the Variables and Watch windows reflect the current context.

The following illustration shows how the contents of the Stack, source code, and Variables windows are related when code execution is suspended.

See also

How to open the Stack window


How to open the Stack window

Choose Debug→Show Stack.


Calling external C functions

The following topics are available. To view the topics in order, use the Browse button (>>) in the Help window's toolbar.

•	About calling external C functions

•	Using C functions with Actuate Basic

•	Declaring a C function

•	Calling a C function


About calling external C functions

Actuate Basic provides a rich set of statements and functions you can use in your report application. Sometimes, however, your program can accomplish more, or execute more efficiently with C functions. Actuate Basic lets you call external C functions as long as they are accessible to your application. In the Windows environment, your Actuate Basic application can call C functions that are stored in dynamic-link libraries (DLLs). On a UNIX system, your application can call C functions if they are stored in shared libraries.

Many companies and third-party developers supply libraries of C functions. For example, Microsoft Windows itself is composed of a collection of DLLs (often referred to as the Windows application programming interface or API) that contain the functions all Windows applications use to perform tasks, such as displaying windows and graphics, managing memory, and so on. By tapping into the vast resources of C libraries, you can extend the power of Actuate Basic.

For information about how to program C functions and publish them in libraries, refer to your C documentation.


Using C functions with Actuate Basic

Because C functions reside in files that are external to your Actuate Basic application, you need to provide information to Actuate Basic so that it can find and execute the C function you want to use.

There are two basic steps to using a C function:

1	Declare the C function in Actuate Basic by using the Declare statement. This step supplies the information Actuate Basic needs to convert and execute the C function.

2	Call the C function as you would an Actuate statement or function. When the function is called, Actuate loads the library if it is not already loaded, then executes the C function.

You declare the C function only once. Then, you can call the C function any number of times.

See also

Making C functions accessible


Making C functions accessible

To make C functions accessible to all parts of your Actuate application, write the Declare statements in a Basic source (.BAS) file, using any text or code editor. Then, include that file with your report project by choosing File→Include Basic File. You can also use the code editor in the Developer Workbench by choosing File→New Basic File.


Declaring a C function

To declare a C function, you need to provide the following information in the Declare statement:

•	Name of the C function.

•	Name of the library that contains the C function. In Windows, this is the name of a DLL, and in UNIX, the name of a shared library.

•	Name with which to call the C function (optional).

•	Arguments that the C function takes.

•	Return value, if any.

For complete syntax information about the Declare statement, see Chapter 2, "Statements and functions," in Actuate Basic Language Reference.

See also

Declaring the C function as a Sub procedure
Declaring the C function as a Function procedure
C function declaration issues
Determining Actuate Basic argument types


Declaring the C function as a Sub procedure

If the C function does not return a value, declare it as a Sub procedure, using the following syntax.

Declare Sub <function name> Lib <"library name">
[Alias <"alias name">][(<argument list>)]

The following is an example of a Sub procedure declaration.

Declare Sub LogError Lib "Kernel32"(ByVal uErr As Integer, lpvInfo As Any)


Declaring the C function as a Function procedure

If the procedure returns a value, declare it as a Function procedure, using the following syntax.

Declare Function < function name> Lib <"library name">
[Alias <"alias name">][(<argument list>)][As <type>]

The following is an example of a Function procedure declaration.

Declare Function GetSystemMetrics Lib "USER32" (i As Integer) As Integer


C function declaration issues

If you plan to declare and call C functions, make sure you have documentation on those procedures. Declarations for C functions can get fairly complex for a number of reasons:

•	C often passes arguments by value whereas Actuate Basic, by default, passes arguments by reference.

•	C type declarations differ from Actuate Basic's.

•	A C function could have a name that is not a valid identifier in Actuate Basic.

The following topics explain the syntax of the Declare statement in more detail so that you can create the correct declaration for the C function.

Specifying the library of C functions

Passing arguments by value or reference

See also

Specifying the library of C functions
Passing arguments by value or reference
Aliasing nonstandard C function names


Specifying the library of C functions

On a Windows system, you specify the name of a DLL in the Lib<"library name"> clause. On a UNIX system, you specify the name of a shared library.

<"library name"> can be a file specification that includes a path. For example,

Declare Function GetSystemMetrics Lib "C:\WINNT\SYSTEM32\USER32" (i As Integer) As Integer

If you omit the path, Actuate searches for the library in the following order.

1	The directory from which Actuate was loaded

2	The current directory

3	The Windows system directory

4	The Windows directory

5	The directories listed in the PATH environment variable

If you omit the file extension, Actuate assumes a .DLL extension.


Passing arguments by value or reference

By default, Actuate Basic passes all arguments by reference. However, many C functions expect arguments to be passed by value. If you pass an argument by reference to a procedure that expects an argument passed by value, the procedure cannot interpret the data and fails to work properly.

To pass an argument by value, use the ByVal keyword before the argument declaration in the Declare statement. This ensures that each time you call the procedure, the argument is passed by value. For example:

Declare Function GetFreeSystemResources Lib "User32" (ByVal fuSysResource As Integer) As Integer

The arguments that you pass by reference to C functions are strings and arrays.

Flexible argument types

Some C functions can accept more than one type of data for the same argument. To pass more than one type of data, declare the argument with As Any to remove type restrictions. For example, you can declare a procedure as follows.

Declare Function SendMessage Lib "User32" (ByVal hWnd As Integer, ByVal msg As Integer, ByVal wp As Integer, ByVal lp As Any) As Long

When you call the function, you can pass either a string or a long integer as its last argument:

FindItem = SendMessage(aList.hWnd, LB_FINDSTRING, -1, target)


Aliasing nonstandard C function names

Occasionally, a C function has a name that is an invalid Actuate Basic identifier. It might start with an underscore, contain a hyphen, or have the same name as an Actuate Basic reserved word. When the C function name is an invalid Basic identifier, use the Alias keyword, as shown in the following example:

Declare Function LOpen Lib "Kernel32" Alias "_lopen" (ByVal fn As String, ByVal f As Integer) As Integer

In this example, you call LOpen in your Basic code. _lopen is the name of the C function in the DLL or shared library.

You can use Alias whenever you want to use a different function name from the actual C function name. You might, for example, want to substitute a shorter name for a long C function name.


Determining Actuate Basic argument types

To call C functions from Actuate Basic, you need to translate them into valid Declare statements. The following table lists the Actuate Basic argument types to declare in the Declare statement based on the C argument types of the function you want to call.

C type	Actuate Basic type
int *x, int &x	x As Integer
int x	ByVal x As Integer
long *x, long &x	x As Long
long x	ByVal x As Long
double *x, double &x	x As Double, x As Single, x As Date
double x	ByVal As Single, ByVal As Double, ByVal As Date
(1) AcCurrency *x	x As Currency
AcCurrency x	ByVal x As Currency
(2) RWCString *x, RWCString &x	x As String
char *x, LPCSTR x (null terminated string)	ByVal x As String
union { int, long, double, AcCurrency, RWCString } *x	x As Variant, x As Any
actual data (int, long, double, AcCurrency, char*)	ByVal x As Variant, ByVal As Any
void**x (pointer to any C/C++ pointer)	x As CPointer
void* x	ByVal x As CPointer
int *x, int x[ ]	x( ) As Integer, ByVal x( ) As Integer
long *x, long x[ ]	x( ) As Long, ByVal x( ) As Long
double *x, double x[ ]	x( ) As Single, ByVal x( ) As Single, x( ) As Double, ByVal( ) As Double, x( ) As Date, ByVal x( ) As Date
AcCurrency *x, AcCurrency x[ ]	x( ) As Currency, ByVal x( ) As Currency
RWCString **x, RWCString &x[ ]	x( ) As String
char **x, char *x[ ]	ByVal x( ) As String
void **x, void *x[ ]	x( ) As CPointer, ByVal x( ) As CPointer

(1) AcCurrency is a C structure whose size is 12 bytes.

struct AcCurrency
{
unsigned long low;
unsigned long mid;

long high;

};

The currency value is stored in fixed point format:

<96-bit integer value in AcCurrency structure> = <currency value> * 10 ** 9

(2) RWCString is a class for string data types provided by Rogue Wave Software, Inc.


Calling a C function

Once you declare a C function, you can call it as you would an Actuate Basic statement or function. Be careful to pass the correct arguments to a C function. Actuate Basic cannot verify the arguments you pass. If you pass incorrect arguments, the procedure might fail, which might cause your Actuate application to shut down.

See also

Calling C functions with specific data types
Return types from C functions


Calling C functions with specific data types

Actuate Basic provides a range of data types, including some--such as variable-length strings, AnyClass, and Object--that are not supported by C functions. The following sections discuss some of the data type issues you might need to consider when passing arguments in function calls.

See also

Passing strings to C functions
Passing arrays to C functions
Passing null pointers to C functions
Passing user-defined types to C functions
Passing object reference variables to C functions


Passing strings to C functions

Procedures in most DLLs expect standard C strings, which end in a null character (binary zero). If a C function expects a null-terminated string as an argument, declare the argument as a string with the ByVal keyword. With string declarations, the ByVal keyword is somewhat misleading because ByVal tells Actuate Basic to convert the string to a null-terminated string, not that the string should be passed by value. In fact, strings are always passed to C functions by reference.


Passing arrays to C functions

You pass individual elements of an array in the same way you pass any variable of the same type as the base type of the array. You can also pass an entire array of any type except user-defined types. Actuate copies data in the Actuate Basic array into a C array before passing it to the C function.

If you pass a multidimensional array, the C function reverses the dimensions. For example, if you declared an Actuate Basic array with Dim x (3, 4, 5) As Integer, it is converted to Int x [5][4][3] in C.

Passing an array by value is faster than passing it by reference. Use the ByVal keyword to pass an array whenever possible.


Passing null pointers to C functions

In Actuate Basic, any of the fundamental data types can be null. Therefore, to pass a null pointer to a C function, use the Null keyword, as the following examples show:

Declare Sub Func1 Lib "LIBABC" (ByVal p As CPointer)

Dim ptr As CPointer

Func1(ptr)	' pass CPointer ptr to the C function

Func1(Null)	' pass null pointer to the C function

Declare Sub Func2 Lib "LIBXYZ" (ByVal s As String)

Dim s As String

s = "text"

Func2(s)	' pass null-termintated string to the C function

Func2(Null)	' pass null pointer to the C function


Passing user-defined types to C functions

You cannot pass user-defined types to a C function.


Passing object reference variables to C functions

You cannot pass object reference variables to a C function. Object reference variables are complex data structures that C functions cannot handle.


Return types from C functions

The following table shows how return values are converted to Actuate Basic data types.

C type	Actuate Basic type
int	Integer
long	Long
double	Single, Double, Date
char* (null terminated)	String
(char*) NULL	String (empty)
any pointer (void*, etc.)	CPointer


The report generation process

The following topics are available. To view the topics in order, use the Browse button (>>) in the Help window's toolbar.

•	Overview of the report generation (Factory) process

•	How the Factory starts

•	Content creation

•	Page creation

•	How the framework instantiates classes

•	Data streams

•	Connections to databases


Overview of the report generation (Factory) process

The following illustration shows when the report-generation or Factory activities occur in relationship to the entire report-building process.

The Factory is a batch program that generates the report according to the design you created through the Design Editor. The Factory runs the report executable (.ROX) and creates a report file (.ROI). If the report is an HTML report, the Factory makes an additional run to produce HTML output, ignoring any page-oriented objects. The Factory then creates the HTML report file (.HTML if the report is running on the client, .ROW if the report is running on the Report Server). You view the .ROI in the viewer or print it; you view the .HTML or .ROW in your web browser.

The .ROI file is composed of persistent objects. The Factory deletes all transient objects, such as data sources, data filters, and data rows, when they have finished their tasks. If the report is an HTML report, the factory deletes the .ROI file by default when report generation finishes.

The following illustration gives a conceptual overview of activities that occur in the Factory.

The following illustration shows a different view of the Factory, that is, it shows the major categories of processing: data streams, content creation, and page creation.

Understanding the framework's default behavior will help you decide which methods to override if you want to modify the report-generation process.


How the Factory starts

When you choose Report→Run, Actuate executes a function called Factory( ), which is specific to your report. Factory( ) performs the following tasks:

1	Creates the report instance (.ROI) file, using a default name (the same root name as the report design (.ROD) file) or the name you typed when the Requester prompted for the output file name.

2	Creates the component relationship map. The component relationship map stores component reference information--how components connect together--derived from the report design's structure. Actuate uses this map to determine each object's contents.

3	Instantiates the report (a subclass of AcReport), the root object that contains all objects in a report.

4	Calls the report's Build( ) method. This step starts the actual report-generation process.

5	If this is an HTML report:

1	Repeats step 2 to create the HTML component relationships.

2	Instantiates the HTML report.

3	Closes the .HTML file.

6	Closes the .ROI file.

See also

Adding startup and cleanup code
How the report starts the build process


Adding startup and cleanup code

You can add code that runs before the Factory initiates the actual report generation task and after it has completed generating the report. You do so by overriding the report's Start( ) and Finish( ) methods. If you override Start( ), you must call Super::Start( ) first. If you override Finish( ), you must call Super::Finish( ) after your code.

Method	Called ...	Example of use
Start( )	After the Factory instantiates the report, before report generation begins (after step 3 in the factory tasks described in the previous section).	• Initialize global variables. • Open a log file to track the objects that are created, or the number of pages that are created.
Finish( )
After the report is generated, before the .ROI file is closed (after step 4 in the factory tasks described in the previous section).
·
Send completion notice to users.
Write statistics to a log file.


How the report starts the build process

As stated earlier, one of the first tasks the Factory does is to instantiate the report class. The Report class establishes the report's content and page structures.

The content structure is made up of all the objects that hold data. The page structure is made up of all the objects that determine how the contents are displayed. The following illustration shows examples of the two structures.

After instantiating the report class, the Factory calls the report's Build( ) method. The report's Build( ) method performs the following tasks:

1	Calls NewPageList( ) to instantiate the page list specified in your report design

2	Calls NewContent( ) to instantiate the top-level content specified in your design. In a typical design, the top-level content is a report section.

3	Calls the top-level content's Build( ) method to build the contents of the report.


Content creation

All report contents--sections, frames, controls, and graphs--follow a protocol that determines how they are built. The protocol makes it possible to connect the report components into a wide variety of configurations. For example, the top-level content component can be a report, a sequential section, or even a frame. You can nest reports within reports, sections within reports, sections within sections, frames within frames, and so on.

The core protocol for content creation

AcReportComponent is the abstract base class that defines the protocol for how report components are created and how they fit together to form a report. The following methods, defined in AcReportComponent, form the core protocol for all persistent content objects:

Method	Description
New( )	Initializes the object.
Start( )	Prepares the object for build operations. For example, the report section's Start( ) method instantiates the connection and the data stream. The frame's Start( ) method instantiates the controls it contains.
Build( ) or BuildFromRow( )	Builds the object's contents. For example, the report section's Build( ) method reads data rows from the data stream, instantiates its contents, and passes the data rows to them. The frame's BuildFromRow( ) method passes the data rows to the controls it contains.
Finish( )	Prepares the object so that it is ready to be written to the .ROI file. For example, the report section's Finish( ) method closes the data stream and connection.
See also

Component references and content creation


Component references and content creation

Component references play a critical role in how the report contents are built. Component references are the relationships among components, as reflected in the structure of the report design. When a component uses or contains another component, the first component has a reference to the other.

Container objects initiate the creation of their contents. For example, the report is the topmost container object and is responsible for creating the report section contained immediately within it. The report section, in turn, is responsible for creating the reports, sections, or frames within it. Finally, the frame is responsible for creating the frames and controls within it.

The following illustration shows an example of a report design and how the component reference relationships determine how the report contents are built.

The following topics describe how some of the typical report content components--report sections, group sections, frames, and controls--implement the core protocol for content creation. Understanding the key methods and the sequence of tasks they perform can help you decide where to place your code if you need to add custom processing.

•	How a report section implements the content-creation protocol

•	How a group section implements the content-creation protocol

•	How a frame implements the content-creation protocol

•	How a control implements the content-creation protocol

See also

How a report section implements the content-creation protocol
How a group section implements the content-creation protocol
How a frame implements the content-creation protocol
How a control implements the content-creation protocol


How a report section implements the content-creation protocol

The report section, a subclass of AcReportSection, is typically the top-level content in a report design. It performs many key functions in the Factory. It gets the data rows from the data stream and passes the rows to its contents.

The following table describes how the report section implements the core protocol.

Method	Task
Start( )	1 Instantiates the connection.
	2 Instantiates the data stream.
	3 Passes the connection to the data stream
	4 Sets the sort key.
Build( )	1 Opens the data stream.
	2 Creates the Before frame.
	3 Reads a row from the data stream.
Build( ) cont.
4
Processes the row.
·
If content already exists, the reports checks if the content needs the row. The report passes the row to the content if the contents needs it.
If content does not exist, or if an existing content rejects the row, the report instantiates a new content, and passes the row to that new content.
	5 Repeats steps 3 and 4 until all data rows have been retrieved.
	6 Creates the After frame.
Finish( )	Closes the data stream and connection.


How a group section implements the content-creation protocol

A group section organizes data by grouping data on a key field, such as customers grouped by sales representative. Group sections are typically the contents of report sections. They, in turn, contain other group sections or frames.

Each group section uses a unique sort key to define the group of rows. You set the group section's sort key, typically the name of a table column, in the Key property.

The group section checks the key of each data row it receives. Rows with the same key belong in the same group section. A change in a row's key indicates the end of one group section and the start of another.

The following table describes how the group section implements the core protocol.

Method	Task
Start( )	Initializes the group section.
BuildFromRow( )	1 Accepts a data row from its container object. 2 Processes the row. • If the row is the first row, the group section determines and stores the value of the sort key. The group section also creates the Before frame, passes the row to its contents, and returns Continue Building. This return value indicates to the container object that the group section needs the next data row. • If the row is not the first, the section checks if the row's key is the same as the stored key value. If the keys match, the group section passes the row to its contents. If the keys do not match, BuildFromRow( ) creates the After frame, and returns Rejected Row, indicating the end of a group section. 3 Repeats steps 1 and 2 until all data rows returning Continue Building have been retrieved.
Finish( )	Finishes the group section.


How a frame implements the content-creation protocol

Frames are key building blocks in a report. They typically contain controls that display data from a data row. Frames get data rows from their container objects, typically sections, and pass those rows to the controls.

The following table describes how frames implement the core protocol.

Method	Task
Start( )	Instantiates and starts the frame's contents in the order in which they appear in the structure. The frame's contents can be other frames, graphs, or controls.
BuildFromRow( )	Passes data rows to the frame's contents.
Finish( )	Calls each control's Finish( ) method.


How a control implements the content-creation protocol

A control typically displays a value from the data row it receives from its container object, the frame. Controls form the lowest branches of the content hierarchy and do not contain any other components.

The following table describes how controls implement the core protocol.

Method	Task
Start( )	Usually, the control needs only the default logic.
BuildFromRow( )
Sets the control's value, using data from the data row.
·
If the control, such as a line or label control, does not need the data row, BuildFromRow( ) returns Finished Building.
·
If the control needs only one row, BuildFromRow( ) sets the value of the control and returns Finished Building.
If the control is an aggregate control, it uses all data rows, so BuildFromRow( ) always returns Continue Building.
Finish( )	For aggregate controls, performs final calculations. For other controls, Finish( ) does nothing.


Page creation

The page creation process is driven by the content creation process. The two processes, however, occur concurrently. As each frame is finished, the section that contains the frame passes the frame to the page list. As each page begins or ends, the page list notifies the section so that it can generate the page headers or footers.

As the illustration shows, the sections and the page list work together. Actuate provides several page list styles: AcSimplePageList, AcLeftRightPageList, and AcTitleBodyPageList. Sections can work with all these types of page lists because page lists follow a standard protocol. The protocol, defined in AcPageList, consists of the AddFrame( ) method. AddFrame( ) places the frame on a page. AddFrame( ) is called from the page list to the page to the flow where the frame is finally placed.

See also

Determining the page on which to place a frame
Determining the page style
Events the page list sends to the section


Determining the page on which to place a frame

The section passes the frame to the page list by calling the page list's AddFrame( ) method. AddFrame( ) determines the page on which to place the frame by checking the following conditions:

•	If the frame's PageBreakBefore property is set to True, AddFrame( ) finishes the current page if one exists, then starts a new page.

•	If the frame's PageBreakBefore property is set to False (the default), AddFrame( ) adds the frame to the page if it fits. If it does not fit, the page list starts a new page.

•	If the frame does not fit on a new page, AddFrame( ) clips the frame to the available space.

•	After placing the frame on a page, AddFrame( ) checks the value of the frame's PageBreakAfter property. If it is set to True, AddFrame( ) finishes the current page. AddFrame( ) starts a new page when it gets the next frame.


Determining the page style

Before instantiating a new page, the page list checks if the section or frame has a specific page style specified through its NewPage( ) method. If the frame or section specifies a page style, the page list uses the specified page. If not, the page list uses its default page style.


Events the page list sends to the section

The page list sends notices to the section when the following events occur:

•	StartFlow

•	StartPage

•	FinishFlow

•	FinishPage

These events traverse the content structure to determine if the contents need to output headers at the start of a new flow or page, or footers at the end of a flow or page. The following illustration shows the order in which headers and footers are output.


How the framework instantiates classes

When you create a report design with the Design Editor, you combine the different components in a particular order, following the predefined set of component reference rules that each component has. For example, AcReportSection supports the following references: Connection, DataStream, Before, PageHeader, Content, PageFooter, and After. The component references are represented as slots in the Structure pane and as properties in the Component Editor, as shown in the following illustration.

For each component reference that a component supports, the component has a corresponding method. These methods have a "New" prefix followed by the name of the reference. For example, AcReportSection's connection component reference has a corresponding NewConnection( ) method and its datastream component reference has a corresponding NewDataStream( ) method.

Container objects use these New<reference> methods to instantiate their contents. If you need to conditionally instantiate components, you can override the New<reference> methods. For example, if your report needs a different connection depending on the data stream being used, you would override NewConnection( ) to write the conditional logic. For an example of how to conditionally instantiate a component, see Conditionally creating a component.


Data streams

A data stream is made of data adapters that collect, process, and deliver data rows to the report and sections. The reports and sections pass these data rows to their contents until all the data controls have the values they need. In the Factory, the report's Start( ) method instantiates the data stream as part of its startup tasks before beginning the build process.

See also

Types of data adapters
How data adapters combine to form the data stream
Protocol for data adapters
How data adapters are instantiated
Sequential and random access to data
About data sources
About data filters
About data rows


Types of data adapters

There are two types of data adapters: data sources and data filters. Data sources retrieve data from an input source, such as a database or spreadsheet, and create data rows. Data filters sort, filter, or perform other computations on data rows.

Data sources, data filters, and data rows are transient objects. The Factory deletes them when they have retrieved, processed, and delivered all the data rows to the report.


How data adapters combine to form the data stream

The following are characteristics of a data stream:

•	It must have at least one data source. It can have multiple data sources. If your report uses data from multiple input sources, you need a data source for each input source.

•	It can have any number of data filters.

The following illustrations show examples of different data stream configurations.


Protocol for data adapters

Data sources and data filters follow a common protocol, which is defined in the abstract base class, AcDataAdapter. This protocol allows data adapters to connect together. The following table describes the protocol:

Method	Description
Start( )	Opens the data adapter
Fetch( )	Retrieves a single row. To retrieve all rows, the data adapter calls Fetch( ) repeatedly until there are no more rows. Fetch( ) returns Nothing when all rows have been retrieved.
Finish( )	Closes the data adapter


How data adapters are instantiated

The report instantiates the data adapter that provides data rows to it. If a data stream is composed of multiple data adapters, each data adapter instantiates the one that delivers data rows to it. The way you set up the component references in the structure pane determines the order in which data adapters are instantiated. In the following illustration, the report section instantiates data filter 2, data filter 2 instantiates data filter 1, and data filter 1 instantiates the data source.


Sequential and random access to data

Typically, input sources allow only sequential access to their data. A SQL database, for example, returns a set of records based on your query and sends one input record at a time to the data source sequentially. Sometimes, however, your report needs random access to data. A report, for example, might use the same data multiple times to present data in both tabular and graph formats. A data filter might also need to make multiple passes over a set of rows for sorting or calculating totals.

To support random access, AcDataAdapter defines the following methods: GetPosition( ), CanSeek( ), SeekTo( ), SeekBy( ), SeekToEnd( ), and Rewind( ). Actuate provides two data filter classes, AcMemoryBuffer( ) and AcMemoryDataSorter( ), that implement the random access methods. You can use these classes in your data stream if you need data filters that can process rows in any order.


About data sources

As stated earlier, a data source retrieves data from an input source and creates data rows. It does so by using three standard steps:

1	Open the input source.

2	Fetch the data. Each time the data source retrieves an input row, it instantiates a data row.

3	Close the input source.

Actuate provides a data source, AcSQLQuerySource, that you can use to retrieve data from SQL databases. If your report uses data from a different source, such as a flat file database, you need to create a custom data source. For an example of how to do so, see Retrieving data from a flat file.

Data sources typically require a connection in order to access the input source. For information about connections, see Connections to databases.

See also

How a data source implements the data adapter protocol


How a data source implements the data adapter protocol

Method	Description
Start( )	Opens the input source. If the input source is a SQL database, Start( ) opens the database cursor. If the input source is a spreadsheet or a flat-file database, Start( ) opens the file.
Fetch( )	Retrieves a single input row and creates a data row.
Finish( )	Closes the input source.


About data filters

Data filters receive data rows from one or more data adapters, process the data, then pass it to the next data adapter or to the report. Actuate provides two abstract classes of data filters:

•	AcSingleInputFilter, which accepts input from one data adapter

•	AcMultipleInputFilter, which accepts input from multiple data adapters

Unlike data sources, data filters are optional. If your report uses data from a SQL database, the database sorts and filters the data, using the criteria you specify in a query. In this case, you use a data filter only if you want to process the data outside of the database.

Typically, you use data filters to process data from a non-SQL database, or data that originates from several different external sources. For example, if your data stream has multiple query data sources, you might need to merge the resulting data through a multiple-input filter before passing it to the report. The processing algorithm is always implemented in the Fetch( ) method. For examples of creating data filters, see Creating a sort filter. How a data filter implements the data adapter protocol

Method	Description
Start( )	Instantiates and opens the input adapter, the data adapter that provides data rows to it.
Fetch( )	Retrieves a single data row and processes it. Optionally, the data filter creates a new data row.
Finish( )	Closes the input adapter.


About data rows

The data source creates a data row instance for each input record it retrieves. A data row is composed of variables, each corresponding to a single field or column of the input record.

If you use the Query Editor to write SQL query, the data source creates the data row automatically. If you create a custom data source or a custom data filter, you need to create a custom data row that works with the data source or filter. For an example of how to do so, see Retrieving data from a flat file.

See also

Data row life cycle
Runtime binding and reusability of data rows


Data row life cycle

This section describes how the framework creates, assigns values to, and uses data rows. Understanding this model is important if you want to create data rows programmatically.

1	The data source instantiates a data row each time it retrieves an input record with Fetch( ).

2	The data source copies the input record's column values to the data row's variables. If your report uses the SQL query data source, the SetupDataRow( ) method of the SQL query source performs this task.

3	Data controls get their values from the data row. The control's SetValue( ) method calls the data row's GetValue( ) method, passing it the column, variable name, or method name specified in the control's ValueExp property.

4	GetValue( ) calls the row's BuildBinding( ) method to create a table that maps table columns to data row variables. The following is an example of code that BuildBinding( ) generates:

5	Using the information in this table, GetValue( ) matches the control's ValueExp property value with the corresponding data row variable, and returns the value of the variable.


Runtime binding and reusability of data rows

Binding the data row values with controls through GetValue( ) at run time makes data streams reusable because GetValue( ) does not rely on a specific data row class. The following code snippet, generated by the Designer, shows how GetValue( ) references a generic data row instead of a specific data row class.

Class TextControl Subclass Of AcTextControl

        ...

        Sub SetValue( row As AcDataRow )

            DataValue = row.GetValue( "customers.customName" )

        End Sub
...

End Class


Connections to databases

The connection establishes the communication link to the database. Actuate provides a flexible model for specifying the connection for a data stream. You can place the connection either in a section or in the data stream. Where you place the connection depends on whether data adapters need to share a connection.

See also

Placing the connection in a data stream
Placing the connection in a section
Connection precedence
SQL support
Using statements and cursors
Connection relationships
Methods used to retrieve data
Creating and executing a SQL statement


Placing the connection in a data stream

The standard way to work with database connections is to place the connection in the data stream. This groups the connection and the data stream together, which makes it easy to publish and reuse the data stream. This technique, however, is efficient only if your report uses just one data stream, or if each data stream in the report requires a different connection.

When you place a connection in the data stream, the data adapter instantiates the connection by calling NewConnection( ) and opens it by calling OpenConnection( ). You can override NewConnection( ) to customize the selection of a connection, and OpenConnection( ) to customize the connection before opening it.


Placing the connection in a section

Place connections in a section if multiple data streams can or need to share a connection. For example, if the report contains multiple subreports, each containing a data stream, you can place a single connection in the section common to all the subreports. By sharing connections whenever possible, you can improve the application's performance.

When you place a connection in a section, the section's Start( ) method instantiates the connection and passes it to the data source. The following illustration shows the relationships among the data source, report section, and connection when the connection is in a report section.


Connection precedence

If the report contains multiple connections, the data stream uses the connection closest to it in the report structure. For example, a connection placed in a data stream takes precedence over a connection in a section. If a data stream does not contain its own connection, the framework searches upward in the report structure until it finds a connection.


SQL support

Actuate provides specific connection classes to connect to the following databases: DB2, Informix, Microsoft SQL, ODBC, Oracle, Progress, and Sybase. If you a need a connection to any other type of input source, subclass AcConnection, the abstract base class for all connection classes, to create the connection.

AcConnection establishes the protocol for connecting and disconnecting, and generating error messages if a connection fails. The connection-specific classes, AcDB2Connection, AcMSSQLConnection, AcInformixConnection, AcODBCConnection, AcOracleConnection, AcProgressConnection, AcSybaseConnection, and AcSybaseDBLibConnection, define the variables, such as user name and password, that are necessary to establish a connection.


Using statements and cursors

To access data in a SQL database, you typically use a SQL query data source. The SQL query data source assembles the SQL SELECT statement you built with the Query Editor and sends the statement to the database. To communicate with the database, the data source uses statements (AcDBStatement) and cursors (AcDBCursor).

The statement provides a way to execute the SQL SELECT statement. The result of a SELECT statement is typically a set of records. When a SQL statement returns table data, you need a database cursor. The cursor manages the retrieval of rows of data. It also keeps track of the row position in the record set as the database sends each row to the data source.


Connection relationships

The following describes the interactions among the report section, data source, connection, statement, and cursor.

•	The report section owns the data source and is responsible for instantiating and deleting it. If the connection is placed in the report section, the report section owns the connection.

•	The data source owns the statement and cursor and is responsible for instantiating and deleting them. The data source calls the connection's Prepare( ) method to instantiate the statement. Then, the data source calls the statement's AllocateCursor( ) method to instantiate the cursor. If the connection is placed in the data source, the data source owns the connection and is responsible for instantiating and deleting it.

•	The data source uses the connection.

•	The statement uses the connection.

•	The cursor uses the statement.


Methods used to retrieve data

The following illustration shows the method calls the report section, query data source, connection, statement and cursor objects execute to retrieve data from a database. For more information about each method, see Chapter 3, "AFC classes," in Actuate Foundation Class Reference.


Creating and executing a SQL statement

When you use the SQL query data source to retrieve data, Actuate executes all the previous steps automatically. You can, however, create and send your own SQL statements to the database. For example, you could send SQL statements to update database records, drop tables, and so on. To do so, call the statement's Execute( ) method after Prepare( ) to execute the SQL statement.

Tip

Use the statement's Execute( ) method only if you are not expecting the database to return a set of rows, for example, to execute statements such as UPDATE, DELETE, or DROP TABLE. If you execute a SQL statement that returns rows, you must use a database cursor.

See also

How to execute a SQL statement that does not require a cursor


How to execute a SQL statement that does not require a cursor

1	Establish the connection.

2	Prepare the SQL statement using the connection's Prepare( ) method.

Prepare( ) returns a reference to the DB statement object.

3	If the SQL statement accepts parameters whose values are provided later, use the statement's BindParameter( ) method to bind the parameters with the values.

4	Execute the statement with the statement's Execute( ) method.

Execute( ) returns True or False, depending on whether the statement ran successfully.

5	Let the framework delete the statement.

6	Let the framework delete the connection.


Working with objects

The following topics are available. To view the topics in order, use the Browse button (>>) in the Help window's toolbar.

•	About working with objects

•	Creating objects

•	Using object reference variables

•	Object lifetime


About working with objects

Everything--frames, labels, images, graphs--in a report is an object. Using the Component Editor, you can set the properties of components at design time. Sometimes, however, you need to modify objects at run time, for example, to display negative numbers in red and positive numbers in black.

Actuate Basic lets you manipulate the objects in your report easily by declaring and using variables that reference objects. These object reference variables allow you to manipulate an object as easily as you manipulate an integer or string variable.

The following topics describe how you create objects and how you use object reference variables to manipulate them. While you can accomplish all the tasks by writing code, you do not have to write everything from scratch because Actuate generates code based on the design you create in the Design Editor. For example, tasks such as creating objects are done by Actuate when you program visually.

Creating objects

Using object reference variables


Creating objects

There are two steps to creating objects:

1	Declare an object reference variable that will hold a reference to the object

2	Create the object using the New or New Persistent keywords.

Declaring object reference variables

You declare an object reference variable in the same way you declare other variables, with

•	Dim

•	Redim

•	Static

•	Global

The only difference is that you assign a class or the AnyClass type as the type of the variable, as shown in the following syntax:

{Dim | Redim | Static | Global} <variable name> As {<class> | AnyClass}

For more information about Set, Redim, Static, and Global, see Chapter 2, "Statements and functions," in Actuate Basic Language Reference.

See also

Declaring as a specific class
Creating objects with New


Declaring as a specific class

You typically declare an object reference variable as a specific class. You can specify an Actuate foundation class, a subclass derived from the foundation classes, or a custom class. Actuate Basic recognizes items as classes if they are declared with the Class statement. For information about class declarations, see Declaring a class.

For example, to create an object reference variable of type AcLabelControl, an Actuate foundation class, the declaration statement might look like the following:

Dim MyLabelControl As AcLabelControl

For a list of all classes in the Actuate Foundation Class library, see Chapter 3, "AFC classes,"in Actuate Foundation Class Reference or open AFC.ROL by choosing View→Browse→Library. For a list of all the classes available to your report application, open the Globals Browser by choosing View→Browse→Globals.

See also

Declaring as AnyClass type


Declaring as AnyClass type

Sometimes, you need to declare an object reference variable that can hold a reference to an object of any class. For example, if you need an object reference variable to refer to an object but you do not know the class of the object, declare the object reference variable as AnyClass.

Dim handle As AnyClass


Creating objects with New

Declaring an object reference variable does not allocate the object itself. The object does not exist in memory until the class is instantiated. To create the object, use the New keyword with Set.

The following syntax shows the syntax for creating objects:

Set <variable name> = New [Persistent] <class> [(<argument list>)]

Set ...New creates a new object of <class> and stores the reference to the object in <variable name>. The following example creates a label object and stores the reference to the label in the MyLabelControl object reference variable declared previously.

Set MyLabelControl = New AcLabelControl

Use Set... New Persistent if you want the object to exist until the report file is deleted. When Actuate generates the report objects that users view and interact with, it creates persistent objects by default. For more information, see "Persistent objects".

For more information about the Set statement, see Chapter 2, "Statements and functions," in Actuate Basic Language Reference.


Using object reference variables

Once you create an object, you can manipulate it through the object reference variables. You can call the object's methods or access the object's member variables. You can also manipulate the object reference variable itself. You can pass object variables to procedures, make them refer to other objects, compare and test them.

See also

Understanding object reference variables
Referencing an object's variables and methods
Referencing methods of a class
Assigning to an object reference variable
Passing object references to procedures
Getting information about objects
Testing object references with the Is operator


Understanding object reference variables

When working with objects, one of the most important concepts to understand is the difference between simple variables (such as an integer or string variable) and object reference variables.

How simple variables work

Simple variables contain values. For example, a string variable contains a character string, and a numeric variable contains a number.

When you assign one variable (Number1) to another (Number2), the contents of Number1 are copied to Number2. Subsequent changes to the contents of Number1 have no effect on Number2. The following example demonstrates this:

Example

Dim Number1, Number2 As Integer
Number1 = 7
Number2 = Number1	'Number2 contains the value 7
Number1 = 77
Print Number1	'Prints 77
Print Number2	'Prints 7

The following illustration shows the result of the preceding code.

How object reference variables work

Object reference variables also contain values. However, the value an object reference variable contains is the reference to, or address of, an object. The object reference variables does not contain the object itself.

When you assign one object reference variable to another, you do not copy the object. Instead, you create another reference to the same object.

The following code creates a label and sets its text property. The object reference variable LabelControl1 refers to the label.

Example

' Declare an object reference variable

Dim LabelControl1 As AcLabelControl

' Create the object

Set LabelControl1 = New AcLabelControl

' Set the Text property of the label
LabelControl1.Text = "April Sales Report"

The following illustration shows the result of the preceding code.

The following code assigns another object reference variable, LabelControl2, to the first object reference variable, LabelControl1.

Example

Dim LabelControl2 As AcLabelControl

Set LabelControl2 = LabelControl1

LabelControl2.Text = "Annual Sales Report"

Print LabelControl2.Text      'Prints "Annual Sales Report"

Print LabelControl1.Text      'Prints "Annual Sales Report"

The following illustration shows the result of the preceding code.

Summary

The previous examples demonstrate two important points about object reference variables:

•	More than one object reference variable can refer to the same object.

•	When you change an object's properties, the change is visible in all references to the object.


Referencing an object's variables and methods

To change, store, or retrieve an object's values, you reference its variables and methods by using the following syntax, commonly called dot notation or the member-of operator:

<object reference variable>.<variable>

<object reference variable>.<method>

The dot tells Actuate Basic to access a variable or method in an object.

For example, to refer to a variable or method in a label control, specify the object reference variable, followed by a period, followed by the variable or method name:

MyLabel.BackgroundColor
MyLabel.Build( )

To change the characteristic of the label, assign a value to one of its variables:

MyLabel.BackgroundColor = Yellow

If an object contains an object reference variable that points to another object, as shown in the following illustration, you can use the dot notation to build a path of references. For example, myLabel.Container.Size


Referencing methods of a class

You typically reference an object's method to execute a task on the object. Sometimes, however, you need to reference a method defined in a superclass. For example, if you override a method, then need to perform its original task, you can call the method from the superclass.

To call a method in an ancestor class, use one of the following syntaxes:

Super::<method>

or

<class name>::<method>

Using Super

When you use Super to access another class' method, Actuate searches from the current class' superclass and continues up the hierarchy until it finds the method. You typically use Super when you override a method and augment its original functionality. Calling Super lets you execute the original code as well as the code you added.

Using a class name

You can specify explicitly the class whose method you want to call. Actuate will search that class only.

Comparison of using Super and the class name

Using Super has the following advantages:

•	By not hard coding a class name, your code is more reusable.

•	You do not have to know the name of the super class.

Specify a class name if you modified the method in each successively derived class and you need to call a specific version, as described in the following example:

Example

You have a hierarchy of classes where ClassC is derived from ClassB and ClassB is derived from ClassA. Each class has its own version of the Build method. If you are writing code for MyLabel, an instance of ClassC, and you want to use ClassA's Build method, use the following statement:

MyLabel.ClassA::Build

If you do not specify ClassA, but write MyLabel.Build instead, ClassC's method executes.

See also

How Actuate resolves ambiguous method calls


How Actuate resolves ambiguous method calls

As the previous section describes, inheritance can result in two methods with the same name executing different actions because they are in different scopes. When a report application contains duplicate method names, Actuate resolves ambiguous method calls in this manner: It searches the method within the current instance first, then within the global scope.

If your application contains duplicate method names and you do not qualify the method name when calling the method, Actuate uses the described search order.

Example

In the following code snippets, DerivedClass is derived from BaseClass. BaseClass defines methods X and Y. DerivedClass defines its own version of method Y. When Y( ) is called from X( ) within MyObject, an instance of DerivedClass, Actuate calls the DerivedClass version of Y( ).

Class BaseClass

...

Sub X

Y( )

...

End Sub

...

Sub Y

Beep

End Sub

...

End Class

Class DerivedClass Subclass of BaseClass

'DerivedClass inherits method X and redefines method Y

...

Sub Y

Super::Y

MsgBox "This operation is invalid"

End Sub

End Class

.
.
Dim MyObject As DerivedClass

Set MyObject = New DerivedClass

X( )	'X( ) calls DerivedClass' version of Y( )

MyObject.Y	'Refers to Y( ) in DerivedClass

MyObject.BaseClass::Y	'Refers explicitly to Y( ) in BaseClass


Assigning to an object reference variable

Use Set to assign an object reference variable to reference an object, as shown in the following syntax:

Set <object reference variable> = <object expression>

Rule

You can assign an object to an object reference variable if the object is of the same type as the object reference variable, or if the object is of a type derived from the type of the object reference variable. You cannot assign an object to an object reference variable of a different class.

Example 1

In the following example, Control1 can be assigned to Control2 because both variables are declared as AcControl.

Dim Control1 As AcControl

Dim Control2 As AcControl

Set Control1 = New AcControl

Set Control2 = Control1

Example 2

In the following example, although Control1 and Control2 are declared as different types, Control1 can be assigned to Control2 because AcTextControl is derived from class AcControl.

Dim Control1 As AcTextControl

Dim Control2 As AcControl

Set Control1 = New AcTextControl

Set Control2 = Control1

See also

Comparison of Set and Let
Setting an object reference variable to Nothing


Comparison of Set and Let

You typically use only Set to assign one object reference variable to another. Let, which takes the form, x = y, is typically used to assign one simple variable to another. Because object reference variables do not contain actual values, using Let, as shown in the following example, results in an error:

Example

Dim x As AcLabelControl
Dim y As AcLabelControl

Set x = New AcLabelControl
y = x	'Results in an error


Setting an object reference variable to Nothing

When an object reference variable does not refer to any object, it has the special value Nothing. When you declare an object reference variable, it is set to Nothing initially. You can assign Nothing to any object reference variable with Set, as follows:

Set MyControl = Nothing


Passing object references to procedures

As with other variables, you can pass object references to procedures as parameters and return them as return values. The following are some examples of when you might want to do this:

Example 1

You want to write a procedure that receives an object reference as a parameter and does something with it. The procedure in the following example receives a reference to an object, AnyControl, and sizes it:

Sub SizeObject(AnyControl As AcControl)

AnyControl.Size.Width = 5000

AnyControl.Size.Height = 1000
End Sub

Example 2

You want to write a function that creates an object and returns it. The function in the following example creates a label and returns a reference to it:

Function NewLabel( ) As MyLabelControl

Set NewLabel = New MyLabelControl

End Function


Getting information about objects

Actuate provides three functions for getting information about objects, as described in the following table:

Function	Description
GetClassID	Returns the unique number that Actuate automatically assigns to all objects. Objects of the same class have the same ID number. Use GetClassID when you want to check if two objects are of the same class without the overhead of a string compare.
GetClassName	Returns the name of the object's class. Use GetClassName when you need to know an object's class before performing an action.
IsKindOf	Tests if an object is of a specified class or is derived from a specified class. It returns True if the object is an instance of the specified class, or is an instance of a subclass of the specified class. Otherwise, it returns False. Use IsKindOf to test if an object is of a particular class before performing an action.
For more information about these functions, see Chapter 2, "Statements and functions,"in Actuate Basic Language Reference.


Testing object references with the Is operator

Use the Is operator to perform the following tasks:

•	Test if an object reference variable is empty (Is Nothing)

•	Compare two object reference variables

See also

Testing for Nothing
Comparing object reference variables


Testing for Nothing

Use Is with Nothing to see if an object reference variable is empty.

Example

The procedure in the following example displays different messages depending on whether an object reference variable is empty or not:

Sub TestContent( element As AcVisualElement )

If element Is Nothing Then

MsgBox "The object reference variable is empty"

Else

MsgBox "The object reference variable is set"

End If

End Sub

You can also use Is with Nothing to check if an object reference variable contains a reference.

Example

The procedure in the following example is similar to the previous example, except that it tests if an object reference variable contains a reference:

Sub TestContent( element As AcVisualElement )

If Not element Is Nothing Then

MsgBox "The object reference variable is set"

Else

MsgBox "The object reference variable is empty"

End If

End Sub


Comparing object reference variables

Use Is if you need to compare two object reference variables to see if they both refer to the same object.

Example

The function in the following example compares controls in a linked list and determines if they are in a given frame:

Function IsInFrame (frame As AcFrame, control As AcControl) As Boolean

Dim element As AcVisualElement

Dim iter As AcListIterator

Set iter = frame.ContentList.NewIterator( )

Do While iter.HasMore( )

Set element = iter.GetNext( )
If element Is Control Then

IsInFrame = True

Exit Function

End If

Loop

IsInFrame = False

End Function


Object lifetime

The lifetime of an object depends on whether the object is transient or persistent. The following topics describe transient and persistent objects.

•	Transient objects

•	Persistent objects

See also

Transient objects
Persistent objects


Transient objects

Some of the objects that Actuate creates are transient or temporary. That is, they are created to perform specialized tasks during report generation and then released from memory once those tasks are finished. Examples of transient objects include data streams and connections.

Actuate automatically releases a transient object from memory when the last reference variable that refers to it is destroyed or is set to reference another object. Actuate keeps track of the reference count. The reference count increases each time a new object reference variable refers to the object. The reference count decreases each time a object reference variable is:

•	Set to Nothing

•	Set to reference another variable

•	Destroyed because it is out of scope, or because it is a variable of an object that is destroyed.

When the reference count is zero, Actuate deletes the object.


Persistent objects

Many of the objects that Actuate creates are persistent, that is, they exist until the report file is deleted. All objects that appear in the report at view time are persistent, including data controls, graphical elements, section, and layout objects.

Actuate saves these objects in the report object instance (.ROI) file. By saving the report data and structure, users can view the report at any time.


Using objects from other applications

The following topics are available. To view the topics in order, use the Browse button (>>) in the Help window's toolbar.

•	Adding objects from other applications

•	About object linking and embedding

•	Linking and embedding OLE objects

•	Editing OLE objects

•	Managing linked objects

•	Inserting an OLE custom control

•	Moving and sizing OLE components

•	Subclassing OLE components


Adding objects from other applications

When designing reports, you can add objects that you've created in other applications. For example, you can add objects such as a Word document, an Excel graph or spreadsheet, a .PDF file, a PaintBrush picture, sound, or an OLE custom control (.OCX). This capability is possible through a Windows protocol called Object linking and embedding (OLE).


About object linking and embedding

OLE is a Windows protocol for sharing data among applications. To use OLE, one application must be an OLE server, and another application must be an OLE container.

An OLE server is an application that can create and edit an OLE object. An OLE container is an application that can contain an OLE object. An OLE object is the data shared by the two applications. Examples of OLE objects are documents, spreadsheets, pictures, and sounds. The file where the object was created is called the object file.

Some Windows applications, such as Microsoft Word, can serve as both OLE server and container. Actuate usually works as an OLE container. Using OLE, an Actuate report can display and manipulate data from other Windows applications that support OLE. Another application, however, cannot display an Actuate report within it. Actuate is an OLE server for the OCX/ActiveX controls and the Actuate Live Report Extension for Microsoft Internet Explorer.

The following illustration shows a Microsoft Excel spreadsheet as an OLE object in an Actuate report. In this example, Microsoft Excel is the OLE server.

See also

What is available for linking and embedding?
The difference between linking and embedding
Guidelines for linking and embedding


What is available for linking and embedding?

When you install an application in Windows, an entry is added to the OLE registry if that application supports OLE. When you insert an OLE object, the Insert Object dialog box lists all supported OLE objects. The list of supported OLE objects varies from system to system, depending on which applications are available and which support OLE.

OLE objects

The following illustration shows an example of the Insert Object dialog box listing the types of OLE objects supported by a particular system.

OLE custom controls (OCX)

Another type of object you can insert in a report design is an OLE custom control (OCX). OLE custom controls are controls, created and distributed by third-party developers, that you can use in Windows 32-bit applications. Microsoft created the concept of the OLE custom control to address the limitations of the Visual Basic custom controls (VBXs), which were designed to work in 16-bit Visual Basic applications.

Like a Visual Basic custom control, an OLE custom control has a set of methods and properties you can use to manipulate the control once you insert it in your application. The following illustration shows an example of an OLE custom control, a calendar control, in a report frame.


The difference between linking and embedding

You can incorporate an OLE object into your Actuate report either by linking or embedding. There is no discernible difference between how a linked object and an embedded object appears in the report.

The difference between linking and embedding is where the object's data is stored. Data in a linked OLE object is stored and maintained by the application that created it. Data in an embedded OLE object is stored and maintained by the container application.

See also

Linked objects
Embedded objects


Linked objects

When you link an object, you are inserting a placeholder (not the actual data itself) for the linked object into the report. For example, when you link a Paintbrush picture in the report, only a reference to the location of the data is stored in the report. The actual data is stored in a Paintbrush file. You can, however, edit the data from within the Actuate report by choosing Edit→Object to activate the Paintbrush application for editing.

When an OLE object is linked to your report, the object's data can also be accessed from any other application that contains links to that data. Just as you can edit the data in the OLE object from within Actuate, you or other users can edit the same data from other container applications or from the OLE server application itself.

For example, the Paintbrush picture linked into your report can also be linked into a Microsoft Word document. If the picture is changed from within Actuate, Word, or Paintbrush, the modified data appears in the Actuate report, the Word document, and the Paintbrush file.

Linking makes it easy to track identical information that appears in multiple applications. Linking is useful when you want to maintain one set of data that several applications access.


Embedded objects

When you embed an OLE object, all the data is stored in the Actuate report. Unlike a linked object, no other application has access to the embedded object. You can edit the OLE object within the Actuate report only. You do so by choosing Edit→Object to activate the server application for editing.

Changes you make to the OLE object have no effect on the original object. For example, if you embed a PaintBrush picture in the report, then edit it in the report, the original picture is unchanged. Conversely, changes you make to the original picture do not affect the picture embedded in your report.

Embedding is useful if you want the report to maintain data that is created and edited in another application.


Guidelines for linking and embedding

Use the following guidelines when deciding whether to link or embed an OLE object in your report.

When to link	When to embed
You want the capability to modify the original object file and have those changes reflected in all applications that have a link to that object.	You want the capability to modify the object and have those changes reflected only in the report application.
The original object file is likely to be modified frequently, or modified from multiple OLE container applications.	The original object file is unlikely to be modified, or modified from the report only.
The original object file will not be moved or deleted. Links break when files move to different locations.	The original object file might be moved or deleted.
You are unlikely to distribute the report application.	You want to distribute the report application without worrying about distributing the object files that your report maintains links to.


Linking and embedding OLE objects

There are two ways to link or embed an OLE object. You can:

•	Select the frame into which to link or embed the OLE object, then choose Edit→Insert New Object. The Insert Object dialog box appears.

•	 Drag the OLE Container component from the Component Palette to the frame in the structure or layout pane into which to link or embed the OLE object. The Insert Object dialog box appears.

To link an OLE object, the object file must already exist. To embed an OLE object, however, you can either use a file that has already been created, or create a new file from within the Actuate application.

The following topics provide step-by-step procedures for linking and embedding OLE objects.

How to link an OLE object

How to embed an existing OLE object

How to create and embed a new OLE object

See also

How to link an OLE object


How to link an OLE object

1	Place the OLE Container component into a frame, or select the frame and choose Edit→Insert New Object.

The Insert Object dialog box appears.

2	Choose Create from File.

3	Type the name of the file or choose Browse to select the file to which you want to link.

The file you want to link to must already exist.

4	Select the Link check box.

5	Choose OK.

The contents of the file you selected appears in the frame.

See also

How to embed an existing OLE object
How to create and embed a new OLE object


How to embed an existing OLE object

1	Place the OLE Container component into a frame, or select the frame and choose Edit→Insert New Object.

The Insert Object dialog box appears.

2	Choose Create from File.

3	Type the name of the file or choose Browse to select the file whose object you want to embed.

4	Choose OK.

The contents of the file you selected appears in the frame.


How to create and embed a new OLE object

1	Place the OLE Container component into a frame, or select the frame and choose Edit→Insert New Object.

The Insert Object dialog box appears.

2	Choose Create New.

3	From the Object Type list, select the type of object to create.

Only objects whose application can be an OLE server appear in this list.

4	Choose OK.

The OLE server application appears.

5	Create the contents of the OLE object.


Editing OLE objects

After you have linked or embedded an OLE object, you can modify it from within the Design Editor. If the object is linked, you can also edit it in the server application without ever opening the Actuate Developer Workbench.

If you edit a linked object from within the Design Editor, a separate window opens the object in its application. For example, if you edit a linked Paintbrush picture in the report, the Paintbrush application opens with the picture displayed.

If you edit an embedded object, sometimes a separate application window opens, and sometimes the server application temporarily takes over the Actuate application, replacing the Actuate menus. It depends on what the server application supports.

When the server application takes over the container application, this is called in-place editing. Microsoft Excel and WordPad, for example, temporarily replace Actuate's menus with their own when you edit an embedded Excel or WordPad object. The following illustration shows the WordPad application taking over the Actuate application when you edit an embedded WordPad document.

See also

How to edit an OLE object


How to edit an OLE object

1	Select the object.

2	From the Edit menu, choose the available Object command. For example, if you selected a Paintbrush picture, Paintbrush Picture Object appears in the Edit menu. If you selected an Excel worksheet, Worksheet Object appears in the Edit menu.

3	From the Object submenu, choose Edit.

Actuate opens the server application with the object displayed.

4	Make the changes you want.

5	When you have finished, return to Actuate by doing one of the following:

•	If you are editing the object in a separate application window, choose File→Exit of the server application. Choose OK if a message asks if you want to update the object.

•	If the server application temporarily replaced some of the Actuate menus (in-place editing), press the Esc key.

Actuate displays the changes in the report.


Managing linked objects

After you create a link between your report and an object file, you can perform any of the following tasks:

•	Update the link to reflect the latest version of the object in the object file

•	Break the link if you no longer want to get the latest version from the object file

•	Change the link so that it points to a different location. For example, if you moved the object file to a different directory, you need to update the link information.

See also

Updating links
Breaking links
Changing links


Updating links

There are two types of links. You choose the type of link depending on how you want updates to occur.

•	Use automatic links, the default, when you want updates to be completely automatic. With automatic links, any change to the object file is immediately reflected in the object in the report.

•	Use manual links when you want to control the update process. With manual links, changes to the object file are not reflected in the object in the report until you explicitly update it.

See also

How to change the type of link
How to update a link manually


How to change the type of link

1	Select the OLE component in the Design Editor.

2	Choose Edit→Links.

The Links dialog box appears.

3	From the Link list, select the link or links you want to change.

4	Choose Automatic or Manual.

5	Choose Close.


How to update a link manually

1	Select the OLE component in the Design Editor.

2	Choose Edit→Links.

The Links dialog box appears.

3	From the Link list, select the link or links you want to update.

4	Choose Update Now.

5	Choose Close.

The object or objects in the report reflect all changes made to the object file since the last update.


Breaking links

In some cases, you might want to break the link to an object file so that the object does not change in the report when the object file changes. For example, you report might display an Excel worksheet that shows revenue for the first quarter. You can break the link so that any data entered into the worksheet after the first quarter is not reflected in your report. You might also break a link to prevent users from editing the object file from within the report. When you break a link, the OLE object becomes an embedded object.

See also

How to break a link


How to break a link

1	Select the OLE component in the Design Editor.

2	Choose Edit→Links.

The Links dialog box appears.

3	From the Link list, select the link or links you want to break.

4	Choose the Break Link button.

5	Choose the Close button.

Changes to the object file no longer affect the object in your report. Conversely, changes you make to the object from within the report no longer affect the object file.


Changing links

You can lose a link if you rename or move the object file. If this happens, you can reconnect the link to the object file or redirect the link to a different file.

See also

How to change a link


How to change a link

1	Select the OLE component in the Design Editor.

2	Choose Edit→Links.

The Links dialog box appears.

3	From the Link list, select the link you want to change.

4	Choose Change Source.

The Change Source dialog box appears.

5	In the File box, type or select the name of the file to which you want to link.

6	Choose OK.

The Links dialog box reappears.

7	Choose Close.


Inserting an OLE custom control

The procedure for inserting an OLE custom control is similar to the procedure for linking and embedding an OLE object. You can:

•	Select the frame into which to insert an OLE custom control, then choose Edit→Insert New Object. The Insert Object dialog box appears.

•	 Drag the OLE Container component from the Component Palette to the frame in the structure or layout pane. The Insert Object dialog box appears.

The Insert Object dialog box displays the list of available OLE controls, that is, the controls that are installed on your system.

See also

How to insert an OLE custom control


How to insert an OLE custom control

1	Place the OLE container component into a frame, or select the frame and choose Edit→Insert New Object.

The Insert Object dialog box appears.

2	Choose Insert Control.

3	From the Object Type list, select the control to insert.

4	Choose OK.

The control you selected appears in the frame.


Moving and sizing OLE components

You move and size OLE components the same way you move and size other components in the Layout pane. For more information on OLE components, see Chapter 8, "Working with frames and controls," in Building Reports.

By default, the size of the OLE component is determined by the OLE server application. For example, if you link or embed a Paintbrush picture that is 2 inches by 3 inches, that is the size of the picture in the report. You can, however, resize the OLE component in the report's Layout pane. If you do so, it might appear stretched or compressed. Note that only the display size changes. The size of the original picture itself is not affected.


Subclassing OLE components

Like other components in your report design, you can subclass OLE components. For information on how to subclass components, see Chapter 6, "Using the Design Editor," in Building Reports. When you subclass an OLE component, the subclass references the superclass' OLE object, as the following illustration shows. Changes you make to the OLE object in the superclass are reflected in the subclass.

If you modify the OLE object in the subclass, Actuate creates an independent copy of the OLE object. Changes to the OLE object in the superclass do not affect the object in the subclass, and vice versa.


Programming other applications' objects

The following topics are available. To view the topics in order, use the Browse button (>>) in the Help window's toolbar.

•	About OLE Automation

•	Creating OLE Automation objects

•	Manipulating OLE Automation objects

•	OLE Automation example


About OLE Automation

OLE Automation is a Windows standard that applications use to expose their OLE objects to development tools, languages, and container applications that support OLE Automation. For example, a spreadsheet application might expose a worksheet, chart, cell, or range of cells as different types of objects. A word processor might expose paragraphs, sentences, bookmarks, or spelling checkers as objects. These exposed objects are called OLE Automation objects.

When an application supports OLE Automation, the objects it exposes can be accessed by Actuate. You use Actuate Basic to manipulate these objects by calling the objects' methods, or by setting the objects' properties--the same way you would with Actuate's own objects.

For example, your report can display additional information stored in an Excel worksheet when users double click a text control. To do this, you write Actuate Basic code that creates an Excel OLE Automation object, activates it, and displays the worksheet.

See also

Difference between OLE Automation objects and other OLE objects
What OLE Automation objects do applications support?


Difference between OLE Automation objects and other OLE objects

Unlike OLE objects, OLE Automation objects can only be accessed using a programming language such as Actuate Basic. OLE Automation objects are not visible to the user, and are mostly used to automate repetitive tasks or tasks that do not involve user interaction. Since they are created using code, they are temporary and do not remain after the code is executed. For this reason, OLE Automation objects cannot be linked or embedded. In comparison, OLE objects created using the Insert New Object or Paste Special commands are visible as linked or embedded objects.


What OLE Automation objects do applications support?

Because each application provides different features, there is no common set of objects. To get a list of objects that an application supports, consult the application's documentation.


Creating OLE Automation objects

You manipulate other application's OLE objects remotely, without creating a linked or embedded OLE object, by using the CreateObject function.

Before creating an object, you need to define a variable that you can use to reference the object. For example:

Dim excelHandle As Object

You then use the CreateObject function to create the OLE object. This function requires a single string argument that indicates the application name and the type of object you want to create. Use the following syntax to specify the argument:

"Application.ObjectType"

For example, to specify an Excel worksheet object, supply the following argument:

"Excel.Worksheet"

Once you know the type of object you want to create, use the Set statement to assign the object returned by the CreateObject function to the object variable. For example:

Set excelHandle = CreateObject( "Excel.Worksheet" )

When this code executes, Actuate starts Excel and creates an OLE Automation object. Unlike the image displayed when you create a linked or embedded object with the OLE component, the object's image is not displayed in Actuate, nor is the object's data maintained by Actuate.


Manipulating OLE Automation objects

Once the object is created, you can manipulate it through its properties and methods in the same way you manipulate any Actuate object. You use the object.property syntax to set or retrieve the object's properties, and the object.method syntax to perform actions on the object. To find out an OLE object's methods and properties, refer to the application's documentation.

The following code shows examples of manipulating an Excel worksheet:

' Format the first row in the worksheet as bold

excelHandle.Row(1).Font.Bold = True

' Put the value 100 in the first cell

excelHandle.Cells( 1,1 ).Value = 100

' Save the worksheet and close Excel

excelHandle.SaveAs( "MySheet.xls" )

Set excelHandle = Nothing

See also

Closing an OLE Automation object


Closing an OLE Automation object

All OLE objects support some method that closes the object and the application that created it. Since OLE objects can use a significant amount of memory in your Actuate application, it is good practice to explicitly close an object when your application no longer needs it.

There are two ways to close an OLE object and its associated application:

•	Use the object's method for closing itself. For most objects, the method is either Close or Quit.

•	Set the variable referencing the object to Nothing. For example:

Set ExcelHandle to Nothing


OLE Automation example

Scenario

A Sales Forecast Detail report shows customer orders. Each customer order has an order number and multiple order items. When users view the report, they can right click any of the order numbers and select the Evaluate Discount option from the context menu to get discount information on the order. The discount information is presented in an Excel worksheet. The report uses OLE automation to access an existing Excel worksheet that contains the formula for calculating discounts and the format for displaying the results. The OLE Automation code is implemented in a user-defined method called OnEvaluateDiscount( ).

Example

Sub OnEvaluateDiscount( )

' Define the object reference variables

Dim excelHandle	As Object

Dim workbookHandle	As Object

Dim cellHandle	As Object

Dim orderSection	As AcLevelBreakSection

Dim customerSection	As AcLevelBreakSection

Dim sectionChild	As AcFrame

Dim theItemFrame	As ::ItemFrame

Dim customerTitle	As ::CustomerTitleFrame

Dim creditRating	As String

Dim purchasePattern	As String

Dim curSheetRow	As Integer

' Create the OLE Automation object and open the appropriate file

Set excelHandle	= CreateObject( "Excel.Application" )

Set workbookHandle	= excelHandle.WorkBooks

workbookHandle.Open( "\Actuate\demo\discount.xls" )

excelHandle.Visible	= True

' Assign values to the variables

Set orderSection	= p_Container.p_Containter

Set customerSection	= orderSection.p_Container

Set customerTitle	= customerSection.GetFirstContent

creditRating	= customerTitle.creditRating.DataValue

purchasePattern	= customerTitle.purchasePattern.DataValue

' Specify the cells in which to display data

Set cellHandle	= excelHandle.Cells( 2, 5 )

cellHandle.Value	= DataValue

Set cellHandle	= excelHandle.Cells( 2, 6 )

cellHandle.Value	= creditRating

Set cellHandle	= excelHandle.Cells( 2, 7 )

cellHandle.Value	= purchasePattern

Set sectionChild	= orderSection.GetFirstContent

curSheetRow	= 10

' Loop through all the order items and enter the values into the cells

Do Until sectionChild is Nothing

If IsKindOf( sectionChild, "ItemFrame" ) Then

Set the ItemFrame	= excelHandle.Cells(1, curSheetRow)

Set cellHandle	= excelHandle.Cells(1, curSheetRow)

cellHandle.Value	= theItemFrame.itemCode.DataValue

Set cellHandle	= excelHandle.Cells(2, curSheetRow)

cellHandle.Value	= theItemFrame.quantity.DataValue

Set cellHandle	= excelHandle.Cells (3, curSheetRow)

cellHandle.Value	= theItemFrame.priceQuote.DataValue

curSheetRow	= curSheetRow + 1

End If

Set sectionChild = orderSection.GetSuccessor( sectionChild )

Loop

End Sub

The following illustrations show how users trigger the OnEvaluateDiscount( ) method, and the Excel worksheet that appears.


Customization examples

The following topics are available. To view the topics in order, use the Browse button (>>) in the Help window's toolbar.

•	Customizing data streams

•	Creating a computed data row variable

•	Filtering out selected data rows

•	Creating a select filter

•	Creating a sort filter

•	Retrieving data from a flat file

•	Working with data from multiple sources

•	Customizing the report layout

•	Conditionally creating a component

•	Dynamically adjusting the size of a text control

•	Dynamically embedding image controls

•	Customizing the report data

•	Conditionally setting properties of a control

•	Changing control properties based on another control's value

•	Rolling up figures and graphing the results


Customizing data streams

Actuate's data stream model does most of the standard work of retrieving and filtering data for your report, particularly if your report uses data from SQL databases. You customize data streams if your report requires special data sources and data filters, for example, to retrieve and process data from text files.

The methods that you typically override are those in the data adapter's core protocol:

•	Start( )

The framework calls Start( ) to open the data adapter. Override this method, for example, to open files from which the data stream gets data.

•	Fetch( )

The framework calls Fetch( ) to retrieve each row. Override this method to perform tasks necessary to populate the variables in a data row with the retrieved row of data.

•	Finish( )

The framework calls Finish( ) to close the data adapter. Override this method, for example, to close files you might have opened in Start( ).

If you need to manipulate a data row after it has been populated with data from the input record, override the data row's OnRead( ) method. The framework calls OnRead( ) after the data row gets its data.

See also

Creating a computed data row variable
Filtering out selected data rows
Creating a select filter
Creating a sort filter
Retrieving data from a flat file
Working with data from multiple sources


Creating a computed data row variable

This topic describes how you create variables in a data row whose values are computed from other variables.

How to create computed data row variables

1	Create the data source and its data row.

2	Add variables to the data row to store the computed values. To do so, use the Variables page of the Component Editor.

3	Override the OnRead( ) method of the data row to compute the new values.

Example

In the following example, the data row's OnRead( ) method is overridden to calculate values for the ExtendedCost and DaysLate variables. These values are computed from values in three other variables (Cost, Quantity, and DueDate) in the data row.

Sub OnRead( )

Super::OnRead( )

ExtendedCost = Cost * Quantity

DaysLate = Date( ) - DueDate

If DaysLate < 0 Then

DaysLate = 0

End If

End Sub

Tips

•	Another way to create computed columns is through the Query Editor. Use the technique described in this section when specifying the computed columns through the Query Editor is not feasible.

•	You can also create a method that calculates and returns the value. For example, instead of creating the DaysLate variable and writing code in OnRead( ) to calculate the value, you can encapsulate the code in a method in the data row class. You can then use the method name in the ValueExp property of the control with which to display the days late value.

•	The data source calls OnRead( ) once for each data row right after the row receives its data from the input record.

•	The call to the super class's OnRead( ) is not necessary, but it is a good programming habit.


Filtering out selected data rows

This topic describes how you conditionally filter out rows from a set of input records by adding custom code to the data source.

How to filter out data rows

1	Create the data source.

2	Override the data source's Fetch( ) method to specify which rows to retrieve.

Example

In the following example, the data source's Fetch( ) method calls the base class's Fetch( ) to get each row. When each row is retrieved, Fetch( ) checks if the state column contains the value "CA." If it does, Fetch returns the row. This process repeats until Fetch( ) has retrieved all rows.

Function Fetch( ) As AcDataRow

Dim row As CustomerRow

Do

Set row = Super::Fetch( )

If row Is Nothing Then

Exit Function

End if

Loop until row.State = "CA"

Set Fetch = row

End Function

Tips

•	A better way to filter rows is through the Query Editor. Use the technique described in this topic when filtering rows through the Query Editor is not feasible.

•	If you want the capability to filter rows when you run the report, you can create parameters which accept run-specific conditions.

•	If you want to encapsulate and reuse the filter code, create a custom data filter instead. This technique is described in Creating a select filter.


Creating a select filter

This topic describes how to create a data filter that conditionally selects rows. You can write the filter code in the Fetch( ) method of the data source, as described in Filtering out selected data rows. However, by encapsulating the filter code in a data filter instead, you can use the data filter in other data streams.

How to create a select data filter

1	If your report design already has a data source, move it to the Scratchpad.

2	 Place a data adapter into the DataStream slot.

3	Select AcSingleInputFilter from the list of data adapters that appears.

4	Place the data source you moved to the Scratchpad into the Input slot of the filter. The data source becomes the input source for the filter. The filter is the data adapter that provides the report section with the data rows.

5	Override the filter's Fetch( ) method to specify which rows to retrieve.

Example

In the following example, the data filter's Fetch( ) method calls the input source's Fetch( ) method to retrieve the row. A reference to the input source is stored in the InputAdapter variable. When each row is retrieved, Fetch( ) checks if the state column contains the value "CA." If it does, Fetch returns the row. This process repeats until Fetch( ) has retrieved all rows.

Function Fetch( ) As AcDataRow

Dim row As CustomerRow

Do

Set row = InputAdapter.Fetch( )

If row Is Nothing Then

Exit Function

End if

Loop until row.State = "CA"

Set Fetch = row

End Function

Tip

A better way to filter rows is through the Query Editor. Use the technique described in this section when filtering rows through the Query Editor is not feasible.


Creating a sort filter

This topic describes how to create a data filter that sorts data rows.

How to create a sort data filter

1	If your report design already has a data source, move it to the Scratchpad.

2	 Place a data adapter into the DataStream slot.

3	Select AcMemoryDataSorter from the list of data adapters that appears.

4	Place the data source you moved to the Scratchpad into the Input slot of the filter. The data source becomes the input source for the filter. The filter is the data adapter that provides the report section with the data rows.

5	Override the filter's Compare( ) method to specify how the rows should be sorted. Compare( ) takes two rows as arguments and should return one of the following values:

•	A positive number if the first row goes after the second row.

•	0 if both rows are the same.

•	A negative number if the first row goes before the second row.

Example

In the following example, Compare( ) compares rows by their state and customer id.

Function Compare( row1 As AcDataRow, row2 As AcDataRow ) As Integer

Dim cust1 As AcCustomerRow

Dim cust2 As AcCustomerRow

Set cust1 = row1

Set cust2 = row2

' Compare the state. CompareKeys( ) is a predefined method

Compare = CompareKeys( cust1.State, cust2.State )

If Compare = 0 Then

Exit Function

End If

' Compare the customer id. Compare two integers by subtracting them

Compare = cust1.ID - cust2.ID

End Function

Tip

A better way to sort rows is through the Query Editor. Use the technique described in this topic when sorting rows through the Query Editor is not feasible.


Retrieving data from a flat file

This topic describes how to retrieve data from a flat file by creating a custom data source and data row.

How to retrieve data from a flat file

1	Create a custom data source by subclassing AcDataSource.

2	Create a custom data row by creating variables to store data from the flat file.

3	Override the data source's Start( ) method to open the flat file.

4	Override the data source's Fetch( ) method to retrieve data from the flat file.

5	Override the data source's Finish( ) method to close the flat file.

Example

A report displays data from a text file, HLC.TXT. This text file contains four types of stock data (date, high price, low price, and close price) in the following format:

9/1/95, 10.5, 10, 10
9/2/95, 12.5, 11.25, 11.5
...

Each piece of data is displayed by a different control in the report. The following illustration shows the key components in the report design.

The following are the main steps to implement the report:

1	Create a data source by subclassing from AcDataSource. To do so, you can drag AcDataSource from the AFC library and drop it into the datastream slot.

2	Create a custom data row by creating the following instance variables: Date (date type), HighPrice (double), LowPrice (double), and ClosePrice (double). For step-by-step information on creating variables, see How to add a variable to a class.

3	Add a new integer variable (Channel) to the data source to store the file number of the text file to open in the next step.

4	Override the data source's Start( ) method to open the text file.

Function Start( ) As Boolean

' Call the super class Start( ) to perform default tasks

Start = Super::Start( )

' Initialize the data source's Channel variable to an available file number

Channel = FreeFile( )

' Open the file using Actuate Basic's Open statement

Open "hlc.txt" For Input As Channel

End Function

5	Override the data source's Fetch( ) method to retrieve data from the text file and write it to the appropriate data row variables.

Function Fetch( ) As AcDataRow

' Declare a variable of type HLCRow, the custom data row
' class you created

Dim row As HLCRow

' Declare a string variable to store the date data. This value is
' converted to a date type later.

Dim dateStr As String

' Exit Fetch( ) if at the end of the text file

If EOF( Channel ) Then

Exit Function

End If

' Create a new instance of the data row class

Set row = NewDataRow( )

' Read data from the text file into the data row variables using
' Actuate Basic's Input statement

Input #Channel, dateStr, row.HighPrice, row.LowPrice,
row.ClosePrice

' Convert dateStr from a string to a date and assign the value to the
' data row variable, PriceDate

row.PriceDate = DateValue( dateStr )

Set Fetch = row

' Tell the adapter there is a new row.

AddRow( Fetch )

End Function

6	Override the data source's Finish( ) method to close the text file.

Sub Finish( )

Super::Finish( )

Close #Channel

End Sub


Working with data from multiple sources

To retrieve data from multiple sources, you use AcMultipleInputFilter and override its methods to specify how to retrieve and process the data. AcMultipleInputFilter accepts input from any number of data adapters, processes the data, then passes it to the next data adapter or to the report.

This section describes two different ways to customize AcMultipleInputFilter to process data from two input adapters:

1	Create a union filter that processes all the data rows from one input adapter before processing data rows from the second input adapter.

2	Create a merge filter that combines the data rows from two input adapters.

The following are the general steps for creating the multiple input filter:

1	 Place a data adapterinto the DataStream slot.

2	Select AcMultipleInputFilter from the list of data adapters that appears.

3	Place the data sources into the Input slot of the multiple input filter. The multiple input filter gets its data from these data sources. If using SQL query data sources, use the Query Editor to select the data to retrieve from the SQL database.

4	Override the multiple input filter's Fetch( ) method to code the filter algorithm.

See also

Example 1 Creating a union filter
Example 2 Creating a merge filter


Example 1 Creating a union filter

A report displays data from two tables: customer names from the Customers table, and order IDs from the Orders table. To get data from these two tables, two SQL query data sources are used. In this example, the multiple input filter processes all the data from the first data source, then from the second data source. The finished report displays data accordingly.

The following illustration shows the report design.

The following are the steps to create the union filter:

1	For the filter to access each input adapter in order, create an iterator. First, define two variables for the filter: UnionIter of type AcIterator and CurrentInput of type AcDataAdapter. UnionIter will store the reference to the iterator you create in the filter's Start( ) method. CurrentInput will store the reference to the input adapter that is sending data to the filter.

2	Override the filter's Start( ) method to create the iterator to access the first input adapter and prepare it to get data.

Function Start( ) As Boolean

    Start = Super::Start( )

If Not Start Then

Exit Function

End If

' Create an iterator to access the input adapter

Set UnionIter = InputAdapters.NewIterator

' Initialize CurrentInput to the first input adapter

Set CurrentInput = UnionIter.GetNext()

End Function

3	Override the filter's Fetch( ) method to retrieve each row of data from the first input adapter and pass it to the report. When the first input adapter has retrieved all rows, retrieve rows from the second input adapter.

Function Fetch( ) As AcDataRow

Do While True

If CurrentInput Is Nothing Then

Exit Function

End If

Set Fetch = CurrentInput.Fetch()

If Not Fetch Is Nothing Then

Exit Function

End If

Set CurrentInput = UnionIter.GetNext()

Loop

End Function

4	Override the filter's Finish( ) method to write cleanup code.

Sub Finish( )

Set UnionIter = Nothing

    Super::Finish( )

End Sub

The following illustration shows the first page of the generated report. The report displays all the data from the first data source, then the second.


Example 2 Creating a merge filter

A report displays data from two tables: customer names from the Customers table, and order IDs from the Orders table. To get data from these two tables, two SQL query data sources are used. In this example, the multiple input filter creates a new data row by merging the data rows retrieved by each data source.

The following illustration shows the report design.

The following are the steps to create the merge filter:

1	Create a new data row for the multiple input filter and define the following variables: customName (string) and orderID (integer).

2	Override the filter's Fetch( ) method to retrieve one row of data from each of the input adapters, create a new data row with the merged data, and pass the data row to the report. This process continues until there are no more rows to retrieve.

Function Fetch( ) As AcDataRow

Dim adapter As AcDataAdapter

' Start the first input adapter

Set adapter = InputAdapters.GetAt(1)

' Retrieve a row from the first input adapter

Dim aOrderDataRow As OrderDataRow

Set aOrderDataRow = adapter.Fetch()

' Start the second input adapter

Set adapter = InputAdapters.GetAt(2)

' Retrieve a row from the second input adapter

Dim aCustomerDataRow As CustomerDataRow

Set aCustomerDataRow = adapter.Fetch()

' Create the new data row

Dim aMergedDataRow As MergedDataRow

Set aMergedDataRow = NewDataRow( )

' Assign the data from the first row to a variable in the new data row

If Not aOrderDataRow Is Nothing Then

aMergedDataRow.orderID = aOrderDataRow.orders_orderID

Else

aMergedDataRow.orderID = Null

End If

' Assign the data from the second row to a variable in the new data row

If Not aCustomerDataRow Is Nothing Then

aMergedDataRow.customName = aCustomerDataRow.customers_customName

End If

If aOrderDataRow Is Nothing And aCustomerDataRow Is Nothing Then

Exit Function

End If

Set Fetch=aMergedDataRow

End Function

The following illustration shows the first page of the generated report. Each row contains data from the Customers and Orders tables.


Customizing the report layout

Actuate generates a report according to the design you created through the Design Editor. You might, however, want to dynamically change the contents of the report as it is being generated.  The following topic provides an example of how you conditionally create a component.

Conditionally creating a component

See also

Conditionally creating a component
Dynamically adjusting the size of a text control
Dynamically embedding image controls


Conditionally creating a component

By default, the Design Editor generates code to instantiate components based on the component references you create in the structure pane. As described in  How the framework instantiates classes, container objects use New<reference> methods to instantiate their contents. You can, however, take control of this process if you need to instantiate components based on certain conditions, for example, creating different frames depending on the value of a data row variable.

How to conditionally create a component

1	Determine the container class of the component whose creation you want to control. You will be adding custom code to this container class. For example, to conditionally instantiate a frame, you would add code to the group section in which it is contained.

2	Determine the component reference over which you want to take control. For example, to conditionally instantiate a frame in a group section, determine if that frame is a Before, PageHeader, Content, PageFooter, or After component in the group section. The name of the component reference appears in the Structure pane as the name of the slot.

3	Override the New<reference> method of the container class. For example, if the component you want to conditionally instantiate is the "Content" component of its container class, override NewContent( ).

Example

A report uses data from an Office table. One of the columns is officeID. When the framework generates the report, it creates different frames depending on the officeID value, as follows:

officeID value	Frame
1	BostonContents
2	NewYorkContents
3	PhiladelphiaContents
The following illustration shows the report design:

The following are the main steps to implement the report:

1	Open the report design's project view and create the three city-specific frames there: BostonContents, NewYorkContents, and PhiladelphiaContents. You can subclass from the "generic" frame, OfficeContents, and add controls or features specific to each frame.

2	Override the NewContent( ) method of the frame's container class, OfficeGroup.

Function NewContent( ) As AcReportComponent

Dim row As ConditionalExampleDataRow

' Get the current row for the group section

Set row = GetCurrentRow( )

' Check the value of officeID to create the appropriate frame

Select Case row.offices_officeID

Case 1

Set NewContent = New Persistent BostonContents

Case 2

Set NewContent = New Persistent NewYorkContents

Case 3

Set NewContent = New Persistent PhiladelphiaContents

End Select

End Function


Dynamically adjusting the size of a text control

This section describes how to create an adjustable container for a multi-line text control. This method works only on NT and Windows systems.

For example, suppose that you have a report that is printed or displayed in a viewer or browser. Because of differences in font sizes and display characteristics, a text control may look different in the viewer than it does when you print it. For example, the frame that contains the control may be too small for the printed text, resulting in some of the text being lost in the printed report. You can take certain steps to ensure that the report looks good whether it is displayed or printed.

You can determine the height required to allow a multi-line control's container to fully display text on either the display or the default printer, when the Factory generates the report. Override the control's Finish( ) method (or another report generation phase method) to call the Actuate Basic function GetDisplayHeight( ). GetDisplayHeight( ) returns the height, in twips, required to display a given text string properly in the viewer, browser, or default printer. Parameters for GetDisplayHeight( ) are the context ("display" or "default_printer"), control width, text, font, and format.

For example, to ensure that a text control's contents display properly when users print the report on the default printer, override the text control's Finish( ) method. This example uses the Size, Font, and TextPlacement properties' members as parameters to GetDisplayHeight( ).

Sub Finish( )

Dim printerheight As Integer

Super::Finish( )

printerheight = GetDisplayHeight( "default_printer", Size.Width, Text, Font, TextPlacement )

Size.height = printerheight

End Sub

To size the same text control for display:

Sub Finish( )

Dim displayheight As Integer

Super::Finish( )

displayheight = GetDisplayHeight( "display", Size.Width, Text, Font, TextPlacement )

Size.height = displayheight

End Sub

If you have several text controls, adjust the height of the frame that will contain the controls. Be sure to adjust the frame's height to the largest of the controls that share the frame.

For more information about the GetDisplayHeight( ) function, see Chapter 2, "Statements and functions," in Actuate Basic Language Reference.


Dynamically embedding image controls

This section describes how to create a report that displays different, embedded images in each report instance, depending upon some criteria.

For example, suppose that you generate reports for different customers using the same .ROX. You want the report for each customer to include that customer's company logo at the top of the report.

To dynamically embed images in a report:

•	Open the report in the Developer Workbench.

•	Select the image control.

•	In the Component Editor's Properties dialog, set Embedded to False.

•	In the Component Editor's Variables dialog, add a string parameter to the image control.

•	Add parameters to the report that allow you to determine the image to include at report-generation time. For example, add an ad hoc parameter, Customer, that allows users to request a report for a specific customer.

•	In the image control's Finish( ) method (or another report generation phase method), use the Actuate Basic function ConvertBFileToString( ) to convert the image file to a string value and set the string parameter to the converted string value.

•	Override the image control method that determines whether to display or print the control.

•	Call the Actuate Basic function ConvertStringToBFile( ). ConvertStringToBFile( ) converts the image's string value back into an image, then stores that image in a temporary file. The viewer uses that file to display the image. For example, override the ShowWhenPrinting( ) or ShowWhenViewing( ) methods to convert the string.

•	Remove the temporary file.

For example, suppose that you print reports for a number of companies. When you print the report for a particular company, you want to include the company's logo in the report. You set up the properties in the Component Editor, including setting Embedded to False, and declaring an ad hoc parameter called CompanyName to contain the customer's company name.

•	In the Component Editor's Variables dialog, add a string parameter called CorpLogo.

•	Override the image control's Finish( ) method:

Sub Finish( )

Super::Finish( )

'Insert your code here

Dim ImageFile As String

ImageFile = CompanyName & ".bmp"

CorpLogo = ConvertBFileToString( ImageFile )

End Sub

•	Override the ShowWhenViewing( ) method to prepare the image for display, display the file, and remove the temporary file. This example handles removing the temporary file by

•	Creating a subclass of the viewer class called CleanupViewer

•	Letting that subclass remove the temporary file in its destructor

Function ShowWhenViewing( ) As Boolean

    ShowWhenViewing = Super::ShowWhenViewing( )

    ' Insert your code here

If ShowWhenViewing = True Then

FileName = ConvertStringToBFile( CorpLogo )

GetCleanupViewer.RemoveFileAtTheEnd( FileName )

End If

End Function

To include dynamic images in a printed report, override the ShowWhenPrinting( ) method in a similar way.

To see a sample report with dynamic images, open Examples\roiimage.rod in Developer Workbench.

For more information about the Actuate Basic functions ConvertStringToBFile( ) and ConvertBFileToString( ), see Chapter 2, "Statements and functions," in Actuate Basic Language Reference.


Customizing the report data

Actuate generates a report with the data you specified through the Design Editor. You might, however, want to dynamically change the data values or properties of controls as the Factory generates the report. The following are examples of some customization tasks:

•	Display a control's data value in a different style depending on the value; for example, positive numbers in plain text and negative numbers in italics.

•	Change the property of a control based on the value of another; for example, set the text of a label control to "Payment overdue" if the value of a currency control is greater than 0.

See also

Conditionally setting properties of a control
Changing control properties based on another control's value
Rolling up figures and graphing the results


Conditionally setting properties of a control

This topic describes how to change the properties of a control based on its value. Actuate stores the control's value in the DataValue variable.

How to conditionally set the properties of a control

Override one of the control's methods to write the conditional code. Choose a method that the framework calls anytime after the control's data value is available. The control's BuildFromRow( ) method is a good choice because it sets the value of the control. Another option is the control's Finish( ) method.

Example

In the following example, an integer control's BuildFromRow( ) method is overridden so that the value is displayed in green when it is greater than 20.

Function BuildFromRow( row As AcDataRow ) As AcBuildStatus

    BuildFromRow = Super::BuildFromRow( row )

    If DataValue > 20 Then

Font.Color = green

End If

End Function


Changing control properties based on another control's value

This topic describes how you access the value of one control to manipulate the properties of another. Actuate provides several ways to access the value of a control.

•	GetControlValue( ) method

•	FindContentByClass( ) method

•	ObjectVariable property

•	Global variables

Each approach is discussed in the following topics.

Example

A report displays data from a database of cars and their prices. The content frame of the report contains controls to display the data, including a currency control (PriceCurrencyControl) that displays the price of the car, and a label control (CodeLabelControl) whose text and text color depends on the value in PriceCurrencyControl. If the price of the car exceeds $35,000, CodeLabelControl displays "Luxury" in red. Otherwise, CodeLabelControl displays the default text set through the Component Editor.

The following illustration shows report design.

See also

Approach 1 Using the GetControlValue( ) method to access the value of another control

Approach 2 Using the ObjectVariable property to access the value of another control

Approach 3 Using the FindContentByClass( ) method to access the value of another control

Approach 4 Using global variables to access the value of another control


Using the GetControlValue( ) method to access the value of another control

GetControlValue( ) returns the value of another control within the same frame. To get the value of PriceCurrencyControl, override one of CodeLabelControl's methods to call GetControlValue( ). Choose a method that the framework calls anytime after the data value is available, for example, BuildFromRow( ) or Finish( ).

In the following code example, the conditional code is written in the control's BuildFromRow( ) method.

Function BuildFromRow( row As AcDataRow ) As AcBuildStatus

BuildFromRow = Super::BuildFromRow( row )

If GetControlValue("PriceCurrencyControl") > 35000 Then

Text = "Luxury"

Font.Color = Red

End If

End Function

Tip

If you use GetControlValue( ) in code like this, you must be aware of the order in which controls are built, otherwise GetControlValue( ) might return a Null value.  Generally, the controls in a frame are built in the same order in which they appear in the structure pane.

In this approach, the code assumes PriceCurrencyControl is built before CodeLabelControl. If CodeLabelControl is built first, GetControlValue( ) would return Null because the value of PriceCurrencyControl will not yet have been set. In such a case, override the OnRow( ) method of the containing frame, and access PriceCurrencyControl through its ObjectVariable property. This technique is explained in Using the ObjectVariable property to access the value of another control.


Using the ObjectVariable property to access the value of another control

A second approach to changing one control based upon the value of another involves choreographing two Actuate Basic elements:

•	Set the ObjectVariable property of each control.

You can think of the ObjectVariable property as a kind of "alias" for each of the controls. More precisely, when you assign a value to ObjectVariable, the framework generates a function to access the control.

•	To write the conditional code, override a method in the frame that contains both controls.

Choose a method that the framework calls anytime after the data values are available, for example the frame's OnRow( ) or BuildFromRow( ) method.

The following steps provide a walk-through of this technique:

1	Using the Component Editor, assign "SalesPrice" (without the quotes) to the ObjectVariable property of PriceCurrencyControl.

Do not use the name of any existing method when you assign a value to ObjectVariable.  If you do so, a compile time error occurs.

2	Similarly, assign "LuxuryFlag" to the ObjectVariable property of CodeLabelControl

3	Override the OnRow( ) method of the containing frame to write the conditional code.

Sub OnRow(row As AcDataRow)

Super::OnRow( row )

If SalesPrice.DataValue > 35000 Then

LuxuryFlag.Text = "Luxury"

LuxuryFlag.Font.Color = Red

End If

End Sub

Tip

Using the ObjectVariable property, you can access a control only from its containing frame, not from another control. To access a control from another control, use GetControlValue( ) as explained in Using the GetControlValue( ) method to access the value of another control.


Using the FindContentByClass( ) method to access the value of another control

FindContentByClass( ) is a method in container components, such as frames and pages. It locates and returns a reference to a content component given the class name of that component.

To access the values of CodeLabelControl and PriceCurrencyControl with FindContentByClass( ), override a method in the frame that contains both controls. Choose a method that the framework calls anytime after the data values are available, for example the frame's OnRow( ) or BuildFromRow( ) method.

In the following code example, the conditional code is written in the frame's BuildFromRow( ) method.

Function BuildFromRow( row As AcDataRow ) As AcBuildStatus

BuildFromRow = Super::BuildFromRow( row )

Dim priceControlVar As AcControl, lblControlVar As AcLabelControl

Set priceControlVar = FindContentByClass( "PriceCurrencyControl" )

Set lblControlVar = FindContentByClass( "CodeLabelControl" )

If priceControlVar.GetValue( ) > 35000 Then

lblControlVar.Text = "Luxury"

lblControlVar.Font.Color = Red

End If

End Function

Tip

Using FindContentByClass( ), you can access controls only from the containing frame, not from another control. To access a control from another control, use GetControlValue( ) as explained in Using the GetControlValue( ) method to access the value of another control.


Using global variables to access the value of another control

The other three approaches to accessing controls work only with controls that are in the same frame.  If you need to access a control's value from a control in a different frame, use a global variable to store the control's value. Doing so makes the value available to any component in the report.

The following steps show how you set the properties of CodeLabelControl based on the value of PriceCurrencyControl using a global variable.

1	In an included Basic source module, declare a global variable, gintPriceValue.

Declare

Global gintPriceValue As Integer

End Declare

2	Override PriceCurrencyControl's BuildFromRow( ) method to assign PriceCurrencyControl's data value to the global variable. Doing so makes the data value available to any component in the report.

Function BuildFromRow( row As AcDataRow ) As AcBuildStatus

BuildFromRow = Super::BuildFromRow( row )

gintPriceValue = DataValue

End Function

3	Override CodeLabelControl's BuildFromRow( ) method to set the properties of CodeLabelControl based on the value of PriceCurrencyControl, which is stored in the global variable, gintPriceValue.

Function BuildFromRow( row As AcDataRow ) As AcBuildStatus

BuildFromRow = Super::BuildFromRow( row )

If gintPriceValue > 35000 Then

Text = "Luxury"

Font.Color = Red

End Function

Tip

Although this approach works well enough, it is generally good practice to avoid using global variables. They can contribute to the creation of complex state machines, and make the logic of an application hard to understand.


Rolling up figures and graphing the results

This section describes how to roll up the projected and actual cost figures for each month, then graph the accumulated results, when the data is not normalized. To see the code used in this example in a fuller context, open Examples\BUDGRPT.ROD, highlight the data filter, SumGraphDataFilter, and examine its Fetch( ) method.

To graph this data, the report must receive the data as one amount per row, in other words one row for January Budget, one row for January Actual, and so on. To normalize the data returned from the query in this manner, you use a data filter.

The following code in the Fetch( ) method of the filter receives a data row from the data source SqlQuerySource, which contains the database query.

Each time Fetch( ) is called, the data filter splits off another amount field and returns it to the report. The dollars are accumulated each month. For example, the budget amount for March shows the sum of January, February, and March budgets.

Function Fetch( ) As AcDataRow

Dim aFilterDataRow As FilterDataRow

' Get the row returned from the SQL Query

If queryDataRow is Nothing Then

Set queryDataRow = InputAdapter.Fetch()

If queryDataRow Is Nothing Then

Exit Function

End If

dataPointCount = 0

actualTotal = 0.0

planTotal = 0.0

End If

' Run through each of the twelve monthly fields on the query data row and

' return one row for each month/amount combination to the report

Set aFilterDataRow = New FilterDataRow

aFilterDataRow.month = ( dataPointCount \ 2 ) + 1

aFilterDataRow.amountType = dataPointCount MOD 2

'Type of 0 = actual, 1 = plan

dataPointCount = dataPointCount + 1

If dataPointCount = 25 Then

Exit Function

End If

Select Case dataPointCount

Case 1

aFilterDataRow.amountToGraph = queryDataRow.aJanDollars

Case 2

aFilterDataRow.amountToGraph = queryDataRow.bJanDollars

Case 3

aFilterDataRow.amountToGraph = queryDataRow.aFebDollars

Case 4

aFilterDataRow.amountToGraph = queryDataRow.bFebDollars

Case 5

aFilterDataRow.amountToGraph = queryDataRow.aMarDollars

Case 6

aFilterDataRow.amountToGraph = queryDataRow.bMarDollars

Case 7

aFilterDataRow.amountToGraph = queryDataRow.aAprDollars

Case 8

aFilterDataRow.amountToGraph = queryDataRow.bAprDollars

Case 9

aFilterDataRow.amountToGraph = queryDataRow.aMayDollars

Case 10

aFilterDataRow.amountToGraph = queryDataRow.bMayDollars

Case 11

aFilterDataRow.amountToGraph = queryDataRow.aJunDollars

Case 12

aFilterDataRow.amountToGraph = queryDataRow.bJunDollars

Case 13

aFilterDataRow.amountToGraph = queryDataRow.aJulDollars

Case 14

aFilterDataRow.amountToGraph = queryDataRow.bJulDollars

Case 15

aFilterDataRow.amountToGraph = queryDataRow.aAugDollars

Case 16

aFilterDataRow.amountToGraph = queryDataRow.bAugDollars

Case 17

aFilterDataRow.amountToGraph = queryDataRow.aSepDollars

Case 18

aFilterDataRow.amountToGraph = queryDataRow.bSepDollars

Case 19

aFilterDataRow.amountToGraph = queryDataRow.aOctDollars

Case 20

aFilterDataRow.amountToGraph = queryDataRow.bOctDollars

Case 21

aFilterDataRow.amountToGraph = queryDataRow.aNovDollars

Case 22

aFilterDataRow.amountToGraph = queryDataRow.bNovDollars

Case 23

aFilterDataRow.amountToGraph = queryDataRow.aDecDollars

Case 24

aFilterDataRow.amountToGraph = queryDataRow.bDecDollars

End Select

If aFilterDataRow.amountType = 0 Then

aFilterDataRow.amountToGraph = aFilterDataRow.amountToGraph + actualTotal

actualTotal = aFilterDataRow.amountToGraph

Else

aFilterDataRow.amountToGraph = aFilterDataRow.amountToGraph + planTotal

planTotal = aFilterDataRow.amountToGraph

End If

Set Fetch = aFilterDataRow

End Function


Writing and using procedures

The following topics are available. To view the topics in order, use the Browse button (>>) in the Help window's toolbar.

•	About procedures

•	Scope of procedures

•	Declaring procedures

•	Declaring arguments

•	Calling procedures

•	Procedure overloading

•	Using control structures


About procedures

Procedures simplify programming tasks by breaking a program into smaller logical components. These components can then become building blocks that let you enhance and extend Actuate Basic.

Procedures are useful for condensing repeated or shared tasks, such as frequently used calculations, text and control manipulations, and database operations.

Programming with procedures has two major benefits:

•	Procedures break your program into discrete logical units, each of which you can debug more easily than an entire program without procedures.

•	Procedures used in one program can be reused in another program, typically without modification.

Actuate Basic has two types of procedures:

•	Sub procedures do not return a value.

•	Function procedures return a value.


Scope of procedures

In a report application, the procedures you create can have one of two scopes: class or global. Procedures you declare in a class are methods. Procedures you declare in a Basic source (.BAS) file are global procedures.

See also

Methods
Global procedures


Methods

A method is a procedure declared within a class that performs an action on an object. Because methods have class scope, they are accessible only to objects of that class. You create methods using the Method Editor. For information on creating methods, see Using the Component Editor.

A report typically has multiple objects of a class. Usually, your code does not need to know which object is currently executing. Sometimes, however, you may need to use a method or property that refers explicitly to a particular object. The Me keyword lets you refer to the object in which the code is running. Use Me just as if it were the Name property of the object.

Sub PrintTotal( )
Me.Print
End Sub

For more information about programming with objects, see Working with objects.


Global procedures

Global procedures are accessible from any part of the report application. To create global procedures and make them accessible to the report application, do the following:

1	Create a Basic source (.BAS) file by choosing File→New Basic File

2	In the source file, write the procedures with the Sub or Function statement. Be sure to include parentheses after the procedure name, even if empty.

3	Save the source file.

The source file is included with the report project automatically.

To include a Basic source file that already exists, choose File→Include Basic File

The following figure shows an outline of a Basic source file with global procedure declarations.

Once you include a module containing procedure declarations, you can call the procedures you defined the same way you call Actuate's built-in functions and statements.


Declaring procedures

Actuate Basic supports two types of procedures: Sub and Function. Sub procedures do not return a value. Function procedures return a value. The following topics describe how you declare Sub and Function procedures.

Declaring Sub procedures

Declaring Function procedures

See also

Declaring Sub procedures
Declaring Function procedures


Declaring Sub procedures

The syntax for a Sub procedure is:

Sub <procedure name>( [<arguments>] )
<statements>
End Sub

<arguments> is an optional list of argument names, separated by commas if there are more than one. Each argument looks like a variable declaration and acts like a variable in the procedure. For more information about arguments, see Declaring arguments.

<statements> are executed each time the procedure is called. Sub procedures can be placed in global modules, where they are accessible throughout the program, or in classes, where they become methods that operate on or are accessible to instances of that class only.


Declaring Function procedures

Actuate Basic includes built-in functions such as Chr, Now, and Cos. Each of these returns a value with an appropriate data type. For example, Now returns a date value containing the current date and time.

You can write your own procedures using the Function statement. The syntax is:

Function <procedure name>( <arguments> ) [As <type>]
<statements>
<procedure name> = <expression>
End Function

A Function is similar to a Sub in that it can take arguments, execute a series of statements, and change the value of its arguments. The arguments for a function procedure work the same way as the arguments for a Sub procedure with the following differences:

•	Typically, you call a function by including the function procedure name and arguments on the right side of a larger statement or expression. The value returned by the function is assigned to the variable on the left of the equal sign.

•	A Function procedure has a data type, just as a variable does, that determines the type of the return value. If you omit the As clause, the function type is Variant.

•	You return a value by assigning it to the procedure name itself within the procedure. Thus, when the function returns, this value can then become part of a larger expression from which you called the function.

For example, you could write a function that calculates and returns the area of a rectangle, given the length and width:

Function RectArea (Width As Single, Length As Single) As Single

RectArea = Width * Length
End Function


Declaring arguments

Arguments are one or more variables you declare in a procedure declaration when you want to use them as vehicles for passing values to the procedure. For example, procedures that perform calculations usually require values for processing, as shown in the following examples. You pass these values to the procedure when you call it. The arguments are the names the procedure privately uses for the values you supply. The first value you supply gets the first parameter name in the list, the second value gets the second name, and so on.

Function Sum (Num1 As Integer, Num2 As Integer) As Integer

Sum = Num1 + Num2

End Function

Function ExtendedCost (Cost As Currency, Quantity As Integer) As Currency

ExtendedCost = Cost * Quantity

End Function

See also

Argument data types
Passing arguments by reference
Passing arguments by value


Argument data types

Procedure arguments have the Variant data type by default. However, you can declare the data type of a variable for procedure arguments. For example, the following function accepts a string and an integer:

Function ExtractLeftString( S As String, n As Integer)
<statements>
End Function


Passing arguments by reference

By default, Actuate Basic passes variables to a procedure by reference. Passing arguments to a procedure by reference gives the procedure access to the actual memory location of the variable. This means that the procedure can change the value of the variable.

If you declare a data type for an argument passed by reference, you must pass a value of that data type when you call the procedure. You can convert the data type if necessary.


Passing arguments by value

Use the ByVal keyword to pass a variable by value. This is useful when you want to pass a variable as an argument to a procedure, but do not want to allow the procedure to change the value of the variable. Actuate Basic passes a copy of the value to the procedure. If the procedure changes the value, only the copy is changed, not the original. When the procedure finished, the copy of the variable goes out of scope.


Calling procedures

The techniques for calling a procedure vary, depending on the type of procedure, where it is located, and how it is used in your program. The following topics describe how you call Sub and Function procedures.

See also

Calling Sub procedures
Calling function procedures


Calling Sub procedures

A call to a Sub procedure is a stand-alone statement. That is, because a Sub procedure does not return a value, it cannot be called by using its name within an expression. However, a Sub procedure, like a Function procedure, can modify values of any variables passed as arguments.

There are two ways to call a Sub procedure. These two examples do the same thing:

Call MyProc( Argument1, Argument2)

MyProc (Argument1, Argument2)


Calling function procedures

Because a function procedure returns a value, you typically call it by using its name as part of a larger expression. You call a function that you have written yourself the same way that you call a built-in function. All of the following examples call a function named UpdateTotal:

Print UpdateTotal

NewTotal = UpdateTotal

If UpdateTotal < 0 Then

MsgBox "Error, total is negative."

EndIf


Procedure overloading

Procedure overloading is an Actuate Basic feature that can make your programs more readable. For example, suppose you write a square root function that operates on integers and another square root function for doubles. In Actuate Basic, you can give both these procedures the same name: square_root. By "overloading" the name square_root, you give it more than one meaning.

Actuate Basic distinguishes between the two versions of the functions by the type and number of arguments. For example:

Function square_root( intInput As Integer) As Integer

Function square_root( doubleInput As Double) As Double


Using control structures

Control structures allow you to control the flow of the program's execution. Most applications do not flow from top to bottom. Rather, they contain decision points that allow the application to change statement order with structures and loops.

The following topics describe the control structures you can use:

Using If...Then

Using...Then...Else

Using Do...Loop

Using For...Next

Using nested control structures

See also

Using If...Then
Using If...Then...Else
Using Do...Loop
Using For...Next
Using nested control structures
Exiting a control structure
Exiting a Sub or Function procedure


Using If...Then

Use an If...Then block to execute one or more statements conditionally.

If <condition> Then

<statements>

End If

The condition is typically a comparison, but it can be any expression that evaluates to a numeric value. Actuate Basic interprets this value as a Boolean: that is, any value other than zero is considered True.

If myDate < Now Then

myDate = Now

Timer.Enabled = True 	'Start the timer

End If


Using If...Then...Else

Use an If...Then...Else block to define several blocks of statements, only one of which will execute.

If <condition1> Then

[<statement block 1>]

ElseIf <condition2> Then

[<statement block 2>]

Else

[<statement block 3>]

End If

If...Then...Else is the general case of If...Then. You can have any number of ElseIf clauses, or none at all. You can include a single Else clause (but only one) whether or not you have ElseIf clauses.


Using Do...Loop

Use a Do loop to execute a block of statements an indefinite number of times. There are several variations on the Do...Loop statement, but each evaluates a numeric conditional as a Boolean to determine whether to continue execution.

The following example first tests the condition statement. If False, the loop code is skipped. If True, the statements are executed and the loop repeated until the condition evaluates to False.

Do While <condition>

<statements>

Loop

The following example is a variation of the Do...Loop that guarantees that the loop will execute at least once.

Do

<statements>

Loop While <condition>


Using For...Next

Do loops work well when you do not know how many times to execute the statements in the loop. If you do know how many times to execute the statements, use the For...Next loop. A For...Next loop uses a counter that increments or decrements in value during each step of the loop.

For <counter> = <start> To <end> [Step <step size>]

<statements>

Next <counter>


Using nested control structures

You can place control structures inside other control structures, such as a For...Next loop within an If...Then block. A control structure placed inside another control structure is said to be nested.

Actuate Basic allows control structures to be nested to as many levels as you need. A common practice is to make nested decision structures and loop structures more readable by indenting the body of the decision structure or loop.


Exiting a control structure

The Exit For and Exit Do statements allow you to exit the corresponding loop without performing any further iterations or statements within the loop.


Exiting a Sub or Function procedure

The Exit Sub and Exit Function statements each allow you to exit a procedure without processing any further statements in it. These may be useful when the procedure has done everything it needs to do, and can immediately pass control to the line following its End statement.


Using stored procedures in SQL databases

The following topics are available. To view the topics in order, use the Browse button (>>) in the Help window's toolbar.

•	About stored procedures

•	Customizing stored procedures


About stored procedures

A stored procedure is a block of SQL statements that perform a specific task. Stored procedures are stored in the database and, like standard procedures, can be called by name from an application.

Actuate report applications can call stored procedures in databases that support stored procedures, including ODBC, Oracle, and Sybase databases. Use of stored procedures improves database performance by reducing the amount of information that must be sent over a network compared to issuing individual SQL statements.

You can use stored procedures to execute any database task, for example, modifying, inserting, or deleting records, exchanging data between the database and Actuate reports, and so on.

You can work with stored procedures in these ways:

•	Using the automated design facilities in the Developer Workbench

•	Overriding Actuate Basic methods to customize stored procedures

The automated design facilities in the Developer Workbench handle only the first set of returned results. Further, Actuate does not currently support using Oracle stored procedures to retrieve data rows from the database for an Actuate report. Instead, you must override Actuate Basic methods to customize stored procedure handling in your application. This chapter discusses how to customize stored procedures.


Customizing stored procedures

In most cases, you use the Stored Procedure DataSource Builder in the Developer Workbench to create data sources that use a stored procedure as a source of data. The Stored Procedure DataSource Builder's automated design facilities enable you to set up and access stored procedures without having to write code. For more information about the Stored Procedure Builder, see Chapter 5, "Query data streams," in Building Reports.

In some cases, you may want to work with stored procedures that return multiple data sets or with databases that are not directly supported by the Stored Procedure DataSource Builder. In these cases, you override certain methods in Actuate Basic to customize the way your application handles the stored procedures and their resulting data.

See also

Accessing stored procedures
Accessing multiple result sets
Mapping Actuate variable types and Visual Basic type codes


Accessing stored procedures

The following are general steps for calling a stored procedure from an Actuate application.

1	Connect to the database.

2	Create and prepare the statement to execute the stored procedure using the connection's Prepare( ) method.

3	If passing a value or values to the stored procedure, define the procedure input parameters using the statement's DefineProcedureInputParameter( ) method. Do not embed the input parameter definitions in the statement itself.

4	If getting a value or values from the stored procedure,

1	Define output parameters using the statement's DefineProcedureOutputParameter( ) method.

2	Call the StartNextSet( ) method.

3	Execute the stored procedure using Execute( )

4	Get the output parameter value or values using GetOutputParameter( ).

5	If the stored procedure returns rows,

1	Create a cursor using the statement's AllocateCursor( ) method.

2	Bind the columns to data-row variables using the cursor's BindColumn( ) method.

3	Create the data-row object using New( )

4	Retrieve the rows using the cursor's Fetch( ) method.

6	If the stored procedure returns a status, get the return status value using GetProcedureStatus( ).

Example 1

The following example shows the code for a Sybase stored procedure and a section of a program written in Actuate Basic that passes input parameters to and retrieves rows from the Sybase database.

The following stored procedure takes an input parameter value and uses that value to determine which rows to retrieve. It returns output parameters and the procedure status.

/* Create the procedure inproc */

create proc inproc

/* Declare the input parameter */

@charin char(20)

/* Declare the output parameters */

@int_out int output,  @charboy char(20) output

/* Specify which rows are to be fetched */

as begin 	select id, name from syscolumns where name > @charin

/* Assign values to output parameters */

Select @charboy = "foobox"

Select @int_out = 88

/* Return procedure status */

return 99

end

The following Actuate Basic code executes the stored procedure and supplies the input parameter value that the stored procedure uses to determine the set of rows to return to the Actuate Basic application.

' Prepare the statement and execute inproc

Set statement = connection.Prepare("exec inproc")

If statement Is Nothing Then

Print #1, "Failed to prepare statement."

Exit sub

End If

' Define the procedure input parameter; pass the value "v" to the procedure

statement.DefineProcedureInputParameter("@charin", "v")

' Define the procedure's output parameters

statement.DefineProcedureOutputParameter( "@charboy", V_STRING )

statement.DefineProcedureOutputParameter( "@int_out", V_INTEGER )

' Open a cursor, which is required when returning rows

Set cursor = statement.AllocateCursor( )

If cursor Is Nothing Then

Print #1, "Failed to allocate cursor."

Exit sub

End If

' Bind the database columns to data row variables

MsgBox ("binding" )

cursor.BindColumn( 1, "syscolRow", "Id" )

cursor.BindColumn( 2, "syscolRow", "theName" )

MsgBox( "bound" )

' Create the data row

set sRow = New syscolRow( )

' Fetch rows from database

do while cursor.Fetch( sRow )

MsgBox( "Name: " & sRow.theName & " Id: " & sRow.Id )

loop

MsgBox( "Expecting false for next set: got: " & cursor.StartNextSet( ) )

' Return the values of the stored procedure's output parameters

rpcParam1 = cursor.GetOutputParameter("@charboy")

MsgBox( "Rpc param 1 is " & rpcParam1 )

rpcParam2 = cursor.GetOutputParameter("@int_out")

MsgBox( "Rpc param 2 is " & rpcParam2 )

'Display procedure status value

MsgBox( "Rpc return val is " & cursor.GetProcedureStatus( ) )

set cursor = Nothing

connection.Disconnect( )

Example 2

The following example shows the code for an Oracle stored procedure and a section of a program written in Actuate Basic that passes input parameters to and gets values from an Oracle database.

The following stored procedure takes an input parameter and returns output parameters.

-- Create stored procedure sprocoutparam with input and output parameters

CREATE OR REPLACE PROCEDURE sprocoutparam ( po_id IN OUT NUMBER, o_name OUT VARCHAR )

-- Declare output parameter

AS

l_id NUMBER;

-- Declare input parameter

BEGIN

l_id := po_id;

SELECT id, name into po_id, o_name from sproctable where id = l_id;

END sprocoutparam;

/

The following Actuate Basic code executes the stored procedure, passing an input parameter value and getting output values.

Sub TestSPStatement( connection As AcDBConnection )

Dim statement As AcDBStatement

Dim id As Long

Dim newId As Long

Dim name As Variant

' Prepare statement

Set statement = connection.Prepare("BEGIN sprocoutparam (:p_id, :p_name);

END;" )

If statement Is Nothing Then

PrintString ( "Failed to prepare statement." )

PrintString ( connection.GetSpecificErrorText( ) )

PrintString ( connection.GetGeneralErrorText( ) )

Exit sub

End If

' Define an input/output parameter

id = 20

If statement.DefineProcedureOutputParameter ( "p_id", V_INTEGER, id ) = 0
 Then

PrintString ( "Failed to define input/output parameter" )

PrintString ( connection.GetSpecificErrorText( ) )

PintString ( connection.GetGeneralErrorText( ) )

Exit sub

End If

' Define an output parameter

If statement.DefineProcedureOutputParameter ( "p_name", V_STRING ) = 0
 Then

PrintString ( "Failed to define output parameter" )

PrintString ( connection.GetSpecificErrorText( ) )

PrintString ( connection.GetGeneralErrorText( ) )

Exit sub

End If

' Execute sprocoutparam

If statement.Execute( ) = 0 Then

PrintString ( "Stored procedure sprocoutparam execution failed." )

PrintString ( connection.GetSpecificErrorText( ) )

PrintString ( connection.GetGeneralErrorText( ) )

Else

PrintString ( "Stored procedure sprocdml execution success." )

End if

' Get and print the output parameter values

name = statement.GetOutputParameter ( "p_name" )

newId = statement.GetOutputParameter ( "p_id" )

Print #1, "Output - name = ", name

Print #1, "Input/Output - id = ", newId

End Sub

Sub PrintString( str As String )

Print #1, str

End Sub

Override the Start method of the datasource to call this code. The call looks something like this:

Function Start( ) As Boolean

    Start = Super::Start( )

    ' Insert your code here

Dim Test As OracleConnection

Set Test = GetConnection()

TestSPStatement (Test)

End Function

Example 3

The following example shows the code for a stored procedure and a section of a program written in Actuate Basic that passes input parameters to and extracts the return value of the stored procedure.

The following stored procedure takes an input parameter value (a name) and uses that value to determine the value (the associated ID) to return to the Actuate application.

-- Create the procedure spfunc

CREATE OR REPLACE FUNCTION spfunc ( p_name IN VARCHAR )

-- Declare output parameter

RETURN NUMBER

AS

l_id NUMBER;

-- Declare input parameter

BEGIN

SELECT id INTO l_id FROM sproctable WHERE name = p_name;

RETURN ( l_id );

END spfunc;

/

The following Actuate Basic code executes the stored procedure to determine the ID associated with a name. The code passes the input parameter value (the name) to the stored procedure. The stored procedure uses the name to determine its associated ID, and returns that ID to the Actuate Basic program.

Sub TestSPStatement( connection As AcDBConnection )

Dim statement  As AcDBStatement

Dim id	  As Long

Dim newId  As Long

Dim name 	 As Variant

' Prepare the statement

Set statement = connection.Prepare("BEGIN :p_id := spfunc (:p_name); END;" )

If statement Is Nothing Then

PrintString ( "Failed to prepare statement." )

PrintString ( connection.GetSpecificErrorText( ) )

PrintString ( connection.GetGeneralErrorText( ) )

Exit sub

End If

'Define the input parameter; pass the value "John Smith" to the procedure

name = "John Smith"

If statement.DefineProcedureInputParameter ( "p_name", name ) = 0 Then

PrintString ( "Failed to define input parameter" )

PrintString ( connection.GetSpecificErrorText( ) )

PrintString ( connection.GetGeneralErrorText( ) )

Exit sub

End If

'Define the output parameter

If statement.DefineProcedureOutputParameter ( "p_id", V_INTEGER ) = 0
 Then

PrintString ( "Failed to define output parameter" )

PrintString ( connection.GetSpecificErrorText( ) )

PrintString ( connection.GetGeneralErrorText( ) )

Exit sub

End If

'Execute spfunc procedure

If statement.Execute( ) = 0 Then

PrintString ( "Stored function spfunc execution failed." )

PrintString ( connection.GetSpecificErrorText( ) )

PrintString ( connection.GetGeneralErrorText( ) )

Else

PrintString ( "Stored function spfunc execution success." )

End if

'Get the output parameter value, the id associated with "John Smith"

newId = statement.GetOutputParameter ( "p_id" )

Print #1, "Function Return Value - id = ", newId

End Sub


Accessing multiple result sets

The Developer WorkBench installation includes an example showing how to access a stored procedure's multiple result sets using ODBC. The example works with Microsoft SQL Server 4.2x and Sybase SQL Server.

The stored procedure returns three result sets:

•	The first select statement returns a result set with information about the name, owner and type of the database object, the syslocks table.

•	The second select statement returns a result set that contains the name of the Data Segment the table is on and the date and time that it was created.

•	The third select statement returns a result set with the name of the columns in the table and their type, length and other miscellaneous information.

The example shows the subclasses to create, as well as the methods to override, to process multiple result sets for an Actuate report. It includes the .ROD, .ROL, and .BAS file. The example is available from the Examples directory in your Developer Workbench installation.


Mapping Actuate variable types and Visual Basic type codes

When you use the DefineProcedureOutputParameter( ) method, you must specify the appropriate Actuate type code. This type code maps to the database data type of the stored-procedure parameter referenced in the define call.

The following table shows the Actuate variable types and the equivalent Visual Basic type code.

Actuate variable type	Type code to use
Currency or Variant	V_CURRENCY
Date or Variant	V_DATE
Double or Variant	V_DOUBLE
Integer or Variant	V_INTEGER
Long or Variant	V_LONG
Single or Variant	V_SINGLE
String or Variant	V_STRING
The following statements are examples of how to map the data types in DefineProcedureOutputParameter( ).

statement.DefineProcedureOutputParameter("@charboy", V_STRING)

statement.DefineProcedureOutputParameter("@int_out", V_INTEGER)


Variables and data types

The following topics are available. To view the topics in order, use the Browse button (>>) in the Help window's toolbar.

•	About variables

•	Declaring variables

•	Declaring arrays

•	About data types

•	Working with variant data

•	Working with string data

•	Working with numeric data

•	Working with date data

•	Working with user-defined data types

•	Working with CPointer data types

•	Converting data types


About variables

When writing Actuate Basic code, you often need to store values in order to perform calculations. You may need to compare values, perform calculations and store the results, or store values such as global configuration settings. Actuate Basic, like most other programming languages, uses variables to store values. A variable is a named location in memory for temporarily storing data.


Declaring variables

You declare a variable using the Dim, Global, or Static statements. The following are examples of variable declarations:

Dim Total As Integer

Global FileName As String

Static Counter As Integer

Some Basic languages support implicit declaration of variables; Actuate Basic does not. Implicit declaration of a variable means that you can use a variable name in your code without having previously declared it. Because good programming practice discourages implicit variable declarations, Actuate Basic requires that you explicitly declare variables before using them.

See also

Scope of variables


Scope of variables

The scope of a variable determines which procedures have access to the variable. Depending on how and where it is declared, a variable has one of three scopes.

Declared with	Declared in	Scope
Global	Declarations section of a Basic source (.BAS) file included with your report design	Global
Dim or Static	A procedure or method	Local
Dim or Static	A class	Class
For more information about the Global, Dim, and Static statements, see Chapter 2, "Statements and functions," in Actuate Basic Language Reference.

See also

Global variables
Local variables
Class variables


Global variables

Global variables are accessible from any part of the report application. They are most useful for storing values that apply to the entire application, for example, file names or default paths to data. The values in global variables are preserved as long as the report application is running.

To create global variables and make them accessible to the report application, do the following:

1	Create a Basic source (.BAS) file by choosing File→New Basic File

2	In the source file, create a declarations section with Declare ...End Declare.

3	In the body of the declarations section, declare the global variables.

4	Save the source file.

The source file is included with the report project automatically.

To include a Basic source file that already exists, choose File→Include Basic File.

The following figure shows an example of a Basic source file with global variables defined in a declaration section.

Initializing global variables

To initialize global variables after you have declared them:

1	Write a Sub procedure in your source file that assigns them whatever initial values you want them to have.

2	Override the OnStartViewer( ) method of the report so that it calls that procedure.

At runtime, Actuate will initialize your global variables when it starts the Viewer.

Although global variables have their uses, it is generally a good idea to avoid using them.  They can contribute to the creation of complex state machines, and make the logic of an application hard to understand. Use local variables whenever you can.


Local variables

Local variables are accessible only within the procedure or method in which they are declared. A local variable is useful for a temporary calculation, for example, as a counter in a For or Do loop.

Limiting the scope of variables is useful because it lets you reuse variable names. As an example, suppose that you declare a variable called Total in a procedure to contain the sum of several numbers. If you later write another procedure that performs an unrelated, but similar task, you can still use the variable name Total within that procedure. Local scope allows each procedure to be a self-contained entity, because other procedures cannot see variables in the local scope.

You declare local variables with Dim or Static.

Dim intTemp As Integer

Static intPermanent As Integer

Declaring with Dim

Variables declared with Dim exist only as long as the procedure is executing. When a procedure has finished executing, Actuate discards the values of its local variables and reclaims the memory. The next time the procedure is executed, all of its local variables are reinitialized.

Declaring with Static

Variables declared with Static are preserved as long as the report application is running. Use Static if you need to retain the value of a local variable, for example, to calculate cumulative totals. In the following example, a function calculates a cumulative total by adding a new value to the total of previous values stored in the static variable Accumulate.

Function CumulativeTotal(ByVal Num As Double) As Double

Static Accumulate As Double

Accumulate = Accumulate + Num

CumulativeTotal = Accumulate

End Function

If you declare Accumulate with Dim instead of Static, the values previously accumulated are not retained across calls to the function, and the function returns the same value with which it was called.


Class variables

Class variables are accessible only within the class in which they are declared. They typically store values that define the state and attributes of an object of the class. You can declare class variables with Dim or Static. For more information about class variables, see About class variables.


Declaring arrays

An array allows you to refer to a series of variables by the same name and to use an index number to tell them apart. This helps you create smaller and simpler code in many instances because you can set up loops that deal efficiently with any number of cases by using the index number. Arrays have both upper and lower bounds, and the elements of the array are contiguous within those bounds. Because Actuate Basic allocates space for each index number, avoid declaring an array larger than necessary.

See also

Array elements
Dynamic arrays


Array elements

All elements in an array have the same data type. You can declare an array as any of the fundamental data types. For example,

Dim Counters(14) As Integer	' Integers, indexed 0-14 or 1-14,
' depending on the Option Base statement

Dim Sums(50) As Double	' Doubles, indexed 0-50 or 1-50

Dim Sums(10 To 20) As Double	' Doubles, indexed 10-20

You can also create a Variant array, and populate it with elements of different data types. For example you can create an array that contains both integers and strings.

You can declare an array that contains multiple dimensions. Be careful when adding dimensions to an array, however, because the total storage needed by the array increases dramatically, especially with Variant arrays.

For information about data types, see About data types.


Dynamic arrays

Sometimes, you do not know in advance exactly how large to make an array, so Actuate Basic gives you the capacity to change its size as your program runs. Such a dynamic array can be resized depending on the needs of the program, thus helping you to manage memory efficiently. For example, you can use a large array for a short time and then free up the memory to the system when you are done. The alternative is to declare a fixed-size array of the largest possible size. However, this approach might cause the operating system to run low on memory.

To declare a dynamic array, you can use a statement like the following, with nothing between the parentheses, then size the array later using ReDim:

Dim AccordionArray( ) As Double

You can declare an array with multiple dimensions. For example, the following statement declares a fixed-size, two-dimensional 10 x 20 Matrix array with bounds:

Dim Matrix(1 To 10, 1 To 20) As Double

Sometimes, you may want to change the size of an array without losing the data already in it. To do so, use ReDim with the Preserve keyword. For example, you can enlarge an array by 5 elements without losing the values of the existing elements:

ReDim Preserve MyArray(UBound(MyArray) + 5)


About data types

Actuate Basic defines a set of standard data representations, called data types. Data types provide a way to classify data stored in a variable. Data types have rules governing how you work with data and what the variable can contain. The Actuate Basic data types include the standard Basic data types as well as data types defined for the Actuate Foundation Classes.

See also

Standard data types
Class data types
Assigning a data type
Constants


Standard data types

Actuate Basic defines the standard Visual Basic data types. The following table lists the names and characteristics of the standard data types.

Data type	Storage size	Range
Integer	4 bytes 32 bits	-2,147,483,648 to 2,147,483,647
Long	4 bytes 32 bits	-2,147,483,648 to 2,147,483,647
Single	8 bytes 64 bits	±2.2250738585072014E-308 to ±1.7976931348623158E+308
Double	8 bytes 64 bits	±2.2250738585072014E-308 to ±1.7976931348623158E+308
Currency	12 bytes 96 bits	-39,614,081,257,132,168,796.771975168 to 39,614,081,257,132,168,796.771975167
Date	8 bytes 64 bits	Dates 1 January 100 to 31 December 9999 Times 0:00:00 to 23:59:59
CPointer	4 bytes 32 bits	Same as for Unsigned Long
String	1 byte per character	0 to approximately 2,147,483,647 (or memory limit).
Variant	As needed	Depends on value stored, up to the range of a Double.
User-defined (defined with Type)	Defined by elements	The range of each element depends on its fundamental type.


Class data types

This section lists the Actuate Foundation Classes (AFC) data types. Data types are either typedefs or structures.

•	A typedef is an alias for a Basic data type, typically an integer. The AFC framework uses typedefs to create a type that has additional rules beyond those a simple Integer provides. These rules govern limitations on range, they direct special display formatting, and they specify related constants. For example, typedef rules about the numbers that represent colors restrict them to the range between 0 and hexadecimal FFFFFF. Some types are intended to be enumerations (enums). An enum is a type whose value can only be one of its associated constants. For example, a Boolean can only be True or False. Other types can have constants also. In such cases, the constants provide convenient shorthands for certain values.

•	A structure is a group of variables that together describe a property. The structure members can be Basic data types, such as an Integer, or other AFC defined structures. In some cases, structures are nested.

The following table summarizes the Actuate AFC data types. AFC data types begin with the letters "Ac". For a complete description of the AFC data types, see Chapter 2, "AFC data types," in Actuate Foundation Class Reference.

Data type name	Base type	Description
AcBitmapStyle	Integer	Specifies how a bitmap appears in a control: stretched to fit if the control is larger than the bitmap, scaled and sized to fit within the control, or displayed in the original size. Parts that don't fit are clipped.
AcColor	Integer	A color expressed as a standard Windows RGB value.
AcCursorShape	Integer	Specifies one of the standard Windows cursors.
AcFlowPlacement	Integer	Describes how a frame will appear within a flow when the frame is narrower than the flow: left, center, or right justified.
AcFont	Structure	Describes the font to use when printing text inside a control.
AcGraphAxisStyle	Integer	Specifies how to display the X-axis for graphs.
AcGraphBar Grouping	Integer	Specifies how bars are to be grouped in bar graphs.
AcGraphBar Orientation	Integer	Specifies the bar orientation for graphs.
AcGraphBorder Style	Integer	Specifies the style of a title in a graph.
AcGraphLabel Style	Integer	Specifies the source of axis labels.
AcGraphLegend Position	Integer	Specifies the position of the legend within a graph.
AcGraphLineStyle	Integer	Specifies the style of the lines in a line graph.
AcGraphPoint	Structure	Contains the data maintained for each data point in a graph.
AcGraphPoint LabelStyle	Integer	Specifies how to display point labels for graphs.
AcGraphTickStyle	Integer	Specifies the tick marks for a graph axis.
AcGraphType	Integer	Specifies the graph type.
AcGraphXAxis Position	Integer	Specifies where to display the X-axis.
AcGraphXValueSet	Integer	Specifies the number of X-values provided.
AcGraphYAxis Position	Integer	Specifies where to display the Y-axis.
AcGraphY Orientation	Integer	Specifies the orientation of the graph Y-Axis title.
AcHtmlBackground Style	Integer	Sets the style of an image that appears as the background of a web page.
AcHtmlFrameStyle	Integer	Enumerates the options for formatting text in an HTML report.
AcHtmlHorizontal Alignment	Integer	Enumerates the options for aligning objects on the horizontal plane.
AcHtmlImage Alignment	Integer	Enumerates the options for aligning images or applets relative to surrounding text.
AcHtmlImageMap Type	Integer	Enumerates the options for associating an image map with an image in an HTML report.
AcHtmlPlugInUnits	Integer	Defines how the size of plug-in windows are measured.
AcHtmlTable CaptionStyle	Integer	Specifies the placement of a table caption.
AcHtmlTableStyle	Integer	Specifies how to create a table in an HTML report.
AcHtmlText Alignment	Integer	Enumerates the options for aligning text horizontally in an HTML frame.
AcHtmlTextVert Alignment	Integer	Specifies how objects are aligned vertically within a table cell.
AcHtmlTocStyle	Integer	Enumerates the options for including a TOC in an HTML report.
AcHtmlUnits	Integer	Defines how object sizes are measured.
AcLinePen	Integer	The pattern to use when drawing lines. The line styles are those supported by the underlying windowing system.
AcLineStyle	Structure	Describes the style, color, and width of a line.
AcMargins	Structure	Gives an amount of space along the borders of a rectangle.
AcPoint	Structure	A two-dimensional point measured in twips. Points are relative to the upper-left of the enclosing rectangle.
AcRectangle	Structure	Contains the size and position of a rectangle relative to an origin.
AcRounding	Structure	Describes how to round a rectangle to give it rounded corners.
AcShadow	Structure	Describes the optional shadow for a frame or control.
AcShiftKeyState	Integer	Specifies a flag indicating if the Alt, Control, Shift, or any combination of those keys was pressed during a mouse event.
AcSize	Structure	Describes the size of a rectangle, measured in twips.
AcTextClipStyle	Integer	Describes how to handle text that is too long for its enclosing rectangle. Leading truncation removes the first part of the string, while trailing truncation removes the end part. There is an option to use ellipsis to show truncation.
AcTextJustify	Integer	Describes how to justify text horizontally within a control: left, center, or right.
AcTextPlacement	Structure	Describes how text is to be placed inside a control.
AcTextVertical Placement	Integer	For single lines of text, describes whether to align the text with the top, bottom, or vertical center of the enclosing rectangle.
AcTwips	Integer	A unit of distance measurement. Actuate Basic uses twips (1/1440") because it provides even divisions of printer's points and inches. It is small enough that it also can represent metric distances easily.
AcWordWrapStyle	Integer	Actions for lines in a multi-line control when a line is longer than the length of the control.


Assigning a data type

A variable has a data type associated with it. The data type determines what kind of data the variable can store. When you declare a variable, you can explicitly assign a data type to it. If you do not assign a data type to a variable, Actuate Basic gives it the Variant data type.

There are two ways to assign a data type to variables:

•	Using the As keyword

•	Using a type-declaration character

See also

Using the As keyword
Using a type-declaration character


Using the As keyword

To declare a variable and assign it a data type with the As keyword, use the following syntax:

Dim <variable> [As <type>]

The following are examples of valid variable declarations:

Dim MyAccount As Currency

Dim YourAccount

Both of these variables have been declared, and they are initialized to the default value of the data type. For these examples, MyAccount is a Currency variable initialized to 0.00 and Your Account is a Variant initialized to Empty.


Using a type-declaration character

Instead of declaring a type with As, you can identify the data type of a variable by appending a type-declaration character to the end of the variable name.

The following table lists the type declaration characters for Actuate Basic.

Data type	Type declaration character
Integer	%
Long	&
Single (real)	!
Double (real)	#
Currency	@
String	$
The following are examples of valid variable declarations:

Dim OurAccount@	' declare Currency variable

Dim CustomerName$	' declare String variable

Dim QuantitySold%	' declare Integer variable


Constants

For values that do not change during the life of your program, Actuate Basic allows you to declare constants. A constant is a reserved memory location whose content does not change. The following examples set constant values.

Const Pi = 3.14159265358979

Const FirstName = "Nola"

Const MyAccount = 25.43

Note that these examples do not contain type declarations. In such cases, Actuate Basic assigns the most appropriate data type: Pi is a Double, FirstName is a String. However, MyAccount could be a Single, Double, or Currency. In this case, Actuate Basic assigns the data type that uses the least amount of space, in this case, a Single, which is probably not what the programmer intended. To specify that MyAccount be Currency, use the following syntax.

Const MyAccount@ = 25.43@

The use of constants improves the readability of your code and reduces the chance for typing errors. Although a constant resembles a variable, you cannot change its value. If you attempt to do so, Actuate Basic gives an error message.


Working with variant data

A Variant data type can store all of the Actuate Basic system-defined data types. When you work with Variants, you typically do not have to be concerned with the type of data that they contain. Actuate Basic performs the necessary conversions automatically.

A Variant variable is a variable that can change its type depending on the program logic. The Variant maintains an internal representation of the data type it stores. This internal representation determines how Actuate Basic treats the value when operations are performed on the variable.

Actuate Basic uses the most compact representation possible to represent the variable. Later operations on the variable may result in its data type changing. You can use the VarType function to determine the data type of a Variant.

For small programs, or for frequently used variables, the Variant data type offers a convenient way to declare variables. However, Variants reduce the performance of the program and can result in unintended conversion of data types. For example, the result of the + operator can be ambiguous when used with two Variants. If both Variants are numbers, then Actuate Basic adds them together. If both Variants are strings, then Actuate Basic concatenates them. If one Variant is a string and the other is a number, Actuate Basic first attempts to convert the string to a number. If the conversion fails, then a Type mismatch error occurs. To avoid such problems, use fewer Variants and more explicitly typed variables in your code.


Working with string data

String variables are those that contain text. Although a string can contain digits, you cannot perform mathematical operations with the digits as a number. Within a string, digits are treated as characters, just like any other characters or punctuation marks. For example, zip codes and telephone numbers are usually stored as string data in tables because you do not usually perform calculations with them.

See also

Declaring a string
Binary string data
String manipulation
Formatting strings
Comparing strings
Changing capitalization
Removing spaces
Embedding quotes


Declaring a string

You can declare a variable as a String if it will always contain text and you never expect to treat it as a number in a calculation.

Dim myString As String

You can then assign strings to this variable and manipulate the variable using string functions. Use quotes to delimit literal strings in expressions.

myString = "Actuate Basic"

newString = Left(myString,7)

By default, a string variable or argument is a variable-length string: the string grows or shrinks as you assign new data to it.


Binary string data

Traditionally in Basic programs, binary data can be stored in a String variable. This worked because ANSI strings were single byte arrays of characters. Actuate Basic code can read and write binary data on a byte by byte basis using strings and manipulate it using the standard string functions, regardless of whether the data had a meaningful ANSI equivalent.


String manipulation

1	You can manipulate strings (including binary strings) with the Actuate Basic string functions. The following table lists the string manipulation functions. The string manipulation functions come in two flavors; one for ANSI strings and another for binary strings. You do not need to do anything before using the ANSI versions. However, to use the binary versions, you should do the following.Put the binary data in a byte array, using Get or some other function.

2	Assign the byte array to a string. This assignment does not translate the binary data. The data is simply copied into a string variable.

3	Use the "Binary version" of the functions in the following table to manipulate the binary data in the string.

4	Assign the contents of the manipulated binary string back to a byte array.

Function	Description
Asc	Returns the ANSI character code for the first character.
InStr	Returns the first occurrence of one string within another.
InStrB	Binary flavor of InStr. Returns the first occurrence of a byte in a binary string.
Mid	Returns a specified number of characters from a string.
MidB	Binary flavor of Mid. Returns a specified number of bytes from a binary string.
Left, Right	Returns a specified number of characters from the right or left sides of a string.
LeftB, RightB	Binary flavors of Left and Right. Returns a specified number of bytes from the left or right side of a binary string.
Chr	Returns a string containing the ANSI character specified.


Formatting strings

The simplest way to format a string is to use the Str function. The syntax is:

Str(<expression>)

When you convert a number to a string using Str, a leading space is placed in front of the string for the sign of the number. If the number is positive, the leading space implies the plus sign (it is not in the string). If the number is negative, the minus sign is put in front of the string.

For more complicated string formatting, use the much more powerful Format function. The syntax is:

Format(<expression>[,<format>])

The Format function syntax has the following parts:

Part	Description
expression	Any valid expression
format	A valid named or user-defined format expression
If you try to format a number without specifying format, Format provides the same functionality as the Str function. Positive numbers formatted as strings using Format lack the leading space reserved for displaying the sign of the value. Numbers converted using Str retain the leading space.

For more information about the Format function, see Chapter 2, "Statements and functions," in Actuate Basic Language Reference.


Comparing strings

The StrComp function returns a value indicating the result of a string comparison.

StrComp(<string1>, <string2>[, <compare>])

The StrComp function syntax has these named arguments:

Part	Description
string1	Any valid string expression
string2	Any valid string expression
compare	Specifies the type of string comparison. The compare argument can be omitted, or it can be 0 or 1. Specify 0 (default) to perform a binary comparison. Specify 1 to perform a textual comparison. If compare is Null, an error occurs. If compare is omitted, the Option Compare setting determines the type of comparison by default
Return Values

If	StrComp returns
string1 is less than string2	-1
string1 is equal to string2	0
string1 is greater than string2	1
string1 or string2 is Null	Null


Changing capitalization

Actuate Basic has two functions that you can use to change the capitalization of a string. The following table lists the case functions.

Function	Description
LCase(<string expression>)	Returns a variant in which all letters are lower case.
LCase$(<string expression>)	Returns a string in which all letters are lower case.
UCase(<string expression>)	Returns a variant in which all letters are upper case.
UCase$(<string expression>)	Returns a string in which all letters are upper case.


Removing spaces

You can delete leading or trailing spaces in a string. The following table lists the functions to remove spaces from a string.

Function	Description
LTrim(<string expression>)	Returns a copy of a string without leading spaces.
RTrim(<string expression>)	Returns a copy of a string without trailing spaces.
Trim(<string expression>)	Returns a copy of a string with neither leading nor trailing spaces.


Embedding quotes

Sometimes you may want to embed quotation marks within a string. For example,

The CEO said, "Sales are fantastic!"

To make this sentence into a valid string expression, you must insert an additional set of quotation marks for each set to display in the string. Actuate Basic interprets two quotation marks in a row as an embedded quotation mark. Continuing with this example, to assign the previous string to a variable, use the following code.

Text.Aside = "The CEO said, ""Sales are fantastic!"" "


Working with numeric data

Actuate Basic offers several data types that can contain numeric data. The type you choose depends on how you want to use the numbers. The following topics describe the different types of numeric data and when to use each.

Numbers

Currency

Using numeric data types

See also

Numbers
Currency
Using numeric data types


Numbers

The Integer and Long data types represent numbers that have no fractional component.

The Single and Double data types allow you to express floating-point numbers. A floating-point number (also called a real number, or a real for short) represents a value that has a fractional component. The term floating point comes from the fact that the decimal point can occur any place within the number. For example, 0.0003, 205.333, and 30000.0. In contrast, the decimal point in the Currency data type is fixed at four places of precision, although more places may be displayed.

If you know that a variable will always contain a whole number (such as 45 or 30000000000) rather than a number with a fractional amount (such as 3.14159 or 123.45), declare it as an Integer or Long type. Operations are faster with integers and these types consume less memory than Variants. They are especially useful as counter variables in For...Next programming constructs.


Currency

The Currency data type is designed to represent monetary values with a precision of four decimal places. Currency variables are stored as 96-bit (12-byte) numbers in an integer format, scaled by 1,000,000,000 (109) to give a fixed-point number with 20 digits to the left of the decimal point and 9 digits to the right. The Currency data type is useful for calculations involving money and for fixed-point calculations in which accuracy is particularly important. Floating-point (Single and Double) numbers have much larger ranges than Currency, but they are subject to small rounding errors. Because Currency is a scalar data type, it is not subject to rounding errors within its range.


Using numeric data types

If a variable contains a fraction, declare it as a Single, Double, or Currency variable, whichever is appropriate. The Currency data type supports up to nine digits to the right of the decimal and twenty digits to the left. It is a scalable fixed-point data type, especially suited for monetary calculations because it is not subject to rounding errors. The Single and Double data types are floating-point numbers with much larger ranges than fixed point data types, but are subject to small rounding errors.

Floating-point values can be expressed as mmmmEeee, where mmmm is the mantissa and eee is the exponent (a power of 10).

All numeric variables can be assigned to each other and to variables of type Variant. Actuate Basic rounds off, rather than truncates, the fractional part of a floating-point number before assigning it to an integer.


Working with date data

Date variables are stored as IEEE 64-bit (8-byte) floating-point numbers that represent dates ranging from 1 January 100 to 31 December 9999 and times from 0:00:00 to 23:59:59. Any recognizable literal date values can be assigned to Date variables. Literal dates must be enclosed within number sign characters (#), like this: #1/1/97# or #11/12/82#.

Date variables display dates according to the short date format recognized by your computer. Times display according to the time format recognized by your computer. That format may be 12 or 24 hour.

When other numeric data types are converted to Date, values to the left of the decimal represent date information while values to the right of the decimal represent time. Midnight is 0 and midday is .5.  Negative whole numbers represent dates before 30 December 1899.

See also

Formatting dates


Formatting dates

You can perform mathematical operations on date/time values. Adding or subtracting integers operates on the date. Adding or subtracting fractions operates on the time.

The following table lists the Actuate Basic date functions.

Function	Description
DateSerial(yy,mm,dd)	Returns a date or date serial number from the year, month, and day numbers entered.
Day(x)	Extracts the day from a date and returns a number.
Weekday(x)	Determines the day of the week the date falls on, and returns it as a number (1 to 7) where Sunday is 1.
Month(x)	Extracts the month component of a date and converts it to a number.
Date	Returns the current date.
Year(x)	Extracts the year from a date and returns a number.
You can use date/time literals by enclosing them within number signs (#). For example:

If Today > #2/25/96# Then ...

You can also include a time.

If Now > #2/25/96 10:00pm# Then ...

If you do not include a time, Actuate Basic uses midnight as the start of the day. If you do not include a date, Actuate Basic uses December 30, 1899.

You can use the IsDate function to determine whether a Variant or other value can be converted to a Date data type. You can then use the CDate function to convert the value to a Date data type.


Working with user-defined data types

The data types that are built into Actuate Basic are useful for most of the fundamental information that is easily represented by strings, numeric data, and dates. However, many programs require data that should be logically clustered together. For such data, you should consider creating user-defined data types whenever possible because they make your programs less error prone and easier to read or update. There are three types of user-defined data types:

•	Aliases

•	Structures

•	Classes

See also

Aliases
Structures
Classes


Aliases

An alias is the simplest type of user-defined data. When you use an alias, you declare a user-defined data type that has the properties of an already existing data type. The syntax is:

Typedef <new data type> As <existing data type>

Typically, the new data type is one of the Actuate Basic data types. The following is an example of an AFC type:

Typedef AcColor As Integer

However, you can also declare an alias to a structure. For example:

Typedef MyPoint As AcPoint


Structures

You create a user-defined data type as a structure whose elements contain previously defined data types. These can be either the Actuate Basic fundamental data types, or other user-defined data types.

For example, suppose that you are writing a program to catalog computers. You might define a structure that represents related information about the computer systems, as the following example shows.

Type SysInfo
ComputerName As String
CPU As String
Memory As Long
DiskDriveType As String
DiskDriveSize As Single
Price As Currency
PurchaseDate As Date
End Type

Once you declare a structure, you declare variables of this data type the same way you declare variables of the fundamental types.

Dim SystemOne As SysInfo, SystemTwo As SysInfo

Once declared, you can assign and retrieve values from the elements of the variable using the "dot" notation. For example,

SystemOne.Price = 1999.95

SystemOne.PurchaseDate = #2/25/96#

You can also assign one user-defined variable to another, provided that they are both of the same user-defined type. This assigns all elements of the first variable to the same elements of the second variable.

SystemTwo = SystemOne

User-defined types can be nested. That is, once you define a user-defined type, you can include it in another user-defined type. Nesting can be as complex as you like. However, to keep your code readable and easy to debug, try to keep all nested user-defined types in one module.


Classes

Classes are user-defined types that define the attributes of objects in a report. To create an object, you declare a variable that contains a reference to the object and assign a class as its type. Actuate provides a collection of classes, called the Actuate Foundation Class Library, that you can use. For more information about classes, see Understanding classes. For more information about objects, see Working with objects.


Working with CPointer data types

CPointer data type variables hold pointers to data allocated in C functions you have created.


Converting data types

Actuate Basic provides several functions to convert values into a specific data type. For example, to convert a value to Currency, use the CCur function.

WeeklyPay = CCur(hours * rate)

The following table lists the functions for converting data types.

Function	Converts an expression to
CCur	Currency
CDate	Date
CDbl	Double
CInt	integer
CLng	Long
CSng	Single
CStr	String
CVar	Variant


Programming for report viewing events

The following topics are available. To view the topics in order, use the Browse button (>>) in the Help window's toolbar.

•	Report viewing (Viewer) events

•	Creating context menus

•	Providing context sensitive help

•	Creating hyperlinks


Report viewing (Viewer) events

Report viewing events occur in the Viewer and are triggered by users when they select objects, press the mouse button, release the mouse button, or choose Help. You can program your report to respond to these events and execute actions, such as linking to another report, displaying context-sensitive help, displaying custom context menus, and so on.

See also

Objects that respond to Viewer events
Mouse events
Responding to mouse events


Objects that respond to Viewer events

The objects that you can write viewer event handlers for are:

•	Frames

•	Controls

•	Graphs

•	Pages

•	Flows


Mouse events

There are four kinds of mouse actions that visual objects in your report can respond to. They are:

•	Mouse button down (press)

•	Mouse button up (release)

•	Click (Mouse button down plus mouse button up)

•	Double click

The following table lists the methods associated with the different mouse events, and the default action the methods execute in response to each event:

Mouse button	Press	Release	Click	Double-click
Left	OnLButtonDown( ) Default action: Selects the object	OnLButtonUp( ) Default action: Nothing	OnLButtonClick( ) Default action: Nothing	OnLButtonDblClk( ) Default action: Nothing
Right	OnRButtonDown( ) Default action: Displays a context menu	OnRButtonUp( ) Default action: Nothing	OnRButtonClick( ) Default action: Nothing	OnRButtonDblClk( ) Default action: Nothing
For more information about each mouse event method, see Class AcVisualComponent in Chapter 3, "AFC classes," in Actuate Foundation Class Reference.


Responding to mouse events

When your report detects a mouse action, it calls the method associated with the corresponding event, and passes four parameters to the method. You can use the information in those parameters to customize your program's responses to the events.

As the previous table shows, Actuate responds to the OnLButtonDown and OnRButtonDown events. You can override the corresponding methods, either to augment the default functionality or replace it completely. Note, however, that Actuate's default responses for OnLButtonDown (selecting objects) and OnRButtonDown (displaying a context menu) are standard for Windows applications.

The following table describes the four parameters the report sends to the mouse event methods when a mouse action occurs:

Parameter	Description
view	The window the report is in.
shift	Indicates the state of the Alt, Ctrl, and Shift keys at the time of the mouse event.
x	The horizontal position of the mouse cursor at the time of the mouse event, measured in pixels relative to the left of the view.
y	The vertical position of the mouse cursor at the time of the mouse event, measured in pixels relative to the top of the view.
See also

Using the shift value in mouse event methods


Using the shift value in mouse event methods

The shift parameter contains one of the following values:

Keys pressed	Value	Shift value
None	0	NoKeys
Shift	1	ShiftKey
Ctrl	2	ControlKey
Shift + Ctrl	3	ShiftKey Bor ControlKey
Alt	4	AltKey
Shift + Alt	5	ShiftKey Bor AltKey
Ctrl + Alt	6	CtrlKey Bor AltKey
Shift + Ctrl + Alt	7	ShiftKey Bor CtrlKey Bor AltKey
Using the shift value, you can assign different actions to the different key and mouse action combinations. For example, the OnLButtonDown( ) method, by default, assigns different actions to different key and mouse actions, as described in the following table:

User action	OnLButtonDown action
Press left mouse button on object	Select the object and deselect other objects, if any were previously selected
Press left mouse button on object + press Shift key	Select the object, adding it to the set of previously selected objects, if any
Press left mouse button on object + press Ctrl key	Toggle the selection state of the object
Press left mouse button on object + press Shift + Ctrl keys	Toggle the selection state of the object
Example

The following example shows how AcVisualElement::OnLButtonDown( ) uses the different Shift values to implement different actions.

Function OnLButtonDown( view As AcReportView, Shift As AcShiftKeyState, x As Integer, y As Integer ) As Boolean

Dim point As AcPoint

point.X = x

point.Y = y

If Not Selectable Then

OnLButtonDown = False

Exit Function

End If

If view.IsSelected( me ) Then

If Shift BAND ControlKey Then

OnLButtonDown = True

view.SelectElement( me, False )

Else

OnLButtonDown = False

End If

Else

OnLButtonDown = True

If Not ( Shift BAND ControlKey Or Shift BAND ShiftKey ) Then

view.DeselectAll

End If

view.SelectElement( me, True )

End If

End Function


Creating context menus

A context menu is a menu that contains commonly-used commands specific to a particular object or window. By default, when users press the right mouse button, the OnRButtonDown( ) method displays a context menu. The following illustration shows the methods Actuate calls to create a context menu when the right mouse down event occurs.

See also

Default context menu items
Customizing context menus


Default context menu items

The context menu displays two items that AddMenuCommands( ) creates by default: "Default Action" and "Help."

About the Help context menu item

When users choose Help from the context menu, the Onhelp( ) method executes and displays the help string you specify in the HelpText property. If you did not specify a help string, OnHelp( ) displays "There is no help for this item." For information about how to implement context-sensitive help, see Providing context sensitive help.

About the Default Action context menu item

When users choose Default Action from the context menu, the OnActuate( ) method executes and calls OnFollowLink( ) by default.

OnFollowLink( ) is the method you override to implement hyperlinks between objects. If you do not implement the hyperlink or do not assign a different action to OnActuate( ), nothing happens when users choose Default Action. For information about implementing hyperlinks, see Creating hyperlinks.


Customizing context menus

If you do not change or add code to OnRButtonDown( ), OnContextMenu( ), or AddMenuCommands( ), pressing the right mouse button causes Actuate to display the default context menu. Similarly, if you do not override OnActuate( ) or OnFollowLink( ), or if you do not set the LinkToExp property, Actuate performs the default action--nothing--when users choose Default Action.

Actuate provides the structure for creating context menus. It is up to you to implement the details. The following are examples of how you might customize context menus.

To ...	Override
Link one object to another and make this capability accessible through the Default Action menu item.	OnFollowLink( ) or set the LinkToExp property.
Assign a different action to the Default Action menu item. For example, a more appropriate default action for a control that contains a sound file would be to play the sound.	OnActuate( )
Add menu items to the default context menu.	AddMenuCommands( )
Omit the context menu. If you do not want to provide help or let users perform any actions through the context menu, it would be better to get rid of the menu altogether.	OnRButtonDown( )
Example

The following code shows how you create a menu item for the context menu.

The following illustration shows the context menu the code creates.


Providing context sensitive help

You can provide online help or information for any of the objects on your report. By default, the Viewer displays help for an object when users take one of the following actions:

•	Press the right mouse button on an object and choose Help from the context menu

•	  Select the help button and click on the object

To provide online help, all you have to do is specify the help text to display by typing the string in the HelpText property on the Properties page of the Component Editor.

The following illustration shows the method calls that implement context-sensitive help when users press the right mouse button.

The following illustration shows how the string you enter in the HelpText property of a particular component appears when users choose Help for that object.


Creating hyperlinks

Hyperlinks connect one visual object in a report, such as a data control, to another object in either the same report or another report. Create hyperlinks to let users access related or detailed information on another page, report, or web page. For example, provide a jump from a customer record to that customer's order record, from a summary report to a detailed report, or from a company name to the company's Internet or intranet site.

Hyperlinks may be simple jumps from one part of a report to another, or complex instructions which pass parameters to a report executable to run dynamic reports based on specific search criteria. For example, you can use hyperlinks to search for a particular location within a report, jump to that location, and display the searched-for page in a web browser.

View .ROI's in the standard Viewer or in a web browser, using the Actuate Live Report Extension (LRX). View HTML reports directly in the web browser.

See also

About hyperlinks
Activating hyperlinks
Types of hyperlinks
Rules for hyperlinks
Defining hyperlinks
LinkExp syntax
Defining hyperlinks programmatically
Using target windows to display web reports
Defining universal hyperlinks
Converting code-based hyperlinks


About hyperlinks

Hyperlinks allow you to specify operations to perform on a target document or report. The types of operations you can specify are:

•	Viewing documents

•	Running a report and viewing the results

Hyperlink targets may be within the current environment (such as within the same Report Encyclopedia or file system), or to the web. You can specify a hyperlink to a location within the same report, or one which connects objects from two different reports.

Here are some useful hyperlink terms.

•	Internal hyperlink, a hyperlink within the same report.

•	External hyperlink, a hyperlink that connects objects from two different reports.

•	Dynamic hyperlink, a hyperlink that creates a new report.

•	Source object, the object from which the link is initiated.

•	Destination object, the object linked to.

See also

Creating a hyperlink between two data controls


Creating a hyperlink between two data controls

Hyperlinks for HTML reports are evaluated at compile time. No run-time processing occurs to determine HTML reports' hyperlinks destinations. If you create a hyperlink for an HTML report, then move the destination or change directory or report names, the hyperlink won't reach its target.

Hyperlinks for ROI's are evaluated at run-time. Processing does occur to determine an ROI hyperlink's destination.

When you create a hyperlink between two data controls using variables or column names, ensure that the value you want to search in the destination object matches the value in the source object. For example, suppose that you want hyperlinks between customer records in a customer report and the related order records in a sales order report. If you are not supplying a constant value to search on, you need a common value, such as the customer name, to make the hyperlink work.

The following illustration shows the relationship between internal and external hyperlinks and their destinations.

Web-based hyperlinks to HTML reports or reports in the Actuate Live Report Extension (LRX) web viewer display their destinations in the current web browser. Web-based hyperlinks from reports displayed in the standard report server viewer start your web browser if it is not already started, then display the destination in the web browser window.

You define a hyperlink in the Developer Workbench using an object's LinkExp property. LinkExp contains an extended URL (Universal Resource Locator; a kind of address) that identifies the link's destination report and defines the hyperlink's "landing place" within the destination report. Because LinkExp is an expression property, you can build expressions which resolve to specific values at run-time. The expressions can include references to columns in a data row, as well.

Since LinkExp is an expression property, enclose the link expression in quotes when you define it. If you include references to specific columns or values in a data row (for example, DataValue), concatenate the references using ampersands (&). To include literal double quotes in an expression ("), use two sets of double quotes. See "How to build a hyperlink expression" later in this chapter for more information on constructing a hyperlink which contains literal double quotes.

LinkExp's run-time counterpart is the LinkTo variable. LinkTo is set to the value that the LinkExp expression evaluates to at run-time. You can write code to explicitly set the LinkTo value, as well. For more information, see "Defining hyperlinks programmatically", later in this chapter.

Hyperlinks in previous versions of the Actuate products were code-based. Code-based links will work in the standard Viewer, but not in the LRX. To convert code-based hyperlinks to LinkExp hyperlinks, see "Converting code-based hyperlinks", later in this chapter.


Activating hyperlinks

By default, users trigger hyperlinks when they take one of the following actions:

•	Double-click an object in the standard Viewer

•	Single-click an object in the LRX

•	Right-click an object, then choose Default Action from the context menu that appears.

You can change this default behavior. For example, if you want to activate hyperlinks with a single click of the left mouse button, you can override OnLButtonClick( ) to call OnFollowLink( ).When a user places the cursor over a hyperlink, the standard viewer displays the hyperlink's text in the status line. If a control has help attached to it, the help text appears in the standard viewer's status line when the user places the cursor over the control.

When a user right-clicks on a hyperlink, the context menu appears. The context menu for hyperlinks includes a "Copy Link" command, which copies the current hyperlink to the Windows clipboard.


Types of hyperlinks

You can define hyperlinks among objects in the same environment, or between a report in any environment and the web. For example, create hyperlinks from one report in a file system to another report in the same file system, between one report on a server to another report on the same server, or between a report on a file system, report server, or the web and a destination on the web.

You can create these types of links between reports in the same environment:

•	Link to a location within the current report. For example:

#DetailReport::CustomerFrame::CustomerID="1234"

•	Link to an external report by specifying the only the report's file name. Such links must be relative to the current report's location. For example:

forecast.roi

•	Link to an external report by specifying a relative path. Such links must be relative to the current report's location. For example:

../Boston/forecast.roi

•	Link to an external report by specifying its absolute path. For example:

/Marketing/Western_Region/Current/results.roi

http://SalesServer/acweb/Mustique/Sales/3rdQtr/forecast.roi

Where acweb indicates that the report is on the web.

Absolute path has special meaning if you're viewing a report using a web browser. If you're viewing an HTML report or viewing an ROI using your web browser, the absolute path refers to the absolute path on the report server; it has no relation to the web server.


Rules for hyperlinks

•	Use the file protocol to create links to reports on the local file system.

•	.ROI's can always link to other .ROI's, regardless of location.

The following table lists the types of hyperlinks allowed with reports viewed on the web, in the LRX, and in the Viewer.

	Relative path	ROTP	File	HTTP (web)
Allowed in HTML reports	Yes	Yes	Yes	Yes
Allowed in the LRX	Yes	No	Yes	Yes
Allowed in the Viewer	Yes	No	Yes	Yes


Defining hyperlinks

To define a hyperlink from a specific source object to a specific destination object in the Developer Workbench:

1	Start two Developer Workbenches: one to display the source report's .ROD, and one to display the destination report's .ROD.

2	In the destination report's .ROD:

1	Select the object you want to link to and display its properties.

2	Select the Class tab. The class window appears:

3	Note the scope. The scope format will be similar to NewReportApp::Frame1. It appears directly below the Super Class.

4	Note the class name. The class name appears at the top of the class window.

5	Select the Properties tab.

6	Note the ValueExp property. The ValueExp property format will be similar to [customers.custID].

3	In the source report's .ROD, select the source object that you want to link from.

4	Right-click. The pull-down menu displays.

5	Select Properties. The Component Editor displays.

6	Scroll down to LinkExp. Click in LinkExp to select it.

7	Type the hyperlink URL expression.

The syntax for linking to numeric values, such as a customer ID, is:

"<Path>#<Scope>::<Class>=" & <Format>([ <ValueExp> ],<Fmt>)

The syntax for linking to string values, such as a customer name, is:

"<Path>#<Scope>::<Class>=" & [ <ValueExp> ]

Where,

•	<Path> is the destination report's URL or pathname

•	<Scope> is the destination object's scope, taken from the destination object's Class window

•	<Class> is the destination object's class, taken from the destination object's Class window

•	<Format> is an Actuate Basic keyword, used to format the value in the destination object at run time

•	<ValueExp> is the expression representing the value of the destination object at run time

•	<Fmt> is the format for the destination object


LinkExp syntax

<Destination URL>[#<Search tag>][?<Dynamic report parameters>]

Rules

•	The destination URL element is required. Elements must be specified in the order shown. Otherwise, the syntax is flexible. Depending upon the purpose of the hyperlink, you can omit parts of an element, omit an entire element, or combine elements in various ways, . The destination URL can be relative to the current environment, or it can be on the web. For more information on the destination URL, see "Destination URL syntax," later in this chapter.

•	LinkExp is an expression property. You must enclose the link expression in quotes. If you reference specific columns in your report's data rows, after the expression's closing quote, you concatenate the columns to the link expression string using an ampersand (&).

•	Specific column values must be enclosed in quotes as well, if they contain spaces at the beginning or end, literal quotes, or relational expressions.

•	To include a quote (") in a link expression you need to use a set of two quotes.

Example

The following LinkExp expression specifies a jump to the detailed information for the current office.

"forecast.roi#ForecastDetail::OfficeTitleFrame::OfficeName=
"""& DataValue""""

DataValue is a column name. Actuate substitutes DataValue with the actual value that the column contains when the user clicks on that column.

See also

How to build a hyperlink expression
Destination URL syntax
Search tag syntax
Dynamic report parameters syntax
How to create a hyperlink with dynamic report parameters


How to build a hyperlink expression

1	Start with the desired end result, the link expression you want to build.

forecast.roi#ForecastDetail::OfficeTitleFrame::OfficeName="<office>"

where <office> is the name of the office that you are searching for information about.

2	Enclose the link expression in quotes.

"forecast.roi#ForecastDetail::OfficeTitleFrame::OfficeName="<office>""

<office> is a placeholder for a column name or data control.

3	To include the current value of DataValue in the hyperlink at runtime, replace <office> with &DataValue &, the current value of DataValue.

"forecast.roi#ForecastDetail::OfficeTitleFrame::OfficeName="&DataValue""

When a user clicks on the office name displayed in the report at runtime, Actuate replaces the placeholder with the current value of the column or data control. In this example, the column name is DataValue.

4	The value we are searching for might contain spaces at the beginning or end, quotes, or relational expressions. Therefore, we enclose the value in two sets of quotes.

"forecast.roi#ForecastDetail::OfficeTitleFrame::OfficeName=
"""&DataValue""""

You now have a complete LinkExp expression.

The next sections describe the elements of the LinkExp syntax in more detail.


Destination URL syntax

The destination URL specifies the location of the destination report. If you do not specify this element, Actuate assumes that the location is internal to the current report.

The destination URL syntax is

[<protocol>:]//<path>/<report name>

<protocol>

The communication protocol to use to reach the destination report. Supported protocols include

Protocol	Description
file	The destination is in a file system
http	The destination is on the World Wide Web. Goes to the browser
none	Action taken depends on the environment. If the source and destination are on the file system, the destination displays in the Viewer. If the destination is on the web, the destination displays in the browser
other	The destination displays in the browser. Other protocols include gopher and ftp
rotp	The destination is on the Report Server
<path>/<report name>

The destination report's path name. Paths may be absolute or relative. You can specify a hyperlink to an ROI, or to an ROX or ROV if the hyperlink runs a dynamic report. The reports must be in the same environment, that is, in the same file system, on the same report server, or on the web.

Examples

The following path specifies that the destination report is in the Boston folder, one level up from the current report's folder.

"../Boston/forecast.roi"

The following path specifies that the destination report is in the current report's folder.

"Q3SalesDetails.roi"

The following path specifies that the destination report is in the file system, in drive C's Forecasts/Boston folder.

"file:C:/Forecasts/Boston/Q3Forecast.roi"

If the current report is on the web, the following path specifies that the hyperlink is to a file on the web server.

"http://gumby/acweb/SalesServer/NewYork/Revenue/Q1Revenue.roi"

The following path specifies that the destination report is on the Report Server.

"rotp://gumby/SalesServer/forecast.rox"


Search tag syntax

The search tag defines the hyperlink's the specific destination within the destination report. If you want the first page of the destination report to display, specify only the report URL, no search expression tag is needed.

The search tag syntax is

#<class[::class...]> [[.<variable>]=<value>] [;<...>]

#

Indicates the beginning of the search tag.

<class[::class...]>

Fully-qualified name of a component class, such as a control class.

<variable>

Class's variable on which to search. If you don't specify a variable name, the search is on the default value for the specified class. If you don't specify a value for the class, the search looks for all instances of the class within the report.

<value>

Value to search for. You need to enclose the value to search for in quotes when the value,

•	Includes one or more spaces at the beginning or end

•	Contains relational expressions, such as <, >, =

•	Contains special characters, such as &, ;, @, #

Examples

Search for the string literal "Gumby Special Pack".

"#ForecastApp::ProductName=""Gumby Special Pack"""

Create a hyperlink to the ForecastApp report's product name.

"#ForecastApp::ProductName=" & [values.ProdName]

The product name control is called ProductName, and ProdName's ValueExp is [values.ProdName]

[;<...>]

Add more search criteria. You can specify several search criteria, separated by semi-colons. However, do not include spaces after the semi-colon. Specifying multiple search criteria is like constructing a Boolean AND statement. Controls or data must match all the search criteria to be found by the hyperlink. If more than one match exists, the hyperlink goes to the first occurance.

Examples

Search for customers in the NorthEast sales region who have a credit ranking of A.

"#ForecastApp::CustInfo.Region=NE;CustInfo.CreditRank=A"

Search for the Positive Changes report within the Q3Sales report.

"Q3Sales.roi#RevenueDetails::PositiveChanges"

Search for any instance of customer ID in the current report.

"#ForecastApp::CustomerNameControl.CustomerID"

Search for customer ID 1562.

"#DetailReport::CustomerFrame::CustomerID=1562"


Dynamic report parameters syntax

You can create a new dynamic report (ROI) from the current ROI, using LinkExp to pass specific parameters to the report executable (ROX) or report object value file (ROV). You can also do searches on the dynamic report once it is created.

Creating dynamic reports is useful for short-run reports, those reports which are of interest right now, but which won't be needed long-term. For example, you can define a dynamic report to allow users to get the details for positive deals on a specific order number, based on the data in the current report. Users can examine the positive deals details, then throw the report away.

The dynamic report parameters syntax is

?<param>=<value>;<param>=<value>[;<...>]

?

Indicates the beginning of the dynamic report parameters section.

<param>

Name of a parameter in the report.

<value>

Parameter value.

[;<...>]

Specify additional parameter value pairs, separated by semi-colons.

Examples

Examine the details for all customers in the Forecasts report who are located in the Northwest region and who have a credit ranking of C.

"Forecast.rox?Region=NW;CreditRank=C"

Examine the same details for an ROX which generates an HTML report, to display on the Web.

"rotp://gumby/ToySales/Forecasts/Forecast.rox?Region=NW;CreditRank=C"


How to create a hyperlink with dynamic report parameters

The following procedure shows how to create a hyperlink which creates a dynamic report. The example is the detail report, available in your Actuate\Devwb\Examples\Detail directory. Suppose that you want to run a dynamic report showing all the customers who have a credit rank of "C".

1	Start two Developer Workbenches, one to display the source report's .ROD, and one to display the destination report's .ROD.

2	Open DETAIL.ROD for the destination report.

3	View→Parameters.

The Parameter Editor window appears.

1	Double-click on the parameter, customers_creditrank, you want to create the dynamic report with.

The Parameter Attributes window displays.

2	Note the parameter name.

3	Close the destination report .ROD.

4	Open the source report. You can create a simple report for this example using the New Report Wizard. Add a label to create the hyperlink in.

5	Double-click on the object you want to link from.

The properties window appears.

6	Scroll down to LinkExp and type the hyperlink expression.

"rotp://<server>/<folder>/<rpt>.rox?<parameter_name>=<val>"

Where:

•	<server> is the name of the destination report's Report Server

•	<folder> is the folder containing the destination report

•	<rpt> is the report name

•	<parameter_name> is the name of the parameter, taken from the Parameter Attributes window

•	<val> is the parameter value

Assuming that this example's report is on the Mustique report server, the hyperlink expression is:

"rotp://mustique/detail/detail.rox?customers_creditrank = C"


Defining hyperlinks programmatically

When you want to access particular hyperlinks depending upon certain conditions, you can generate hyperlinks programmatically. Override the GetLinkTo() and SetLinkTo() methods to get and set the value of the LinkTo run-time variable.

1	Call GetLinkTo() to get the current value of LinkTo.

2	Override SetLinkTo() to set the current value of LinkTo.

3	Override the SetLinkTo() method from the Method Editor in the Developer Workbench.

In the following example, if a customer has an excellent credit ranking, link to the new credit card application site. If they have a bad credit ranking, send them to the Financial Advisers page.

Sub SetLinkTo( row As AcDataRow )

if CreditRank = "A" then

LinkTo="http://www.fs.credit/newcredit.html"

else

LinkTo="http://www.fs.advice/finan-advice.html"

end if

If you override SetLinkTo and also set the LinkExp property, the SetLinkTo method takes precedence over the LinkExp property setting.

The following example shows how to override the OnLButtonDblClk method to allow users to click on points on a graph to jump to another area of a report.

Function OnLButtonDblClk( view As AcReportView, Shift As AcShiftKeyState, x As Integer, y As Integer ) As Boolean

    ' OnLButtonDblClk = Super::OnLButtonDblClk( view, Shift, x, y )

    OnLButtonDblClk = True

Dim MyPoint As Integer

'Get the bar number you clicked on  (0 thru n)

MyPoint = GetHitSet()

If MyPoint >= 0 Then

  ' Set HitPoint as a string and display HitPoint value

  HitPoint = Str$(GetHitXValue())

  ' msgbox "XValue" + Str$(GetHitXValue())

      LinkTo = "#Portrait::Frame1::TopDealsCustIDControl="  & HitPoint

  OnFollowLink()

Else

  msgbox "Missed..." + Chr$(10) + "Bummer, dude."

End If

End Function


Using target windows to display web reports

If a report appears in a web browser, you can specify the target window in which to display the report. The target window may be a new browser window, or a frame within the existing browser window.

Specify the target window using the source object's TargetWindowName property. In the Developers Workbench:

1	Select the source object.

2	Right-click. The pull-down menu displays.

3	Select Properties. The Component Editor displays.

4	Scroll down to TargetWindowName. Click in TargetWindowName to select it.

5	Type the name of the target window.


Defining universal hyperlinks

Suppose that users run reports on a variety of systems, and view reports on the Web as well as locally and in the Navigator. For example, reports can be in the file system or an encyclopedia folder, with users viewing them in the standard Viewer, and reports can be in a directory published by a Web server, with users viewing them in the LRX. Defining universal hyperlinks, or hyperlinks which work in a variety of environments, helps your users view reports in various environments. To define universal hyperlinks:

•	Ensure that the source and destination reports are in the same environment: they're both in the file system, on a report encyclopedia, or on a Web server.

•	Ensure that the reports' locations are fixed, and are simple: they should be in the same directory or folder, or in the same directory or folder structure.

•	Use a consistent directory or folder structure for each environment. For example, put all the forecast reports in a structure similar to Forecasts/Region/Q2.

•	Use relative links to locate the destination report. You should be able to specify locations such as ../Q3/report.roi to reach the destination report.

Universal hyperlinks work between and among .ROI's, .ROX's, .ROV's, HTML reports, and .HTML pages.


Converting code-based hyperlinks

Hyperlinks in previous versions of the Actuate product were code-based. These links still work in the standard Viewer, but are inactive in the LRX. You must convert code-based hyperlinks by hand, in the Developer Workbench, to make them work in the LRX.

Code-based hyperlinks work with reports on the client file system or on the Report Encyclopedia.

See also

Determining whether or not to convert a hyperlink
How to convert the hyperlink


Determining whether or not to convert a hyperlink

Internal hyperlinks (hyperlinks within the same report) continue to work as usual.

How to determine whether or not to convert an external hyperlink.

•	Is the destination report available on the client which runs the LRX? If so, the hyperlink will continue to work as-is.

•	Does the destination report have a web interface? If so, you can modify the report to determine its run-time context (is it running on the web?), and if it is, use the web-style hyperlink for linking to the web version of the report.

•	Do you want to use the report with an LRX? If so, you must convert the external hyperlink.

•	Do you plan to generate HTML from the report? If so, you must convert the external hyperlink.

Determining the report's run-time context

Use the function GetContext() to determine the report's run-time context. GetContext() returns an enumerated type which identifies the context. For more information about GetContext(), see Chapter 2, "Statements and functions," in Actuate Basic Language Reference.


How to convert the hyperlink

Once you start the Developer Workbench, the procedure is:

1	Convert the hyperlink:

1	Open the .ROD for the report.

2	Select the object whose link you'd like to convert.

3	Right-click on the object, then choose Properties. The Component Editor appears.

4	Select the Properties tab. The object's properties display.

5	Scroll down to the LinkExp property.

6	Select the LinkExp property, and type in the appropriate hyperlink string.

2	Remove the object's old OnFollowLink code:

1	In the Component Editor window, choose the Methods tab.

2	Click the pull-down arrow to display the list of methods. Scroll down to OnFollowLink.

3	Select OnFollowLink.

4	Click the Default button. The OnFollowLink code reverts to the default code.

If your OnFollowLink code is in a library, you must go to that library and remove the old code there.

The following illustration shows how two data controls are hyperlinked, using the controls' DataValue value. The link is from the customer control's DataValue variable in the Customer report to the customer control's DataValue variable in the Sales Order report.

The following shows the OnFollowLink( ) code to implement the source DataValue to destination DataValue hyperlink.

The corresponding LinkExp property is

"salesord.roi#SalesOrder::CustFrame::CustName=" & DataValue