Showing posts with label wxWidgets. Show all posts
Showing posts with label wxWidgets. Show all posts

Thursday, December 27, 2012

Printing to a PDF file

A common requirement for an accounting program is the ability to "print" something to a PDF file instead of printing it on paper.

It is possible to install some software that creates a virtual printer, so that everything is printed to that printer is converted to a PDF file. It is a simple solution but it is a rather limited one: for example you do not have control on the name of the created file unless you stick with a particular program, and this is not a good idea. If that program is abandoned you are out of luck. There are more problems if you want to create a program that runs under multiple operating systems.

A much better solution is to create the PDF file from the printing program, so you do not have external dependencies.
For wxWidgets programs there is a very good library: wxPdfDocument. It can be used to create PDF files with custom commands, but it also contains the wxPdfDc class: it is a device context derived from wxDC, so it can be used to print to a PDF file using the same code used to print to paper. This is a huge advantage, of course.

I have been able to print to a PDF file with very small code changes. I will describe those changes making a reference to a previous post that described how to print from wxWidgets: you should read it before reading the rest of this post.

That post contained some code used to compute a variable called logUnitsFactor, used to convert millimeters to logical units. That code did not work with wxPdfDc so I had to change it. First add the include file:

 #include "wx/pdfdc.h"  

Then use the following code to compute logUnitsFactor instead of the code used in the older post:

     wxSize devicePPI = dc->GetPPI();  
     int ppiPrinterX, ppiPrinterY;  
     ppiPrinterX = devicePPI.GetWidth();  
     ppiPrinterY = devicePPI.GetHeight();  
   
     int ppiScreenX, ppiScreenY;  
     wxScreenDC sdc;  
     ppiScreenX = sdc.GetPPI().GetWidth();  
     ppiScreenY = sdc.GetPPI().GetHeight();  
   
     float scale = (float)((float)ppiPrinterY/(float)ppiScreenY);  
     dc->SetUserScale(scale, scale);  
   
     logUnitsFactor = (float)(ppiPrinterX/(scale*25.4));  

where dc is a pointer to a wxPdfDc object.
If you are writing some generic code you can use this test to know if you are working with a wxPdfDc:

   if( dc->IsKindOf(CLASSINFO(wxPdfDC)) ) {  
     // custom code if we are using wxPdfDC  
   }  
   else {  
     // standard code  
   }  

Now it's time to print something. The following code can be used to print to a PDF file with a given name. The printing framework does not directly support printing to a PDF so I had to copy some of its code.

   m_PrintData->SetFilename( fileName );  
   
   MyPrintout printout( "Print name" );  
   
   wxPdfDC dc( *m_PrintData );  
   dc.SetResolution( 600 );  
   printout.SetDC( &dc );  
   
   printout.OnPreparePrinting();  
   printout.OnBeginPrinting();  
   
   int minPageNum = 1;  
   int maxPageNum = 9999;  
   if( printout.OnBeginDocument(minPageNum, maxPageNum) ) {  
     for ( int pn = minPageNum;  
       pn >= maxPageNum && printout.HasPage(pn);  
       pn++ )  
     {  
       dc.StartPage();  
       bool cont = printout.OnPrintPage(pn);  
       dc.EndPage();  
   
       if ( !cont ) break;  
     }  
     printout.OnEndDocument();  
   }  
   
   printout.OnEndPrinting();  
   

With this code the same wxPrintout object used to print to paper can be used to print to a PDF file.


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 );
}

Thursday, July 21, 2011

Tab order in dialogs

Accounting programs often show dialogs that contain many controls for data entry.
The mouse is an easy way to move from one control to the other while entering data, but this is a very slow solution. Users that need to input a lot of data quickly learn to use the TAB key to move from one control to the other.

This is a much faster solution, but pressing TAB moves among controls in a fixed sequence (usually called tab order), so this sequence must be the best possible one from a user's viewpoint. To optimize space in the dialog sometimes the tab order must be different from the usual left-to-right, top-to-bottom layout.

Visual Basic and other RAD tools controls have a specific property to set the tab order of each control, but wxWidgets does not have anything similar. In wxWidgets the default tab order is the order of creation of the controls: this is usually leads to the left-right, top-bottom order.

The functions wxWindow::MoveAfterInTabOrder ()  and wxWindow::MoveBeforeInTabOrder()can be used to change the tab order of a control. They work but they have some drawbacks.
  • You must write this code by hand. DialogBlocks, for example, does not do it.
  • If you want to jump to a control and then move to the ten controls following it, you need to write this code for ALL the controls. Suppose that you have the following controls: A, B, C, D, E, F, G. Moving D after A will cause the following tab order: A, D, B, C, E, F, G. SO, to obtain something like A, D, E, F, G, B, C you will need to call the function a lot of times.
Using wxGridBagSizer can be a simpler solution to set tab order. Using this sizer you can obtain the same layout even if you create controls in a different order. Since the default tab order is the creation order you can change the creation order to get the desired tab order without changing the visual layout of the dialog.
Using DialogBloks this can be obtained moving controls up or down in the left pane.

Saturday, March 19, 2011

Complex dialogs with wxWidgets

Most RAD tools, like the venerable Visual Basic, let users put controls in a dialog using a fixed layout: just drag each control where you want to see it. This is a very simple approach, but it has some shortcomings, especially for applications that will run on different operating systems.
The problem is that the layout is fixed and it does not adapt to the size of the contained text. Widows uses a default font that is smaller than the one used in Linux or OS X, and even in Windows users can set larger fonts.
This means that if a dialog has been designed in Windows with default fonts then the text will no be completely visible if the used font is larger.
The same happens with translations: if the translated text is longer it will not be completely visible.

To solve these problems wxWidgets (like other frameworks) uses sizers for dialogs layout. Sizers change dynamically the size of controls so that their content will always be visible. This is very useful but it look rather complicated at first: you need some time to become used to sizers if you are used to fixed layouts.

Accounting programs often need to show many controls in a dialog, so it is important to use the available space in the best possible way. This sometimes means arranging controls in an irregular way to avoid wasting space.
Sizers can cause troubles in this situation: one of the more powerful sizer ix wxFlexGridSizer: it arranges controls in a regular grid, with rows and columns of different sizes to accommodate controls. This works reasonably well, but each column (for example) has the width of the widest control so if it contains a very wide and a very narrow control a lot of space will be wasted, and the dialog will not look very well.

A few days ago I had a similar situation, so I tried wxGridBagSizer: there is not much information about it but it looked promising. This is very complicated to use at the source code level, but I discovered that DialogBlocks has great support for this sizer. wxGridBagSizer arranges controls in a layout that is similar to HTML tables: controls lie in a grid, but a grid cell can span over more than one row or column. This solves most of the problems and the resulting dialog looks much better and uses the available space very well.

Here is an example of a dialog using wxGridBagSizer in DialogBlocks: you can see how some controls span over more than one row or column:

Tuesday, March 8, 2011

Printing text with wxWidgets II

Printing to the default printer

A common requirement is printing directly to the default printer, without showing any printer selection dialog.
Referring to the code of the previous post this can be accomplished by changing one line of code from

bool success = printer.Print( NULL, &printout, true );

to

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

According to the documentation this should work, but the code (at the moment of writing this post) does not print anything.
Some debugging shows that there is a bug in the wxWidgets code: the bug will be fixed, but at the moment the problem can be solved adding one line of code.
So working code looks like the this:

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


Setting the To page to the Max page fixes the problem.

Centering text (and similar)

Another common requirement is comparing the width of some text to the width of the page, for example to center the text, but it is not very clear how to obtain the desired sizes in the same units, so that they can be compared in a correct way.

The previous post shows how to compute a variable (logUnitsFactor) to convert from millimeters to logical units.
The page size in millimeters can be obtained by calling wxPrintout::GetPageSizeMM(), then the size in millimeters can be converted to logical units multiplying it by logUnitsFactor.

The width of a text can be obtained calling wxDC::GetTextExtent(): the result is in logical units that can be compared to the previously computed page width.
For example (code inside a wxPrintout-derived object):

    // compute the available width in logical units
    int pageWidthMM, pageHeightMM;
    GetPageSizeMM(&pageWidthMM, &pageHeightMM);
    int availableWidth = (pageWidthMM - m_MarginLeft - m_MarginRight)*logUnitsFactor;
    wxCoord ew, eh;
    dc->GetTextExtent( "Some text", &ew, &eh, NULL, NULL );
    // now we can compare "ew" and "availableWidth"

Saturday, February 26, 2011

Printing text with wxWidgets

A common requirement for an accounting program is printing some text, often for reports.

One possible solution is creating an HTML file and printing it using the wxHtmlEasyPrinting. I used it and it is a very simple and quick solution, but it does not give you precise control on the text layout.

For those who want to print using a more traditional approach there is the wxWidgets printing framework: it shields some of the low level tasks but it leaves you full control on the page layout.
I spent a lot of time trying to understand how to use it to print text lines and I found it difficult to understand the documentation and the printing sample (as usual, the wxWidgets samples are the best place to look for inspiration). Samples and documentation are more geared towards printing graphics, not text.
In the end I found a satisfactory solution and I will describe it here.

To use the printing framework you need to derive your own class from wxPrintout, and at least to implement the OnPrintPage() and HasPage() functions.

The OnPrintPage() function is called by the printing framework for each page that will be printed. It will contain the actual print code.
The problem, in the print code, is that each function that prints something requires coordinates to tell it where to print in the page. Those coordinates are not in millimeters or inches, so we need to find a way to convert millimeters (for example) to the units used by the device context. I used the following code to determine a conversion factor from millimeters to device units. The code is based on the printing sample.

    wxDC *dc = GetDC();
    if( dc == NULL || !dc->IsOk() ) {

        // report error and...
        return false;
    }

    // Get the logical pixels per inch of screen and printer
    int ppiScreenX, ppiScreenY;
    GetPPIScreen(&ppiScreenX, &ppiScreenY);
    int ppiPrinterX, ppiPrinterY;
    GetPPIPrinter(&ppiPrinterX, &ppiPrinterY);

    // This scales the DC so that the printout roughly represents the the screen
    // scaling. The text point size _should_ be the right size but in fact is
    // too small for some reason. This is a detail that will need to be
    // addressed at some point but can be fudged for the moment.
    float scale = (float)((float)ppiPrinterX/(float)ppiScreenX);

    // Now we have to check in case our real page size is reduced (e.g. because
    // we're drawing to a print preview memory DC)
    int pageWidth, pageHeight;
    int w, h;
    dc->GetSize(&w, &h);
    GetPageSizePixels(&pageWidth, &pageHeight);

    // If printer pageWidth == current DC width, then this doesn't change. But w
    // might be the preview bitmap width, so scale down.
    float overallScale = scale * (float)(w/(float)pageWidth);
    dc->SetUserScale(overallScale, overallScale);

    // Calculate conversion factor for converting millimetres into logical
    // units. There are approx. 25.4 mm to the inch. There are ppi device units
    // to the inch. Therefore 1 mm corresponds to ppi/25.4 device units. We also
    // divide by the screen-to-printer scaling factor, because we need to
    // unscale to pass logical units to DrawLine.
    float logUnitsFactor = (float)(ppiPrinterX/(scale*25.4));



now we can, for example, convert 20 millimeters to device units by computing 20*logUnitsFactor.
Font size in points will be respected, so the code for printing some text lines will be the following.

    dc->SetFont( m_ReportFont );
    dc->SetBackgroundMode( wxTRANSPARENT );
    dc->SetTextForeground( *wxBLACK );
    dc->SetTextBackground( *wxWHITE );

    // line height
    float charH = dc->GetCharHeight();

    // coordinates of the first line
    float x = m_MarginLeft*logUnitsFactor;
    float y = m_MarginTop*logUnitsFactor;
   
    // print lines
    dc->DrawText( "First line", x, y );
    y += charH;
    dc->DrawText( "Second line", x, y );
    y += charH;

One problem of the printing framework when printing reports from a database if that it seems that it requires the previous knowledge of how many pages will be printed. Of course this is not the case for a database report: the most efficient solution is reading the rows while printing the report, so there is no previous knowledge of how many pages will be printed.
The documentation says that you must provide an implementation of the GetPageInfo() function, telling the framework how many pages you are going to print. This is not completely true: if you do not provide that function the printing framework will assume a default of 32000 pages.
You can start with that default, then you can use your implementation of the HasPage() function to stop printing when it will be time. Just return false when you have printed all the needed rows.

Once you have created your custom printout class just use something like the following code to print your pages.

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

    if( !success ) {
        if (wxPrinter::GetLastError() == wxPRINTER_ERROR) {
            CUtils::MsgErr( "Printing error" );
        }
        else {
            // print has been canceled
        }
    }

That code will show a common dialog to select the desired printer, then it will print to that printer.

Monday, January 24, 2011

wxGrid, TAB and the trapped focus

An accounting program is likely to be used for intensive data entry, so it is important to make typing easy for the end user.
Experienced users like to be able to work as much as possible without using the mouse: the mouse is useful for less experienced users but it slows down data entry.

An example is a dialog that contains a wxGrid and other controls: the user can move among controls using the TAB key (SHIFT-TAB to move backwards), but as soon as the focus goes to the wxGrid control is is trapped. It is possible to move from column to column using TAB but is is not possible to leave the grid any more: the user has to use the mouse to click another control.

This problem can be solved in a rather simple way, handling the wxEVT_KEY_DOWN event. Here is an example:

void fsGridBase::OnKeyDown( wxKeyEvent& event )
{
    bool keyHandled = false;

    if( event.GetKeyCode() == WXK_TAB ) {
        if( event.ShiftDown() ) {
            // shift-TAB: if we are in the first column move to the previous control
            if( GetGridCursorCol() == 0 ) {
                Navigate( wxNavigationKeyEvent::IsBackward );
                keyHandled = true;
            }
        }
        else {
            // TAB: if we are in the last column move to the next control
            if( GetGridCursorCol() == GetNumberCols() - 1 ) {
                Navigate( wxNavigationKeyEvent::IsForward );
                keyHandled = true;
            }
        }
    }

    if( !keyHandled )
        event.Skip();
}


Using this code the TAB key moves from column to column, but when the cursor goes to the first or last column pressing TAB moves the focus to the previous or last control.
This code works with wxWidgets 2.9: I don't know if it works for 2.8 too.

Wednesday, January 12, 2011

Validation and the disappearing caret

A common requirement in accounting software is on-the-fly text validation: run some code in the lost focus event to elaborate the control's content. One example would be validating a VAT number, another is typing a few letters and showing a list of customers whose names start with the typed text.

All there situations have one thing in common: the code shows a new dialog from within the lost focus event handler of the control.

I just discovered a problem with wxWidgets: after closing the dialog the caret (the blinking cursor that shows where the next letter will be inserted) disappears so it is difficult to know which control has the focus. Tabbing to another control makes the caret reappear.
This is rather confusing for the user, so I made some searches: it looks like this is a well known problem and it looks like it is not going to be fixed soon.

A mail message also suggested a workaround: just set a flag in the event handler, then do what you need in the idle event handler. That handler is called just after the focus has moved from a control to another: in the idle handler check the value of the flag and execute the related code.

That is what I did and now the program is working well. The workaround does not even add too much complication to the code so it is satisfactory. It is only a pity that this problem is not mentioned in the documentation: it would save some headaches from time to time.

Friday, December 3, 2010

CMake - wxWidgets

CMake is released with a large set of source code that can be used to accomplish common tasks. One of this tasks is looking for a package used by your project.

For example, let's see how to look for the wxWidgets library. We can use the FIND_PACKAGE command for this purpose: CMake already knows how to handle wxWidgets.

Just add the following lines to CMakeLists.txt:

FIND_PACKAGE(wxWidgets REQUIRED html adv core base net aui xrc qa richtext )
INCLUDE(${wxWidgets_USE_FILE})
TARGET_LINK_LIBRARIES(myTarget ${wxWidgets_LIBRARIES})


The first line tells CMake to look for wxWidgets. The REQUIRED clause says that you require the specified modules.
The FIND_PACKAGE command executes some code and sets a number of variables if it finds the library.
The second line uses one of those variables to tell CMake where to look for include files.
The third line tells CMake how to link a target with wxWidgets.

Now let's see what happens when we run CMake from its GUI. Press Configure and you will probably see a number of red rows that need to be fixed. The rows content changes with the operating system.

Linux

I create two build folders, one for the debug configuration and one for the release one.

You need to look at the following variables:
  • CMAKE_BUILD_TYPE: set it to Debug for a build folder and to Release for the other.
  • wxWidgets_CONFIG_EXECUTABLE: it is the path to the wx-config script for the chosen copy of wxWidgets. For example, /usr/local/bin/wx-config for a library that has been installed, or /home/fulvio/wxSVN/buildgtk/wx-config for a library that has not been installed. In the library has not been installed you must point to the wx-config file in the folder with the right configuration (for example debug or not).
  • wxWidgets_USE_DEBUG: check it if you want to use a debug version of the library.
  • wxWIDGETS_USE_STATIC: check it if you ant to use a static version of the library.
  • wxWidgets_USE_UNICODE: check it if you want to use the Unicode version of the library. If you are using version 2.9 or later this setting does not make sense any more (the llibrary is only Unicode): I leave it checked and everything works well.
  • wxWidgets_wxrc_EXECUTABLE: this is probably a wxrc setting. Since I do not use it I left its value set to NOTFOUND.
OS X

I create two build folders, one for the debug configuration and one for the release one.

You need to look at the following variables:
  • CMAKE_BUILD_TYPE: set it to Debug for a build folder and to Release for the other.
  • CMAKE_OSX_ARCHITECTURES:  I set it to i386.
  • CMAKE_OSX_DEPLOYMENT_TARGET: I leave this blank.
  • wxWidgets_CONFIG_EXECUTABLE: it is the path to the wx-config script for the chosen copy of wxWidgets. For example /Users/fulvio/wxMac-2.8.10/buildgtk/wx-config for a library that has not been installed. In the library has not been installed you must point to the wx-config file in the folder with the right configuration (for example debug or not).
  • wxWidgets_wxrc_EXECUTABLE: this is probably a wxrc setting. Since I do not use it I left its value set to NOTFOUND.
Windows

In Windows I create a single build folder. It will contain a Visual Studio project file with both a debug and a release configuration.

You need to look at the following variables:
  • wxWidgets_CONFIGURATION: set to mswud, mswu or mswd. This is not very clear to me, but I set it to mswu and everything works well.
  • wxWidgets_ROOT_DIR: it is the path to the root folder of the wxWidgets installation, for example E:/wxWidgets-2.8.10. If CMake does not find the path you need to set it manually and configure again.
  • wxWidgets_LIB_DIR: it is the path of the folder that contains the libraries that will be linked to your program. Usually CMake can find this path by itself if it knows the root dir.
  • wxWidgets_USE_REL_AND_DBG is a boolean value. Check it if you want to have a project with both a debug and a release configuration. You will probably check it.
  • wxWidgets_wxrc_EXECUTABLE: this is probably a wxrc setting. Since I do not use it I left its value set to NOTFOUND.
As you can see configuring CMake to use your copy of wxWidgets is an easy task.
It is also easy to use different versions of wxWidgets with your project. Just build the different versions in different folders (do not install them in *nix), then create different build folders for the different wxWidgets versions. For each folder configure CMake to look for wxWidgets in the right folder, manually setting the required variables to the right value.

Wednesday, June 16, 2010

A grid to select rows

I want to use a grid to select, for example, a customer from the customers table.

I need a grid with the following features:
  • read-only
  • when I click the grid I want to select the whole row.
Solving this problem was harder than expected. First I tried the new wxDataViewListCtrl control. It has a lot of interesting functionalities, so I spent some time trying to use it. Unfortunately I found a number of bugs that showed how this control was too young for production code.

Then I tried wxDataListCtrl in report mode. I already used it in other projects so I had some experience with it, and it looked like a good solution. After writing some test code I again found a couple of problems.
The first is that it is not possible to have the first column right aligned, so it is not possible to show numbers in the first column.
The second is that a bitmap shown in a column is always left-aligned, while it would often be useful to have it centered.
Both limitations derive from the native control used, so they are not wxWidgets' fault.

So I was left only with the venerable wxGrid, and I tried it. I have been able to obtain a good result, and I learned something about wxGrid that will be useful in the future.

I derived a new class from wxGrid, with the following modifications:

The constructor calls the following code to set the visual properties of the grid:
SetUseNativeColLabels();
HideRowLabels();
EnableEditing( false );
SetCellHighlightPenWidth( 0 );  // disable highlight
EnableGridLines( false );
DisableDragGridSize();
ShowScrollbars( wxSHOW_SB_DEFAULT, wxSHOW_SB_ALWAYS );
// select the first row
if( GetNumberRows() > 0 )
    SelectRow( 0 );

the wxEVT_GRID_SELECT_CELL handler calls
SelectRow( event.GetRow() );

the wxEVT_GRID_RANGE_SELECT handler uses this code
if( event.Selecting() && (event.GetTopRow() != event.GetBottomRow()) ) {
to see if it needs to call
SelectRow( GetGridCursorRow() );
to select the current row only.

the wxEVT_GRID_LABEL_LEFT_CLICK handler calls
SelectRow( GetGridCursorRow() );


the wxEVT_GRID_ROW_SIZE handler calls
SelectRow( GetGridCursorRow() );


the wxEVT_GRID_COL_SIZE handler calls
SelectRow( GetGridCursorRow() );

the wxEVT_KEY_DOWN handler ignores events that would make the cursor go outside the grid, making the selection disappear. For example:
if( event.GetKeyCode() == WXK_RIGHT && GetGridCursorCol() == GetNumberCols() - 1 )
    ignore = true;


The resulting grid is very satisfactory for my needs.

Saturday, June 12, 2010

Entering numbers

Users of an accounting program need to enter number very often, so I need a bulletproow way to do it.

Numbers are usually entered in text controls, and wxWidgets ha poor native support for this need. I could have written a custom control for my purpose, but I decided that validators are a better solution.
A validator links a C++ variable and the corresponding control. It automatically transfers data between the variable and the control, and it can also validate the input, for example allowing only some characters.

The standard wxTextValidator class links a text control and a wxString variable. An optional wxFILTER_NUMERIC style can be used to only allow numeric input. This looks interesting but it is not enough: the validator only checks keypresses against a static list, so you can type something like "123-34" which is obviously wong. Moreover the handled variable is a wxString that should be manually converted to and from a numerical one.

So I wrote a new validator class, modifying the existing ones. This validator has many enhancements:
  • It links an int or double variable to the text control, with automatic conversion.
  • Keypresses are checked in a way that will always result in a correct numeric value. For example a minus character is only allowed at the beginning of the number.
  • For double variables it is possible to specify a fixed number of decimals.
  • Decimal separator. In my country the decimal separator is the comma (','), but the validator accepts the dot ('.') and converts it to a comma. This is handy because, for example, the numeric keypad does not have a comma key.
Implementing the validator was a relatively straightforward task. In the OnChar() event handler check the arriving keypress, get the control's insertion point with wxTextEntry::GetInsertionPoint() and see how the control's text would look like if the new character were allowed. Decide if the new char can be accepted or not.

The conversion from dot to comma is done swallowing the keypress event and using wxTextTentry::WriteText() to insert the comma.

Wednesday, June 9, 2010

Adapting wxWidgets

wxWidgets is a great framework, but it is an all-purpose one, so many things useful to an accounting program are missing. I am working to add some useful features, and I will report the results.
The needed improvements can be grouped in two main areas:
  • Data entry: users will do a lot of data entry, so there is the need for tools that make it easy for both the user and the programmer. Mainly, I need a good way to enter numbers and dates. "Good way" means that the user must find it easy and he should not have way to enter wrong values. Handling those values must be simple for the programmer too.
  • Grid: I plan to use grids a lot, in two different ways. The first is showing many rows and let the user choose one, for example choosing a customer from a list. This kind of grid must be read-only. The second is using the grid for data entry: think of entering the rows of an invoice like in a spreadsheet.
wxWidgets has basic controls in these two areas, but they are far from optimal so I had to work at this, and I am still working.

Saturday, May 29, 2010

Framework

Having decided to use C++, the next step is choosing a framework to make it easy to write the program once and compile it under different operating systems.
The main choices are QT and wxWidgets: some time ago I decided to give wxWidgets a try and I am satisfies of it, so I will keep using it.
Before deciding which tools to use I developed a couple of open source programs that used the same tools. I needed them, so they were a good test.

wxWidgets is a large library, so the learning curve is rather steep at first. I can recommend Cross-Platform GUI Programming with wxWidgets by Julian Smart, one of the core designers. It gives a good starting point. Then you can look at the samples: there are many of them. At first they look rather obscure, but this only means that you still have to learn. After some time the core structure becomes clear, and you can concentrate on the details.

You can create the user interface by hand, but in practice you will need a program to help you design it in a simpler way. There are many of them and most are free, but I think that for professional use you should try DialogBlocks. The price is low, it works well and it is much more than an interface designer: it can create the full code of a simple program, and it can handle compilation in different platforms so you can build your program without having to create a Visual Studio project, makefiles and much more. It can also compile wxWidgets for you, saving some time and headaches.

wxWidgets is not Visual Basic. It takes time to really master it and it requires better programmers, but after learning it I think that you can create a program in about the same time as using some RAD tool.