hythmbox-2.98/widgets/rb-rating-helper.c

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 }