Xfce Foundation Classes
 « Index | Adding a menubar and toolbar Adding a statusbar »

Chapter 5: Writing a composite statsubar


By now you should know how to go about adding a statusbar to XfcApp. You create an instance of Gtk::Statusbar and pack it into main_vbox. The statusbar should appear at the bottom of the main window so you would call pack_end(), not pack_start(). The code looks like this:

#include <xfc/gtk/statusbar.hh>

Gtk::Statusbar *statusbar = new Gtk::Statusbar;
main_vbox->pack_end(*statusbar, false);
statusbar->show();

It's that easy, but that's not what we're going to do. Instead, we will create composite statusbar that displays a progress bar that looks and behaves just like the GNOME appbar. The statusbar is a new widget so it should have its own header file and source file. I have called this widget 'Statusbar'.

The header file for Statusbar is <statusbar.hh>:

#include <xfc/gtk/statusbar.hh>
#include <xfc/gtk/progressbar.hh>

using namespace Xfc;

class Statusbar : public Gtk::Statusbar
{
    Gtk::ProgressBar *progress_bar_;

    bool activity_mode_;
    sigc::connection timeout_connection;
   
    bool on_timeout();

public:
    Statusbar(bool show_progress);
    virtual ~Statusbar();
   
    Gtk::ProgressBar* progress_bar() const;

    void push(const String& text);
    void pop();
   
    void begin_progress(unsigned int interval, bool activity_mode = false);
    void set_progress(double percentage);
    void end_progress();
   
    sigc::signal<void> update_progress_signal;
};


The Statusbar constructor takes one argument, a bool value 'show_progress' that if set to true will show the progress bar and if set to false will hide it. Statusbar declares a private Gtk::ProgessBar pointer and provides the public function, progress_bar() to return the pointer.  You can call this function to show and hide the progress bar as required, like this:

statusbar->progress_bar()->show();

statusbar->progress_bar()->hide();

The two public functions, push() and pop(), wrap the Gtk::Statusbar functions of the same name in a function signature that can be connected to the Gtk::Item "select" and "deselect" signals (see later). These two functions are well documented in the Gtk::Statusbar API reference.

Statusbar declares one signal called "update_progress"  which is emitted to notify the user that the progress bar needs updating. This signal is not emitted when the progress bar is run in activity mode.

The source file for Statusbar is <statusbar.cc>:

#include "statusbar.hh"
#include <xfc/glib/main.hh>
#include <gconf/gconf-client.h>

Statusbar::Statusbar(bool show_progress)
: activity_mode_(false)
{
    progress_bar_ = new Gtk::ProgressBar;

    // Use the GNOME value for 'status_bar_meter_on_right' to place the progress bar.
    GConfClient *client = gconf_client_get_default();
    if (!gconf_client_get_bool(client, "/desktop/gnome/interface/status_bar_meter_on_right", 0))
        insert_start(*progress_bar_, 0, false, false);
    else
    {
        pack_end(*progress_bar_, false, false);
        set_has_resize_grip(false);
    }
   
    set_spacing(4);
    if (show_progress)   
        progress_bar_->show();
    g_object_unref(client);
}

Statusbar::~Statusbar()
{
}

bool
Statusbar::on_timeout()
{
    // If actvivity mode is true just pulse the progress bar, otherwise emit the "update_progress" signal.   
    if (activity_mode_)
        progress_bar_->pulse();
    else
        update_progress_signal.emit();
   
    return true;
}

Gtk::ProgressBar*
Statusbar::progress_bar() const
{
    return progress_bar_;
}

void
Statusbar::begin_progress(unsigned int interval, bool activity_mode)
{
    // Add a timer callback to update the value of the progress bar
    timeout_connection = G::timeout_signal.connect(sigc::mem_fun(this, &Statusbar::on_timeout), interval);
    activity_mode_ = activity_mode;
}

void
Statusbar::set_progress(double fraction)
{
    progress_bar_->set_fraction(fraction);
}

void
Statusbar::end_progress()
{
    // Remove timeout callback
    timeout_connection.disconnect();
}

void
Statusbar::push(const String& text)
{
    Gtk::Statusbar::push(text);
}

void
Statusbar::pop()
{
    Gtk::Statusbar::pop();
}


The first line in the Statusbar constructor creates an instance of Gtk::ProgressBar.

progress_bar = new Gtk::ProgressBar;

In the next eight lines of code the GNOME value 'status_bar_meter_on_right' is retrieved from the GConf database. This is a user defined value that specifies whether the progress bar should appear on the left or right side of a GNOME appbar. Depending on this value, we either insert our progress bar at position zero (on the left) or pack it at the end (on the right) of the statusbar.

GConfClient *client = gconf_client_get_default();
if (!gconf_client_get_bool(client, "/desktop/gnome/interface/status_bar_meter_on_right", 0))
    insert_start(*progress_bar, 0, false, false);
else
{
    pack_end(*progress_bar, false, false);
    set_has_resize_grip(false);
}

How is this code possible? If you look in the header file <xfc/gtk/statusbar.hh> you will see that Gtk::Statusbar derives from Gtk::HBox. If you also take a look at the GtkStatusbar source code you will see that GtkStatusbar is a GtkHBox that has one child, a GtkFrame, which also has one child, a GtkLabel. Because Gtk::Stausbar is also a Gtk::HBox you can pack another widget into it, either before or after the GtkFrame.

The set_has_size_grip() function is called to hide the size grip if the progress bar is on the right. If you want to, you can call this function each time you show and hide the progress bar (when the progress bar is on the right), so that the size grip is displayed when the progress bar is hidden and the size grip is hidden when the progress bar is displayed.

For convenience, Statusbar provides three progress bar functions: begin_progress(), set_progress() and end_progress(). The first function, begin_progress(), registers our private timeout callback, on_timeout(), to be called every 'interval' milliseconds:

void
Statusbar::begin_progress(unsigned int interval, bool activity_mode)
{
    timeout_connection = G::timeout_signal.connect(sigc::mem_fun(this, &Statusbar::on_timeout), interval);
    activity_mode_ = activity_mode;
}

If 'activity_mode_' is set to false, on_timeout() emits the "update_progress" signal. Otherwise on_timeout() just pulses the progress bar. The callback returns true to ensure that it gets called again after the timeout interval has elapsed. If on_timeout() returned false it would be disconnected and not called again.

Here is the on_timeout() callback:

bool
Statusbar::on_timeout()
{
    if (activity_mode_)
        progress_bar_->pulse();
    else
        update_progress_signal.emit();
   
    return true;
}


The second function, set_progress(), calls the Gtk::ProgressBar::set_fraction() method which updates the progress bar:

void
Statusbar::set_progress(double fraction)
{
    progress_bar_->set_fraction(fraction);
}


To actually update the progress bar, set_progress() should be called from the user's signal handler connected to the "update_progress" signal. The 'fraction' argument is the fraction of the task currently completed.

The third function, end_progress(), must be called when the task is complete and the user has finished updating the progress bar:

void
Statusbar::end_progress()
{
    timeout_connection.disconnect();
}


As you can see, end_progress() disconnects the timeout callback so it no longer gets called. The way this API is designed, each call to begin_progress() should be paired with a call to end_progress().

The "update_progress" signal is a libsigc++ signal that notifies the user when the progress bar needs updating. It gets emitted every time on_timeout() is called if the progress bar is not in activity mode. In response to this signal, the user should recalculate the fraction of their task completed and call set_progress() with the new value to update the progress bar.

You can connect a signal handler to the "update_progress" signal like this:

statusbar->update_progress_signal.connect(sigc::mem_fun(this, &MyClass::on_update_progress));   

where on_update_progress() has the following prototype:

void on_update_progress();

Well that's it! The ease with which you can write composite widgets is one of the advantages of programming in C++. Compare the amount of source code required to implement our Statusbar with that for the GNOME appbar. Our statusbar requires a lot less code because it takes advantage of inheritance which reuses existing code.

In the next chapter we will add an instance of Statusbar to our XfcApp program and link it to the Gtk::UIManager so that the statusbar displays an action's tooltip on menu item selection.


Copyright © 2004-2005 The XFC Development Team Top
XFC 4.4
Index