1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 
3 /*
4  *  GThumb
5  *
6  *  Copyright (C) 2008 The Free Software Foundation, Inc.
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include "gth-empty-list.h"
23 
24 /* Properties */
25 enum {
26 	PROP_0,
27 	PROP_TEXT
28 };
29 
30 struct _GthEmptyListPrivate {
31 	char        *text;
32 	PangoLayout *layout;
33 };
34 
35 
G_DEFINE_TYPE_WITH_CODE(GthEmptyList,gth_empty_list,GTK_TYPE_WIDGET,G_ADD_PRIVATE (GthEmptyList))36 G_DEFINE_TYPE_WITH_CODE (GthEmptyList,
37 			 gth_empty_list,
38 			 GTK_TYPE_WIDGET,
39 			 G_ADD_PRIVATE (GthEmptyList))
40 
41 
42 static void
43 gth_empty_list_finalize (GObject *obj)
44 {
45 	g_free (GTH_EMPTY_LIST (obj)->priv->text);
46 	G_OBJECT_CLASS (gth_empty_list_parent_class)->finalize (obj);
47 }
48 
49 
50 static void
gth_empty_list_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)51 gth_empty_list_set_property (GObject      *object,
52 			     guint         property_id,
53 			     const GValue *value,
54 			     GParamSpec   *pspec)
55 {
56 	GthEmptyList *self;
57 
58         self = GTH_EMPTY_LIST (object);
59 
60 	switch (property_id) {
61 	case PROP_TEXT:
62 		g_free (self->priv->text);
63 		self->priv->text = g_value_dup_string (value);
64 		gtk_widget_queue_resize (GTK_WIDGET (self));
65 		g_object_notify (object, "text");
66 		break;
67 
68 	default:
69 		break;
70 	}
71 }
72 
73 
74 static void
gth_empty_list_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)75 gth_empty_list_get_property (GObject    *object,
76 			     guint       property_id,
77 			     GValue     *value,
78 			     GParamSpec *pspec)
79 {
80 	GthEmptyList *self;
81 
82         self = GTH_EMPTY_LIST (object);
83 
84 	switch (property_id) {
85 	case PROP_TEXT:
86 		g_value_set_string (value, self->priv->text);
87 		break;
88 
89 	default:
90 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
91 		break;
92 	}
93 }
94 
95 
96 static void
gth_empty_list_realize(GtkWidget * widget)97 gth_empty_list_realize (GtkWidget *widget)
98 {
99 	GthEmptyList    *self;
100 	GtkAllocation    allocation;
101 	GdkWindowAttr    attributes;
102 	int              attributes_mask;
103 	GdkWindow       *window;
104 	GtkStyleContext *style_context;
105 
106 	g_return_if_fail (GTH_IS_EMPTY_LIST (widget));
107 	self = (GthEmptyList*) widget;
108 
109 	gtk_widget_set_realized (widget, TRUE);
110 
111 	/**/
112 
113 	gtk_widget_get_allocation (widget, &allocation);
114 
115 	attributes.window_type = GDK_WINDOW_CHILD;
116 	attributes.x           = allocation.x;
117 	attributes.y           = allocation.y;
118 	attributes.width       = allocation.width;
119 	attributes.height      = allocation.height;
120 	attributes.wclass      = GDK_INPUT_OUTPUT;
121 	attributes.visual      = gtk_widget_get_visual (widget);
122 	attributes.event_mask = (GDK_EXPOSURE_MASK
123 				 | GDK_SCROLL_MASK
124 				 | GDK_POINTER_MOTION_MASK
125 				 | GDK_ENTER_NOTIFY_MASK
126 				 | GDK_LEAVE_NOTIFY_MASK
127 				 | GDK_BUTTON_PRESS_MASK
128 				 | GDK_BUTTON_RELEASE_MASK
129 				 | gtk_widget_get_events (widget));
130 	attributes_mask        = (GDK_WA_X
131 				  | GDK_WA_Y
132 				  | GDK_WA_VISUAL);
133 	window = gdk_window_new (gtk_widget_get_parent_window (widget),
134 			         &attributes,
135 			         attributes_mask);
136 	gtk_widget_register_window (widget, window);
137 	gtk_widget_set_window (widget, window);
138 
139 	/* Style */
140 
141 	style_context = gtk_widget_get_style_context (widget);
142 	gtk_style_context_set_background (style_context, window);
143 
144 	/* 'No Image' message Layout */
145 
146 	if (self->priv->layout != NULL)
147 		g_object_unref (self->priv->layout);
148 
149 	self->priv->layout = gtk_widget_create_pango_layout (widget, NULL);
150 	pango_layout_set_wrap (self->priv->layout, PANGO_WRAP_WORD_CHAR);
151 	pango_layout_set_alignment (self->priv->layout, PANGO_ALIGN_CENTER);
152 }
153 
154 
155 static void
gth_empty_list_unrealize(GtkWidget * widget)156 gth_empty_list_unrealize (GtkWidget *widget)
157 {
158 	GthEmptyList *self;
159 
160 	g_return_if_fail (GTH_IS_EMPTY_LIST (widget));
161 
162 	self = (GthEmptyList*) widget;
163 
164 	if (self->priv->layout != NULL) {
165 		g_object_unref (self->priv->layout);
166 		self->priv->layout = NULL;
167 	}
168 
169 	GTK_WIDGET_CLASS (gth_empty_list_parent_class)->unrealize (widget);
170 }
171 
172 
173 static void
gth_empty_list_map(GtkWidget * widget)174 gth_empty_list_map (GtkWidget *widget)
175 {
176 	gtk_widget_set_mapped (widget, TRUE);
177 	gdk_window_show (gtk_widget_get_window (widget));
178 }
179 
180 
181 static void
gth_empty_list_unmap(GtkWidget * widget)182 gth_empty_list_unmap (GtkWidget *widget)
183 {
184 	gtk_widget_set_mapped (widget, FALSE);
185 	gdk_window_hide (gtk_widget_get_window (widget));
186 }
187 
188 
189 static void
gth_empty_list_size_allocate(GtkWidget * widget,GtkAllocation * allocation)190 gth_empty_list_size_allocate (GtkWidget     *widget,
191 			      GtkAllocation *allocation)
192 {
193 	gtk_widget_set_allocation (widget, allocation);
194 
195 	if (gtk_widget_get_realized (widget))
196 		gdk_window_move_resize (gtk_widget_get_window (widget),
197 					allocation->x,
198 					allocation->y,
199 					allocation->width,
200 					allocation->height);
201 }
202 
203 
204 static gboolean
gth_empty_list_draw(GtkWidget * widget,cairo_t * cr)205 gth_empty_list_draw (GtkWidget *widget,
206 		     cairo_t   *cr)
207 {
208 	GthEmptyList    *self = (GthEmptyList*) widget;
209 	GtkStyleContext *style_context;
210 	GtkAllocation    allocation;
211 
212 	if (! gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget)))
213 		return FALSE;
214 
215 	style_context = gtk_widget_get_style_context (widget);
216 
217 	gtk_widget_get_allocation (widget, &allocation);
218 
219 	gtk_render_background (style_context, cr,
220 			       0,
221 			       0,
222 			       allocation.width,
223 			       allocation.height);
224 
225 	gtk_render_frame (style_context, cr,
226 			  0,
227 			  0,
228 			  allocation.width,
229 			  allocation.height);
230 
231 	if (self->priv->text != NULL) {
232 		PangoRectangle        bounds;
233 		GdkRGBA               color;
234 		PangoFontDescription *font;
235 
236 		pango_layout_set_width (self->priv->layout, allocation.width * PANGO_SCALE);
237 		pango_layout_set_text (self->priv->layout, self->priv->text, strlen (self->priv->text));
238 		pango_layout_get_pixel_extents (self->priv->layout, NULL, &bounds);
239 		gtk_style_context_get (style_context, gtk_widget_get_state_flags (widget), "font", &font, NULL);
240 		pango_layout_set_font_description (self->priv->layout, font);
241 		gtk_style_context_get_color (style_context, gtk_widget_get_state_flags (widget), &color);
242 		gdk_cairo_set_source_rgba (cr, &color);
243 		gtk_render_layout (style_context, cr, 0, (allocation.height - bounds.height) / 2, self->priv->layout);
244 		cairo_fill (cr);
245 
246 		pango_font_description_free (font);
247 	}
248 
249 	return FALSE;
250 }
251 
252 
253 static void
gth_empty_list_class_init(GthEmptyListClass * klass)254 gth_empty_list_class_init (GthEmptyListClass *klass)
255 {
256 	GObjectClass   *object_class;
257 	GtkWidgetClass *widget_class;
258 
259 	object_class = (GObjectClass*) (klass);
260 	object_class->set_property = gth_empty_list_set_property;
261 	object_class->get_property = gth_empty_list_get_property;
262 	object_class->finalize = gth_empty_list_finalize;
263 
264 	widget_class = (GtkWidgetClass*) klass;
265 	widget_class->realize = gth_empty_list_realize;
266 	widget_class->unrealize = gth_empty_list_unrealize;
267 	widget_class->map = gth_empty_list_map;
268 	widget_class->unmap = gth_empty_list_unmap;
269 	widget_class->size_allocate = gth_empty_list_size_allocate;
270 	widget_class->draw = gth_empty_list_draw;
271 
272 	/* properties */
273 
274 	g_object_class_install_property (object_class,
275 					 PROP_TEXT,
276 					 g_param_spec_string ("text",
277                                                               "Text",
278                                                               "The text to display",
279                                                               NULL,
280                                                               G_PARAM_READABLE | G_PARAM_WRITABLE));
281 }
282 
283 
284 static void
gth_empty_list_init(GthEmptyList * self)285 gth_empty_list_init (GthEmptyList *self)
286 {
287 	GtkStyleContext *style_context;
288 
289 	gtk_widget_set_has_window (GTK_WIDGET (self), TRUE);
290 	gtk_widget_set_can_focus (GTK_WIDGET (self), FALSE);
291 
292 	self->priv = gth_empty_list_get_instance_private (self);
293 	self->priv->layout = NULL;
294 	self->priv->text = NULL;
295 
296 	style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
297 	gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_VIEW);
298 	gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_FRAME);
299 }
300 
301 
302 GtkWidget *
gth_empty_list_new(const char * text)303 gth_empty_list_new (const char *text)
304 {
305 	return g_object_new (GTH_TYPE_EMPTY_LIST, "text", text, NULL);
306 }
307 
308 
309 void
gth_empty_list_set_text(GthEmptyList * self,const char * text)310 gth_empty_list_set_text (GthEmptyList *self,
311 			 const char   *text)
312 {
313 	g_object_set (self, "text", text, NULL);
314 }
315