[Home]  [Prev]  [Next]    A guide, a tutorial for developing well-designed cross-platform applications

6. Preferences

Preferences allow a user to customize the application in certain areas. Preferences can be set in two different ways, either through a preferences dialog or through menu commands. Usually the preferences dialog sets them permanently while menu commands set them only for the current session. That means whenever you start an application it gets the preferences set by the last dialog while any changes through menu commands are lost.


6.1 Preferences dialog

The preference dialog is the usual place to specify any setting a user may configure to his liking. When the dialog is left with the OK button the current preferences are save and persists until they are changed again with this dialog. Any optional Apply button save the preferences as well but doesn't close the dialog.

It's getting more common that changes in the preferences get directly forwarded and applied to the main windows so an immediate feedback is given. While this is a nice feature it's not always easy to implement but if you plan this for your application do it either always or never.

Sample code

#include "prefdlg.h"     // Preferences dialog

class AppFrame: public wxFrame {
    ...
    void OnPreferences (wxCommandEvent &event);
    ...
}

BEGIN_EVENT_TABLE (AppFrame, wxFrame)
    ...
    EVT_MENU (myID_PREFS, AppFrame::OnPreferences)
    ...
END_EVENT_TABLE ()

void AppFrame::OnPreferences (wxCommandEvent &event) {
    PreferenceDlg (this);
}

...

BEGIN_EVENT_TABLE (PreferenceDlg, wxDialog)
    EVT_BUTTON (wxID_APPLY,  PreferenceDlg::OnApply)
    EVT_BUTTON (wxID_CANCEL, PreferenceDlg::OnCancel)
    EVT_BUTTON (wxID_OK,     PreferenceDlg::OnOkay)
    EVT_BUTTON (wxID_RESET,  PreferenceDlg::OnReset)
END_EVENT_TABLE()

PreferenceDlg::PreferenceDlg (wxWindow *parent,
                              long style)
             : wxDialog (parent, -1, _("Preferences"),
                         wxDefaultPosition, wxDefaultSize) {

    // load configuration
    LoadValues ();

    // create preference panel
    wxPanel *panel = new wxPanel (this, -1, wxDefaultPosition, wxDefaultSize,
                                 wxTAB_TRAVERSAL|wxCLIP_CHILDREN|wxNO_BORDER);

    // lauout the buttons
    wxButton *resetButton = new wxButton (panel, wxID_RESET, _("Default"));
    wxButton *okButton = new wxButton (panel, wxID_OK, _("OK"));
    okButton->SetDefault();
    wxButton *applyButton = new wxButton (panel, wxID_APPLY, _("Apply"));
    wxButton *cancelButton = new wxButton (panel, wxID_CANCEL, _("Cancel"));
    wxBoxSizer *buttonpane = new wxBoxSizer (wxHORIZONTAL);
    buttonpane->Add (resetButton, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL);
    buttonpane->Add (16, 0, 1);
    buttonpane->Add (okButton, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL);
    buttonpane->Add (6, 0);
    buttonpane->Add (applyButton, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL);
    buttonpane->Add (6, 0);
    buttonpane->Add (cancelButton, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL);

    // set sizer
    wxBoxSizer *panelsizer = new wxBoxSizer (wxVERTICAL);
    panelsizer->Add (...
    panelsizer->Add (0, 6);
    panelsizer->Add (buttonpane, 0, wxEXPAND|wxLEFT|wxRIGHT|wxBOTTOM, 10);

    panel->SetSizer (panelsizer);
    panelsizer->Fit (this);
    Centre (wxBOTH);
    ShowModal();
}

PreferenceDlg::~PreferenceDlg () {
}

//----------------------------------------------------------------------------
// event handlers

void PreferenceDlg::OnApply (wxCommandEvent &WXUNUSED(event)) {
    ...
    SaveValues ();
}

void PreferenceDlg::OnCancel (wxCommandEvent &WXUNUSED(event)) {
    EndModal (wxID_CANCEL);
}

void PreferenceDlg::OnOkay (wxCommandEvent &WXUNUSED(event)) {
    ...
    SaveValues ();
    EndModal (wxID_OK);
}

void PreferenceDlg::OnReset (wxCommandEvent &WXUNUSED(event)) {
    ...
}

void PreferenceDlg::LoadValues () {
    wxString key = KEYNAME;
    key.Append (_T("/"));
    wxConfig *cfg = new wxConfig (wxTheApp->GetAppName());
    cfg->Read (key + VALUENAME, ...);
    ...
    delete cfg;
}

void PreferenceDlg::SaveValues () {
    wxString key = KEYNAME;
    key.Append (_T("/"));
    wxConfig *cfg = new wxConfig (wxTheApp->GetAppName());
    cfg->Write (key + VALUENAME, ...);
    ...
    delete cfg;
}


6.2 Preferences in the menu

Setting preferences via menu command allow changing them for the current session. When the application ends these settings are lost. Usually only the immediately necessary settings are available as menu commands.

Sample code

class AppFrame: public wxFrame {
    ...

    // View menu
    wxMenu *menuView = new wxMenu;
    menuView->AppendCheckItem (myID_TOOLBARS, _("Tool &bar"));
    menuView->AppendCheckItem (myID_STATUSBAR, _("Status b&ar"));

    ...
}


6.3 Load/save of preferences

Preferences are usually stored with in the registry (Windows) or config file (Linux, etc.). wxWidgets supports this through wxConfig choosing the platform specific solution itself. Be carefully when keeping the preferences open over a longer time period. For safety reasons keep this period as small as possible.

One might use wxFileConfig instead to force the use of config file even on Windows. The default location of the config file is based on the $HOME variable on Linux, etc. and $HOMEDRIVE + HOMEPATH on Windows. The file will only be written back to disk when the config objected is deleted or the Flush() method is called.

Sample code

#include <wx/config.h>   // configuration support

const wxString PAGE_COMMON = _T("Common");
...
const wxString NOSPLASH = _T("NoSplashScreen");
const wxString SHOWTOOLBAR = _T("ShowToolbar");
const wxString SHOWSTATUSBAR = _T("ShowStatusbar");
const wxString USEPAGES = _T("UsePages");

CommonPrefs g_CommonPrefs = {
    // application prefs
    false, // noSplash
    true,  // showToolbar
    true,  // showStatusbar
    false, // usePages
};
...

void PreferenceDlg::LoadValuesPageCommon () {
    wxString key = PAGE_COMMON;
    key.Append (_T("/"));

    // common prefs
    wxConfig *cfg = new wxConfig (wxTheApp->GetAppName());
    cfg->Read (key + NOSPLASH, &g_CommonPrefs.noSplash);
    cfg->Read (key + SHOWTOOLBAR, &g_CommonPrefs.showToolbar);
    cfg->Read (key + SHOWSTATUSBAR, &g_CommonPrefs.showStatusbar);
    cfg->Read (key + USEPAGES, &g_CommonPrefs.usePages);
    delete cfg;
}

void PreferenceDlg::SaveValuesPageCommon () {
    wxString key = PAGE_COMMON;
    key.Append (_T("/"));

    // common prefs
    wxConfig *cfg = new wxConfig (wxTheApp->GetAppName());
    cfg->Write (key + NOSPLASH, g_CommonPrefs.noSplash);
    cfg->Write (key + SHOWTOOLBAR, g_CommonPrefs.showToolbar);
    cfg->Write (key + SHOWSTATUSBAR, g_CommonPrefs.showStatusbar);
    cfg->Write (key + USEPAGES, g_CommonPrefs.usePages);
    delete cfg;
}