1 /*
2  *
3  *   Copyright (C) 2005-2018 by C.H. Huang
4  *   plushuang.tw@gmail.com
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public
8  *  License as published by the Free Software Foundation; either
9  *  version 2.1 of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  *  ---
21  *
22  *  In addition, as a special exception, the copyright holders give
23  *  permission to link the code of portions of this program with the
24  *  OpenSSL library under certain conditions as described in each
25  *  individual source file, and distribute linked combinations
26  *  including the two.
27  *  You must obey the GNU Lesser General Public License in all respects
28  *  for all of the code used other than OpenSSL.  If you modify
29  *  file(s) with this exception, you may extend this exception to your
30  *  version of the file(s), but you are not obligated to do so.  If you
31  *  do not wish to do so, delete this exception statement from your
32  *  version.  If you delete this exception statement from all source
33  *  files in the program, then also delete it here.
34  *
35  */
36 
37 #include <UgUtil.h>
38 #include <UgtkUtil.h>
39 #include <UgtkBanner.h>
40 
41 #include <glib/gi18n.h>
42 
43 static GdkCursor* hand_cursor = NULL;
44 static GdkCursor* regular_cursor = NULL;
45 
46 static gboolean
47 motion_notify_event (GtkWidget* tv_widget, GdkEventMotion* event, UgtkBanner* banner);
48 
49 static gboolean
50 event_after (GtkWidget* text_view, GdkEvent* ev, UgtkBanner* banner);
51 
52 static GtkWidget* create_x_button (UgtkBanner* banner);
53 
ugtk_banner_init(struct UgtkBanner * banner)54 void ugtk_banner_init (struct UgtkBanner* banner)
55 {
56 	GtkStyleContext* style_context;
57 	GdkRGBA    rgba;
58 
59 	hand_cursor = gdk_cursor_new (GDK_HAND2);
60 	regular_cursor = gdk_cursor_new (GDK_XTERM);
61 
62 	banner->self = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
63 	banner->buffer = gtk_text_buffer_new (NULL);
64 	banner->tag_link = gtk_text_buffer_create_tag (banner->buffer, NULL,
65 			"underline", PANGO_UNDERLINE_SINGLE,
66 			NULL);
67 
68 	banner->text_view = (GtkTextView*) gtk_text_view_new_with_buffer (banner->buffer);
69 	g_object_unref (banner->buffer);
70 	gtk_text_view_set_cursor_visible (banner->text_view, FALSE);
71 	gtk_text_view_set_editable (banner->text_view, FALSE);
72 	gtk_box_pack_start (GTK_BOX (banner->self),
73 			GTK_WIDGET (banner->text_view), TRUE, TRUE, 0);
74 
75 	g_signal_connect (banner->text_view, "event-after",
76 			G_CALLBACK (event_after), banner);
77 	g_signal_connect (banner->text_view, "motion-notify-event",
78 			G_CALLBACK (motion_notify_event), banner);
79 	// style: color
80 	style_context = gtk_widget_get_style_context (GTK_WIDGET (banner->text_view));
81 	gtk_style_context_get_background_color (style_context,
82 			GTK_STATE_FLAG_SELECTED, &rgba);
83 //	gtk_widget_override_background_color (
84 //			GTK_WIDGET (banner->text_view), GTK_STATE_FLAG_NORMAL, &rgba);
85 	gtk_style_context_get_color (style_context,
86 			GTK_STATE_FLAG_SELECTED, &rgba);
87 //	gtk_widget_override_color (
88 //			GTK_WIDGET (banner->text_view), GTK_STATE_FLAG_NORMAL, &rgba);
89 	// close button
90 	gtk_box_pack_end (GTK_BOX (banner->self),
91 			create_x_button (banner), FALSE, FALSE, 0);
92 
93 	banner->show_builtin = 2;
94 	banner->rss.self = NULL;
95 	banner->rss.feed = NULL;
96 	banner->rss.item = NULL;
97 }
98 
ugtk_banner_show_rss(UgtkBanner * banner,UgetRss * urss)99 int  ugtk_banner_show_rss (UgtkBanner* banner, UgetRss* urss)
100 {
101 	banner->rss.self = urss;
102 	banner->rss.feed = NULL;
103 	banner->rss.item = NULL;
104 
105 	banner->rss.feed = uget_rss_find_updated (urss, NULL);
106 	if (banner->rss.feed)
107 		banner->rss.item = uget_rss_feed_find (banner->rss.feed, banner->rss.feed->checked);
108 	if (banner->rss.item)
109 		ugtk_banner_show (banner, banner->rss.item->title, banner->rss.item->link);
110 	else {
111 		gtk_widget_hide (banner->self);
112 		return FALSE;
113 	}
114 	return TRUE;
115 }
116 
ugtk_banner_show(UgtkBanner * banner,const char * title,const char * url)117 void  ugtk_banner_show (UgtkBanner* banner, const char* title, const char* url)
118 {
119 	GtkTextIter iter;
120 
121 	gtk_text_buffer_set_text(banner->buffer, "", 0);
122 	gtk_text_buffer_get_iter_at_offset (banner->buffer, &iter, 0);
123 	gtk_text_buffer_insert (banner->buffer, &iter, "  ", 2);
124 
125 	g_free (banner->link);
126 	if (url == NULL) {
127 		banner->link = NULL;
128 		gtk_text_buffer_insert (banner->buffer, &iter, title, -1);
129 	}
130 	else {
131 		banner->link = g_strdup (url);
132 		gtk_text_buffer_insert_with_tags (banner->buffer, &iter,
133 				title, -1, banner->tag_link, NULL);
134 	}
135 	gtk_widget_show (banner->self);
136 }
137 
ugtk_banner_show_donation(UgtkBanner * banner)138 void  ugtk_banner_show_donation (UgtkBanner* banner)
139 {
140 	GtkTextIter iter;
141 
142 	gtk_text_buffer_set_text(banner->buffer, "", 0);
143 	gtk_text_buffer_get_iter_at_offset (banner->buffer, &iter, 0);
144 	gtk_text_buffer_insert (banner->buffer, &iter, "  ", 2);
145 	gtk_text_buffer_insert (banner->buffer, &iter, _("Attention uGetters:"), -1);
146 	gtk_text_buffer_insert (banner->buffer, &iter, " ", 1);
147 	gtk_text_buffer_insert (banner->buffer, &iter,
148 			_("we are running a Donation Drive "
149 			  "for uGet's Future Development, please click "), -1);
150 	gtk_text_buffer_insert_with_tags (banner->buffer, &iter,
151 			_("HERE"), -1, banner->tag_link, NULL);
152 
153 	g_free (banner->link);
154 	banner->link = g_strdup ("http://ugetdm.com/donate");
155 	gtk_widget_show (banner->self);
156 }
157 
ugtk_banner_show_survey(UgtkBanner * banner)158 void  ugtk_banner_show_survey (UgtkBanner* banner)
159 {
160 	GtkTextIter iter;
161 
162 	gtk_text_buffer_set_text(banner->buffer, "", 0);
163 	gtk_text_buffer_get_iter_at_offset (banner->buffer, &iter, 0);
164 	gtk_text_buffer_insert (banner->buffer, &iter, "  ", 2);
165 	gtk_text_buffer_insert (banner->buffer, &iter, _("Attention uGetters:"), -1);
166 	gtk_text_buffer_insert (banner->buffer, &iter, " ", 1);
167 	gtk_text_buffer_insert (banner->buffer, &iter,
168 			_("please fill out this quick User Survey for uGet."), -1);
169 	gtk_text_buffer_insert (banner->buffer, &iter, " - ", 3);
170 	gtk_text_buffer_insert_with_tags (banner->buffer, &iter,
171 			_("click here to take survey"), -1, banner->tag_link, NULL);
172 
173 	g_free (banner->link);
174 	banner->link = g_strdup ("http://ugetdm.com/survey");
175 	gtk_widget_show (banner->self);
176 }
177 
178 // ----------------------------------------------------------------------------
179 // static functions
180 
181 /* Links can also be activated by clicking.
182  */
183 static gboolean
event_after(GtkWidget * text_view,GdkEvent * ev,UgtkBanner * banner)184 event_after (GtkWidget* text_view, GdkEvent* ev, UgtkBanner* banner)
185 {
186 	GtkTextIter start, end, iter;
187 	GtkTextBuffer *buffer;
188 	GdkEventButton *event;
189 	gint x, y;
190 	GSList* slist;
191 
192 	if (ev->type != GDK_BUTTON_RELEASE)
193 		return FALSE;
194 
195 	event = (GdkEventButton *)ev;
196 
197 	if (event->button != GDK_BUTTON_PRIMARY)
198 		return FALSE;
199 
200 	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
201 
202 	/* we shouldn't follow a link if the user has selected something */
203 	gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
204 	if (gtk_text_iter_get_offset (&start) != gtk_text_iter_get_offset (&end))
205 		return FALSE;
206 
207 	gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
208                                          GTK_TEXT_WINDOW_WIDGET,
209                                          (gint) event->x, (gint) event->y, &x, &y);
210 
211 	gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (text_view), &iter, x, y);
212 
213 	slist = gtk_text_iter_get_tags (&iter);
214 	if (slist) {
215 		if (banner->link)
216 			ugtk_launch_uri (banner->link);
217 		g_slist_free (slist);
218 	}
219 
220 	return FALSE;
221 }
222 
223 /* Update the cursor image if the pointer moved.
224  */
225 /* Looks at all tags covering the position (x, y) in the text view,
226  * and if one of them is a link, change the cursor to the "hands" cursor
227  * typically used by web browsers.
228  */
229 static gboolean
motion_notify_event(GtkWidget * tv_widget,GdkEventMotion * event,UgtkBanner * banner)230 motion_notify_event (GtkWidget* tv_widget, GdkEventMotion* event, UgtkBanner* banner)
231 {
232 	GtkTextView* text_view;
233 	gint x, y;
234 	gboolean  hovering = FALSE;
235 	GSList* slist;
236 	GtkTextIter iter;
237 
238 	text_view = GTK_TEXT_VIEW (tv_widget);
239 	gtk_text_view_window_to_buffer_coords (text_view,
240 			GTK_TEXT_WINDOW_WIDGET, (gint) event->x, (gint) event->y, &x, &y);
241 
242 	// set cursor if appropriate
243 	gtk_text_view_get_iter_at_location (text_view, &iter, x, y);
244 
245 	slist = gtk_text_iter_get_tags (&iter);
246 	if (slist)
247 		hovering = TRUE;
248 	if (banner->hovering_over_link != hovering) {
249 		banner->hovering_over_link = hovering;
250 
251 		if (banner->hovering_over_link) {
252 			gdk_window_set_cursor (gtk_text_view_get_window (text_view,
253 					GTK_TEXT_WINDOW_TEXT), hand_cursor);
254 		}
255 		else {
256 			gdk_window_set_cursor (gtk_text_view_get_window (text_view,
257 					GTK_TEXT_WINDOW_TEXT), regular_cursor);
258 		}
259 	}
260 	if (slist)
261 		g_slist_free (slist);
262 
263 	return FALSE;
264 }
265 
266 // ------------------------------------
267 
268 static gboolean
on_x_button_release(GtkWidget * text_view,GdkEvent * ev,UgtkBanner * banner)269 on_x_button_release (GtkWidget* text_view, GdkEvent* ev, UgtkBanner* banner)
270 {
271 	GdkEventButton* event;
272 
273 	event = (GdkEventButton*) ev;
274 	if (event->button != GDK_BUTTON_PRIMARY)
275 		return FALSE;
276 
277 	if (banner->rss.self) {
278 		if (banner->rss.feed && banner->rss.item)
279 			banner->rss.feed->checked = banner->rss.item->updated;
280 		if (ugtk_banner_show_rss (banner, banner->rss.self))
281 			return FALSE;
282 	}
283 
284 	if (banner->show_builtin > 0) {
285 		banner->show_builtin--;
286 		if (banner->show_builtin == 1)
287 			ugtk_banner_show_donation (banner);
288 		else {
289 			ugtk_banner_show_survey (banner);
290 			banner->show_builtin = 0;
291 		}
292 		return FALSE;
293 	}
294 
295 	gtk_widget_hide (banner->self);
296 	return FALSE;
297 }
298 
create_x_button(UgtkBanner * banner)299 static GtkWidget* create_x_button (UgtkBanner* banner)
300 {
301 	GtkWidget* event_box;
302 	GtkWidget* label;
303 
304 	label = gtk_label_new (" X ");
305 	event_box = gtk_event_box_new ();
306 	gtk_container_add (GTK_CONTAINER (event_box), label);
307 
308 	g_signal_connect (event_box, "button-release-event",
309 			G_CALLBACK (on_x_button_release), banner);
310 	return event_box;
311 }
312 
313