Saturday, April 28, 2012

C++: rounding a double to a fixed number of decimals

An accounting program handles money amounts, so it is important to avoid computing errors.

The problem is that, using a double, most values cannot be stored exactly but there is a small error. Numbers are stored in base 2, and math tells us that only the decimal numbers obtained dividing some integer value by a power of two will have an exact representation in that base.
So 2.5, 5.25, 3.125 and similar numbers will be stored exactly, while all other number will be represented by a periodic number.
What happens is similar to representing 1/3 in base 10: the result is 0.333333333... with an infinite number of decimals.

A double value has a finite size so it cannot contain infinite decimals: the result is a small error.
There other ways to store decimal values exactly, using a custom class, but I prefer to use a standard type so I use doubles.
Doubles have a precision of about 15 digits, so there is plenty of room to handle money amounts. Rounding errors will be much smaller than the printed values (we will print few decimals) so there should be no problems if computations are handled correctly.

This is true, but I have found a situation where rounding errors lead to a wrong result. I was testing the program and it computed the VAT for an invoice: the right value was 3.16 but the program printed 3.15.

A quick investigation showed that the exact value was 3.155: rounding to two decimals leads to 3.16.
The debuggers showed me that the approximated value contained in the double variable was 3.1549999999999998. The error is very small, but is is enough to cause rounding to 3.15.

I made some quick searches but I did not find anything about this problem, so I had to find a solution by myself.
In the end I decided to modify the function that I use to round to a certain number of decimals: now, before rounding, I add a very small amount (say 0.00000000001) to the number that will be rounded. The value is very small but it is enough to make the number (in the previous example) to become a little more than 3.155 so now rounding is correct.

I don't know if there are better solution to this problem, but I think that adding such a small value to a number that represents a money amount is not likely to introduce errors (at least I have not found any), and it solves the problem.

Friday, February 24, 2012

Firebird: choosing an owner for database deployment

Like most users when I started using Firebird I connected using the SYSDBA username. That is the default username for server administration: every server has it.

It looked like a good idea because I did not have to care with users management, but I have now realized that using SYSDBA for database development can cause problems when the database is deployed to the customer's computer.

A Firebird database and all its tables have an owner: it is the username that was used for connecting to the server when the object was created. Tables can have a different owner than the database, but this an advanced topic. The simplest solution is having a single owner for the database and all the tables.

Both the owner and SYSDBA have full access to the database, so you need to connect to the server as the owner or as SYSDBA. Of course things can be more complicated about database security, but I want to keep things simple.

The server stores usernames and passwords in a special database (security2.fdb). When a user connects to the server the username and the password are checked using the security database. If the username does not exist or the password is incorrect the connection is refused. When a user is connected to a database he can open a table only if he is the owner or if he is SYSDBA.

I am targeting small customers, so most of them will use the embedded server. The embedded server works differently about security: it will not check the username and the password (there is no security2.fdb) so you will always be able to connect with any username/password couple. Nevertheless Firebird will still check that the username is the owner or SYSDBA before opening a table. Other usernames can connect to the server but cannot open tables.

Customers that will require a multi-user solution will need to install the full server, but they will probably have no experience with Firebird or other database servers. Most of them will use Firebird only for our software, but there is a chance that some of them will already have a Firebird server installed and used by other software.

Deploying a database owned by SYSDBA will cause problems to those users. The only way to connect to such a database is using the SYSDBA username, and since the server has been installed by other people users might not know the SYSDBA password. A database administrator would not like to let a software connect as SYSDBA, since that user has full access to all the databases handled by the server.

For this reason a much better solution is creating a new username and connecting with that username to create and develop the database, so that the owner is not SYSDBA. Then, after database deployment, the software can connect in different ways for different needs.

The software can connect to an embedded server using the SYSDBA username and any password. It will be able to access data without problems. Users will not not be asked for a username.

When connecting to a full server the software will ask for a username and a password. There are two options:
  • If the server will only host our database the simplest option is connecting as SYSDBA. It will be possible to access the database with minimal effort.
  • If using SYSDBA is not an option somebody must create a new user named as the database owner (the same username used for database development) using any password he likes. The software will connect using that username and password so it will be able to access the database without administrator privileges. The username must be the same as the owner, but the password does not need to be the same used for development. Remember that the password is only used to connect to the server: once you are connected Firebird only checks the username to grant access to tables.
If you already have a database owned by SYSDBA and you want to change its owner you can save a script to recreate the database using some administration tool ("Extract metadata" or similar). Then create a new user that will became the database owner, connect to Firebird using that username and create a new database. Then execute the script to recreate the database structure.

If you have a lot of data and you need to keep them you can try FBOwnerMigrator by Thomas Steinmaurer.

Tuesday, January 17, 2012

wxGrid editors and the arrow keys

When editing a wxGrid cell the standard behaviour is not very user-friendly: if you press one of the arrow keys you can move the caret in the editor, but you cannot select another cell. For example, a user would expect that pressing the up key would close the cell editor and move the selection to the cell above, but nothing happens.

It is possible to add this behaviour by handling the EVT_GRID_EDITOR_CREATED event for the grid. For example:

   EVT_GRID_EDITOR_CREATED( fsGridBase::OnEditorCreated )  

The OnEditorCreated() function simply sets an event handler for the EVT_KEY_DOWN event for the editor control:

 void fsGridBase::OnEditorCreated( wxGridEditorCreatedEvent& event )  
 {  
   event.GetControl()->Bind( wxEVT_KEY_DOWN, &fsGridBase::OnGridEditorKey, this );  
   event.Skip();  
 }  

The OnGridEditorKey() function can handle the EVT_KEY_DOWN event as desired by the developer. The basic rule is the following: call event.Skip() to send the event to the editor control, getting the usual behaviour.
Call GetEventHandler()->ProcessEvent(event) to send the event directly to the grid, bypassing the cell editor. This choice will let the arrow keys move to another cell.

One example could be the following:

 void fsGridBase::OnGridEditorKey( wxKeyEvent& event )  
 {  
   switch ( event.GetKeyCode() ) {  
     case WXK_DOWN:  
     case WXK_UP:  
       GetEventHandler()->ProcessEvent( event );  
       break;  
     case WXK_LEFT:  
       {  
         wxGridCellEditor* editor = GetCellEditor( GetGridCursorRow(), GetGridCursorCol() );  
         wxControl* ctrl = editor->GetControl();  
         if( ctrl->IsKindOf(CLASSINFO(wxTextCtrl)) ) {  
           wxTextCtrl* tp = dynamic_cast<wxTextCtrl*>( ctrl );  
           if( tp->GetInsertionPoint() == 0 ) {  
             // we are at the beginning of the text control so let's move to the previous column  
             GetEventHandler()->ProcessEvent( event );  
           }  
           else {  
             event.Skip();  
           }  
         }  
         editor->DecRef();  
       }  
       break;  
     case WXK_RIGHT:  
       {  
         wxGridCellEditor* editor = GetCellEditor( GetGridCursorRow(), GetGridCursorCol() );  
         wxControl* ctrl = editor->GetControl();  
         if( ctrl->IsKindOf(CLASSINFO(wxTextCtrl)) ) {  
           wxTextCtrl* tp = dynamic_cast<wxTextCtrl*>( ctrl );  
           if( tp->GetInsertionPoint() == tp->GetLastPosition() ) {  
             // we are at the end of the text control so let's move to the next column  
             GetEventHandler()->ProcessEvent( event );  
           }  
           else {  
             event.Skip();  
           }  
         }  
         editor->DecRef();  
       }  
       break;  
     default:  
       event.Skip();  
   }  
 }  

The up and down arrows will always move to another cell. The left and right arrows will move the caret in the control as usual, but if the caret is at the beginning or at the end of the control the key will move to another cell.

The example above is a good starting point, but it will cause problems if the grid uses wxGridCellChoiceEditors because that editor would not work well with keyboard keys. It will work well with the mouse, anyway.

Monday, January 9, 2012

Easily adding lines to a wxGrid

wxGrid is a good control, but users expect a behaviour more similar to a spreadsheet, so I derived my own control from wxGrid, with a number of customizations.

The first is the ability to easily add a new row at the end of the grid. With some simple code it is possible to always have an empty row at the end of the grid, so users can simply go to that line and start typing data: a new row will pop up at the end of the grid and so on...

All we need to do is handling the EVT_GRID_SELECT_CELL event:

 void fsGridBase::OnSelectCell( wxGridEvent& event )  
 {  
     int row = event.GetRow();  
     int lastRow = GetTable()->GetNumberRows() - 1;  
     if( row == lastRow ) {  
         AppendRows( 1 );  
     }  
   
   event.Skip();  
 }  
   

as soon as a cell is selected in the last row a new row is appended. This makes the grid behaviour more similar to the one of a spreadsheet.

Sunday, November 6, 2011

Hiding and showing controls in a dialog

Often a single dialog can be used for different purposes, with slight variations. Sometimes a control is not needed if the uses chooses a certain option, but it is needed if he chooses a different option.

If disabling the control is enough the simplest solution is to use UPDATE_UI events, but those events cannot be used to hide and show controls.

Dynamically hiding a control requires a few lines of code, but this feature is not very well documented.
Suppose that m_Control is a pointer to the control to be hidden, and that m_Sizer is a pointer to the sizer that contains that control. The following code, in a method of a wxDialog-derived window, hides the control at run time, rearranging the other controls and the window to reuse the resulting empty space:

    m_Sizer->Hide( m_Control );
    m_Sizer->Layout();
    GetSizer()->SetSizeHints(this);

Monday, September 26, 2011

Directly printing to a certain printer

When users have more than one printer available, sometimes a requirement is being able to print many documents to a certain printer. That printer is not necessarily the default printer.
Think of printing a set of invoices, for example. This is not a trivial task in wxWidgets.

The standard code to print a document is the following (see also this post):

    wxPrintDialogData printDialogData(* m_PrintData);
    wxPrinter printer( &printDialogData );
    bool success = printer.Print( NULL, &printout, true );


The code above shows a printer selection dialog, then it prints to the printer selected by the user.

After printing you can get the name of the selected printer with this code:

wxString pName = printer.GetPrintDialogData().GetPrintData().GetPrinterName();


Then you can use the printer name to print again directly to that printer:


    m_PrintData->SetPrinterName( pName );
    wxPrintDialogData printDialogData(* m_PrintData);

    wxPrinter printer( &printDialogData );
    bool success = printer.Print( NULL, &printout, false );


 The code above sets the printer name and prints without showing a printer selection dialog.
As you can see the solution a a simple one, but I had problems finding it because nobody seems to have this problem and I had to look hard in the documentation.

Friday, September 9, 2011

Printing images with wxWidgets

Printing an image, mixed with text, is a common requirement. Just think of printing a customer's logo on an invoice.

Printing an image at a given size in wxWidgets has been a rather difficult task, mainly for the lack of documentation or examples. After some trial and error I found a working solution and I will describe it here. This solution is based on what I described for printing text. You will need to read that post too.

1 - Load the image in a wxImage object, resize it, convert it to a wxBitmap. You need to resize the image so that you can print it pixel by pixel and obtain a printed image of the desired size. To define the image size you need to know the desired printed size or the image DPI. You can know the printer's resolution using wxDC::GetPPI().
Starting from the original image size and the printer's resolution you can compute the new image size and rescale it.
If you know the image's DPI you can compute the new image size in pixels using this formula:
newWidth_pixels = printerPPI / imageDPI * originalWidth_pixels

If you know the printed image's width (in mm) that you want to obtain you can compute the new image size in pixels using this formula:
newWidth_pixels =  printedWidth_mm * PPI / 25.4

2 - Print the bitmap, making sure that wxWidgets does not resize it. This is the difficult part, since I ended up with a much larger image than what I wanted. The problem is that I set up a user scale with wxDC::SetUserScale() and that scale was also applied to the image, enlarging it. The solution is, obviously, to set the user scale to 1 before drawing the bitmap. Here is a function that I use for the purpose (x and y are the print coordinates in mm):

void PrintBitmap( wxDC *dc, const float logUnitsFactor, wxBitmap &bitmap, const float x, const float y )
{
        double xScale, yScale;
        dc->GetUserScale( &xScale, &yScale );
        dc->SetUserScale( 1, 1 );
        dc->DrawBitmap( bitmap, x*logUnitsFactor*xScale, y*logUnitsFactor*yScale, true );
        dc->SetUserScale( xScale, yScale );
}