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_progess);
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_progess)
: 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_progess)
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_progess(). 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
|
|