Back to index

easystroke  0.5.5.1
stats.cc
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2008-2009, Thomas Jaeger <ThJaeger@gmail.com>
00003  *
00004  * Permission to use, copy, modify, and/or distribute this software for any
00005  * purpose with or without fee is hereby granted, provided that the above
00006  * copyright notice and this permission notice appear in all copies.
00007  *
00008  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
00009  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00010  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
00011  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00012  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
00013  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
00014  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00015  */
00016 #include "win.h"
00017 #include "actiondb.h"
00018 #include "main.h"
00019 #include <iomanip>
00020 #include <glibmm/i18n.h>
00021 #include <sys/time.h>
00022 
00023 Stats::Stats() {
00024        Gtk::Button *button_matrix;
00025        widgets->get_widget("button_matrix", button_matrix);
00026        widgets->get_widget("treeview_recent", recent_view);
00027        widgets->get_widget("treeview_ranking", ranking_view);
00028 
00029        button_matrix->signal_clicked().connect(sigc::mem_fun(*this, &Stats::on_pdf));
00030 
00031        recent_store = Gtk::ListStore::create(cols);
00032        recent_view->set_model(recent_store);
00033        recent_view->append_column(_("Stroke"), cols.stroke);
00034        recent_view->append_column(_("Name"), cols.name);
00035        recent_view->append_column(_("Score"), cols.score);
00036        recent_view->signal_cursor_changed().connect(sigc::mem_fun(*this, &Stats::on_cursor_changed));
00037 
00038        ranking_view->set_model(Gtk::ListStore::create(cols));
00039        ranking_view->append_column(_("Stroke"), cols.stroke);
00040        if (verbosity >= 4)
00041               ranking_view->append_column("Debug", cols.debug);
00042        ranking_view->append_column(_("Name"), cols.name);
00043        ranking_view->append_column(_("Score"), cols.score);
00044 }
00045 
00046 void Stats::on_cursor_changed() {
00047        Gtk::TreePath path;
00048        Gtk::TreeViewColumn *col;
00049        recent_view->get_cursor(path, col);
00050        Gtk::TreeRow row(*recent_store->get_iter(path));
00051 
00052        Glib::RefPtr<Gtk::ListStore> ranking_store = row[cols.child];
00053        ranking_view->set_model(ranking_store);
00054 }
00055 
00056 class Feedback {
00057        boost::shared_ptr<Gtk::Window> icon;
00058        boost::shared_ptr<Gtk::Window> text;
00059 public:
00060        Feedback(RStroke s, Glib::ustring t, int x, int y) {
00061               x += (prefs.left_handed.get() ? 1 : -1)*3*STROKE_SIZE / 2;
00062               int w,h;
00063               if (s) {
00064                      icon.reset(new Gtk::Window(Gtk::WINDOW_POPUP));
00065                      icon->set_type_hint(Gdk::WINDOW_TYPE_HINT_TOOLTIP);
00066                      icon->set_name("gtk-tooltip");
00067                      WIDGET(Gtk::Image, image, s->draw(STROKE_SIZE));
00068                      icon->set_accept_focus(false);
00069                      icon->add(image);
00070                      image.show();
00071                      icon->get_size(w,h);
00072                      icon->move(x - w/2, y - h/2);
00073                      y += h/2;
00074               }
00075 
00076               if (t != "") {
00077                      text.reset(new Gtk::Window(Gtk::WINDOW_POPUP));
00078                      text->set_type_hint(Gdk::WINDOW_TYPE_HINT_TOOLTIP);
00079                      text->set_name("gtk-tooltip");
00080                      text->set_accept_focus(false);
00081                      text->set_border_width(2);
00082                      WIDGET(Gtk::Label, label, t);
00083                      text->add(label);
00084                      label.show();
00085                      text->get_size(w,h);
00086                      text->move(x - w/2, y + h/2);
00087               }
00088               if (text) {
00089                      text->show();
00090                      text->get_window()->input_shape_combine_region(Gdk::Region(), 0, 0);
00091               }
00092               if (icon) {
00093                      icon->show();
00094                      icon->get_window()->input_shape_combine_region(Gdk::Region(), 0, 0);
00095               }
00096        }
00097 };
00098 
00099 void Ranking::queue_show(RRanking r, RTriple e) {
00100        r->x = (int)e->x;
00101        r->y = (int)e->y;
00102        Glib::signal_idle().connect(sigc::bind(sigc::ptr_fun(&Ranking::show), r));
00103 }
00104 
00105 bool delete_me(boost::shared_ptr<Feedback>) {
00106        return false;
00107 }
00108 
00109 bool Ranking::show(RRanking r) {
00110        if (prefs.tray_feedback.get())
00111               win->set_icon(r->stroke, !r->best_stroke);
00112        if (prefs.feedback.get() && r->best_stroke) {
00113               if (prefs.advanced_popups.get() || !(r->best_stroke->button || r->best_stroke->timeout)) {
00114                      boost::shared_ptr<Feedback> popup(new Feedback(r->best_stroke, r->name, r->x, r->y));
00115                      Glib::signal_timeout().connect(sigc::bind(sigc::ptr_fun(&delete_me), popup), 600);
00116               }
00117        }
00118        Glib::signal_timeout().connect(sigc::bind(sigc::mem_fun(*win->stats, &Stats::on_stroke), r), 200);
00119        return false;
00120 }
00121 
00122 Glib::ustring format_float(float x) {
00123        return Glib::ustring::format(std::fixed, std::setprecision(2), x);
00124 }
00125 
00126 Glib::RefPtr<Gdk::Pixbuf> Stroke::drawDebug(RStroke a, RStroke b, int size) {
00127        // TODO: This is copy'n'paste from win.cc
00128        Glib::RefPtr<Gdk::Pixbuf> pb = drawEmpty_(size);
00129        if (!a || !b || !a->stroke || !b->stroke)
00130               return pb;
00131        int w = size;
00132        int h = size;
00133        int stride = pb->get_rowstride();
00134        guint8 *row = pb->get_pixels();
00135        // This is all pretty messed up
00136        // http://www.archivum.info/gtkmm-list@gnome.org/2007-05/msg00112.html
00137        Cairo::RefPtr<Cairo::ImageSurface> surface = Cairo::ImageSurface::create(row, Cairo::FORMAT_ARGB32, w, h, stride);
00138        const Cairo::RefPtr<Cairo::Context> ctx = Cairo::Context::create(surface);
00139 
00140        for (unsigned int s = 0; s+1 < a->size(); s++)
00141               for (unsigned int t = 0; t+1 < b->size(); t++) {
00142                      double col = 1.0 - stroke_angle_difference(a->stroke.get(), b->stroke.get(), s, t);
00143                      ctx->set_source_rgba(col,col,col,1.0);
00144                      ctx->rectangle(a->time(s)*size, (1.0-b->time(t+1))*size,
00145                                    (a->time(s+1)-a->time(s))*size, (b->time(t+1)-b->time(t))*size);
00146                      ctx->fill();
00147               }
00148        int path_x[a->size() + b->size()];
00149        int path_y[a->size() + b->size()];
00150        stroke_compare(a->stroke.get(), b->stroke.get(), path_x, path_y);
00151        ctx->set_source_rgba(1,0,0,1);
00152        ctx->set_line_width(2);
00153        ctx->move_to(size, 0);
00154        for (int i = 0;; i++) {
00155               ctx->line_to(a->time(path_x[i])*size, (1.0-b->time(path_y[i]))*size);
00156               if (!path_x[i] && !path_y[i])
00157                      break;
00158        }
00159        ctx->stroke();
00160 
00161        for (int i = 0; i < w; i++) {
00162               guint8 *px = row;
00163               for (int j = 0; j < h; j++) {
00164                      guint8 a = px[3];
00165                      guint8 r = px[2];
00166                      guint8 g = px[1];
00167                      guint8 b = px[0];
00168                      if (a) {
00169                             px[0] = ((((guint)r) << 8) - r) / a;
00170                             px[1] = ((((guint)g) << 8) - g) / a;
00171                             px[2] = ((((guint)b) << 8) - b) / a;
00172                      }
00173                      px += 4;
00174               }
00175               row += stride;
00176        }
00177        return pb;
00178 }
00179 
00180 bool Stats::on_stroke(RRanking r) {
00181        Gtk::TreeModel::Row row = *(recent_store->prepend());
00182        row[cols.stroke] = r->stroke->draw(STROKE_SIZE);
00183        row[cols.name] = r->name;
00184        row[cols.score] = format_float(r->score*100) + "%";
00185        Glib::RefPtr<Gtk::ListStore> ranking_store = Gtk::ListStore::create(cols);
00186        row[cols.child] = ranking_store;
00187 
00188        Gtk::TreePath path = recent_store->get_path(row);
00189        recent_view->scroll_to_row(path);
00190 
00191        Gtk::TreeModel::Children ch = recent_store->children();
00192        if (ch.size() > 8) {
00193               Gtk::TreeIter last = ch.end();
00194               last--;
00195               recent_store->erase(last);
00196 
00197        }
00198 
00199        for (std::multimap<double, std::pair<std::string, RStroke> >::iterator i = r->r.begin(); i != r->r.end(); i++) {
00200               Gtk::TreeModel::Row row2 = *(ranking_store->prepend());
00201               row2[cols.stroke] = i->second.second->draw(STROKE_SIZE);
00202               if (verbosity >= 4)
00203                      row2[cols.debug] = Stroke::drawDebug(r->stroke, i->second.second, STROKE_SIZE);
00204               row2[cols.name] = i->second.first;
00205               row2[cols.score] = format_float(i->first * 100) + "%";
00206        }
00207        return false;
00208 }
00209 
00210 void Stats::on_pdf() {
00211        struct timeval tv1, tv2;
00212        if (verbosity >= 1)
00213               gettimeofday(&tv1, 0);
00214        const int S = 32;
00215        const int B = 1;
00216        std::list<RStroke> strokes;
00217        actions.get_root()->all_strokes(strokes);
00218        const int n = strokes.size();
00219        Cairo::RefPtr<Cairo::PdfSurface> surface = Cairo::PdfSurface::create("/tmp/strokes.pdf", (n+1)*S, (n+1)*S);
00220        const Cairo::RefPtr<Cairo::Context> ctx = Cairo::Context::create(surface);
00221        int k = 1;
00222        for (std::list<RStroke>::iterator i = strokes.begin(); i != strokes.end(); i++, k++) {
00223               (*i)->draw(surface, k*S+B, B, S-2*B, S-2*B);
00224               (*i)->draw(surface, B, k*S+B, S-2*B, S-2*B);
00225 
00226               ctx->set_source_rgba(0,0,0,1);
00227               ctx->set_line_width(1);
00228               ctx->move_to(k*S, B);
00229               ctx->line_to(k*S, (n+1)*S-B);
00230               ctx->move_to(B, k*S);
00231               ctx->line_to((n+1)*S-B, k*S);
00232               ctx->stroke();
00233 
00234               int l = 1;
00235               for (std::list<RStroke>::iterator j = strokes.begin(); j != strokes.end(); j++, l++) {
00236                      double score;
00237                       int match = Stroke::compare(*i, *j, score);
00238                      if (match < 0)
00239                             continue;
00240                      if (match) {
00241                             ctx->save();
00242                             ctx->set_source_rgba(0,0,1,score-0.6);
00243                             ctx->rectangle(l*S, k*S, S, S);
00244                             ctx->fill();
00245                             ctx->restore();
00246                      }
00247                      Glib::ustring str = format_float(score);
00248                      Cairo::TextExtents te;
00249                      ctx->get_text_extents(str, te);
00250                      ctx->move_to(l*S+S/2 - te.x_bearing - te.width/2, k*S+S/2 - te.y_bearing - te.height/2);
00251                      ctx->show_text(str);
00252               }
00253        }
00254        if (verbosity >= 1) {
00255               gettimeofday(&tv2, 0);
00256               printf("creating table took %ld us\n", (tv2.tv_sec - tv1.tv_sec)*1000000 + tv2.tv_usec - tv1.tv_usec);
00257        }
00258        if (!fork()) {
00259               execlp("xdg-open", "xdg-open", "/tmp/strokes.pdf", NULL);
00260               exit(EXIT_FAILURE);
00261        }
00262 }
00263