No issues found
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2004 Christophe Fergeau <teuf@gnome.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * The Rhythmbox authors hereby grant permission for non-GPL compatible
11 * GStreamer plugins to be used and distributed together with GStreamer
12 * and Rhythmbox. This permission is above and beyond the permissions granted
13 * by the GPL license by which Rhythmbox is covered. If you modify this code
14 * you may extend this exception to your version of the code, but you are not
15 * obligated to do so. If you do not wish to do so, delete this exception
16 * statement from your version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
26 *
27 */
28
29 #include "config.h"
30
31 #include <math.h>
32
33 #include <gtk/gtk.h>
34 #include <glib/gi18n.h>
35
36 #include "rb-cut-and-paste-code.h"
37 #include "rb-rating-helper.h"
38 #include "rb-stock-icons.h"
39
40
41 /**
42 * SECTION:rb-rating-helper
43 * @short_description: helper functions for displaying song ratings
44 *
45 * A few helper functions for dealing with ratings. These are shared
46 * between #RBRating and #RBCellRendererRating.
47 */
48
49 struct _RBRatingPixbufs {
50 GdkPixbuf *pix_star;
51 GdkPixbuf *pix_dot;
52 GdkPixbuf *pix_blank;
53 };
54
55 /**
56 * rb_rating_pixbufs_free:
57 * @pixbufs: #RBRatingPixbufs instance
58 *
59 * Frees a set of rating pixbufs.
60 */
61 void
62 rb_rating_pixbufs_free (RBRatingPixbufs *pixbufs)
63 {
64 if (pixbufs->pix_star != NULL)
65 g_object_unref (pixbufs->pix_star);
66 if (pixbufs->pix_dot != NULL)
67 g_object_unref (pixbufs->pix_dot);
68 if (pixbufs->pix_blank != NULL)
69 g_object_unref (pixbufs->pix_blank);
70 }
71
72 /**
73 * rb_rating_install_rating_property:
74 * @klass: a #GObjectClass to add the property to
75 * @prop: property index to use
76 *
77 * Installs a 'rating' property in the specified class.
78 */
79 void
80 rb_rating_install_rating_property (GObjectClass *klass, gulong prop)
81 {
82 g_object_class_install_property (klass, prop,
83 g_param_spec_double ("rating",
84 ("Rating Value"),
85 ("Rating Value"),
86 0.0, (double)RB_RATING_MAX_SCORE,
87 (double)RB_RATING_MAX_SCORE/2.0,
88 G_PARAM_READWRITE));
89
90 }
91
92 /**
93 * rb_rating_pixbufs_new:
94 *
95 * Creates and returns a structure holding a set of pixbufs
96 * to use to display ratings.
97 *
98 * Return value: #RBRatingPixbufs structure, or NULL if not all of
99 * the pixbufs could be loaded.
100 */
101 RBRatingPixbufs *
102 rb_rating_pixbufs_new (void)
103 {
104 RBRatingPixbufs *pixbufs;
105 GtkIconTheme *theme;
106 gint width;
107
108 pixbufs = g_new0 (RBRatingPixbufs, 1);
109 if (pixbufs == NULL) {
110 return NULL;
111 }
112
113 theme = gtk_icon_theme_get_default ();
114 gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, NULL, &width);
115
116 pixbufs->pix_star = gtk_icon_theme_load_icon (theme,
117 RB_STOCK_SET_STAR,
118 width,
119 0,
120 NULL);
121 pixbufs->pix_dot = gtk_icon_theme_load_icon (theme,
122 RB_STOCK_UNSET_STAR,
123 width,
124 0,
125 NULL);
126 pixbufs->pix_blank = gtk_icon_theme_load_icon (theme,
127 RB_STOCK_NO_STAR,
128 width,
129 0,
130 NULL);
131 if (pixbufs->pix_star != NULL &&
132 pixbufs->pix_dot != NULL &&
133 pixbufs->pix_blank != NULL) {
134 return pixbufs;
135 }
136
137 rb_rating_pixbufs_free (pixbufs);
138 g_free (pixbufs);
139 return NULL;
140 }
141
142 /**
143 * rb_rating_render_stars:
144 * @widget: a #GtkWidget to render on behalf of
145 * @cr: cairo context to render into
146 * @pixbufs: a #RBRatingPixbufs structure
147 * @x: source X coordinate within the rating pixbufs (usually 0)
148 * @y: source Y coordinate within the rating pixbufs (usually 0)
149 * @x_offset: destination X coordinate within the window
150 * @y_offset: destination Y coordinate within the window
151 * @rating: the rating to display (between 0.0 and 5.0)
152 * @selected: TRUE if the widget is currently selected for input
153 *
154 * Renders a rating as a row of stars. floor(@rating) large stars
155 * are drawn, followed by 5-floor(@rating) small stars.
156 *
157 * Return value: TRUE if the stars were drawn successfully
158 */
159 gboolean
160 rb_rating_render_stars (GtkWidget *widget,
161 cairo_t *cr,
162 RBRatingPixbufs *pixbufs,
163 int x,
164 int y,
165 int x_offset,
166 int y_offset,
167 gdouble rating,
168 gboolean selected)
169 {
170 int i, icon_width;
171 gboolean rtl;
172
173 g_return_val_if_fail (widget != NULL, FALSE);
174 g_return_val_if_fail (pixbufs != NULL, FALSE);
175
176 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
177 gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &icon_width, NULL);
178
179 for (i = 0; i < RB_RATING_MAX_SCORE; i++) {
180 GdkPixbuf *buf;
181 GtkStateType state;
182 gint star_offset;
183 int offset;
184 GdkRGBA color;
185
186 if (selected == TRUE) {
187 offset = 0;
188 if (gtk_widget_has_focus (widget))
189 state = GTK_STATE_SELECTED;
190 else
191 state = GTK_STATE_ACTIVE;
192 } else {
193 offset = 120;
194 if (gtk_widget_get_state (widget) == GTK_STATE_INSENSITIVE)
195 state = GTK_STATE_INSENSITIVE;
196 else
197 state = GTK_STATE_NORMAL;
198 }
199
200 if (i < rating)
201 buf = pixbufs->pix_star;
202 else if (i >= rating && i < RB_RATING_MAX_SCORE)
203 buf = pixbufs->pix_dot;
204 else
205 buf = pixbufs->pix_blank;
206
207 if (buf == NULL) {
208 return FALSE;
209 }
210
211 gtk_style_context_get_color (gtk_widget_get_style_context (widget), state, &color);
212 buf = eel_create_colorized_pixbuf (buf,
213 ((guint16)(color.red * G_MAXUINT16) + offset) >> 8,
214 ((guint16)(color.green * G_MAXUINT16) + offset) >> 8,
215 ((guint16)(color.blue * G_MAXUINT16) + offset) >> 8);
216 if (buf == NULL) {
217 return FALSE;
218 }
219
220 if (rtl) {
221 star_offset = (RB_RATING_MAX_SCORE - i - 1) * icon_width;
222 } else {
223 star_offset = i * icon_width;
224 }
225
226 gdk_cairo_set_source_pixbuf (cr, buf, x_offset + star_offset, y_offset);
227 cairo_paint (cr);
228 g_object_unref (buf);
229 }
230
231 return TRUE;
232 }
233
234 /**
235 * rb_rating_get_rating_from_widget:
236 * @widget: the #GtkWidget displaying the rating
237 * @widget_x: x component of the position to query
238 * @widget_width: width of the widget
239 * @current_rating: the current rating displayed in the widget
240 *
241 * Updates the rating for a widget after the user clicks on the
242 * rating. If the user clicks on the Nth star, the rating is set
243 * to N, unless the rating is already N, in which case the rating is
244 * set to N-1. This allows the user to set the rating to 0.
245 *
246 * Return value: the updated rating
247 */
248 double
249 rb_rating_get_rating_from_widget (GtkWidget *widget,
250 gint widget_x,
251 gint widget_width,
252 double current_rating)
253 {
254 int icon_width;
255 double rating = -1.0;
256
257 gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &icon_width, NULL);
258
259 /* ensure the user clicks within the good cell */
260 if (widget_x >= 0 && widget_x <= widget_width) {
261 gboolean rtl;
262
263 rating = (int) (widget_x / icon_width) + 1;
264
265 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
266 if (rtl) {
267 rating = RB_RATING_MAX_SCORE - rating + 1;
268 }
269
270 if (rating < 0)
271 rating = 0;
272
273 if (rating > RB_RATING_MAX_SCORE)
274 rating = RB_RATING_MAX_SCORE;
275
276 if (rating == current_rating) {
277 /* Make it possible to give a 0 rating to a song */
278 rating--;
279 }
280 }
281
282 return rating;
283 }
284
285 /**
286 * rb_rating_set_accessible_name:
287 * @widget: a #GtkWidget for which to set the accessible name
288 * @rating: the rating value to set
289 *
290 * Sets the accessible object name for the specified widget to reflect the
291 * rating.
292 */
293 void
294 rb_rating_set_accessible_name (GtkWidget *widget, gdouble rating)
295 {
296 AtkObject *aobj;
297 int stars;
298 char *aname;
299
300 aobj = gtk_widget_get_accessible (widget);
301
302 stars = floor (rating);
303 if (stars == 0) {
304 aname = g_strdup (_("No Stars")); /* is this really necessary */
305 } else {
306 aname = g_strdup_printf (ngettext ("%d Star", "%d Stars", stars), stars);
307 }
308
309 atk_object_set_name (aobj, aname);
310 g_free (aname);
311 }