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 #ifdef HAVE_GLADEMM
00048 /** Constructor.
00049  * Special ctor to be used with Glade's get_widget_derived().
00050  * @param cobject Gtk C object
00051  * @param refxml Glade's XML reference
00052  */
00053 Throbber::Throbber(BaseObjectType* cobject,
00054                    const Glib::RefPtr<Gnome::Glade::Xml>& refxml)
00055   : Gtk::Image(cobject)
00056 {
00057   Gtk::Container *parent = get_parent();
00058   Gtk::ToolItem  *toolitem = dynamic_cast<Gtk::ToolItem *>(parent);
00059   if ( toolitem ) {
00060     ctor(toolitem->get_icon_size());
00061   } else {
00062     // We have no clue, just try button
00063     ctor(Gtk::IconSize(Gtk::ICON_SIZE_BUTTON));
00064   }
00065 }
00066 #endif
00067 
00068 
00069 /** Constructor.
00070  * @param icon_size desired icon size. Be aware that the icon may not be available
00071  * in all sizes in the current theme.
00072  */
00073 Throbber::Throbber(Gtk::IconSize &icon_size)
00074 {
00075   ctor(icon_size);
00076 }
00077 
00078 
00079 void
00080 Throbber::ctor(Gtk::IconSize icon_size)
00081 {
00082   __timeout   = SPINNER_DEFAULT_TIMEOUT;
00083   __icon_size = icon_size;
00084 
00085   int isw = 0, ish = 0;
00086 #if GTKMM_MAJOR_VERSION > 2 || ( GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 14 )
00087   Glib::RefPtr<Gtk::Settings> settings = Gtk::Settings::get_for_screen(get_screen());
00088   if ( ! Gtk::IconSize::lookup(icon_size, isw, ish, settings) ) {
00089     throw Exception("Could not get icon sizes");
00090   }
00091 #else
00092   if ( ! Gtk::IconSize::lookup(icon_size, isw, ish) ) {
00093     throw Exception("Could not get icon sizes");
00094   }
00095 #endif
00096   int requested_size = std::max(isw, ish);
00097 
00098   Glib::RefPtr<Gtk::IconTheme> icon_theme = Gtk::IconTheme::get_for_screen(get_screen());
00099   Gtk::IconInfo icon_info = icon_theme->lookup_icon(SPINNER_ICON_NAME,
00100                                                     requested_size,
00101                                                     Gtk::IconLookupFlags());
00102   if ( ! icon_info ) {
00103     icon_info = icon_theme->lookup_icon(SPINNER_FALLBACK_ICON_NAME,
00104                                         requested_size, Gtk::IconLookupFlags());
00105     if ( ! icon_info ) {
00106       throw Exception("Could not find neither default nor fallback throbber icon");
00107     }
00108   }
00109 
00110   int size = icon_info.get_base_size();
00111 
00112 #ifdef GLIBMM_EXCEPTIONS_ENABLED
00113   Glib::RefPtr<Gdk::Pixbuf> icon = icon_info.load_icon();
00114 #else
00115   std::auto_ptr<Glib::Error> error;
00116   Glib::RefPtr<Gdk::Pixbuf> icon = icon_info.load_icon(error);
00117 #endif
00118 
00119   int pixwidth  = icon->get_width();
00120   int pixheight = icon->get_height();
00121 
00122   for (int y = 0; y < pixheight; y += size) {
00123     for (int x = 0; x < pixwidth ; x += size) {
00124       if ( (x + size <= icon->get_width()) &&
00125            (y + size <= icon->get_height()) ) {
00126         Glib::RefPtr<Gdk::Pixbuf> p = Gdk::Pixbuf::create_subpixbuf(icon, x, y, size, size);
00127         __pixbufs.push_back(p);
00128       }
00129     }
00130   }
00131 
00132   if ( __pixbufs.empty() ) {
00133     throw Exception("Could not extract any throbber images from pixbuf");
00134   }
00135 
00136   __current = 0;
00137   set(__pixbufs.front());
00138 }
00139 
00140 
00141 /** Draw next image.
00142  * @return always true
00143  */
00144 bool
00145 Throbber::draw_next()
00146 {
00147   __current = (__current + 1) % __pixbufs.size();
00148   if ( (__current == 0) && (__pixbufs.size() > 1) ) {
00149     __current = 1;
00150   }
00151   set(__pixbufs[__current]);  
00152 
00153   return true;
00154 }
00155 
00156 
00157 /** Set the animation timeout.
00158  * The animation timeout is the time between two frames. It defaults to 100ms.
00159  * @param timeout new timeout for animation in ms
00160  */
00161 void
00162 Throbber::set_timeout(unsigned int timeout)
00163 {
00164   __timeout = timeout;
00165 }
00166 
00167 
00168 /** Check if animation is running.
00169  * @return true if animation is currently running, false otherwise.
00170  */
00171 bool
00172 Throbber::anim_running()
00173 {
00174   return (__timeout_connection && __timeout_connection.connected());
00175 }
00176 
00177 /** Start animation. */
00178 void
00179 Throbber::start_anim()
00180 {
00181   if ( ! __timeout_connection || ! __timeout_connection.connected()) {
00182     __timeout_connection = Glib::signal_timeout().connect(
00183                              sigc::mem_fun(*this, &Throbber::draw_next), __timeout);
00184   }
00185 }
00186 
00187 /** Stop animation. */
00188 void
00189 Throbber::stop_anim()
00190 {
00191   if (__timeout_connection && __timeout_connection.connected()) {
00192     __timeout_connection.disconnect();
00193   }
00194 
00195   __current = 0;
00196   set(__pixbufs.front());
00197 }
00198 
00199 
00200 /** Set image from stock ID.
00201  * The image will be overwritten by a running animation or when the
00202  * animation is started again. It will not be automatically reset to this
00203  * stock ID if the animation stops, rather you have to do this by yourself.
00204  * @param stock_id stock ID of image to set
00205  */
00206 void
00207 Throbber::set_stock(const Gtk::StockID& stock_id)
00208 {
00209   set(stock_id, __icon_size);
00210 }
00211 
00212 
00213 } // end namespace fawkes