Sunday, October 22, 2006

Lit Window Library status

The Lit Window Library project is definitely alive and development continues.

Abstract

Summary: Building a database front end with Lit Window Library…

Audience: Software Developers

Level: Beginner

Introduction

Dear Diary!

I am sitting in my favorite café in Erlangen, sipping a Latte Macchiato and am very happy. Yesterday I visited my customer and showed off the latest version...

Yes, yesterday was a great day for me. After more than a year of development my customer began entering live business data yesterday. I sat a desk away, ready to intervene and fix any last minute crashes and show stoppers, but there were none.

And only today it slowly dawns on me that this is a major, major milestone for the Lit Window Library as well. No, the project is not dead. Far from it. I was simply too busy in the last months to bother with any kind of publicity. Now, as my workload hopefully lessens a little, I intend to publish more again.

What works *today*…

I have written a lot about grand ideas, design schemes and how great all of this is going to be - once the library has actually been implemented. But things have progressed a lot since my first note over at slashdot and in today's blog entry I will write only about what is working here and now.

The program I installed yesterday is a tool to help plan and organize business information, from financial data to individual project management issues. Different bits and pieces of information, each needing a table in a database and a form on a graphical user interface. And many interdependencies.

The application I wrote is built upon a (closed source) framework called ‘Mikado’, which I’ve been developing as the successor for my bug tracking tool BugLister. Mikado is a database front end framework which uses ODBC to connect to a database server. Its a little bit like MS-Access, written in C++ for wxWidgets and the Lit Window Library. A core component of the Mikado framework is the record_form control, a single C++ class that houses an arbitrary form (a wxPanel loaded from XRC), handles the user interface and load/save operations and also calls DLL plugins to allow implementation of specific business logic that goes beyond the capabilities of the framework. (Now imagine Python as a script language and a decent WYSIWYG form editor... But I promised I'd write only about what is working today. ;)

To explain how this works, I am going to walk through the steps to add a new kind of record ‘Project’. My client develops leading-edge communication equipment. A ‘Project’ record shall describe the financial planning aspect of such a development project. It has a unique project name, start date, customer(s), planned cash flow and required development resources. Other records will reference it. Invoices, for example, will be assigned a project, so the project ‘contains’ a list of invoices and we’d like to show all invoices written for a specific project in a list on the project’s form.

CREATE TABLE project

The first step is to define a database table. Here is the shortened version of the SQL statement. We are using postgres, by the way, but I've tested Mikado with MS-SQL, MS-Access and Oracle as well.

CREATE TABLE projects

(

id serial NOT NULL, -- unique id

name varchar(32) NOT NULL, -- project name

start_date date, -- start of the project

customer int4, -- the customer paying for the project

status int2, -- status

notes text, -- some notes

planscript text, -- financial planning information

sys__parent int4, -- a parent project

CONSTRAINT pk_projects_id PRIMARY KEY (id),

-- foreign keys omitted for brevity

);

Create the user input form for projects

Get DialogBlocks, if you haven’t already. It’s the best GUI designer for wxWidgets that I know and is written by Julian Smart. If you are using Visual Studio, get wxVisualSetup as well, as it not only integrates wxWidgets into the Visual Studio IDE. It also helps developing the Lit Window Library, because I am the one who gets the money. Okay, enough advertising ;)

A form is actually a wxPanel with sizers and widgets inside. To create the form, I add a wxPanel to the list of documents and add a wxFlexGridSizer.

id

I first add a wxStaticTextCtrl and set the label to “Id:”. This is the label the user sees so he knows that the number next to it is the id of the project. The id itself is read only. The user should not be able to change it, so I add another wxStaticTextCtrl next to the label. wxWidgets has one HUGE advantage over other GUIs I know, such as the MFC. In wxWidgets, every widget/control can have a name. The Lit Window Library data binding mechanism builds upon this. I give the new wxStaticTextCtrl a name: “id”. Note that the column in the database table has the same name.

start_date, name, notes

The next field I add to my form is the project “start_date”. I add another wxStaticTextCtrl as the label and set its value to “Start date:”. The column “start_date” is of type “date”. The Mikado framework converts this to a wxDateTime object. I add a wxDatePickerCtrl to the form, next to the label, and give it the name “start_date”. Again the control has the same name as the column in the database. I repeat these steps for “name” with a wxTextCtrl singleline and “notes” with a wxTextCtrl multiline.

customer, status

The “customer” column is an enumeration. It contains only a number, not the actual name of the customer. This number references the corresponding entry in a “customers” table in a database. We need a control that lets a user select one entry out of a list, 1 of n. That’s what a wxComboBox is for. I add one and name it “customer”. The Mikado framework does the rest. At program start, it loads the definition of the “projects” table and also resolves foreign key references. When it encounters a wxComboBox in a form, it fills the combo box with names from the “customers” table and binds the selection to the “customer” column of the “project” record. The result: the user sees all customers in the list and the current selection is read from and written to the “customer” column.

I add another wxComboBox for the foreign key column "status", a list with values such as “open”, “completed”, “possible”, “obsolete”.

automatic text completion

Actually I am not using a wxComboBox here, but a custom control "lwAutoCompleteTextCtrl", part of the Mikado framework. This control works like the autocomplete text controls you see when you are using a web browser and start typing an URL in the address bar. The list of customers is much too long to be using a combo box. lwAutoCompleteTextCtrl lets you type a few characters and the list is narrowed down immediately. But the underlying mechanisms – foreign key table and current selection – are exactly the same as for wxComboBox.

sys__parent

“sys__parent” is an interesting one. In Mikado, columns starting with “sys__” always have a special meaning. The “sys__parent” column defines a hierarchy. For table “projects”, “sys__parent” specifies the parent project of the current record, pointing to another row in the “projects” table. I wrote another custom control lwAutoCompleteTreeCtrl, which works like lwAutoCompleteTextCtrl, except that it shows a tree instead of a list. Adding the lwAutoCompleteTreeCtrl to the form lets the user enter the parent project by typing its first few letters. Or, if the user presses F4 to drop down the tree control, he can see the entire project hierarchy and select the parent project.

planscript

This text column contains the actual planning, written in a small scripting language which we designed specifically for that purpose. The parser that handles this is contained in a plugin and reacts to events sent by the framework. I am not going into more details here and simply add a multiline wxTextCtrl for it.

saving the form

The form (wxPanel) itself, like all the controls inside, also has a name property. I name my form “f_projects” and save everything to an .XRC file. The .XRC files are XML files containing the GUI layout. Here is a short code snipped of how you would use them in your C++ code.

// load XRC file into memory

wxXmlResource::Get()->Load(filename);

// create a wxPanel from the “f_projects” definition

wxPanel *panel=wxXmlResource::Get()->LoadPanel(“f_projects”);

This would return a wxPanel object containing all sizers and widgets and all that I designed with DialogBlocks above.

Registering table and form

Now that I have a database table and an XRC form, I need to register them with the framework. To register the table, I add the value “projects” to a special table “l_types”. “l_types” contains the list of the database tables that Mikado knows about. At startup, when Mikado reads it and finds the value “projects”, it will query the database server for the definition of “projects” and build a column and key catalog for it. This is the moment when the framework discovers that column “customer” references another table and that wxComboBoxes later need to be filled with values from “customers”.

I also need to tell Mikado that the form “f_projects” is the right form for “project” records and I also want a human readable name for the form. To do that I add a record “f_projects”, “projects”, “Project folder” to another special table “v_views”. This table contains a list of all forms the user can choose from. The GUI shows this list on the left side of the frame, like the folders list of your email program.

Voíla…

To test it I start Mikado. It shows an entry “Project folder” in the view list, which I click on. Mikado then shows me a list of records from the “projects” table in the database. I select one. Mikado loads the form “f_projects” (a wxPanel containing controls, remember?) from the XRC file, shows it on the screen, loads the record I selected from the database and fills the form with values from the record.

That last part is handled entirely by Lit Window’s RapidUI object, which I wrote so much about already. The framework calls AddWindow() to add the form to the RapidUI object. It then calls AddData() to add the record. After that the RapidUI object creates default rules: “for each data element” - search for a window element of the same name and if one exists, create a simple rule “window.element = data.element”. Together, these default rules bind all columns to their controls.

The code inside Mikado’s “record_form” is pretty simple, thanks to the Lit Window Library:

// load the form, init the RapidUI object

void record_form::prepare_for(wxString type_name, wxString form_name)

{

// m_working_record is of type mikado_record

// create a new, empty record of type ‘type_name’

// also changes the type of m_working_record

m_working_record.create(type_name);

// add the data to the RapidUI mediator

m_rapidUI.AddData(m_working_record);

// load the form from the XRC file

wxPanel *m_form=wxXmlResource::Get()->LoadPanel(form_name);

// add the form to the RapidUI object

m_rapidUI.AddWindow(*m_form);

// start the RapidUI object

m_rapidUI.Start();

}


// load a record from the table

void record_form::load(long bookmark)

{

// create a rowset (similar to wxDbTable)

mikado_rowset rows(m_connection);

// bind the current working record to the rowset

rows.bind(m_working_record);


// set the SQL where clause to load a particular record

// ([in]bookmark) is OLEDB syntax to specify a named parameter

// ‘bookmark’. Mikado’s ODBC layer uses the same syntax.


rows.set_where(“id=?([in]bookmark)”);


// bind the parameter to the actual variable


rows.bind(bookmark, “bookmark”);


// open the table


rows.open();


// fetch the first entry

// this stores the values in the m_working_record


rows.fetch();


// now tell the library that m_working_record has changed

// this triggers the RapidUI rules system and will update

// everything in the form


litwindow::NotifyChanged(m_working_record);

}


void record_form::test_project_form()

{

// first prepare everything for objects of type

// “projects” and load the form “f_projects”


prepare_for(“projects”, “f_projects”);


// now load record with id==5


load(5);

}

That’s all. And the point is, I’ve written this code once and will never have to write it again, because it works with all kinds of records. This is the ultimate goal of the Lit Window Library, a library of building blocks at a significantly higher level of abstraction that other libraries.

High level library of building blocks

The “record_form” is actually a mediator class (search google for mediator design pattern). It connects a record and a form and handles everything in between. A “record_form” can load, create and save arbitrary database records using arbitrary forms. Written once, used many times. If you need to show a single database record, use the “record_form” mediator from the library. Want to display a list of records and handle select/edit/insert/delete? Use a “select/edit/insert/delete” mediator object in combination with the “record_form” mediator. Instantiate them, connect a list control, a form and some action triggers (buttons and menu entries) for the insert/delete/save actions. Everything I have implemented so far is only a means to write generic mediator classes such as “record_form”.

Conclusion

When my customers started entering project information for the first time yesterday, we hit a minor snag immediately. Some projects did not have a start date yet, but the form required an entry. To fix this, I loaded the form in DialogBlocks and set the SP_ALLOWNONE flag for the “start_date” wxDatePickerCtrl. I saved the XRC file and ran Inno Setup to create a new setup.exe. They installed it and the problem was gone. This took less than five minutes and I didn't even have to recompile the application.

They also wanted to categorize projects by region (Asia, Europe, US). While they were waiting I

  • added a new column “region” to the projects table definition

  • added a simple table “regions”

  • added a simple form to be able to add regions and

  • added another lwAutoCompleteTextCtrl to the projects form

and created a new setup.exe. All of it without recompiling the application. I stress this because the chance of breaking something is a lot higher when you change actual C++ code. Mikado gives me a lot more confidence to make changes “on the fly”, while the customer is waiting.

Outlook

I’ve saved the juiciest part for my next blog entry: showing the list of invoices attached to a project in the projects form. In that entry I will add a lwMikadoDBGridCtrl and connect its properties and actions to the main form. These "connections" use the Lit Window Library rules language and are contained in a simple text field in the XRC file. Like above, changes to that connection can be made without recompiling the application.

Here is the code from the “LITWINDOW_RULES” field in the XRC file. This is actual, working code, except that I have changed the identifier names because of Copyright issues.

// set the invoice_grid database connection

// to the form’s database connection


invoice_grid.Connection:=form.connection;


// tell the invoice grid to reload itself

// when the current_bookmark of the form changes

// the invoice will no longer show all invoices,

// it will only show invoices assigned to an object

// ‘current_bookmark’


invoice_grid.ParentBookmark:=form.current_bookmark;


// tell invoice to save changes when the

// form.IsSaving trigger fires


invoice_grid.SaveAction:=form.IsSaving;

// tell form to mark data as dirty when the

// invoice_grid.IsDirty trigger fires


form.DirtyAction:=invoice_grid.IsDirty;


// this is not c++ code, these are lit window

// library rules (or constraints), compiled and

// evaluated at runtime

That’s all for now. I hope you found it interesting reading. If you did, please help spread the word and send the link to my blog to your colleagues. And as always I welcome your comments. Do you want more details? Less? Are these blog entries too long to read? Comments on the library?


Best regards

Hajo Kirchhoff

New layout for my blog...

Just a quick note. I've changed the layout of my blogs as my posts tend to be rather long and contain a lot of text. Things should be easier to read now, especially the code parts.

Thursday, October 12, 2006

Lit Window Library Design Thoughts #1

Abstract

Summary: Explains how to call C++ methods from the Lit Window Library rules system.

Audience: C++ GUI Developer

Difficulty: Advanced

Introduction

Hello, this is the first in a series of articles detailing the ideas behind the Lit Window Library. I want to share my thoughts about the design process and also answer questions I am getting via eMail. If you have not heard of the Lit Window Library yet, you might want to head over to www.litwindow.com/library and browse the official site. This blog keeps you informed about what is going on behind the scenes.

In short: The Lit Window Library extends the decade old 'Method, Property, Event' paradigm in GUI programming with a fourth concept: 'Rules', sometimes also called 'Constraints'. A 'Constraint' is a condition, such as 'frame.title = listbox.selection', which you set at design/compile time. The Lit Window Library constraints solver will ensure that the condition will always be true and update the values - frame.title in this example - whenever a dependent value changes. This greatly reduces the amount of bookkeeping code and together with 'data adapters' (please see www.litwindow.com/library) allows creating a library of frequently used user interface mechanisms. I hope to be able to reduce GUI coding times by up to 90% while at the same time allow tight integration between the new 'rules' mechanism and traditional C++ coding methods.

Which leads me to my first topic, which I received as a question via email:

Q. How do I tell the Lit Window Library to call my C++ code whenever a value changes? I want to play a sound everytime the selection in a list box changes.

Let's assume you have a list box and a frame window and you want to display the value of the current list box selection in the title of the frame. The rule would be 'my_frame.title = my_listbox.selection'. Once set, the Lit Window Library will update the frame's title automatically whenever the list box selection changes. Now let's assume you want to play a sound everytime the selection changes. Or call any other of your own C++ code.

Note: what follows is my design idea, which has not been implemented yet.

There are three different ways at different abstraction level to achive this. Actions, named rules and change notifications.

Actions

Actually I stole the term from Delphi. If you've ever used Delphi, you will be familiar with the concept. An action object is something that is fired by a trigger. A trigger is usually either a button or a menu event. The action calls a method. The advantage of this over other C++ libraries is simple: if you disable the action, all connected triggers will be disabled automatically. If you have, say, a 'Save As...' action and disable it, all toolbar buttons, menu entries, popup menus etc... that are connected to this action will appear disabled without any further work on your part. I like this approach a lot better than the usual OnSaveAsUpdate event where you have to query the SaveAs state for every menu entry, every button and every toolbar separately. A lot of work just to reflect the state of the application in a handful controls.

In the Lit Window Library you create an action object and connect a C++ method to it using a 'function adapter'. A 'function adapter' is a simple piece of code that reflects the C++ method into the rules world, making the method known by name. Think of it as a language binding, much like the language binding between Python and C++. Syntax in the rule system could look like this:

Action SaveAsAction;

SaveAsAction.method = MyCppMethod;

SaveAsAction = SaveAsButton + SaveAsMenu + SaveAsToolbar;

SaveAsAction.Enabled = somedata.state == IsDirty;

You declare the object SaveAsAction, assign the C++ method "MyCppMethod" to it, connect a button, a menu entry and a toolbar button and tell the Lit Window Library system to enable the action - and thus the button, menu and toolbar, whenever somedata.state equals IsDirty.

How does this help to play a sound whenever the list box selection changes? Simple. A changing selection is a trigger, just like a button that is pressed. Here is the code:

Action PlaySoundOnSelectChanges;

PlaySoundOnSelectChanges.method = MyCppPlaySoundMethod;

PlaySoundOnSelectChanges = my_listbox.OnSelectionChanging;

This looks very similar to the 'OnEvent' mechanism used by GUI class libraries. Not suprising, because it is the same concept, spiced up using the action idea. PlaySoundOnSelectChanges is an action object that calls 'MyCppPlaySoundMethod' whenever it is enabled and triggered. my_listbox.OnSelectionChanging is a trigger that fires everytime the selection changes.

Thats the first method to play a sound whenever the selection is changing.

Named rules

Somewhat lower in the hierarchy are named rules and rule evaluation events. The Lit Window Library uses a constraints solver to update values when dependent values change. The constraints solver uses rules as its input. You've seen examples of rules above. All of these rules where unnamed rules. The Lit Window Library rules syntax allows named rules, giving a rule or a set of rules a name by which they can be accessed. Here is the syntax:

selection_changing: frame.title = listbox.selection;

The general syntax is {name :} rule, where the name and colon are optional. If they are omitted you are specifying an unnamed rule. You can also group several rules together and give the whole group a name. { name : } '{' rule '}'.

The constraints solver publishes boost::signals which your code can connect to. Different signals are fired for different actions and events. One signal is the 'value(s) changing' signal. It is fired everytime the value or values of a rule or group of rules are changing. To play a sound whenever the list box selection changes, you need to write your OnCppPlaySoundMethod and connect it to the constraints solver 'value(s) changing' signal.

RapidUI m_rapidUI;

// init rapidui object with

// data and window objects

// now connect the event

m_rapidUI.get_rule("selection_changing").value_changing.connect(boost::bind(&OnCppPlaySoundMethod));

get_rule(string) returns the named rule object. It has a member 'value_changing', which is a boost::signal (see www.boost.org for more info). connect takes a function object which gets called everytime the signal is fired. The rest is taken care of by the constraints solver. Whenever the value of my_listbox.selection changes, the constraints solver will reevaluate the rule and will change the value of the frame.title. Before it does that, it fires the 'values_changing' signal of this rule, giving your C++ code a chance to act. Not shown here is the actual interface of the OnCppPlaySoundMethod, but the interface also allows your C++ code to change the value before it is assigned or otherwise modify the rules behaviour. This allows very complex interactions between the Lit Window Library rules system and your own C++ code and this is also one of the very important and strong points of the system: I wanted to create system that allows a programmer to switch between both world very, very easily - the rules world and the classic C++ sequential world.

Change notifications

Change notifications are at the lowest level of the abstraction. For the rules system to work, it needs to be told whenever a value changes. Values can be C++ members, in which case you - the programmer - must call litwindow::NotifyChanged(value) every time you assign a new value to the member variable. Lit Window Library predefined objects, such as Listbox and other GUI objects already do this behind the scenes everytime you call SetSelection or a user changes the selection via mouse or keyboard for example. litwindow::NotifyChanged sends the information that object 'x' at location 'y' has changed to all active constraints solvers. The constraints solvers in turn search their lists for rules that are affected by this change and reevaluate them, possibly updating other values, sending more change notifications and updating yet other values until all rules have been reevaluated and a new set of values has been assigned - or until a conflict has been found.

These notifications are low level change notifications. They are not sent for rules, they are sent for individual objects. Like with rules you can connect your own C++ code to a change notification.

RapidUI m_rapidUI;

m_rapidUI.get_data("frame.title").value_changing.connect(boost::bind(&OnCppPlaySoundMethod));

Note that I connected the OnCppPlaySoundMethod to the frames title object here. I could have connected it to my_listbox.selection. But since a change in the selection (actually a change in the value of the selection - the string value of the currently selected list box entry) will automatically change the title as well, OnCppPlaySoundMethod will still be called when the selection changes. The change is propagated to the frame.title object by the constraints system.

Thats all from me for tonight. I hope you found my first public rambling on the Lit Window Library ideas interesting and perhaps you want to send me some feedback. I always like to hear other ideas, thoughts, critisism and support.

Best regards

Hajo Kirchhoff (www.litwindow.com)