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 
10 /* This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Library General Public License for more details.
19  *
20  * Read the full text in the LICENSE.GPL file in the doc directory.
21  */
22 
23 #include "graph_viewport.h"
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",
62  Gtk::FILE_CHOOSER_ACTION_SAVE);
63  __fcd->set_transient_for(*w);
64  } else {
65  __fcd = new Gtk::FileChooserDialog("Save Graph",
66  Gtk::FILE_CHOOSER_ACTION_SAVE);
67  }
68 
69  //Add response buttons the the dialog:
70  __fcd->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
71  __fcd->add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
72 
73  Gtk::FileFilter *filter_pdf = Gtk::manage(new Gtk::FileFilter());
74  filter_pdf->set_name("Portable Document Format (PDF)");
75  filter_pdf->add_pattern("*.pdf");
76  Gtk::FileFilter *filter_svg = Gtk::manage(new Gtk::FileFilter());;
77  filter_svg->set_name("Scalable Vector Graphic (SVG)");
78  filter_svg->add_pattern("*.svg");
79  Gtk::FileFilter *filter_png = Gtk::manage(new Gtk::FileFilter());;
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(sigc::hide(sigc::mem_fun(*this, &SkillGuiGraphViewport::render)));
90  signal_expose_event().connect_notify(sigc::mem_fun(*this, &SkillGuiGraphViewport::on_expose));
91 }
92 
93 
94 /** Destructor. */
96 {
97  gvFreeContext(__gvc);
98  delete __fcd;
99 }
100 
101 
102 /** Set current Graphviz job.
103  * @param job current Graphviz job
104  */
105 void
107 {
108  __gvjob = job;
109 }
110 
111 
112 /** Set graph's FSM name.
113  * @param fsm_name name of FSM the graph belongs to
114  */
115 void
117 {
118  if ( __graph_fsm != fsm_name ) {
119  __translator->set_translate(0, 0);
120  }
121  __graph_fsm = fsm_name;
122 }
123 
124 
125 /** Set graph.
126  * @param graph string representation of the current graph in the dot language.
127  */
128 void
130 {
131  __graph = graph;
132 }
133 
134 
135 /** Add a drawable.
136  * To be called only by the Graphviz plugin.
137  * @param d drawable to add
138  */
139 void
140 SkillGuiGraphViewport::add_drawable(Papyrus::Drawable::pointer d)
141 {
142  canvas()->add(d);
143  __translator->insert(d);
144 }
145 
146 
147 /** Clear all drawables.
148  * To be called only by the Graphviz plugin.
149  */
150 void
152 {
153  Papyrus::Gtk::Viewport::clear();
154  __translator->clear();
155 }
156 
157 
158 /** Set bounding box.
159  * To be called only by the Graphviz plugin.
160  * @param bbw bounding box width
161  * @param bbh bounding box height
162  */
163 void
164 SkillGuiGraphViewport::set_bb(double bbw, double bbh)
165 {
166  __bbw = bbw;
167  __bbh = bbh;
168 }
169 
170 
171 /** Set padding.
172  * To be called only by the Graphviz plugin.
173  * @param pad_x padding in x
174  * @param pad_y padding in y
175  */
176 void
177 SkillGuiGraphViewport::set_pad(double pad_x, double pad_y)
178 {
179  __pad_x = pad_x;
180  __pad_y = pad_y;
181 }
182 
183 
184 /** Set translation.
185  * To be called only by the Graphviz plugin.
186  * @param tx translation in x
187  * @param ty translation in y
188  */
189 void
191 {
192  __translation_x = tx;
193  __translation_y = ty;
194 }
195 
196 
197 /** Set scale.
198  * To be called only by the Graphviz plugin.
199  * @param scale scale value
200  */
201 void
203 {
204  __scale = scale;
205 }
206 
207 /** Check if graph is being updated.
208  * @return true if the graph will be update if new data is received, false otherwise
209  */
210 bool
212 {
213  return __update_graph;
214 }
215 
216 
217 /** Set if the graph should be updated on new data.
218  * @param update true to update on new data, false to disable update
219  */
220 void
222 {
223  __update_graph = update;
224 }
225 
226 
227 /** Zoom in.
228  * Sets scale override and increases the scale by 0.1.
229  */
230 void
232 {
233  double sx, sy;
234  Gtk::Allocation alloc = get_allocation();
235 
236  __affine->get_scale(sx, sy);
237  sx += 0.1; sy += 0.1;
238  __affine->set_scale(sx, sy);
239  __affine->set_translate((alloc.get_width() - __bbw * sx) / 2.0,
240  (alloc.get_height() - __bbh * sy) / 2.0);
241 
242  __scale_override = true;
243 }
244 
245 
246 /** Zoom out.
247  * Sets scale override and decreases the scale by 0.1.
248  */
249 void
251 {
252  double sx, sy;
253  __affine->get_scale(sx, sy);
254  if ( (sx > 0.1) && (sy > 0.1) ) {
255  Gtk::Allocation alloc = get_allocation();
256  sx -= 0.1; sy -= 0.1;
257  __affine->set_scale(sx, sy);
258  __affine->set_translate((alloc.get_width() - __bbw * sx) / 2.0,
259  (alloc.get_height() - __bbh * sy) / 2.0);
260  }
261  __scale_override = true;
262 }
263 
264 
265 /** Zoom to fit.
266  * Disables scale override and draws with values suggested by Graphviz plugin.
267  */
268 void
270 {
271  __affine->set_scale(__scale);
272  __affine->set_translate(__pad_x + __translation_x, __pad_y + __translation_y);
273  __translator->set_translate(0, 0);
274  __scale_override = false;
275 }
276 
277 
278 /** Zoom reset.
279  * Reset zoom to 1. Enables scale override.
280  */
281 void
283 {
284  __affine->set_scale(1.0);
285  if ( __scale == 1.0 ) {
286  __affine->set_translate(__pad_x + __translation_x, __pad_y + __translation_y);
287  } else {
288  __affine->set_translate(__pad_x, __pad_y);
289  }
290  __scale_override = true;
291 }
292 
293 
294 /** Check if scale override is enabled.
295  * @return true if scale override is enabled, false otherwise
296  */
297 bool
299 {
300  return __scale_override;
301 }
302 
303 
304 /** Get scaler.
305  * @return scaler controller
306  */
307 Papyrus::AffineController::pointer
309 {
310  return __affine;
311 }
312 
313 /** Render current graph. */
314 void
316 {
317  Gtk::Window *w = dynamic_cast<Gtk::Window *>(get_toplevel());
318 
319  int result = __fcd->run();
320  if (result == Gtk::RESPONSE_OK) {
321 
322  double old_scale_x, old_scale_y, old_translate_x, old_translate_y;
323  __affine->get_scale(old_scale_x, old_scale_y);
324  __affine->get_translate(old_translate_x, old_translate_y);
325  __affine->set_scale(1);
326  __affine->set_translate(__pad_x, __pad_y);
327 
328  Papyrus::Canvas::pointer c = canvas();
329 
330  Cairo::RefPtr<Cairo::Surface> surface;
331 
332  std::string filename = __fcd->get_filename();
333  bool write_to_png = false;
334  if (filename != "") {
335  Gtk::FileFilter *f = __fcd->get_filter();
336  if (f->get_name().find("PDF") != Glib::ustring::npos) {
337  surface = Cairo::PdfSurface::create(filename, __bbw, __bbh);
338  } else if (f->get_name().find("SVG") != Glib::ustring::npos) {
339  surface = Cairo::SvgSurface::create(filename, __bbw, __bbh);
340  } else if (f->get_name().find("PNG") != Glib::ustring::npos) {
341  surface = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, __bbw, __bbh);
342  write_to_png = true;
343  }
344 
345  if (surface) {
346  Cairo::RefPtr<Cairo::Context> context = Cairo::Context::create(surface);
347  c->render(context);
348  if (write_to_png) {
349  surface->write_to_png(filename);
350  }
351  }
352 
353  } else {
354  Gtk::MessageDialog md(*w, "Invalid filename",
355  /* markup */ false, Gtk::MESSAGE_ERROR,
356  Gtk::BUTTONS_OK, /* modal */ true);
357  md.set_title("Invalid File Name");
358  md.run();
359  }
360 
361  __affine->set_scale(old_scale_x, old_scale_y);
362  __affine->set_translate(old_translate_x, old_translate_y);
363  }
364 
365  __fcd->hide();
366 }
367 
368 
369 /** Render current graph. */
370 void
372 {
373  if (! __update_graph) return;
374 
375  Papyrus::Canvas::pointer c = canvas();
376 #ifdef HAVE_TIMS_PAPYRUS_PATCHES
377  c->set_redraw_enabled(false);
378 #endif
379  Agraph_t *g = agmemread((char *)__graph.c_str());
380  if (g) {
381  gvLayout(__gvc, g, (char *)"dot");
382  gvRender(__gvc, g, (char *)"skillgui", NULL);
383  gvFreeLayout(__gvc, g);
384  agclose(g);
385  } else {
386  clear();
387  }
388 #ifdef HAVE_TIMS_PAPYRUS_PATCHES
389  c->set_redraw_enabled(true);
390 #endif
391 }
392 
393 
394 /** Called on explose.
395  * @param event Gdk event structure
396  */
397 void
398 SkillGuiGraphViewport::on_expose(GdkEventExpose *event)
399 {
400  if (__scale_override) {
401  Gtk::Allocation alloc = get_allocation();
402 
403  double sx, sy;
404  __affine->get_scale(sx, sy);
405  __affine->set_translate(((alloc.get_width() - __bbw * sx) / 2.0) + __pad_x,
406  ((alloc.get_height() - __bbh * sy) / 2.0) + __pad_y);
407 
408  }
409 }
void on_expose(GdkEventExpose *event)
Called on explose.
void zoom_in()
Zoom in.
SkillGuiGraphViewport()
Constructor.
void set_graph_fsm(std::string fsm_name)
Set graph&#39;s FSM name.
void set_pad(double pad_x, double pad_y)
Set padding.
void set_scale(double scale)
Set scale.
bool scale_override()
Check if scale override is enabled.
void save()
Render current graph.
void add_drawable(Papyrus::Drawable::pointer d)
Add a drawable.
void set_bb(double bbw, double bbh)
Set bounding box.
void set_update_graph(bool update)
Set if the graph should be updated on new data.
virtual void clear()
Clear all drawables.
void set_gvjob(GVJ_t *job)
Set current Graphviz job.
Papyrus::AffineController::pointer get_affine()
Get scaler.
void set_graph(std::string graph)
Set graph.
void zoom_out()
Zoom out.
void set_translation(double tx, double ty)
Set translation.
~SkillGuiGraphViewport()
Destructor.
void zoom_fit()
Zoom to fit.
void zoom_reset()
Zoom reset.
void render()
Render current graph.
bool get_update_graph()
Check if graph is being updated.