Exemple de palette d'outils
Dans cet exemple, des objets ToolPalette et DrawingArea sont associés à une fenêtre. L'utilisateur est autorisé à faire un glisser-déposer depuis la palette d'outils sur la surface de tracé. La palette d'outils contient divers groupes d'éléments. Des boîtes combinées permettent à l'utilisateur de changer le style et l'orientation de la palette d'outils.
File: canvas.h (For use with gtkmm 3, not gtkmm 2)
#ifndef GTKMM_EXAMPLE_CANVAS_H
#define GTKMM_EXAMPLE_CANVAS_H
#include <gtkmm.h>
// This little canvas class is only here
// because gtkmm does not have a canvas class yet.
// Applications should probably use GooCanvas::Canvas (goocanvasmm) instead.
class Canvas : public Gtk::DrawingArea
{
public:
Canvas();
virtual ~Canvas();
private:
class CanvasItem
{
public:
CanvasItem(Gtk::Widget* canvas, Gtk::ToolButton* button, double x, double y)
{
const Gtk::StockID stock_id(button->get_stock_id());
this->pixbuf = canvas->render_icon_pixbuf(stock_id, Gtk::ICON_SIZE_DIALOG);
this->x = x;
this->y = y;
}
Glib::RefPtr<Gdk::Pixbuf> pixbuf;
double x, y;
};
void item_draw(const CanvasItem *item,
const Cairo::RefPtr<Cairo::Context>& cr,
bool preview);
virtual bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr);
virtual void on_drag_data_received(const Glib::RefPtr<Gdk::DragContext>& context,
int x, int y, const Gtk::SelectionData& selection_data, guint info, guint time);
virtual bool on_drag_motion(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, guint time);
virtual bool on_drag_drop(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, guint time);
virtual void on_drag_leave(const Glib::RefPtr<Gdk::DragContext>& context, guint time);
bool m_drag_data_requested_for_drop; //So we know what to do in on_drag_data_received().
CanvasItem* m_drop_item;
typedef std::vector<CanvasItem*> type_vec_items;
type_vec_items m_canvas_items;
};
#endif //GTKMM_EXAMPLE_CANVAS_H
File: examplewindow.h (For use with gtkmm 3, not gtkmm 2)
#ifndef GTKMM_EXAMPLEWINDOW_H
#define GTKMM_EXAMPLEWINDOW_H
#include <gtkmm.h>
#include "canvas.h"
class ExampleWindow : public Gtk::Window
{
public:
ExampleWindow();
virtual ~ExampleWindow();
private:
void load_stock_items();
void load_toggle_items();
void load_special_items();
//Signal handlers:
void on_combo_orientation_changed();
void on_combo_style_changed();
//Tree model columns:
class ModelColumnsOrientation : public Gtk::TreeModel::ColumnRecord
{
public:
ModelColumnsOrientation()
{ add(m_col_value); add(m_col_name); }
Gtk::TreeModelColumn<Gtk::Orientation> m_col_value;
Gtk::TreeModelColumn<Glib::ustring> m_col_name;
};
ModelColumnsOrientation m_ColumnsOrientation;
//Tree model columns:
class ModelColumnsStyle : public Gtk::TreeModel::ColumnRecord
{
public:
ModelColumnsStyle()
{ add(m_col_value); add(m_col_name); }
Gtk::TreeModelColumn<int> m_col_value; //We use int to also allow -1
Gtk::TreeModelColumn<Glib::ustring> m_col_name;
};
ModelColumnsStyle m_ColumnsStyle;
//Child widgets:
Gtk::Box m_VBox;
Gtk::Box m_HBox;
Gtk::ComboBox m_ComboOrientation;
Glib::RefPtr<Gtk::ListStore> m_refTreeModelOrientation;
Gtk::ComboBox m_ComboStyle;
Glib::RefPtr<Gtk::ListStore> m_refTreeModelStyle;
Gtk::ToolPalette m_ToolPalette;
Gtk::ScrolledWindow m_ScrolledWindowPalette;
Gtk::ScrolledWindow m_ScrolledWindowCanvas;
Canvas m_Canvas;
};
#endif //GTKMM_EXAMPLEWINDOW_H
File: examplewindow.cc (For use with gtkmm 3, not gtkmm 2)
#include "examplewindow.h"
static bool sort_predicate(const Gtk::StockID& a, const Gtk::StockID& b)
{
return a.get_string() < b.get_string();
}
void ExampleWindow::load_stock_items()
{
Gtk::ToolItemGroup* group_af =
Gtk::manage(new Gtk::ToolItemGroup("Stock Icons (A-F)"));
m_ToolPalette.add(*group_af);
Gtk::ToolItemGroup* group_gn =
Gtk::manage(new Gtk::ToolItemGroup("Stock Icons (G-N)"));
m_ToolPalette.add(*group_gn);
Gtk::ToolItemGroup* group_or =
Gtk::manage(new Gtk::ToolItemGroup("Stock Icons (O-R)"));
m_ToolPalette.add(*group_or);
Gtk::ToolItemGroup* group_sz =
Gtk::manage(new Gtk::ToolItemGroup("Stock Icons (S-Z)"));
m_ToolPalette.add(*group_sz);
// Obtain the IDs of all stock items:
typedef std::vector<Gtk::StockID> type_vecIDs;
type_vecIDs vecIDs = Gtk::Stock::get_ids();
std::sort(vecIDs.begin(), vecIDs.end(), &sort_predicate);
Gtk::ToolItemGroup* group = 0;
// Iterate through them, populating the ListStore as appropriate:
for(type_vecIDs::const_iterator iterIDs = vecIDs.begin(); iterIDs != vecIDs.end(); ++iterIDs)
{
const Gtk::StockID& stockid = *iterIDs;
const Glib::ustring str = stockid.get_string();
if(str.size() < 4)
continue;
switch(str[4])
{
case 'a':
group = group_af;
break;
case 'g':
group = group_gn;
break;
case 'o':
group = group_or;
break;
case 's':
group = group_sz;
break;
default:
//Use the previous group
//(They are sorted.)
break;
}
if(!group)
continue;
Gtk::ToolButton* button = Gtk::manage(new Gtk::ToolButton(stockid));
button->set_tooltip_text(str);
button->set_is_important();
group->insert(*button);
Gtk::StockItem stockitem;
if(!Gtk::StockItem::lookup(stockid, stockitem) ||
stockitem.get_label().empty())
button->set_label(str);
}
}
void ExampleWindow::load_toggle_items()
{
Gtk::ToolItemGroup* group =
Gtk::manage(new Gtk::ToolItemGroup("Radio Item"));
m_ToolPalette.add(*group);
Gtk::RadioToolButton::Group radio_group;
for(int i = 1; i <= 10; ++i)
{
const Glib::ustring label = Glib::ustring::compose("#%1", i);
Gtk::RadioToolButton* button = Gtk::manage(new Gtk::RadioToolButton());
button->set_group(radio_group);
button->set_label(label);
group->insert(*button);
}
}
static Gtk::ToolItem* create_entry_item(const Glib::ustring& text)
{
Gtk::Entry* entry = Gtk::manage(new Gtk::Entry());
entry->set_text(text);
entry->set_width_chars(5);
Gtk::ToolItem* item = Gtk::manage(new Gtk::ToolItem());
item->add(*entry);
return item;
}
void ExampleWindow::load_special_items()
{
Gtk::ToolItemGroup* group = Gtk::manage(new Gtk::ToolItemGroup());
Gtk::Button *label_button = Gtk::manage(new Gtk::Button("Advanced Features"));
label_button->show();
group->set_label_widget(*label_button);
m_ToolPalette.add(*group);
Gtk::ToolItem* item = create_entry_item ("homogeneous=false");
group->insert(*item);
//TODO: Add Gtk::Container::set_child_property().
gtk_container_child_set (GTK_CONTAINER (group->gobj()), GTK_WIDGET (item->gobj()),
"homogeneous", FALSE, NULL);
item = create_entry_item ("homogeneous=FALSE, expand=TRUE");
group->insert(*item);
gtk_container_child_set (GTK_CONTAINER (group->gobj()), GTK_WIDGET (item->gobj()),
"homogeneous", FALSE, "expand", TRUE,
NULL);
item = create_entry_item ("homogeneous=FALSE, expand=TRUE, fill=FALSE");
group->insert(*item);
gtk_container_child_set (GTK_CONTAINER (group->gobj()), GTK_WIDGET (item->gobj()),
"homogeneous", FALSE, "expand", TRUE,
"fill", FALSE, NULL);
item = create_entry_item ("homogeneous=FALSE, expand=TRUE, new-row=TRUE");
group->insert(*item);
gtk_container_child_set (GTK_CONTAINER (group->gobj()), GTK_WIDGET (item->gobj()),
"homogeneous", FALSE, "expand", TRUE,
"new-row", TRUE, NULL);
item = Gtk::manage(new Gtk::ToolButton(Gtk::Stock::GO_UP));
item->set_tooltip_text("Show on vertical palettes only");
group->insert(*item);
item->set_visible_horizontal(false);
item = Gtk::manage(new Gtk::ToolButton(Gtk::Stock::GO_FORWARD));
item->set_tooltip_text("Show on horizontal palettes only");
group->insert(*item);
item->set_visible_vertical(false);
item = Gtk::manage(new Gtk::ToolButton(Gtk::Stock::FULLSCREEN));
item->set_tooltip_text("Expanded this item");
group->insert(*item);
gtk_container_child_set (GTK_CONTAINER (group->gobj()), GTK_WIDGET (item->gobj()),
"homogeneous", FALSE,
"expand", TRUE,
NULL);
item = Gtk::manage(new Gtk::ToolButton(Gtk::Stock::HELP));
item->set_tooltip_text("A regular item");
group->insert(*item);
}
ExampleWindow::ExampleWindow()
: m_VBox(Gtk::ORIENTATION_VERTICAL, 6),
m_HBox(Gtk::ORIENTATION_HORIZONTAL, 6)
{
set_title("Gtk::ToolPalette example");
set_size_request(600, 600);
set_border_width(6);
add(m_VBox);
//The Orientation ComboBox:
m_refTreeModelOrientation = Gtk::ListStore::create(m_ColumnsOrientation);
Gtk::TreeModel::Row row = *(m_refTreeModelOrientation->append());
row[m_ColumnsOrientation.m_col_value] = Gtk::ORIENTATION_HORIZONTAL;
row[m_ColumnsOrientation.m_col_name] = "Horizontal";\
row = *(m_refTreeModelOrientation->append());
row[m_ColumnsOrientation.m_col_value] = Gtk::ORIENTATION_VERTICAL;
row[m_ColumnsOrientation.m_col_name] = "Vertical";
m_ComboOrientation.set_model(m_refTreeModelOrientation);
m_VBox.pack_start(m_ComboOrientation, Gtk::PACK_SHRINK);
m_ComboOrientation.pack_start(m_ColumnsOrientation.m_col_name);
m_ComboOrientation.signal_changed().connect(
sigc::mem_fun(*this, &ExampleWindow::on_combo_orientation_changed) );
m_ComboOrientation.set_active(row);
//The Style ComboBox:
m_refTreeModelStyle = Gtk::ListStore::create(m_ColumnsStyle);
row = *(m_refTreeModelStyle->append());
row[m_ColumnsStyle.m_col_value] = Gtk::TOOLBAR_TEXT;
row[m_ColumnsStyle.m_col_name] = "Text";\
row = *(m_refTreeModelStyle->append());
row[m_ColumnsStyle.m_col_value] = Gtk::TOOLBAR_BOTH;
row[m_ColumnsStyle.m_col_name] = "Both";
row = *(m_refTreeModelStyle->append());
row[m_ColumnsStyle.m_col_value] = Gtk::TOOLBAR_BOTH_HORIZ;
row[m_ColumnsStyle.m_col_name] = "Both: Horizontal";
row = *(m_refTreeModelStyle->append());
row[m_ColumnsStyle.m_col_value] = Gtk::TOOLBAR_ICONS;
row[m_ColumnsStyle.m_col_name] = "Icons";
row = *(m_refTreeModelStyle->append());
row[m_ColumnsStyle.m_col_value] = -1; // A custom meaning for this demo.
row[m_ColumnsStyle.m_col_name] = "Default";
m_ComboStyle.set_model(m_refTreeModelStyle);
m_VBox.pack_start(m_ComboStyle, Gtk::PACK_SHRINK);
m_ComboStyle.pack_start(m_ColumnsStyle.m_col_name);
m_ComboStyle.signal_changed().connect(
sigc::mem_fun(*this, &ExampleWindow::on_combo_style_changed) );
m_ComboStyle.set_active(row);
//Add and fill the ToolPalette:
load_stock_items();
load_toggle_items();
load_special_items();
m_VBox.pack_start(m_HBox, Gtk::PACK_EXPAND_WIDGET);
m_ScrolledWindowPalette.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
m_ScrolledWindowPalette.set_border_width(6);
m_ScrolledWindowPalette.add(m_ToolPalette);
m_HBox.pack_start(m_ScrolledWindowPalette);
on_combo_orientation_changed();
m_ScrolledWindowCanvas.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_ALWAYS);
m_ScrolledWindowCanvas.set_border_width(6);
m_ScrolledWindowCanvas.add(m_Canvas);
m_ScrolledWindowCanvas.set_size_request(200, -1);
m_HBox.pack_start(m_ScrolledWindowCanvas);
m_ToolPalette.add_drag_dest(m_Canvas,
Gtk::DEST_DEFAULT_HIGHLIGHT, Gtk::TOOL_PALETTE_DRAG_ITEMS, Gdk::ACTION_COPY);
show_all_children();
}
ExampleWindow::~ExampleWindow()
{
}
void ExampleWindow::on_combo_orientation_changed()
{
Gtk::TreeModel::iterator iter = m_ComboOrientation.get_active();
if(!iter)
return;
Gtk::TreeModel::Row row = *iter;
const Gtk::Orientation value = row[m_ColumnsOrientation.m_col_value];
m_ToolPalette.set_orientation(value);
if(value == Gtk::ORIENTATION_HORIZONTAL)
m_ScrolledWindowPalette.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_NEVER);
else
m_ScrolledWindowPalette.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
}
void ExampleWindow::on_combo_style_changed()
{
Gtk::TreeModel::iterator iter = m_ComboStyle.get_active();
if(!iter)
return;
Gtk::TreeModel::Row row = *iter;
const int value = row[m_ColumnsStyle.m_col_value];
if(value == -1)
m_ToolPalette.unset_style();
else
m_ToolPalette.set_style((Gtk::ToolbarStyle)value);
}
File: canvas.cc (For use with gtkmm 3, not gtkmm 2)
#include "canvas.h"
#include <iostream>
Canvas::Canvas()
: m_drag_data_requested_for_drop(false),
m_drop_item()
{
set_app_paintable();
}
Canvas::~Canvas()
{
while(!m_canvas_items.empty())
{
type_vec_items::iterator iter = m_canvas_items.begin();
CanvasItem* item = *iter;
delete item;
m_canvas_items.erase(iter);
}
if(m_drop_item)
delete m_drop_item;
}
void Canvas::item_draw(const CanvasItem *item,
const Cairo::RefPtr<Cairo::Context>& cr,
bool preview)
{
if(!item || !item->pixbuf)
return;
const double cx = item->pixbuf->get_width();
const double cy = item->pixbuf->get_height();
Gdk::Cairo::set_source_pixbuf(cr,
item->pixbuf,
item->x - cx * 0.5, item->y - cy * 0.5);
if(preview)
cr->paint_with_alpha(0.6);
else
cr->paint();
}
bool Canvas::on_draw(const Cairo::RefPtr<Cairo::Context>& cr)
{
cr->set_source_rgb(1.0, 1.0, 1.0);
const Gtk::Allocation allocation = get_allocation();
cr->rectangle(0, 0, allocation.get_width(), allocation.get_height());
cr->fill();
for(type_vec_items::iterator iter = m_canvas_items.begin();
iter != m_canvas_items.end(); ++iter )
{
item_draw(*iter, cr, false);
}
if(m_drop_item)
item_draw (m_drop_item, cr, true);
return true;
}
bool Canvas::on_drag_motion(const Glib::RefPtr<Gdk::DragContext>& context,
int x, int y, guint time)
{
m_drag_data_requested_for_drop = false; //It's for drag-motion instead.
if(m_drop_item)
{
// We already have a drop indicator so just update its position.
m_drop_item->x = x;
m_drop_item->y = y;
queue_draw();
context->drag_status(Gdk::ACTION_COPY, time);
}
else
{
// Request DnD data for creating a drop indicator.
// This will cause on_drag_data_received() to be called.
const Glib::ustring target = drag_dest_find_target(context);
if (target.empty())
return false;
drag_get_data(context, target, time);
}
Gtk::DrawingArea::on_drag_motion(context, x, y, time);
return true;
}
void Canvas::on_drag_data_received(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, const Gtk::SelectionData& selection_data, guint info, guint time)
{
// Find the tool button which is the source of this DnD operation.
Gtk::Widget* widget = drag_get_source_widget(context);
Gtk::ToolPalette* drag_palette = dynamic_cast<Gtk::ToolPalette*>(widget);
while(widget && !drag_palette)
{
widget = widget->get_parent();
drag_palette = dynamic_cast<Gtk::ToolPalette*>(widget);
}
Gtk::ToolItem* drag_item = 0;
if(drag_palette)
drag_item = drag_palette->get_drag_item(selection_data);
// Create a drop indicator when a tool button was found:
Gtk::ToolButton* button = dynamic_cast<Gtk::ToolButton*>(drag_item);
if(!button)
return;
if(m_drop_item)
{
delete m_drop_item;
m_drop_item = 0;
}
CanvasItem* item = new CanvasItem(this, button, x, y);
if(m_drag_data_requested_for_drop)
{
m_canvas_items.push_back(item);
// Signal that the item was accepted and then redraw.
context->drag_finish(true /* success */, false /* del */, time);
}
else
{
m_drop_item = item;
// We are getting this data due to a request in drag_motion,
// rather than due to a request in drag_drop, so we are just
// supposed to call gdk_drag_status (), not actually paste in
// the data.
context->drag_status(Gdk::ACTION_COPY, time);
}
queue_draw();
Gtk::DrawingArea::on_drag_data_received(context, x, y, selection_data, info, time);
}
bool Canvas::on_drag_drop(const Glib::RefPtr<Gdk::DragContext>& context, int /* x */, int /* y */, guint time)
{
// Request DnD data for creating a dopped item.
// This will cause on_drag_data_received() to be called.
const Glib::ustring target = drag_dest_find_target(context);
if (target.empty())
return false;
m_drag_data_requested_for_drop = true;
drag_get_data(context, target, time);
return true;
}
void Canvas::on_drag_leave(const Glib::RefPtr<Gdk::DragContext>& context, guint time)
{
//This signal is emitted to clean up the item used for drag-motion,
//either when the cursor moves out of the widget or when we drop.
if(!m_drop_item)
return;
delete m_drop_item;
m_drop_item = 0;
queue_draw();
Gtk::DrawingArea::on_drag_leave(context, time);
}
File: main.cc (For use with gtkmm 3, not gtkmm 2)
#include "examplewindow.h"
#include <gtkmm/application.h>
int main(int argc, char *argv[])
{
Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example");
ExampleWindow window;
//Shows the window and returns when it is closed.
return app->run(window);
}
