Fawkes API  Fawkes Development Version
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
throbber.cpp
1 
2 /***************************************************************************
3  * throbber.cpp - Fawkes throbber
4  *
5  * Created: Tue Nov 04 16:38:03 2008
6  * Copyright 2008 Tim Niemueller [www.niemueller.de]
7  *
8  ****************************************************************************/
9 
10 /* This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version. A runtime exception applies to
14  * this software (see LICENSE.GPL_WRE file mentioned below for details).
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Library General Public License for more details.
20  *
21  * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
22  */
23 
24 #include <gui_utils/throbber.h>
25 
26 #include <core/exception.h>
27 #include <algorithm>
28 
29 namespace fawkes {
30 #if 0 /* just to make Emacs auto-indent happy */
31 }
32 #endif
33 
34 #define SPINNER_ICON_NAME "process-working"
35 #define SPINNER_FALLBACK_ICON_NAME "gnome-spinner"
36 #define SPINNER_DEFAULT_TIMEOUT 100
37 
38 
39 /** @class Throbber <gui_utils/throbber.h>
40  * Simple Gtk Throbber/Spinner.
41  * The throbber shows a spinning icon as a small image. It has been specifically
42  * prepared to be used as a custom image Gtk::ToolItem in a Gtk::Toolbar.
43  * The icon is defined by the currently active Gtk theme.
44  * @author Tim Niemueller
45  */
46 
47 /** Constructor.
48  * Special ctor to be used with Gtk::Builder's get_widget_derived().
49  * @param cobject Gtk C object
50  * @param builder Gtk builder
51  */
52 Throbber::Throbber(BaseObjectType* cobject,
53  const Glib::RefPtr<Gtk::Builder> &builder)
54  : Gtk::Image(cobject)
55 {
56  Gtk::Container *parent = get_parent();
57  Gtk::ToolItem *toolitem = dynamic_cast<Gtk::ToolItem *>(parent);
58  if ( toolitem ) {
59  ctor(toolitem->get_icon_size());
60  } else {
61  // We have no clue, just try button
62  ctor(Gtk::IconSize(Gtk::ICON_SIZE_BUTTON));
63  }
64 }
65 
66 
67 /** Constructor.
68  * @param icon_size desired icon size. Be aware that the icon may not be available
69  * in all sizes in the current theme.
70  */
71 Throbber::Throbber(Gtk::IconSize &icon_size)
72 {
73  ctor(icon_size);
74 }
75 
76 
77 void
78 Throbber::ctor(Gtk::IconSize icon_size)
79 {
80  __timeout = SPINNER_DEFAULT_TIMEOUT;
81  __icon_size = icon_size;
82 
83  int isw = 0, ish = 0;
84 #if GTKMM_MAJOR_VERSION > 2 || ( GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 14 )
85  Glib::RefPtr<Gtk::Settings> settings = Gtk::Settings::get_for_screen(get_screen());
86  if ( ! Gtk::IconSize::lookup(icon_size, isw, ish, settings) ) {
87  throw Exception("Could not get icon sizes");
88  }
89 #else
90  if ( ! Gtk::IconSize::lookup(icon_size, isw, ish) ) {
91  throw Exception("Could not get icon sizes");
92  }
93 #endif
94  int requested_size = std::max(isw, ish);
95 
96  Glib::RefPtr<Gtk::IconTheme> icon_theme = Gtk::IconTheme::get_for_screen(get_screen());
97  Gtk::IconInfo icon_info = icon_theme->lookup_icon(SPINNER_ICON_NAME,
98  requested_size,
99  Gtk::IconLookupFlags());
100  if ( ! icon_info ) {
101  icon_info = icon_theme->lookup_icon(SPINNER_FALLBACK_ICON_NAME,
102  requested_size, Gtk::IconLookupFlags());
103  if ( ! icon_info ) {
104  throw Exception("Could not find neither default nor fallback throbber icon");
105  }
106  }
107 
108  int size = icon_info.get_base_size();
109 
110 #ifdef GLIBMM_EXCEPTIONS_ENABLED
111  Glib::RefPtr<Gdk::Pixbuf> icon = icon_info.load_icon();
112 #else
113  std::auto_ptr<Glib::Error> error;
114  Glib::RefPtr<Gdk::Pixbuf> icon = icon_info.load_icon(error);
115 #endif
116 
117  int pixwidth = icon->get_width();
118  int pixheight = icon->get_height();
119 
120  for (int y = 0; y < pixheight; y += size) {
121  for (int x = 0; x < pixwidth ; x += size) {
122  if ( (x + size <= icon->get_width()) &&
123  (y + size <= icon->get_height()) ) {
124  Glib::RefPtr<Gdk::Pixbuf> p = Gdk::Pixbuf::create_subpixbuf(icon, x, y, size, size);
125  __pixbufs.push_back(p);
126  }
127  }
128  }
129 
130  if ( __pixbufs.empty() ) {
131  throw Exception("Could not extract any throbber images from pixbuf");
132  }
133 
134  __current = 0;
135  set(__pixbufs.front());
136 }
137 
138 
139 /** Draw next image.
140  * @return always true
141  */
142 bool
143 Throbber::draw_next()
144 {
145  __current = (__current + 1) % __pixbufs.size();
146  if ( (__current == 0) && (__pixbufs.size() > 1) ) {
147  __current = 1;
148  }
149  set(__pixbufs[__current]);
150 
151  return true;
152 }
153 
154 
155 /** Set the animation timeout.
156  * The animation timeout is the time between two frames. It defaults to 100ms.
157  * @param timeout new timeout for animation in ms
158  */
159 void
160 Throbber::set_timeout(unsigned int timeout)
161 {
162  __timeout = timeout;
163 }
164 
165 
166 /** Check if animation is running.
167  * @return true if animation is currently running, false otherwise.
168  */
169 bool
171 {
172  return (__timeout_connection && __timeout_connection.connected());
173 }
174 
175 /** Start animation. */
176 void
178 {
179  if ( ! __timeout_connection || ! __timeout_connection.connected()) {
180  __timeout_connection = Glib::signal_timeout().connect(
181  sigc::mem_fun(*this, &Throbber::draw_next), __timeout);
182  }
183 }
184 
185 /** Stop animation. */
186 void
188 {
189  if (__timeout_connection && __timeout_connection.connected()) {
190  __timeout_connection.disconnect();
191  }
192 
193  __current = 0;
194  set(__pixbufs.front());
195 }
196 
197 
198 /** Set image from stock ID.
199  * The image will be overwritten by a running animation or when the
200  * animation is started again. It will not be automatically reset to this
201  * stock ID if the animation stops, rather you have to do this by yourself.
202  * @param stock_id stock ID of image to set
203  */
204 void
205 Throbber::set_stock(const Gtk::StockID& stock_id)
206 {
207  set(stock_id, __icon_size);
208 }
209 
210 
211 } // end namespace fawkes