Back to index

easystroke  0.5.5.1
win.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 "actions.h"
00017 #include "prefs.h"
00018 #include "win.h"
00019 #include "main.h"
00020 #include <glibmm/i18n.h>
00021 
00022 Glib::RefPtr<Gtk::Builder> widgets;
00023 
00024 void Stroke::draw(Cairo::RefPtr<Cairo::Surface> surface, int x, int y, int w, int h, double width, bool inv) const {
00025        const Cairo::RefPtr<Cairo::Context> ctx = Cairo::Context::create (surface);
00026        x += width; y += width; w -= 2*width; h -= 2*width;
00027        ctx->save();
00028        ctx->translate(x,y);
00029        ctx->scale(w,h);
00030        ctx->set_line_width(2.0*width/(w+h));
00031        if (size()) {
00032               ctx->set_line_cap(Cairo::LINE_CAP_ROUND);
00033               int n = size();
00034               float lambda = sqrt(3)-2.0;
00035               float sum = lambda / (1 - lambda);
00036               std::vector<Point> y(n);
00037               y[0] = points(0) * sum;
00038               for (int j = 0; j < n-1; j++)
00039                      y[j+1] = (y[j] + points(j)) * lambda;
00040               std::vector<Point> z(n);
00041               z[n-1] = points(n-1) * (-sum);
00042               for (int j = n-1; j > 0; j--)
00043                      z[j-1] = (z[j] - points(j)) * lambda;
00044               for (int j = 0; j < n-1; j++) {
00045                      // j -> j+1
00046                      if (inv)
00047                             ctx->set_source_rgba(time(j), 0.0, 1.0-time(j), 1.0);
00048                      else
00049                             ctx->set_source_rgba(0.0, time(j), 1.0-time(j), 1.0);
00050                      Point p[4];
00051                      p[0] = points(j);
00052                      p[3] = points(j+1);
00053                      p[1] = p[0] + y[j] + z[j];
00054                      p[2] = p[3] - y[j+1] - z[j+1];
00055                      ctx->move_to(p[0].x, p[0].y);
00056                      ctx->curve_to(p[1].x, p[1].y, p[2].x, p[2].y, p[3].x, p[3].y);
00057                      ctx->stroke();
00058               }
00059        } else if (!button) {
00060               if (inv)
00061                      ctx->set_source_rgba(1.0, 1.0, 0.0, 1.0);
00062               else
00063                      ctx->set_source_rgba(0.0, 0.0, 1.0, 1.0);
00064               ctx->move_to(0.33, 0.33);
00065               ctx->line_to(0.67, 0.67);
00066               ctx->move_to(0.33, 0.67);
00067               ctx->line_to(0.67, 0.33);
00068               ctx->stroke();
00069        }
00070        ctx->restore();
00071        Glib::ustring str;
00072        if (trigger)
00073               str = Glib::ustring::compose("%1\xE2\x86\x92", trigger);
00074        if (timeout)
00075               str += "x";
00076        if (button)
00077               str += Glib::ustring::compose("%1", button);
00078        if (str == "")
00079               return;
00080        if (inv)
00081               ctx->set_source_rgba(0.0, 1.0, 1.0, 0.8);
00082        else
00083               ctx->set_source_rgba(1.0, 0.0, 0.0, 0.8);
00084        ctx->set_font_size(h*0.5);
00085        Cairo::TextExtents te;
00086        ctx->get_text_extents(str, te);
00087        ctx->move_to(x+w/2 - te.x_bearing - te.width/2, y+h/2 - te.y_bearing - te.height/2);
00088        ctx->show_text(str);
00089 }
00090 
00091 void Stroke::draw_svg(std::string filename) const {
00092        const int S = 32;
00093        const int B = 1;
00094        Cairo::RefPtr<Cairo::SvgSurface> s = Cairo::SvgSurface::create(filename, S, S);
00095        draw(s, B, B, S-2*B, S-2*B);
00096 }
00097 
00098 
00099 Glib::RefPtr<Gdk::Pixbuf> Stroke::draw_(int size, double width, bool inv) const {
00100        Glib::RefPtr<Gdk::Pixbuf> pb = drawEmpty_(size);
00101        int w = size;
00102        int h = size;
00103        int stride = pb->get_rowstride();
00104        guint8 *row = pb->get_pixels();
00105        // This is all pretty messed up
00106        // http://www.archivum.info/gtkmm-list@gnome.org/2007-05/msg00112.html
00107        Cairo::RefPtr<Cairo::ImageSurface> surface = Cairo::ImageSurface::create(row, Cairo::FORMAT_ARGB32, w, h, stride);
00108        draw(surface, 0, 0, pb->get_width(), size, width, inv);
00109        for (int i = 0; i < w; i++) {
00110               guint8 *px = row;
00111               for (int j = 0; j < h; j++) {
00112                      guint8 a = px[3];
00113                      guint8 r = px[2];
00114                      guint8 g = px[1];
00115                      guint8 b = px[0];
00116                      if (a) {
00117                             px[0] = ((((guint)r) << 8) - r) / a;
00118                             px[1] = ((((guint)g) << 8) - g) / a;
00119                             px[2] = ((((guint)b) << 8) - b) / a;
00120                      }
00121                      px += 4;
00122               }
00123               row += stride;
00124        }
00125        return pb;
00126 }
00127 
00128 
00129 Glib::RefPtr<Gdk::Pixbuf> Stroke::drawEmpty_(int size) {
00130        Glib::RefPtr<Gdk::Pixbuf> pb = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB,true,8,size,size);
00131        pb->fill(0x00000000);
00132        return pb;
00133 }
00134 
00135 Source<bool> disabled(false);
00136 
00137 class MenuCheck : private Base {
00138        IO<bool> &io;
00139        Gtk::CheckMenuItem *check;
00140        virtual void notify() { check->set_active(io.get()); }
00141        void on_changed() {
00142               bool b = check->get_active();
00143               if (b == io.get()) return;
00144               io.set(b);
00145        }
00146 public:
00147        MenuCheck(IO<bool> &io_, Gtk::CheckMenuItem *check_) : io(io_), check(check_) {
00148               io.connect(this);
00149               notify();
00150               check->signal_toggled().connect(sigc::mem_fun(*this, &MenuCheck::on_changed));
00151        }
00152 };
00153 
00154 extern void quit();
00155 
00156 Win::Win() : actions(new Actions), prefs_tab(new Prefs), stats(new Stats) {
00157        show_hide_icon();
00158        prefs.tray_icon.connect(new Notifier(sigc::mem_fun(*this, &Win::show_hide_icon)));
00159        disabled.connect(new Notifier(sigc::mem_fun(*this, &Win::timeout)));
00160 
00161        WIDGET(Gtk::CheckMenuItem, menu_disabled, _("D_isabled"), true);
00162        menu.append(menu_disabled);
00163        new MenuCheck(disabled, &menu_disabled);
00164 
00165        WIDGET(Gtk::ImageMenuItem, menu_about, Gtk::Stock::ABOUT);
00166        menu.append(menu_about);
00167        menu_about.signal_activate().connect(sigc::mem_fun(*this, &Win::on_about));
00168 
00169        WIDGET(Gtk::SeparatorMenuItem, menu_sep);
00170        menu.append(menu_sep);
00171 
00172        WIDGET(Gtk::ImageMenuItem, menu_quit, Gtk::Stock::QUIT);
00173        menu.append(menu_quit);
00174        menu_quit.signal_activate().connect(sigc::ptr_fun(&quit));
00175 
00176        menu.show_all();
00177 
00178        widgets->get_widget("main", win);
00179        RStroke trefoil = Stroke::trefoil();
00180        std::vector<Glib::RefPtr<Gdk::Pixbuf> > icons;
00181        icons.push_back(trefoil->draw(24));
00182        icons.push_back(trefoil->draw(64));
00183        win->set_icon_list(icons);
00184 
00185        Gtk::Button* button_hide[4];
00186        widgets->get_widget("button_hide1", button_hide[0]);
00187        widgets->get_widget("button_hide2", button_hide[1]);
00188        widgets->get_widget("button_hide3", button_hide[2]);
00189        widgets->get_widget("button_hide4", button_hide[3]);
00190        for (int i = 0; i < 4; i++)
00191               button_hide[i]->signal_clicked().connect(sigc::mem_fun(win, &Gtk::Window::hide));
00192 }
00193 
00194 extern void icon_warning();
00195 
00196 static gboolean icon_clicked(GtkStatusIcon *status_icon, GdkEventButton *event, gpointer) {
00197        if (event->button == 2)
00198               disabled.set(!disabled.get());
00199        return TRUE;
00200 }
00201 
00202 void Win::show_hide_icon() {
00203        bool show = prefs.tray_icon.get();
00204        if (show) {
00205               if (icon)
00206                      return;
00207               icon = Gtk::StatusIcon::create("");
00208               icon->signal_size_changed().connect(sigc::mem_fun(*this, &Win::on_icon_size_changed));
00209               icon->signal_activate().connect(sigc::mem_fun(*this, &Win::show_hide));
00210               icon->signal_popup_menu().connect(sigc::mem_fun(*this, &Win::show_popup));
00211               if (gtk_major_version >= 2 && gtk_minor_version >= 15)
00212                      g_signal_connect(icon->gobj(), "button-release-event", G_CALLBACK(icon_clicked), NULL);
00213        } else {
00214               if (icon)
00215                      icon.reset();
00216               icon_warning();
00217        }
00218 }
00219 
00220 void Win::show_popup(guint button, guint32 activate_time) {
00221        if (icon)
00222               icon->popup_menu_at_position(menu, button, activate_time);
00223 }
00224 
00225 extern const char *version_string;
00226 void Win::on_about() {
00227        Gtk::AboutDialog *about;
00228        widgets->get_widget("aboutdialog", about);
00229        about->set_logo(Stroke::trefoil()->draw(96, 4.0));
00230        about->set_version(version_string);
00231        about->set_program_name("easystroke\n");
00232        about->show();
00233        about->run();
00234        about->hide();
00235 }
00236 
00237 void Win::show_hide() {
00238        if (win->is_mapped())
00239               win->hide();
00240        else
00241               win->show();
00242 }
00243 
00244 bool Win::on_icon_size_changed(int size) {
00245        icon_pb[0] = Stroke::trefoil()->draw(size);
00246        icon_pb[1] = Stroke::trefoil()->draw(size);
00247        icon_pb[1]->saturate_and_pixelate(icon_pb[1], 0.0, true);
00248        if (icon)
00249               icon->set(icon_pb[disabled.get() ? 1 : 0]);
00250        return true;
00251 }
00252 
00253 void Win::timeout() {
00254        if (icon)
00255               icon->set(icon_pb[disabled.get() ? 1 : 0]);
00256 }
00257 
00258 void Win::set_icon(RStroke stroke, bool invert) {
00259        if (!icon)
00260               return;
00261        icon->set(stroke->draw(icon->get_size(), 2.0, invert));
00262        set_timeout(10000);
00263 }
00264 
00265 void error_dialog(const Glib::ustring &text) {
00266        Gtk::MessageDialog dialog(win->get_window(), text, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
00267        dialog.show();
00268        dialog.run();
00269 }
00270 
00271 Glib::ustring app_name_hr(Glib::ustring src) {
00272        return src == "" ? _("(window manager frame)") : src;
00273 }