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