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  */
104 void
106 {
107  gvjob_ = job;
108 }
109 
110 /** Set graph's FSM name.
111  * @param fsm_name name of FSM the graph belongs to
112  */
113 void
114 SkillGuiGraphViewport::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  */
125 void
126 SkillGuiGraphViewport::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  */
135 void
136 SkillGuiGraphViewport::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  */
145 void
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  */
157 void
158 SkillGuiGraphViewport::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  */
169 void
170 SkillGuiGraphViewport::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  */
181 void
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  */
192 void
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  */
201 bool
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  */
210 void
212 {
213  update_graph_ = update;
214 }
215 
216 /** Zoom in.
217  * Sets scale override and increases the scale by 0.1.
218  */
219 void
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  */
238 void
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  */
257 void
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  */
269 void
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  */
284 bool
286 {
287  return scale_override_;
288 }
289 
290 /** Get scaler.
291  * @return scaler controller
292  */
293 Papyrus::AffineController::pointer
295 {
296  return affine_;
297 }
298 
299 /** Render current graph. */
300 void
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. */
357 void
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  */
384 void
385 SkillGuiGraphViewport::on_expose(GdkEventExpose *event)
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.