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