Fawkes API  Fawkes Development Version
throbber.cpp
00001 
00002 /***************************************************************************
00003  *  throbber.cpp - Fawkes throbber
00004  *
00005  *  Created: Tue Nov 04 16:38:03 2008
00006  *  Copyright  2008  Tim Niemueller [www.niemueller.de]
00007  *
00008  ****************************************************************************/
00009 
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version. A runtime exception applies to
00014  *  this software (see LICENSE.GPL_WRE file mentioned below for details).
00015  *
00016  *  This program is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  *  GNU Library General Public License for more details.
00020  *
00021  *  Read the full text in the LICENSE.GPL_WRE file in the doc directory.
00022  */
00023 
00024 #include <gui_utils/throbber.h>
00025 
00026 #include <core/exception.h>
00027 #include <algorithm>
00028 
00029 namespace fawkes {
00030 #if 0 /* just to make Emacs auto-indent happy */
00031 }
00032 #endif
00033 
00034 #define SPINNER_ICON_NAME               "process-working"
00035 #define SPINNER_FALLBACK_ICON_NAME      "gnome-spinner"
00036 #define SPINNER_DEFAULT_TIMEOUT         100
00037 
00038 
00039 /** @class Throbber <gui_utils/throbber.h>
00040  * Simple Gtk Throbber/Spinner.
00041  * The throbber shows a spinning icon as a small image. It has been specifically
00042  * prepared to be used as a custom image Gtk::ToolItem in a Gtk::Toolbar.
00043  * The icon is defined by the currently active Gtk theme.
00044  * @author Tim Niemueller
00045  */
00046 
00047 /** Constructor.
00048  * Special ctor to be used with Gtk::Builder's get_widget_derived().
00049  * @param cobject Gtk C object
00050  * @param builder Gtk builder
00051  */
00052 Throbber::Throbber(BaseObjectType* cobject,
00053                    const Glib::RefPtr<Gtk::Builder> &builder)
00054   : Gtk::Image(cobject)
00055 {
00056   Gtk::Container *parent = get_parent();
00057   Gtk::ToolItem  *toolitem = dynamic_cast<Gtk::ToolItem *>(parent);
00058   if ( toolitem ) {
00059     ctor(toolitem->get_icon_size());
00060   } else {
00061     // We have no clue, just try button
00062     ctor(Gtk::IconSize(Gtk::ICON_SIZE_BUTTON));
00063   }
00064 }
00065 
00066 
00067 /** Constructor.
00068  * @param icon_size desired icon size. Be aware that the icon may not be available
00069  * in all sizes in the current theme.
00070  */
00071 Throbber::Throbber(Gtk::IconSize &icon_size)
00072 {
00073   ctor(icon_size);
00074 }
00075 
00076 
00077 void
00078 Throbber::ctor(Gtk::IconSize icon_size)
00079 {
00080   __timeout   = SPINNER_DEFAULT_TIMEOUT;
00081   __icon_size = icon_size;
00082 
00083   int isw = 0, ish = 0;
00084 #if GTKMM_MAJOR_VERSION > 2 || ( GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 14 )
00085   Glib::RefPtr<Gtk::Settings> settings = Gtk::Settings::get_for_screen(get_screen());
00086   if ( ! Gtk::IconSize::lookup(icon_size, isw, ish, settings) ) {
00087     throw Exception("Could not get icon sizes");
00088   }
00089 #else
00090   if ( ! Gtk::IconSize::lookup(icon_size, isw, ish) ) {
00091     throw Exception("Could not get icon sizes");
00092   }
00093 #endif
00094   int requested_size = std::max(isw, ish);
00095 
00096   Glib::RefPtr<Gtk::IconTheme> icon_theme = Gtk::IconTheme::get_for_screen(get_screen());
00097   Gtk::IconInfo icon_info = icon_theme->lookup_icon(SPINNER_ICON_NAME,
00098                                                     requested_size,
00099                                                     Gtk::IconLookupFlags());
00100   if ( ! icon_info ) {
00101     icon_info = icon_theme->lookup_icon(SPINNER_FALLBACK_ICON_NAME,
00102                                         requested_size, Gtk::IconLookupFlags());
00103     if ( ! icon_info ) {
00104       throw Exception("Could not find neither default nor fallback throbber icon");
00105     }
00106   }
00107 
00108   int size = icon_info.get_base_size();
00109 
00110 #ifdef GLIBMM_EXCEPTIONS_ENABLED
00111   Glib::RefPtr<Gdk::Pixbuf> icon = icon_info.load_icon();
00112 #else
00113   std::auto_ptr<Glib::Error> error;
00114   Glib::RefPtr<Gdk::Pixbuf> icon = icon_info.load_icon(error);
00115 #endif
00116 
00117   int pixwidth  = icon->get_width();
00118   int pixheight = icon->get_height();
00119 
00120   for (int y = 0; y < pixheight; y += size) {
00121     for (int x = 0; x < pixwidth ; x += size) {
00122       if ( (x + size <= icon->get_width()) &&
00123            (y + size <= icon->get_height()) ) {
00124         Glib::RefPtr<Gdk::Pixbuf> p = Gdk::Pixbuf::create_subpixbuf(icon, x, y, size, size);
00125         __pixbufs.push_back(p);
00126       }
00127     }
00128   }
00129 
00130   if ( __pixbufs.empty() ) {
00131     throw Exception("Could not extract any throbber images from pixbuf");
00132   }
00133 
00134   __current = 0;
00135   set(__pixbufs.front());
00136 }
00137 
00138 
00139 /** Draw next image.
00140  * @return always true
00141  */
00142 bool
00143 Throbber::draw_next()
00144 {
00145   __current = (__current + 1) % __pixbufs.size();
00146   if ( (__current == 0) && (__pixbufs.size() > 1) ) {
00147     __current = 1;
00148   }
00149   set(__pixbufs[__current]);  
00150 
00151   return true;
00152 }
00153 
00154 
00155 /** Set the animation timeout.
00156  * The animation timeout is the time between two frames. It defaults to 100ms.
00157  * @param timeout new timeout for animation in ms
00158  */
00159 void
00160 Throbber::set_timeout(unsigned int timeout)
00161 {
00162   __timeout = timeout;
00163 }
00164 
00165 
00166 /** Check if animation is running.
00167  * @return true if animation is currently running, false otherwise.
00168  */
00169 bool
00170 Throbber::anim_running()
00171 {
00172   return (__timeout_connection && __timeout_connection.connected());
00173 }
00174 
00175 /** Start animation. */
00176 void
00177 Throbber::start_anim()
00178 {
00179   if ( ! __timeout_connection || ! __timeout_connection.connected()) {
00180     __timeout_connection = Glib::signal_timeout().connect(
00181                              sigc::mem_fun(*this, &Throbber::draw_next), __timeout);
00182   }
00183 }
00184 
00185 /** Stop animation. */
00186 void
00187 Throbber::stop_anim()
00188 {
00189   if (__timeout_connection && __timeout_connection.connected()) {
00190     __timeout_connection.disconnect();
00191   }
00192 
00193   __current = 0;
00194   set(__pixbufs.front());
00195 }
00196 
00197 
00198 /** Set image from stock ID.
00199  * The image will be overwritten by a running animation or when the
00200  * animation is started again. It will not be automatically reset to this
00201  * stock ID if the animation stops, rather you have to do this by yourself.
00202  * @param stock_id stock ID of image to set
00203  */
00204 void
00205 Throbber::set_stock(const Gtk::StockID& stock_id)
00206 {
00207   set(stock_id, __icon_size);
00208 }
00209 
00210 
00211 } // end namespace fawkes