Chapter
4: Adding a menubar and toolbar
The easiest way to add a menubar and toolbar(s) to an application is to
use
the Gtk::UIManager interface. First create a set of application
specific actions and then use an instance of Gtk::UIManager to
create the menubar and toolbar(s) using those actions, from an XML
description. The advantage of Gtk::UIManager is that it makes it easier
to coordinate using the same item in a menu and a toolbar, and it
simplifies the implementation of toolbar editing.
So lets add an action-based menubar and toolbar to the XfcApp
program. First we need to add an application instance of Gtk::UIManager
and
Gtk::ActionGroup. Usually an application has only one instance of
Gtk::UIManager but it can have several action groups. Action groups are
a good way to group similar actions together, however, in XfcApp we
will use only one. Gtk::UIManager and Gtk::ActionGroup derive directly
from G::Object so they are created with a reference count of one that
must be unreferenced. The easiest way to manage reference counting in
XFC is to use a smart pointer.
To the class declaration in <xfcapp.hh> we need to add two
private smart
pointers, one for the user interface manager and one for the action
group:
Pointer<Gtk::ActionGroup>
group;
Pointer<Gtk::UIManager> manager;
and for convenience, we need to declare a private add_actions() method
to create all
the
actions:
void add_actions();
and lastly, we need to declare eleven public methods, one for each
action
type XfcApp will use:
void on_file_new();
void
on_file_open();
void
on_file_save();
void
on_file_save_as();
void
on_file_quit();
void on_edit_cut();
void on_edit_copy();
void on_edit_paste();
void on_edit_clear();
void on_edit_preferences();
void
on_help_about();
Your <xfcapp.hh> header file should now look like this:
#include <xfc/main.hh>
#include <xfc/gtk/window.hh>
#include <xfc/gtk/uimanager.hh>
using namespace Xfc;
class XfcApp : public Gtk::Window
{
Pointer<Gtk::ActionGroup> action_group;
Pointer<Gtk::UIManager> manager;
void add_actions();
public:
XfcApp();
virtual ~XfcApp();
void on_file_new();
void on_file_open();
void on_file_save();
void on_file_save_as();
void on_file_quit();
void on_edit_cut();
void on_edit_copy();
void on_edit_paste();
void on_edit_clear();
void on_edit_preferences();
void on_help_about();
};
The UI definitons that XfcApp will use are very simple
and are defined as a string in <xfcapp.ui>:
static const char *ui_info =
"<ui>"
" <menubar name='MenuBar'>"
" <menu action='FileMenu'>"
" <menuitem action='New'/>"
" <menuitem action='Open'/>"
" <menuitem action='Save'/>"
" <menuitem action='SaveAs'/>"
" <separator/>"
" <menuitem action='Quit'/>"
" </menu>"
" <menu action='EditMenu'>"
" <menuitem action='Cut'/>"
" <menuitem action='Copy'/>"
" <menuitem action='Paste'/>"
" <menuitem action='Clear'/>"
" <separator/>"
" <menuitem action='Preferences'/>"
" </menu>"
" <menu action='HelpMenu'>"
" <menuitem action='About'/>"
" </menu>"
" </menubar>"
" <toolbar name='ToolBar'>"
" <toolitem action='New'/>"
" <toolitem action='Open'/>"
" <toolitem action='Save'/>"
" <separator/>"
" <toolitem action='Quit'/>"
" </toolbar>"
"</ui>";
Our menubar has three submenus: File, Edit and Help. The file menu has
six
menu items: New, Open, Save, Save As, Quit and a separator. The edit
menu has six menu items: Cut, Copy, Paste, Clear, Preferences and a
separator. The help
menu has only one menu item: About. The toolbar has five tool
items: New, Open, Save, Quit and a separator.
The constructor for the first XfcApp application was very small and
only called one function, set_title(). Now we need to add the code that
will implement our UI description. First we create the UI action group
and give it an
arbitrary name. An application
can use more than one action group but XfcApp will use only one.
action_group = new
Gtk::ActionGroup("XfcAppActions");
add_actions();
XfcAppActions
is the name used to identify the action group. add_actions() is a
convenience method that is used to create the
application's actions. It looks like this:
void
XfcApp::add_actions()
{
using namespace Gtk;
using namespace sigc;
action_group->add("FileMenu", "_File");
action_group->add("EditMenu", "_Edit");
action_group->add("HelpMenu", "_Help");
Action *action = action_group->add("New", "_New",
StockId::NEW, AccelKey("<control>N"), "Create a new
file");
action->signal_activate().connect(mem_fun(this,
&XfcApp::on_file_new));
action = action_group->add("Open", "_Open",
StockId::OPEN, AccelKey("<control>O"), "Open a
file");
action->signal_activate().connect(mem_fun(this,
&XfcApp::on_file_open));
action = action_group->add("Save", "_Save",
StockId::SAVE, AccelKey("<control>S"), "Save current
file");
action->signal_activate().connect(mem_fun(this,
&XfcApp::on_file_save));
action = action_group->add("SaveAs", "Save
_As...", StockId::SAVE, "Save to a file");
action->signal_activate().connect(mem_fun(this,
&XfcApp::on_file_save_as));
action = action_group->add("Quit", "_Quit",
StockId::QUIT, AccelKey("<control>Q"), "Quit");
action->signal_activate().connect(mem_fun(this,
&XfcApp::on_file_quit));
action = action_group->add("Cut", "C_ut",
StockId::CUT, AccelKey("<control>X"), "Cut the
selection");
action->signal_activate().connect(mem_fun(this,
&XfcApp::on_edit_cut));
action = action_group->add("Copy", "_Copy",
StockId::COPY, AccelKey("<control>C"), "Copy the selection");
action->signal_activate().connect(mem_fun(this,
&XfcApp::on_edit_copy));
action = action_group->add("Paste", "_Paste",
StockId::PASTE, AccelKey("<control>V"), "Paste the clipboard");
action->signal_activate().connect(mem_fun(this,
&XfcApp::on_edit_paste));
action = action_group->add("Clear", "C_lear",
StockId::CLEAR, "Clear the selection");
action->signal_activate().connect(mem_fun(this,
&XfcApp::on_edit_clear));
action = action_group->add("Preferences",
"Prefere_nces", StockId::PREFERENCES, "Configure the application");
action->signal_activate().connect(mem_fun(this,
&XfcApp::on_edit_preferences));
action = action_group->add("About", "_About",
AccelKey("<control>A"), "About");
action->signal_activate().connect(mem_fun(this,
&XfcApp::on_help_about));
}
Gtk::ActionGroup has several overloaded add() methods that create and
add an action to an action group. The first three calls to add()
adds actions that will display the file, edit and help menus when
activated. add() returns a pointer to the newly created action which is
then used to connect a function slot to the action's "activate" signal.
This function slot will be called whenever the action is activated.
Next, create an instance of Gtk::UIManager and insert the action group
into it:
manager
= new Gtk::UIManager;
manager->insert_action_group(*action_group);
To activate the accelerators we need add the user interface manager's
accelerator group to the window:
add_accel_group(manager->get_accel_group());
Next, we add a vertical box to XfcApp. Remember from the packing boxes HOWTO that
windows
are
bin
widgets and can only have one child, whereas boxes are layout widgets
and
can have more than one child. To display a menubar properly it should
be packed into a vertical
box. Try adding the menubar
to a horizontal box and see what happens.
Gtk::VBox
*main_vbox = new Gtk::VBox;
add(*main_vbox);
The vertical box is called 'main_vbox' because this is the box we will
add all other widgets to. Later we will add another vertical box to
be used as the client area of the window, and we will add a statusbar.
Next, load the UI description defined in
<xfcapp.ui>:
G::Error
error;
if
(!manager->add_ui_from_string(ui_info, -1, &error))
{
std::cout << "building menus and toolbar
failed: << " << error.message() << std::endl;
}
The add_ui_from_string() function returns false if an error occurs.
The G::Error object stores the error message which is later sent to the
standard output stream (or console). There is a complimentary function,
add_ui_from_file(), that loads a UI description from a disk file.
Next, retrieve a pointer to the menubar and toolbar created by
our instance of Gtk::UIManager and pack them into main_vbox:
Gtk::Widget
*menubar = manager->get_widget("/MenuBar");
main_vbox->pack_start(*menubar, false);
menubar->show();
Gtk::Toolbar *toolbar =
static_cast<Gtk::Toolbar*>(manager->get_widget("/ToolBar"));
toolbar->set_tooltips(true);
main_vbox->pack_start(*toolbar, false)
;
toolbar->show();
The second argument passed to pack_start() is the 'expand' property and
for the
menubar and toolbar it's set to false so they wont expand to fill
main_vbox. We need to leave space for the
statusbar and client_vbox that will be added later.
The last thing to do is show main_vbox so that it is visible:
main_vbox->show();
There is another function, show_all(), that can be called to show
a widget and its children all at once, but be careful. It will make
every child widget of every child widget visible also. If that is
acceptable then one call to show_all() can replace all those child
widget calls to show().
After adding definitions for the eleven action methods declared in the
header file, your <xfcapp.cc> source file should look like
this:
#include "xfcapp.hh"
#include "xfcapp.ui"
#include <xfc/gtk/accelgroup.hh>
#include <xfc/gtk/box.hh>
#include <xfc/gtk/menubar.hh>
#include <xfc/gtk/stock.hh>
#include <xfc/gtk/toolbar.hh>
#include <xfc/glib/error.hh>
#include <gconf/gconf-client.h>
#include <iostream>
XfcApp::XfcApp()
{
// Set the window title and default size
set_title("XfcApp");
set_default_size(400, 300);
// Create the action group and add the actions
action_group = new Gtk::ActionGroup("XfcAppActions");
add_actions();
// Create the user interface manager and insert the
action group
manager = new Gtk::UIManager;
manager->insert_action_group(*action_group);
// Associate the user interface manager's AccelGroup
with the window
add_accel_group(manager->get_accel_group());
// Create main vertical box and add to
window
Gtk::VBox *main_vbox = new Gtk::VBox;
add(*main_vbox);
// Load XML description of the menus and toolbar
from the definition string.
G::Error error;
if (!manager->add_ui_from_string(ui_info, -1,
&error))
{
std::cout << "building
menus and toolbar failed: << " << error.message() <<
std::endl;
}
// Get a pointer to the menubar and pack it into
main_vbox
Gtk::Widget *menubar =
manager->get_widget("/MenuBar");
main_vbox->pack_start(*menubar,
false);
menubar->show();
// Get a pointer to the toolbar and pack it into
main_vbox
Gtk::Toolbar *toolbar =
static_cast<Gtk::Toolbar*>(manager->get_widget("/ToolBar"));
toolbar->set_tooltips(true);
main_vbox->pack_start(*toolbar,
false) ;
toolbar->show();
// Use the GNOME value for 'toolbar_style' to place
the progress bar.
GConfClient *client = gconf_client_get_default();
String text = gconf_client_get_string(client,
"/desktop/gnome/interface/toolbar_style", 0);
Gtk::ToolbarStyle toolbar_style;
if (text.compare("text") == 0)
toolbar_style = Gtk::TOOLBAR_TEXT;
else if (text.compare("both") == 0)
toolbar_style = Gtk::TOOLBAR_BOTH;
else if (text.compare("both_horiz") == 0)
toolbar_style =
Gtk::TOOLBAR_BOTH_HORIZ;
else
toolbar_style =
Gtk::TOOLBAR_ICONS;
toolbar->set_style(toolbar_style);
// Show main_vbox so it's visible
main_vbox->show();
}
XfcApp::~XfcApp()
{
}
void
XfcApp::add_actions()
{
using namespace Gtk;
using namespace sigc;
action_group->add("FileMenu", "_File");
action_group->add("EditMenu", "_Edit");
action_group->add("HelpMenu", "_Help");
Action *action = action_group->add("New", "_New",
StockId::NEW, AccelKey("<control>N"), "Create a new
file");
action->signal_activate().connect(mem_fun(this,
&XfcApp::on_file_new));
action = action_group->add("Open", "_Open",
StockId::OPEN, AccelKey("<control>O"), "Open a
file");
action->signal_activate().connect(mem_fun(this,
&XfcApp::on_file_open));
action = action_group->add("Save", "_Save",
StockId::SAVE, AccelKey("<control>S"), "Save current
file");
action->signal_activate().connect(mem_fun(this,
&XfcApp::on_file_save));
action = action_group->add("SaveAs", "Save
_As...", StockId::SAVE, "Save to a file");
action->signal_activate().connect(mem_fun(this,
&XfcApp::on_file_save_as));
action = action_group->add("Quit", "_Quit",
StockId::QUIT, AccelKey("<control>Q"), "Quit");
action->signal_activate().connect(mem_fun(this,
&XfcApp::on_file_quit));
action = action_group->add("Cut", "C_ut",
StockId::CUT, AccelKey("<control>X"), "Cut the
selection");
action->signal_activate().connect(mem_fun(this,
&XfcApp::on_edit_cut));
action = action_group->add("Copy", "_Copy",
StockId::COPY, AccelKey("<control>C"), "Copy the selection");
action->signal_activate().connect(mem_fun(this,
&XfcApp::on_edit_copy));
action = action_group->add("Paste", "_Paste",
StockId::PASTE, AccelKey("<control>V"), "Paste the clipboard");
action->signal_activate().connect(mem_fun(this,
&XfcApp::on_edit_paste));
action = action_group->add("Clear", "C_lear",
StockId::CLEAR, "Clear the selection");
action->signal_activate().connect(mem_fun(this,
&XfcApp::on_edit_clear));
action = action_group->add("Preferences",
"Prefere_nces", StockId::PREFERENCES, "Configure the application");
action->signal_activate().connect(mem_fun(this,
&XfcApp::on_edit_preferences));
action = action_group->add("About", "_About",
AccelKey("<control>A"), "About");
action->signal_activate().connect(mem_fun(this,
&XfcApp::on_help_about));
}
void
XfcApp::on_file_new()
{
std::cout << "You activated action: New"
<< std::endl;
}
void
XfcApp::on_file_open()
{
std::cout << "You activated action: Open"
<< std::endl;
}
void
XfcApp::on_file_save()
{
std::cout << "You activated action: Save"
<< std::endl;
}
void
XfcApp::on_file_save_as()
{
std::cout << "You activated action: SaveAs"
<< std::endl;
}
void
XfcApp::on_file_quit()
{
dispose();
}
void
XfcApp::on_edit_cut()
{
std::cout << "You activated action: Cut"
<< std::endl;
}
void
XfcApp::on_edit_copy()
{
std::cout << "You activated action: Copy"
<< std::endl;
}
void
XfcApp::on_edit_paste()
{
std::cout << "You activated action: Paste"
<< std::endl;
}
void
XfcApp::on_edit_clear()
{
std::cout << "You activated action: Clear"
<< std::endl;
}
void
XfcApp::on_edit_preferences()
{
std::cout << "You activated action:
Preferences" << std::endl;
}
void
XfcApp::on_help_about()
{
std::cout << "You activated action: About"
<< std::endl;
}
int main (int argc, char *argv[])
{
using namespace Main;
init(&argc, &argv);
XfcApp window;
window.signal_destroy().connect(sigc::ptr_fun(&Xfc::Main::quit));
window.show();
run();
return 0;
}
Notice that on_file_quit() calls the window's inherited dispose()
method which results in the emission of a destroy signal. We connected
the window's destroy signal to Xfc::Main::quit() in
our main() function so calling dispose() will terminate the
application.
Compiling XfcApp
If you compiled and installed XFC yourself, you will find the source
code for this version of XfcApp in the
<tutorial/chapter04> source directory along with a Makefile. If
XFC came pre-installed, or you installed it from an RPM package, you
will
find the source code in the
</usr/share/doc/xfcui-X.X/tutorial/chapter04> subdirectory. In
this case you will have to create the Makefile yourself (replace X.X
with the
version number of the libXFCui library you have installed).
To create a Makefile for XfcApp, add the following lines to a
new
text file
and save it using the name "Makefile":
CC = g++
CFLAGS = -Wall -O2
xfcapp: xfcapp.cc xfcapp.hh xfcapp.ui
$(CC) xfcapp.cc -o xfcapp `pkg-config xfcui-X.X
--cflags --libs`
clean:
rm -f *.o xfcapp
If you cut and paste these lines make sure the whitespace before $(CC)
and rm is a tab character. When
you
compile and run this program you will see the following window appear:
In the next chapter we will write a
composite statusbar that looks and
behaves just like the GNOME appbar.
Copyright
© 2004-2005 The XFC
Development Team |
Top
|
XFC
4.4
|
|