00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "graph_drawing_area.h"
00024 #include "gvplugin_skillgui_cairo.h"
00025
00026 #include <cmath>
00027 #include <libgen.h>
00028
00029
00030
00031
00032
00033
00034
00035
00036 SkillGuiGraphDrawingArea::SkillGuiGraphDrawingArea()
00037 {
00038 add_events(Gdk::SCROLL_MASK | Gdk::BUTTON_MOTION_MASK);
00039
00040 __gvc = gvContext();
00041
00042 __graph_fsm = "";
00043 __graph = "";
00044
00045 __bbw = __bbh = __pad_x = __pad_y = 0.0;
00046 __translation_x = __translation_y = 0.0;
00047 __scale = 1.0;
00048 __scale_override = false;
00049 __update_graph = true;
00050 __recording = false;
00051
00052 gvplugin_skillgui_cairo_setup(__gvc, this);
00053
00054 __fcd_save = new Gtk::FileChooserDialog("Save Graph",
00055 Gtk::FILE_CHOOSER_ACTION_SAVE);
00056 __fcd_open = new Gtk::FileChooserDialog("Load Graph",
00057 Gtk::FILE_CHOOSER_ACTION_OPEN);
00058 __fcd_recording = new Gtk::FileChooserDialog("Recording Directory",
00059 Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER);
00060
00061
00062 __fcd_save->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
00063 __fcd_save->add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
00064 __fcd_open->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
00065 __fcd_open->add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
00066 __fcd_recording->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
00067 __fcd_recording->add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
00068
00069 __filter_pdf = new Gtk::FileFilter();
00070 __filter_pdf->set_name("Portable Document Format (PDF)");
00071 __filter_pdf->add_pattern("*.pdf");
00072 __filter_svg = new Gtk::FileFilter();
00073 __filter_svg->set_name("Scalable Vector Graphic (SVG)");
00074 __filter_svg->add_pattern("*.svg");
00075 __filter_png = new Gtk::FileFilter();
00076 __filter_png->set_name("Portable Network Graphic (PNG)");
00077 __filter_png->add_pattern("*.png");
00078 __filter_dot = new Gtk::FileFilter();
00079 __filter_dot->set_name("DOT Graph");
00080 __filter_dot->add_pattern("*.dot");
00081 __fcd_save->add_filter(*__filter_pdf);
00082 __fcd_save->add_filter(*__filter_svg);
00083 __fcd_save->add_filter(*__filter_png);
00084 __fcd_save->add_filter(*__filter_dot);
00085 __fcd_save->set_filter(*__filter_pdf);
00086
00087 __fcd_open->add_filter(*__filter_dot);
00088 __fcd_open->set_filter(*__filter_dot);
00089
00090 add_events(Gdk::SCROLL_MASK | Gdk::BUTTON_MOTION_MASK |
00091 Gdk::BUTTON_PRESS_MASK );
00092
00093 #ifndef GLIBMM_DEFAULT_SIGNAL_HANDLERS_ENABLED
00094 signal_expose_event().connect(sigc::mem_fun(*this, &SkillGuiGraphDrawingArea::on_expose_event));
00095 signal_button_press_event().connect(sigc::mem_fun(*this, &SkillGuiGraphDrawingArea::on_button_press_event));
00096 signal_motion_notify_event().connect(sigc::mem_fun(*this, &SkillGuiGraphDrawingArea::on_motion_notify_event));
00097 #endif
00098 }
00099
00100 SkillGuiGraphDrawingArea::~SkillGuiGraphDrawingArea()
00101 {
00102 gvFreeContext(__gvc);
00103
00104 delete __fcd_save;
00105 delete __fcd_open;
00106 delete __fcd_recording;
00107 delete __filter_pdf;
00108 delete __filter_svg;
00109 delete __filter_png;
00110 delete __filter_dot;
00111 }
00112
00113
00114
00115
00116
00117 sigc::signal<void>
00118 SkillGuiGraphDrawingArea::signal_update_disabled()
00119 {
00120 return __signal_update_disabled;
00121 }
00122
00123
00124
00125
00126
00127 void
00128 SkillGuiGraphDrawingArea::set_graph_fsm(std::string fsm_name)
00129 {
00130 if ( __update_graph ) {
00131 if ( __graph_fsm != fsm_name ) {
00132 __scale_override = false;
00133 }
00134 __graph_fsm = fsm_name;
00135 } else {
00136 __nonupd_graph_fsm = fsm_name;
00137 }
00138 }
00139
00140
00141
00142
00143
00144 void
00145 SkillGuiGraphDrawingArea::set_graph(std::string graph)
00146 {
00147 if ( __update_graph ) {
00148 __graph = graph;
00149 queue_draw();
00150 } else {
00151 __nonupd_graph = graph;
00152 }
00153
00154 if ( __recording ) {
00155 char *tmp;
00156 timespec t;
00157 if (clock_gettime(CLOCK_REALTIME, &t) == 0) {
00158 struct tm tms;
00159 localtime_r(&t.tv_sec, &tms);
00160
00161 if ( asprintf(&tmp, "%s/%s_%04i%02i%02i-%02i%02i%02i.%09li.dot",
00162 __record_directory.c_str(), __graph_fsm.c_str(),
00163 tms.tm_year + 1900, tms.tm_mon + 1, tms.tm_mday,
00164 tms.tm_hour, tms.tm_min, tms.tm_sec, t.tv_nsec) != -1) {
00165
00166
00167 save_dotfile(tmp);
00168 free(tmp);
00169 } else {
00170 printf("Warning: Could not create file name for recording, skipping graph\n");
00171 }
00172 } else {
00173 printf("Warning: Could not time recording, skipping graph\n");
00174 }
00175 }
00176 }
00177
00178
00179
00180
00181
00182
00183 void
00184 SkillGuiGraphDrawingArea::set_bb(double bbw, double bbh)
00185 {
00186 __bbw = bbw;
00187 __bbh = bbh;
00188 }
00189
00190
00191
00192
00193
00194
00195
00196 void
00197 SkillGuiGraphDrawingArea::set_pad(double pad_x, double pad_y)
00198 {
00199 __pad_x = pad_x;
00200 __pad_y = pad_y;
00201 }
00202
00203
00204
00205
00206
00207
00208
00209 void
00210 SkillGuiGraphDrawingArea::get_pad(double &pad_x, double &pad_y)
00211 {
00212 if (__scale_override) {
00213 pad_x = pad_y = 0;
00214 } else {
00215 pad_x = __pad_x;
00216 pad_y = __pad_y;
00217 }
00218 }
00219
00220
00221
00222
00223
00224
00225
00226 void
00227 SkillGuiGraphDrawingArea::set_translation(double tx, double ty)
00228 {
00229 __translation_x = tx;
00230 __translation_y = ty;
00231 }
00232
00233
00234
00235
00236
00237
00238 void
00239 SkillGuiGraphDrawingArea::set_scale(double scale)
00240 {
00241 __scale = scale;
00242 }
00243
00244
00245
00246
00247
00248 double
00249 SkillGuiGraphDrawingArea::get_scale()
00250 {
00251 return __scale;
00252 }
00253
00254
00255
00256
00257
00258 void
00259 SkillGuiGraphDrawingArea::get_translation(double &tx, double &ty)
00260 {
00261 tx = __translation_x;
00262 ty = __translation_y;
00263 }
00264
00265
00266
00267
00268
00269
00270 void
00271 SkillGuiGraphDrawingArea::get_dimensions(double &width, double &height)
00272 {
00273 Gtk::Allocation alloc = get_allocation();
00274 width = alloc.get_width();
00275 height = alloc.get_height();
00276 }
00277
00278
00279
00280
00281
00282 void
00283 SkillGuiGraphDrawingArea::zoom_in()
00284 {
00285 Gtk::Allocation alloc = get_allocation();
00286 __scale += 0.1;
00287 __scale_override = true;
00288 __translation_x = (alloc.get_width() - __bbw * __scale) / 2.0;
00289 __translation_y = (alloc.get_height() - __bbh * __scale) / 2.0 + __bbh * __scale;
00290 queue_draw();
00291 }
00292
00293
00294
00295
00296 void
00297 SkillGuiGraphDrawingArea::zoom_out()
00298 {
00299 __scale_override = true;
00300 if ( __scale > 0.1 ) {
00301 Gtk::Allocation alloc = get_allocation();
00302 __scale -= 0.1;
00303 __translation_x = (alloc.get_width() - __bbw * __scale) / 2.0;
00304 __translation_y = (alloc.get_height() - __bbh * __scale) / 2.0 + __bbh * __scale;
00305 queue_draw();
00306 }
00307 }
00308
00309
00310
00311
00312
00313 void
00314 SkillGuiGraphDrawingArea::zoom_fit()
00315 {
00316 __scale_override = false;
00317 queue_draw();
00318 }
00319
00320
00321
00322
00323
00324 void
00325 SkillGuiGraphDrawingArea::zoom_reset()
00326 {
00327 Gtk::Allocation alloc = get_allocation();
00328 __scale = 1.0;
00329 __scale_override = true;
00330 __translation_x = (alloc.get_width() - __bbw) / 2.0 + __pad_x;
00331 __translation_y = (alloc.get_height() - __bbh) / 2.0 + __bbh - __pad_y;
00332 queue_draw();
00333 }
00334
00335
00336
00337
00338
00339 bool
00340 SkillGuiGraphDrawingArea::scale_override()
00341 {
00342 return __scale_override;
00343 }
00344
00345
00346
00347
00348
00349
00350
00351 Cairo::RefPtr<Cairo::Context>
00352 SkillGuiGraphDrawingArea::get_cairo()
00353 {
00354 return __cairo;
00355 }
00356
00357
00358
00359
00360
00361
00362 bool
00363 SkillGuiGraphDrawingArea::get_update_graph()
00364 {
00365 return __update_graph;
00366 }
00367
00368
00369
00370
00371
00372 void
00373 SkillGuiGraphDrawingArea::set_update_graph(bool update)
00374 {
00375 if (update && ! __update_graph) {
00376 if ( __graph_fsm != __nonupd_graph_fsm ) {
00377 __scale_override = false;
00378 }
00379 __graph = __nonupd_graph;
00380 __graph_fsm = __nonupd_graph_fsm;
00381 queue_draw();
00382 }
00383 __update_graph = update;
00384 }
00385
00386
00387 void
00388 SkillGuiGraphDrawingArea::save_dotfile(const char *filename)
00389 {
00390 FILE *f = fopen(filename, "w");
00391 if (f) {
00392 if (fwrite(__graph.c_str(), __graph.length(), 1, f) != 1) {
00393
00394 printf("Failed to write dot file '%s'\n", filename);
00395 }
00396 fclose(f);
00397 }
00398 }
00399
00400
00401
00402
00403
00404
00405
00406
00407 bool
00408 SkillGuiGraphDrawingArea::set_recording(bool recording)
00409 {
00410 if (recording) {
00411 Gtk::Window *w = dynamic_cast<Gtk::Window *>(get_toplevel());
00412 __fcd_recording->set_transient_for(*w);
00413 int result = __fcd_recording->run();
00414 if (result == Gtk::RESPONSE_OK) {
00415 __record_directory = __fcd_recording->get_filename();
00416 __recording = true;
00417 }
00418 __fcd_recording->hide();
00419 } else {
00420 __recording = false;
00421 }
00422 return __recording;
00423 }
00424
00425
00426
00427 void
00428 SkillGuiGraphDrawingArea::save()
00429 {
00430 Gtk::Window *w = dynamic_cast<Gtk::Window *>(get_toplevel());
00431 __fcd_save->set_transient_for(*w);
00432
00433 int result = __fcd_save->run();
00434 if (result == Gtk::RESPONSE_OK) {
00435
00436 Gtk::FileFilter *f = __fcd_save->get_filter();
00437 std::string filename = __fcd_save->get_filename();
00438 if (filename != "") {
00439 if (f == __filter_dot) {
00440 save_dotfile(filename.c_str());
00441 } else {
00442 Cairo::RefPtr<Cairo::Surface> surface;
00443
00444 bool write_to_png = false;
00445 if (f == __filter_pdf) {
00446 surface = Cairo::PdfSurface::create(filename, __bbw, __bbh);
00447 } else if (f == __filter_svg) {
00448 surface = Cairo::SvgSurface::create(filename, __bbw, __bbh);
00449 } else if (f == __filter_png) {
00450 surface = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32,
00451 (int)ceilf(__bbw),
00452 (int)ceilf(__bbh));
00453 write_to_png = true;
00454 }
00455
00456 if (surface) {
00457 __cairo = Cairo::Context::create(surface);
00458
00459 bool old_scale_override = __scale_override;
00460 double old_tx = __translation_x;
00461 double old_ty = __translation_y;
00462 double old_scale = __scale;
00463 __translation_x = __pad_x;
00464 __translation_y = __bbh - __pad_y;
00465 __scale = 1.0;
00466 __scale_override = true;
00467
00468 Agraph_t *g = agmemread((char *)__graph.c_str());
00469 if (g) {
00470 gvLayout(__gvc, g, (char *)"dot");
00471 gvRender(__gvc, g, (char *)"skillguicairo", NULL);
00472 gvFreeLayout(__gvc, g);
00473 agclose(g);
00474 }
00475
00476 if (write_to_png) {
00477 surface->write_to_png(filename);
00478 }
00479
00480 __cairo.clear();
00481
00482 __translation_x = old_tx;
00483 __translation_y = old_ty;
00484 __scale = old_scale;
00485 __scale_override = old_scale_override;
00486 }
00487 }
00488
00489 } else {
00490 Gtk::MessageDialog md(*w, "Invalid filename",
00491 false, Gtk::MESSAGE_ERROR,
00492 Gtk::BUTTONS_OK, true);
00493 md.set_title("Invalid File Name");
00494 md.run();
00495 }
00496 }
00497
00498 __fcd_save->hide();
00499 }
00500
00501
00502
00503 void
00504 SkillGuiGraphDrawingArea::open()
00505 {
00506 Gtk::Window *w = dynamic_cast<Gtk::Window *>(get_toplevel());
00507 __fcd_open->set_transient_for(*w);
00508
00509 int result = __fcd_open->run();
00510 if (result == Gtk::RESPONSE_OK) {
00511 __update_graph = false;
00512 __graph = "";
00513 char *basec = strdup(__fcd_open->get_filename().c_str());
00514 char *basen = basename(basec);
00515 __graph_fsm = basen;
00516 free(basec);
00517
00518 FILE *f = fopen(__fcd_open->get_filename().c_str(), "r");
00519 while (! feof(f)) {
00520 char tmp[4096];
00521 size_t s;
00522 if ((s = fread(tmp, 1, 4096, f)) > 0) {
00523 __graph.append(tmp, s);
00524 }
00525 }
00526 fclose(f);
00527 __signal_update_disabled.emit();
00528 queue_draw();
00529 }
00530
00531 __fcd_open->hide();
00532 }
00533
00534
00535
00536
00537
00538
00539 bool
00540 SkillGuiGraphDrawingArea::on_expose_event(GdkEventExpose* event)
00541 {
00542
00543 Glib::RefPtr<Gdk::Window> window = get_window();
00544 if(window) {
00545
00546
00547
00548
00549
00550
00551
00552
00553
00554 __cairo = window->create_cairo_context();
00555 __cairo->set_source_rgb(1, 1, 1);
00556 __cairo->paint();
00557
00558 Agraph_t *g = agmemread((char *)__graph.c_str());
00559 if (g) {
00560 gvLayout(__gvc, g, (char *)"dot");
00561 gvRender(__gvc, g, (char *)"skillguicairo", NULL);
00562 gvFreeLayout(__gvc, g);
00563 agclose(g);
00564 }
00565
00566 __cairo.clear();
00567 }
00568
00569 return true;
00570 }
00571
00572
00573
00574
00575
00576 bool
00577 SkillGuiGraphDrawingArea::on_scroll_event(GdkEventScroll *event)
00578 {
00579 if (event->direction == GDK_SCROLL_UP) {
00580 zoom_in();
00581 } else if (event->direction == GDK_SCROLL_DOWN) {
00582 zoom_out();
00583 }
00584 return true;
00585 }
00586
00587
00588
00589
00590
00591
00592 bool
00593 SkillGuiGraphDrawingArea::on_button_press_event(GdkEventButton *event)
00594 {
00595 __last_mouse_x = event->x;
00596 __last_mouse_y = event->y;
00597 return true;
00598 }
00599
00600
00601
00602
00603
00604
00605 bool
00606 SkillGuiGraphDrawingArea::on_motion_notify_event(GdkEventMotion *event)
00607 {
00608 __scale_override = true;
00609 __translation_x -= __last_mouse_x - event->x;
00610 __translation_y -= __last_mouse_y - event->y;
00611 __last_mouse_x = event->x;
00612 __last_mouse_y = event->y;
00613 queue_draw();
00614 return true;
00615 }
00616