Wiki copymirror

This is a mirror of the wiki at pascal.chat. Editing, history and sign-in are there.

Turbopower Visual PlanIt

English · Português · Русский

About

Visual PlanIt is a set of synchronized, data-aware components for adding time, task, & contact management capabilities to applications. Get that Outlook look & feel without the hassle.

This component was designed for cross-platform applications.

Screenshot

Author

Author: Turbo Power Software

LCL Port: Christian Ulrich and Werner Pamler

License

Mozilla Public Licence 1.1

Download

SVN

You can checkout the actual source from https://lazarus-ccr.svn.sourceforge.net/svnroot/lazarus-ccr/components/tvplanit

The commandline to do this is

text
svn co https://lazarus-ccr.svn.sourceforge.net/svnroot/lazarus-ccr/components/tvplanit

Change Log: New features

Incompatible changes

Bug reporting / Feature request

Bug reports and feature requests should be sent to the Lazarus/Free Pascal Bug Tracker; please specify the "Lazarus-CCR" project: http://bugs.freepascal.org/

Dependencies / System Requirements

Installation

Documentation

If you need a full documentation of the the VisualPlanIt suite get TurboPower's original 300-page pdf file from the official SourceForge site of the Delphi version (https://sourceforge.net/projects/tpvplanit/files/tpvplanit_docs/))

Using the planner components

Getting started

A simple project

Here is a quick tutorial for your first steps with the VisualPlanIt components.

pascal
procedure TForm1.FormCreate(Sender: TObject);
begin
  // Use this code if the filename is not set in the Object Inspector...
  VpXmlDatastore1.FileName := name_of_the_datastore_file;
  // ... and the datastore is not autoconnected.
  VpXmlDatastore1.Connected := true;
  // This code selects the first resource (index 0) found in the datastore.
  if VpXmlDatastore1.Resources.Count > 0 then
    VpXmlDatastore1.Resource := VpXmlDatastore1.Resources.Items[0];  
end;

Event categories

The event editor is used to enter or edit event details. You'll find here a combobox labeled "Category". The combobox items can be used to classify the events, like "Business", "Private", "Birthday" etc. The items, however, only have generic names, such as "Category 1", "Category 2", etc. You can select more descriptive names by visiting the datastore property CategoryColorMap. The field "Descriptions" defines the text to be displayed in the combobox. In order to be able to distinguish the categories in the DayView control you can also set up a background color and a bitmap here. The background colors can also be applied to the WeekView and MonthView controls when their ApplyCategoryInfos property is set to true (requires v1.7 at least).

Playing an event sound

The planner components are prepared to play a sound some pre-defined time ahead of the occurence of an event. For this to happen the "Reminder" checkbox must be checked in the event editor.

There are several ways to set up the sound:

In Windows, the sounds are played automatically without further code because there universal sound utilities are embedded. In Linux, however, this is less standardized. But the datastore provides an event OnPlaySound where the correct sound playing command can be executed. The fulldemo project in the examples folder contains a unit which checks for the sound engines typically available in various systems; this unit is an adapted version of the playsound component by Gordon Bamber in the Lazarus components and code repository (https://sourceforge.net/p/lazarus-ccr/svn/HEAD/tree/components/playsoundpackage/))

Datastores

Datastores are responsible for persistent storage of event, task and contact data records.

The ancestor of this hierachy of classes is TVpCustomDatastore. It implements the basic functionality and provides fundamental abstract methods, such as LoadEvents, LoadTasks, LoadContacts, PostResources, PostEvents, PostTasks, PostContacts. Its member class Resources (type: TVpResources) stores all resources in a list as TVpResource items. Such a resource item, in turn, contains other lists: Schedule, Tasks, and Contacts for all event, task and contact records. The currently selected resource can be accessed by the datastore's Resource or ResourceID properties (the latter is a unique numerical ID to identify the resource).

TVpCustomDBDatastore inherited directly from TVpCustomDatastore. It is another (pseudo-) abstract class which provides the basic database functionality by means of four datasets, ResourceTable, EventsTable, TasksTable, and ContactsTable. Since datebases have very efficient methods of searching and filterung these datastores provide a property DayBuffer which defines a date range before and after the currently selected date for which records are loaded.

TVpIniDatastore

This is a simple file datastore which inherits from TVpCustomDatastore. It stores the data records in a simple ini file structure. The name of the ini file must be specified in the corresponding property. The file is loaded after the datastore has been created if the design-time property AutoConnect is true, or when the run-tim property Connected is switched to true. The file is saved automatically when Connected goes back to false - this happens before the datastore is destroyed.

The demo project examples/datastores/ini is an example how the ini datastore can be used.

TVpXmlDatastore

This is another simple file datastore. It works similarly as the ini datastore except that the file is an xml file now. The datastore's tree of xml nodes can be inserted into any existing xml file, e.g. files written by TXMLDataStorage. For this purpose, the node must be specified to which the datastore's nodes will be attached (property ParentNode).

Have a look at the demo project in examples/datastores/xml to see the xml datastore at work.

TVpJsonDatastore

Yet another simple file datastore inherited from TVpCustomDatastore. It takes the json file format to store the data records. Usage is demonstrated in the sample code in folder examples/datastores/json.

TVpBufDSDatastore

The following datastores inherit from TVpCustomDBDatastore. The first one, TBufDSDataset, uses simple TBufDataset tables for storage of the planner data. The four required files are named "Resources.db", "Events.db", "Contacts.db" and "Tasks.db", and are stored in the folder specified by the property Directory. If AutoCreate is true then the tables are created automatically when the program runs for the first time; otherwise the datastore method CreateTables must be called.

Application of this datastore is demonstrated in examples/datastores/bufdataset and in examples/fulldemo.

TVpSqlite3Datastore

This datastore is a specialized storage for SQLite3 tables. It uses SQLDB components for this purpose. You must add a TSQLite3Connection and a TSQLTransaction to the form, link them together using the corresponding properties. Link the SQLite3Connection to the Connection of the datastore. The SQLQueries required are created automatically if the datastore's AutoCreate is true or after CreateTables is called. The database is stored in the file specified by the connection's DatabaseName.

The project in examples/datastores/sqlite3 demonstrates how the sqlite3 datastore can be used.

TVpFirebirdDatastore

A Firebird database provides the storage for TVpFirebirdDatastore. Again, SQLDB components are used here. To establish a connection to the database, a TIBConnection must be linked to the datastore's Connection. It must provide all information to connect to the database, such as database name, user name and password. In addition, a TSQlTransaction component is needed. The database should be created by the tools provided by Firebird or by third-party database administration tools. It is possible to do this also from the datastore, but this results in an exception. If the datastore's AutoCreate the required tables are created automatically when not found; alternatively call CreateTables for the datastore.

Usage of the datastore is demonstrated in examples/datastores/fb.

TVpZeosDatastore

The TVpZeosDatastore works with the ZEOS database components. Since they are not contained in the standard Lazarus distribution it is contained in a separate package, laz_visualplanit_zeos, which is found in the source/addons/zeos folder and must be installed after laz_visualplanit.

A TZConnection is required to establishe a connection to the database. The tables are created internally, either automatically if AutoCreate is true, or manually by calling CreateTable.

The demo project can be found in examples/datastores/zeos.

TVpFlexDatastore

In the database-aware datastores mentioned so far, the tables are created by the datastore. This makes sure that the fields are named in the way as expected by the datastore. If, on the other hand, tables are already existing or are provided by other applications it is very likely that field names will not be reckognized. TVpFlexDatastore can be a solution. Its name indicates that this is a very flexible datastore. In particular, it is possible to link to any database system if a TDataset-descendent is available.

You must add four TDataset components to the form, each one corresponding to one of the Resources, Events, Contacts, and Tasks data, and link them to the DataSsource of the datastore via TDataSource components. Then double-click on the TvVpFlexDatastore to open the FieldMapper. This is a tool to assign the database fields to the internal fields of the datastore. For each database field that you want to include in the planner select the corresponding datastore field in the right list box. After clicking "Add" the pair of fields is shown in the "Field Mappings" listbox.

There are two sample projects:

Other datastores

The following datastores from the original Delphi distribution are included in the source folder, but not added to the package because they require the BDE which is not available for Lazarus:

Holidays

TvPlanIt has no ready-to-use support of holidays because this would involve a huge database covering all countries. Rather than that each datastore provides an event OnHoliday which queries the holiday name for the specified date; the holiday name is left empty when the day is not a holiday. The best way to apply this model is to calculate a list of the dates and names of all holidays for each year:

pascal
uses
  fgl, DateUtils;

type
  THolidayList = specialize TFPGMap<TDateTime, string>;  

var 
  FHolidays: THolidayList = nil;  // must be created and destroyed by the main form.

{ Calculates the date of some U.S. holidays for the specified year. }
procedure TMainForm.CalcHolidays(AYear: Integer);

  // If a federal holiday falls on a Saturday the preceding Friday is a holiday.
  // If a federal holiday falls on a Sunday the following Monday is a holiday.
  // https://www.officeholidays.com/countries/usa/2022
  function InLieuHoliday(ADate: TDateTime): TDateTime;
  var
    wd: Integer;
  begin
    wd := DayOfTheWeek(ADate);
    if wd = DaySaturday then 
      Result := ADate - 1
    else
    if wd = DaySunday then
      Result := ADate + 1
    else
      Result := ADate;
  end;
    
var
  d, d1: TDate;
begin
  FHolidays.Clear;
  
  // New Year
  d := EncodeDate(AYear, 1, 1);
  FHolidays.Add(d, 'New Year');
  d1 := InLieuHoliday(d);
  if d <> d1 then
    FHolidays.Add(d1, 'New Year (in lieu)');
  
  // Easter & associated
  d := Easter(AYear);  // Calculation of the Easter date is shown in the "Easter" example in the Lazarus installation.
  FHolidays.Add(d-2, 'Good Friday');
  FHolidays.Add(d, 'Easter Sunday');
  FHolidays.Add(d+49, 'Whitsunday');
  
  // Independence day
  d := EncodeDate(AYear, 7, 4);
  FHolidays.Add(d, 'Independence Day');
  d1 := InLieuHoliday(d);
  if d1 <> d then
    FHolidays.Add(d1, 'Independence Day (in lieu)');
  
  // Labor Day (1st Monday in September)
  d := EncodeDayOfWeekInMonth(AYear, 9, 1, DayMonday);  
  FHolidays.Add(d, 'Labor Day (U.S.)');
  
  // Thanksgiving (4th Thursday in November)
  d := EncodeDayOfWeekInMonth(AYear, 11, 4, DayThursday); 
  FHolidays.Add(d, 'Thanksgiving (U.S.)');
  
  // Christmas
  d := EncodeDate(AYear, 12, 25);
  FHolidays.Add(d, 'Christmas Day');
  d1 := InLieuHoliday(d);
  if d1 <> d then
    FHolidays.Add(d1, 'Christmas Day (in lieu)');
  
  // Due to the off-days in the calendar, add also Christmas of the preceding
  // and New Year of the following years
  d := EncodeDate(AYear-1, 12, 25);   // -1 --> previous year
  FHolidays.Add(d, 'Christmas Day');
  d1 := InLieuHoliday(d);
  if d1 <> d then
    FHolidays.Add(d1, 'Christmas Day (in lieu)');
  
  // New Year
  d := EncodeDate(AYear+1, 1, 1);   // +1 --> following year
  FHolidays.Add(d, 'New Year');
  d1 := InLieuHoliday(d);
  if d <> d1 then
    FHolidays.Add(d1, 'New Year (in lieu)');
end;

Since this code calculates the holidays for one year only the list must be updated when the year changes. This can be done in the OnDateChanged handler of the datastore; perform the calculation only when the year of the new date is different from the previously calculated year (FHolidayYear: Integer):

pascal
procedure TMainForm.DateChangedHandler(Sender: TObject; ADate: TDateTime);
var
  year: Integer;
begin
  year := YearOf(ADate);
  if year <> FHolidayYear then
  begin
    CalcHolidays(year);
    FHolidayYear := year;
  end;
end;

In the OnHoliday event, finally, the holiday name then can be found easily:

pascal
procedure TMainForm.VpHoliday(Sender: TObject; ADate: TDateTime;
  var AHolidayName: String);
var
  idx: Integer;
begin
  if FHolidays.Find(ADate, idx) then
    AHolidayName := FHolidays.Data[idx];;
end;

Overlaying events

The DayView and WeekView components primarily display events of a single resource. It is possible, however, to switch into an overlay mode where other resources can be displayed within the same controls. This can be achieved by assigning a TVResourceGroup to the resource (property Group):

pascal
type
  TVpOverlayPattern = (opSolid, opClear, opHorizontal, opVertical, opFDiagonal, opBDiagonal, opCross, opDiagCross);

  TVpOverlayDetail = (odResource, odEventDescription, odEventCategory);
  TVpOverlayDetails = set of TVpOverlayDetail;  

  TVpResourceGroup = class
    //...
    property Caption: String;
    property Count: Integer;
    property Items[AIndex: Integer]: TVpResource;
    property Pattern: TVpOverlayPattern;
    property ResourceID: Integer;
    property ReadOnly: boolean read FReadOnly;
    property ShowDetails: TVpOverlayDetails;
  end;

The most convenient way to create a resource group is the OverlayResources method of a resource. It takes an array of the resource instances to be grouped as a parameter. When the array is empty an existing group is ungrouped. An optional ACaption parameter defines the name of the resource group. The following code snippet (taken from the FullDemo sample project) assumes that all resources which can be grouped with the currently active resource are listed in a TCheckListBox (named lbOtherResources) and is executed when one of the listbox entries is checked or unchecked:

pascal
procedure TMainForm.lbOtherResourcesClickCheck(Sender: TObject);
var
  i, n: Integer;
  resArray: TVpResourceArray;
begin
  // Collect resources checked for overlaying in an array
  SetLength(resArray, lbOtherResources.Items.Count);
  n := 0;
  for i := 0 to lbOtherResources.Items.Count-1 do
    if lbOtherResources.Checked[i] then
    begin
      resArray[n] := TVpResource(lbOtherResources.Items.Objects[i]);
      inc(n);
    end;
  SetLength(resArray, n);

  // Overlay the checked resources to the currently active resource
  VpControlLink1.DataStore.Resource.OverlayResources(resArray);
end;

Another way is to call the method AddResourceGroup of the datastore's Resources list. Here the IDs of the overlayed resources must be specified as an array in the first parameter of the procedure. The first id plays a special role: it refers to the resource to which the group is attached. If this resource is displayed in a Dayview or WeekView component then the events of the other specified ID(s) are overlayed within the same control.

pascal
procedure TMainForm.CreateResourceGroup;
const
  NAME_OF_GROUP = '';  // empty --> use resource descriptions instead
  PARENT_ID = 1;       // ID of the resource which will display the entire group
  OVERLAYED_ID = 2;    // ID of the resource to be overlayed in the view of the PARENT_ID resource.
var
  datastore: TVpCustomDatastore;
  grp: TVpResourceGroup;
begin
  datastore := VpControlLink1.Datastore;
  grp := datastore.Resources.AddResourceGroup([PARENT_ID, OVERLAYED_ID], NAME_OF_GROUP);
  grp.ReadOnly := true;
  grp.Pattern := opDiagCross;
  if datastore.Resource <> nil then
    datastore.Resource.Group := grp 
  else
    datastore.Resource.Group := nil;
  datastore.RefreshEvents; 
end;

The level of detail visible to the user seeing overlayed events is controlled by the property ShowDetails of the resource group. By default, only the name of the overlayed resource is visible. If more details are to be exposed you can add the other elements of TVpOverlayDetails. The background of overlayed events in the dayview is hatched as specified by the Pattern property of the resource group if the event category is not excluded from the visible details; otherwise the event is drawn as specified by the property HiddenCategories of the Datastore.

Editing, deleting, drag-and-drop of overlayed events is off by default, but can be allowed by setting the ReadOnly property of their resource group to false.

In order to turn resource groups on and off the context menu of DayView and WeekView components is extended by a corresponding submenu. The submenu contains an item "none" meaning "no overlayed events" as well as an item for each resource group in which the currently selected resource is the parent. The caption of the menu items is defined by the (optional) second parameter of the OverlayResources and AddResourceGroup calls; if empty the group name is derived from the descriptions of the overlayed resources.

Printing and print preview

The VisualPlanIt package contains routines for printing the planner controls. Even a print preview is implemented. The printing process is defined by means of templates which are called "print formats" here. They can be created at designtime or by means of an xml file loaded at runtime, contain a list of the components to be printed.

The heart of printing support is the TVpPrinter class - it is accessible by the Printer property of the TVpControlLink. It contains extra information needed for the printing process and contains a list of the available PrintFormats.

In order to quickly access the print format designer double-click on the TVpControlLink component. Click on the upper "New" button to open the basic format editor. Here you can assign a reasonable name to the print format. Clicking on the lower "Edit" opens the "Element" editor - this is the tool to define the contents on the print print format, similar to a report designer, however less comfortable. In addition to the planner controls, DayView, WeekView, MonthView, TaskList and ContactGrid, the editor allows also to add additional text and shape objects to the format. Multiple elements can be contained within the same format. Position the elements by using the Top/Left/Width/Height specifications at the right. In addition, the entire format page can be rotated in 90° steps.

A print preview can be generated by means of a TVpPrintPreview component. Assign the ControlLink of the form to the ControlLink and the Printer of the Printers units to the Printer property of the preview component. For printing to work correctly, you must have the package Printer4Lazarus in the requirements of the project; this can be done most easily by adding a standard TPrintDialog to the form which you will probably need anyway. Open the print preview by calling its Execute method. It should be mentioned that the print preview itself does not issue any printing commands. Therefore, extra printing code must be written if the user decides to initiate a print-out from the preview, something like this:

pascal
if VpPrintPreviewDialog1.Execute then
  if PrintDialog1.Execute then begin
    Printer.BeginDoc;
    try
      VpPrintPreviewDialog1.ControlLink.Printer.Print(
        Printer, 
        VpPrintPreviewDialog1.StartDate, 
        VpPrintPreviewDialog1.EndDate
      );
    finally
      Printer.EndDoc;
     end;
  end;

See the fulldemo project in the examples folder for a worked-out example.

Localization

All internal strings used by the VisualPlanIt package can be localized by means of the translation utilities available within Lazarus. The translated po files can be found in the folder languages of the distribution. Copy them to the language folder of your application, and translate them by calling

pascal
uses
  translations;
var
  langdir: String;  // language folder of your application 
  lang: String;     // language abbreviation, e.g. "de" for German
//...
  TranslateUnitResourceStrings('vpsr', langdir + 'vpsr.' + lang + 'po');

Note: Users are cordially invited to submit translations of the resource strings to their native language

In addition to providing translated strings a variety of date/time related properties of the planner controls should be adapted:

Finally, the TVpControlLink implements procedures for localizing addresses in the Contact Editor Dialog. For this purpose, the file vplocalize.xml accompanies the VisualPlanIt package. It contains rules for construction of addresses in the correct format varying from country to country. If this filename is assigned to the property LocalizationFile of the ControlLink then these rule are effective when the Contact Editor Dialog is created. Please note that this file is not translated by the po files.

TVpDayView, TVpWeekView, and TVpMonthView reference

These are the main visual components of the TvPlanIt library, they display the same information, but group it on different time-scales.

Common

Properties
Events

Please be aware that the word "event" has two meanings in the following section: a Lazarus event or a TvPlanit event...

TVpDayView only

Properties
Events

TVpWeekView only

Properties

TVpMonthView only

Properties
Events

Using the navigation bar

TVpNavBar, seen in the left of this screenshot of the navbar demo, is a navigation tool smiliar to the one known from Outlook. It consists of several folders represented by button-like controls and several items - these are the labelled icons in the large space between two buttons. The folder button - which can be drawn in several button- or tab-like styles - is always shown above the associated item area. Clicking on a folder divides the stack of folders in an upper and lower part, and displays the items assigned to this folder between the corresponding folder at the top and the next folder at the bottom. The index of the currently expanded folder is given by the property ActiveFolder, and the index of the clicked items within this folder is given by property ActiveItem. An event OnItemClick fires when an item is clicked, it has the index of the clicked item as a parameter.

An icon can be assigned to each item. These images are collected in the image list assigned to the property Images of the navigation bar. The images normally should be 32x32 pixels. The property IconSize of each folder determines whether these images are displayed directly as "large images" or reduced by a factor 2 (16x16 pixels) as "small images". The down-scaling is done by the component.

Populating a TVpNavBar at designtime is easy if the version of VisualPlanIt is at least 1.0.10 in which the component editor of the NavBar is functional. Just double-click on the component, or select "Layout Tool..." from the context menu of the component. This opens the component editor displayed here as a screenshot. It contains three listboxes:

If the VisualPlanIt version is older than version 1.0.10, then getting folders and items into the NavBar is a bit more complicated because the component editor had not yet been ported/adapted to Lazarus. In this case, it is best to learn this task by means of the object inspector:

Gadgets

In addition to the planner components the Visual PlanIt package contains also two stand-alone gadgets:

TVpLEDLabel

This is a 14-segment LED display which can display any ASCII string. The string is defined by means of the property Caption, but the count of LED characters must be defined explicitly using the property Columns

TVpClock

This is a digital or analog clock - depending on the property DisplayMode. A variety of settings can be adjusted for each mode separately by using the properties in AnalogOptions and DigitalOptions. The properties are pretty much self-explanatory, except maybe the DigitalOptions.MilitaryTime which activates the 24-hour display if true, or the 12-hour am/pm mode if false. The AnalogOptions.ClockFace replaces the default-drawn clock face by a bitmap - see screenshot.

With the property ClockMode the operation of the clock can be changed from standard clock to count-up or count-down timer, cmClock, cmTimer, cmCountdownTimer, respectively.

The property Active starts the clock when set to true, or stops it when set to false. There is also a property Hold which temporarily pauses the clock when set to true and resumes it when set to false. Convenience methods Start, Stop, Pause and Resume have the same purpose.

Property TimeResolution determines how often the display is updated (in milliseconds).


From the Free Pascal wiki, page "Turbopower Visual PlanIt", as it stood on 2023-07-22 after a change by Wp, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's Licensing page. If you have concerns about this material, message the admin.

Revision 2, last changed by WikiArchive .