Fawkes API Fawkes Development Version
graph_viewport.cpp
1
2/***************************************************************************
3 * graph_viewport.cpp - FSM Graph Viewport for Skill GUI
4 *
5 * Created: Mon Dec 15 15:40:36 2008
6 * Copyright 2008-2009 Tim Niemueller [www.niemueller.de]
7 ****************************************************************************/
8
9/* This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Library General Public License for more details.
18 *
19 * Read the full text in the LICENSE.GPL file in the doc directory.
20 */
21
22#include "graph_viewport.h"
23
24#include "gvplugin_skillgui_papyrus.h"
25
26#include <gtk/gtk.h>
27
28/** @class SkillGuiGraphViewport "graph_viewport.h"
29 * Skill FSM Graph Viewport.
30 * @author Tim Niemueller
31 */
32
33/** Constructor. */
35{
36 Cairo::RefPtr<Cairo::SolidPattern> bp = Cairo::SolidPattern::create_rgb(1, 1, 1);
37 Papyrus::Paint::pointer pp = Papyrus::Paint::create(bp);
38
39 Papyrus::Canvas::pointer c = canvas();
40 c->set_scroll_anchor(Papyrus::SCROLL_ANCHOR_TOP_LEFT);
41 c->set_background(pp);
42
43 affine_ = Papyrus::AffineController::create();
44 affine_->insert(c);
45 translator_ = Papyrus::Translator::create();
46 add_controller(translator_);
47
48 gvc_ = gvContext();
49 gvjob_ = NULL;
50
51 graph_fsm_ = "";
52 graph_ = "";
53
54 bbw_ = bbh_ = pad_x_ = pad_y_ = 0.0;
55 translation_x_ = translation_y_ = 0.0;
56 scale_ = 1.0;
57 update_graph_ = true;
58
59 Gtk::Window *w = dynamic_cast<Gtk::Window *>(get_toplevel());
60 if (w) {
61 fcd_ = new Gtk::FileChooserDialog(*w, "Save Graph", Gtk::FILE_CHOOSER_ACTION_SAVE);
62 fcd_->set_transient_for(*w);
63 } else {
64 fcd_ = new Gtk::FileChooserDialog("Save Graph", Gtk::FILE_CHOOSER_ACTION_SAVE);
65 }
66
67 //Add response buttons the the dialog:
68 fcd_->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
69 fcd_->add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
70
71 Gtk::FileFilter *filter_pdf = Gtk::manage(new Gtk::FileFilter());
72 filter_pdf->set_name("Portable Document Format (PDF)");
73 filter_pdf->add_pattern("*.pdf");
74 Gtk::FileFilter *filter_svg = Gtk::manage(new Gtk::FileFilter());
75 ;
76 filter_svg->set_name("Scalable Vector Graphic (SVG)");
77 filter_svg->add_pattern("*.svg");
78 Gtk::FileFilter *filter_png = Gtk::manage(new Gtk::FileFilter());
79 ;
80 filter_png->set_name("Portable Network Graphic (PNG)");
81 filter_png->add_pattern("*.png");
82 fcd_->add_filter(*filter_pdf);
83 fcd_->add_filter(*filter_svg);
84 fcd_->add_filter(*filter_png);
85 fcd_->set_filter(*filter_pdf);
86
87 gvplugin_skillgui_setup(gvc_, this);
88
89 signal_size_allocate().connect_notify(
90 sigc::hide(sigc::mem_fun(*this, &SkillGuiGraphViewport::render)));
91 signal_expose_event().connect_notify(sigc::mem_fun(*this, &SkillGuiGraphViewport::on_expose));
92}
93
94/** Destructor. */
96{
97 gvFreeContext(gvc_);
98 delete fcd_;
99}
100
101/** Set current Graphviz job.
102 * @param job current Graphviz job
103 */
104void
106{
107 gvjob_ = job;
108}
109
110/** Set graph's FSM name.
111 * @param fsm_name name of FSM the graph belongs to
112 */
113void
114SkillGuiGraphViewport::set_graph_fsm(const std::string &fsm_name)
115{
116 if (graph_fsm_ != fsm_name) {
117 translator_->set_translate(0, 0);
118 }
119 graph_fsm_ = fsm_name;
120}
121
122/** Set graph.
123 * @param graph string representation of the current graph in the dot language.
124 */
125void
126SkillGuiGraphViewport::set_graph(const std::string &graph)
127{
128 graph_ = graph;
129}
130
131/** Add a drawable.
132 * To be called only by the Graphviz plugin.
133 * @param d drawable to add
134 */
135void
136SkillGuiGraphViewport::add_drawable(Papyrus::Drawable::pointer d)
137{
138 canvas()->add(d);
139 translator_->insert(d);
140}
141
142/** Clear all drawables.
143 * To be called only by the Graphviz plugin.
144 */
145void
147{
148 Papyrus::Gtk::Viewport::clear();
149 translator_->clear();
150}
151
152/** Set bounding box.
153 * To be called only by the Graphviz plugin.
154 * @param bbw bounding box width
155 * @param bbh bounding box height
156 */
157void
158SkillGuiGraphViewport::set_bb(double bbw, double bbh)
159{
160 bbw_ = bbw;
161 bbh_ = bbh;
162}
163
164/** Set padding.
165 * To be called only by the Graphviz plugin.
166 * @param pad_x padding in x
167 * @param pad_y padding in y
168 */
169void
170SkillGuiGraphViewport::set_pad(double pad_x, double pad_y)
171{
172 pad_x_ = pad_x;
173 pad_y_ = pad_y;
174}
175
176/** Set translation.
177 * To be called only by the Graphviz plugin.
178 * @param tx translation in x
179 * @param ty translation in y
180 */
181void
183{
184 translation_x_ = tx;
185 translation_y_ = ty;
186}
187
188/** Set scale.
189 * To be called only by the Graphviz plugin.
190 * @param scale scale value
191 */
192void
194{
195 scale_ = scale;
196}
197
198/** Check if graph is being updated.
199 * @return true if the graph will be update if new data is received, false otherwise
200 */
201bool
203{
204 return update_graph_;
205}
206
207/** Set if the graph should be updated on new data.
208 * @param update true to update on new data, false to disable update
209 */
210void
212{
213 update_graph_ = update;
214}
215
216/** Zoom in.
217 * Sets scale override and increases the scale by 0.1.
218 */
219void
221{
222 double sx, sy;
223 Gtk::Allocation alloc = get_allocation();
224
225 affine_->get_scale(sx, sy);
226 sx += 0.1;
227 sy += 0.1;
228 affine_->set_scale(sx, sy);
229 affine_->set_translate((alloc.get_width() - bbw_ * sx) / 2.0,
230 (alloc.get_height() - bbh_ * sy) / 2.0);
231
232 scale_override_ = true;
233}
234
235/** Zoom out.
236 * Sets scale override and decreases the scale by 0.1.
237 */
238void
240{
241 double sx, sy;
242 affine_->get_scale(sx, sy);
243 if ((sx > 0.1) && (sy > 0.1)) {
244 Gtk::Allocation alloc = get_allocation();
245 sx -= 0.1;
246 sy -= 0.1;
247 affine_->set_scale(sx, sy);
248 affine_->set_translate((alloc.get_width() - bbw_ * sx) / 2.0,
249 (alloc.get_height() - bbh_ * sy) / 2.0);
250 }
251 scale_override_ = true;
252}
253
254/** Zoom to fit.
255 * Disables scale override and draws with values suggested by Graphviz plugin.
256 */
257void
259{
260 affine_->set_scale(scale_);
261 affine_->set_translate(pad_x_ + translation_x_, pad_y_ + translation_y_);
262 translator_->set_translate(0, 0);
263 scale_override_ = false;
264}
265
266/** Zoom reset.
267 * Reset zoom to 1. Enables scale override.
268 */
269void
271{
272 affine_->set_scale(1.0);
273 if (scale_ == 1.0) {
274 affine_->set_translate(pad_x_ + translation_x_, pad_y_ + translation_y_);
275 } else {
276 affine_->set_translate(pad_x_, pad_y_);
277 }
278 scale_override_ = true;
279}
280
281/** Check if scale override is enabled.
282 * @return true if scale override is enabled, false otherwise
283 */
284bool
286{
287 return scale_override_;
288}
289
290/** Get scaler.
291 * @return scaler controller
292 */
293Papyrus::AffineController::pointer
295{
296 return affine_;
297}
298
299/** Render current graph. */
300void
302{
303 Gtk::Window *w = dynamic_cast<Gtk::Window *>(get_toplevel());
304
305 int result = fcd_->run();
306 if (result == Gtk::RESPONSE_OK) {
307 double old_scale_x, old_scale_y, old_translate_x, old_translate_y;
308 affine_->get_scale(old_scale_x, old_scale_y);
309 affine_->get_translate(old_translate_x, old_translate_y);
310 affine_->set_scale(1);
311 affine_->set_translate(pad_x_, pad_y_);
312
313 Papyrus::Canvas::pointer c = canvas();
314
315 Cairo::RefPtr<Cairo::Surface> surface;
316
317 std::string filename = fcd_->get_filename();
318 if (!filename.empty()) {
319 bool write_to_png = false;
320 Gtk::FileFilter *f = fcd_->get_filter();
321 if (f->get_name().find("PDF") != Glib::ustring::npos) {
322 surface = Cairo::PdfSurface::create(filename, bbw_, bbh_);
323 } else if (f->get_name().find("SVG") != Glib::ustring::npos) {
324 surface = Cairo::SvgSurface::create(filename, bbw_, bbh_);
325 } else if (f->get_name().find("PNG") != Glib::ustring::npos) {
326 surface = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, bbw_, bbh_);
327 write_to_png = true;
328 }
329
330 if (surface) {
331 Cairo::RefPtr<Cairo::Context> context = Cairo::Context::create(surface);
332 c->render(context);
333 if (write_to_png) {
334 surface->write_to_png(filename);
335 }
336 }
337
338 } else {
339 Gtk::MessageDialog md(*w,
340 "Invalid filename",
341 /* markup */ false,
342 Gtk::MESSAGE_ERROR,
343 Gtk::BUTTONS_OK,
344 /* modal */ true);
345 md.set_title("Invalid File Name");
346 md.run();
347 }
348
349 affine_->set_scale(old_scale_x, old_scale_y);
350 affine_->set_translate(old_translate_x, old_translate_y);
351 }
352
353 fcd_->hide();
354}
355
356/** Render current graph. */
357void
359{
360 if (!update_graph_)
361 return;
362
363 Papyrus::Canvas::pointer c = canvas();
364#ifdef HAVE_TIMS_PAPYRUS_PATCHES
365 c->set_redraw_enabled(false);
366#endif
367 Agraph_t *g = agmemread((char *)graph_.c_str());
368 if (g) {
369 gvLayout(gvc_, g, (char *)"dot");
370 gvRender(gvc_, g, (char *)"skillgui", NULL);
371 gvFreeLayout(gvc_, g);
372 agclose(g);
373 } else {
374 clear();
375 }
376#ifdef HAVE_TIMS_PAPYRUS_PATCHES
377 c->set_redraw_enabled(true);
378#endif
379}
380
381/** Called on explose.
382 * @param event Gdk event structure
383 */
384void
386{
387 if (scale_override_) {
388 Gtk::Allocation alloc = get_allocation();
389
390 double sx, sy;
391 affine_->get_scale(sx, sy);
392 affine_->set_translate(((alloc.get_width() - bbw_ * sx) / 2.0) + pad_x_,
393 ((alloc.get_height() - bbh_ * sy) / 2.0) + pad_y_);
394 }
395}
void set_graph(const std::string &graph)
Set graph.
~SkillGuiGraphViewport()
Destructor.
void set_gvjob(GVJ_t *job)
Set current Graphviz job.
bool scale_override()
Check if scale override is enabled.
void render()
Render current graph.
void set_update_graph(bool update)
Set if the graph should be updated on new data.
void zoom_fit()
Zoom to fit.
Papyrus::AffineController::pointer get_affine()
Get scaler.
void save()
Render current graph.
void set_graph_fsm(const std::string &fsm_name)
Set graph's FSM name.
void set_pad(double pad_x, double pad_y)
Set padding.
virtual void clear()
Clear all drawables.
void set_bb(double bbw, double bbh)
Set bounding box.
SkillGuiGraphViewport()
Constructor.
void zoom_out()
Zoom out.
void on_expose(GdkEventExpose *event)
Called on explose.
bool get_update_graph()
Check if graph is being updated.
void zoom_reset()
Zoom reset.
void add_drawable(Papyrus::Drawable::pointer d)
Add a drawable.
void set_scale(double scale)
Set scale.
void set_translation(double tx, double ty)
Set translation.