diff options
Diffstat (limited to 'src/notificationwindow.c')
| -rw-r--r-- | src/notificationwindow.c | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/src/notificationwindow.c b/src/notificationwindow.c new file mode 100644 index 0000000..966c708 --- /dev/null +++ b/src/notificationwindow.c | |||
| @@ -0,0 +1,347 @@ | |||
| 1 | /* | ||
| 2 | * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client | ||
| 3 | * Copyright (C) 1999-2014 Hiroyuki Yamamoto | ||
| 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 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License | ||
| 16 | * along with this program; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include <glib.h> | ||
| 25 | #include <glib/gi18n.h> | ||
| 26 | #include <gtk/gtk.h> | ||
| 27 | |||
| 28 | #ifdef G_OS_WIN32 | ||
| 29 | # include <windows.h> | ||
| 30 | #endif | ||
| 31 | |||
| 32 | #include "notificationwindow.h" | ||
| 33 | #include "mainwindow.h" | ||
| 34 | #include "utils.h" | ||
| 35 | #include "gtkutils.h" | ||
| 36 | |||
| 37 | #define NOTIFICATIONWINDOW_NOTIFY_PERIOD 10000 | ||
| 38 | #define NOTIFICATIONWINDOW_WIDTH 380 | ||
| 39 | #define NOTIFICATIONWINDOW_HEIGHT 80 | ||
| 40 | #define FADE_REFRESH_RATE 50 /* ms */ | ||
| 41 | #define FADE_SPEED 5 /* pixels */ | ||
| 42 | #define FADE_IN_LENGTH 10 /* counts */ | ||
| 43 | |||
| 44 | typedef struct _NotificationWindow | ||
| 45 | { | ||
| 46 | GtkWidget *window; | ||
| 47 | |||
| 48 | GtkWidget *msglabel; | ||
| 49 | GtkWidget *sublabel; | ||
| 50 | |||
| 51 | guint notify_tag; | ||
| 52 | |||
| 53 | gint x; | ||
| 54 | gint y; | ||
| 55 | gint width; | ||
| 56 | gint height; | ||
| 57 | gint fade_length; | ||
| 58 | gint fade_count; | ||
| 59 | gint notify_event_count; | ||
| 60 | guint timeout; | ||
| 61 | } NotificationWindow; | ||
| 62 | |||
| 63 | static NotificationWindow notify_window; | ||
| 64 | |||
| 65 | static void notification_window_destroy(void); | ||
| 66 | |||
| 67 | #if GTK_CHECK_VERSION(2, 12, 0) | ||
| 68 | static gboolean notify_fadein_timeout_cb(gpointer data); | ||
| 69 | #endif | ||
| 70 | |||
| 71 | static gboolean notify_timeout_cb (gpointer data); | ||
| 72 | |||
| 73 | static gboolean nwin_button_pressed (GtkWidget *widget, | ||
| 74 | GdkEventButton *event, | ||
| 75 | gpointer data); | ||
| 76 | static gboolean nwin_entered (GtkWidget *widget, | ||
| 77 | GdkEventCrossing *event, | ||
| 78 | gpointer data); | ||
| 79 | static gboolean nwin_motion_notify (GtkWidget *widget, | ||
| 80 | GdkEventMotion *event, | ||
| 81 | gpointer data); | ||
| 82 | |||
| 83 | static void nwin_destroy_cb (GtkWidget *widget, | ||
| 84 | gpointer data); | ||
| 85 | |||
| 86 | |||
| 87 | static void get_work_area(GdkRectangle *rect) | ||
| 88 | { | ||
| 89 | #ifdef G_OS_WIN32 | ||
| 90 | RECT rc; | ||
| 91 | |||
| 92 | SystemParametersInfoW(SPI_GETWORKAREA, 0, &rc, 0); | ||
| 93 | rect->x = rc.left; | ||
| 94 | rect->y = rc.top; | ||
| 95 | rect->width = rc.right - rc.left; | ||
| 96 | rect->height = rc.bottom - rc.top; | ||
| 97 | #else | ||
| 98 | rect->x = 0; | ||
| 99 | rect->y = 0; | ||
| 100 | rect->width = gdk_screen_width(); | ||
| 101 | rect->height = gdk_screen_height(); | ||
| 102 | #endif | ||
| 103 | } | ||
| 104 | |||
| 105 | gint notification_window_open(const gchar *message, const gchar *submessage, | ||
| 106 | guint timeout) | ||
| 107 | { | ||
| 108 | GtkWidget *window; | ||
| 109 | GtkWidget *vbox; | ||
| 110 | GtkWidget *msglabel; | ||
| 111 | GtkWidget *sublabel; | ||
| 112 | GdkRectangle rect; | ||
| 113 | gint x, y; | ||
| 114 | GtkRequisition requisition; | ||
| 115 | gint window_width; | ||
| 116 | gint window_height; | ||
| 117 | |||
| 118 | window_width = NOTIFICATIONWINDOW_WIDTH * gtkut_get_dpi_multiplier(); | ||
| 119 | window_height = NOTIFICATIONWINDOW_HEIGHT * gtkut_get_dpi_multiplier(); | ||
| 120 | |||
| 121 | if (notify_window.window) { | ||
| 122 | notification_window_destroy(); | ||
| 123 | } | ||
| 124 | |||
| 125 | window = gtk_window_new(GTK_WINDOW_POPUP); | ||
| 126 | gtk_window_set_title(GTK_WINDOW(window), _("Notification")); | ||
| 127 | gtk_window_set_wmclass(GTK_WINDOW(window), "notification", "Sylpheed"); | ||
| 128 | gtk_container_set_border_width(GTK_CONTAINER(window), 8); | ||
| 129 | gtk_widget_set_events(window, GDK_EXPOSURE_MASK|GDK_BUTTON_MOTION_MASK|GDK_POINTER_MOTION_MASK|GDK_POINTER_MOTION_HINT_MASK|GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK|GDK_ENTER_NOTIFY_MASK|GDK_LEAVE_NOTIFY_MASK); | ||
| 130 | gtk_window_set_skip_taskbar_hint(GTK_WINDOW(window), TRUE); | ||
| 131 | gtk_window_set_gravity(GTK_WINDOW(window), GDK_GRAVITY_SOUTH_EAST); | ||
| 132 | gtk_widget_set_size_request(window, window_width, -1); | ||
| 133 | #if GTK_CHECK_VERSION(2, 12, 0) | ||
| 134 | gtk_window_set_opacity(GTK_WINDOW(window), 0.0); | ||
| 135 | #endif | ||
| 136 | gtk_widget_realize(window); | ||
| 137 | gdk_window_set_type_hint(window->window, GDK_WINDOW_TYPE_HINT_NOTIFICATION); | ||
| 138 | |||
| 139 | /* move window bottom-right */ | ||
| 140 | get_work_area(&rect); | ||
| 141 | x = rect.x + rect.width - window_width - 2; | ||
| 142 | if (x < 0) x = 0; | ||
| 143 | y = rect.y + rect.height - window_height - 2; | ||
| 144 | if (y < 0) y = 0; | ||
| 145 | gtk_window_move(GTK_WINDOW(window), x, y); | ||
| 146 | |||
| 147 | g_signal_connect(G_OBJECT(window), "destroy", | ||
| 148 | G_CALLBACK(nwin_destroy_cb), NULL); | ||
| 149 | g_signal_connect(G_OBJECT(window), "button_press_event", | ||
| 150 | G_CALLBACK(nwin_button_pressed), NULL); | ||
| 151 | g_signal_connect(G_OBJECT(window), "enter_notify_event", | ||
| 152 | G_CALLBACK(nwin_entered), NULL); | ||
| 153 | g_signal_connect(G_OBJECT(window), "motion_notify_event", | ||
| 154 | G_CALLBACK(nwin_motion_notify), NULL); | ||
| 155 | |||
| 156 | vbox = gtk_vbox_new(FALSE, 8); | ||
| 157 | gtk_container_add(GTK_CONTAINER(window), vbox); | ||
| 158 | |||
| 159 | msglabel = gtk_label_new(message); | ||
| 160 | gtk_box_pack_start(GTK_BOX(vbox), msglabel, FALSE, FALSE, 0); | ||
| 161 | |||
| 162 | sublabel = gtk_label_new(""); | ||
| 163 | gtk_label_set_ellipsize(GTK_LABEL(sublabel), PANGO_ELLIPSIZE_END); | ||
| 164 | gtk_label_set_markup(GTK_LABEL(sublabel), submessage); | ||
| 165 | gtk_box_pack_start(GTK_BOX(vbox), sublabel, FALSE, FALSE, 0); | ||
| 166 | gtk_label_set_justify(GTK_LABEL(sublabel), GTK_JUSTIFY_LEFT); | ||
| 167 | gtk_misc_set_alignment(GTK_MISC(sublabel), 0.0, 0.5); | ||
| 168 | |||
| 169 | gtk_widget_show_all(window); | ||
| 170 | |||
| 171 | /* adjust window size and position */ | ||
| 172 | gtk_widget_get_child_requisition(window, &requisition); | ||
| 173 | notify_window.width = window_width; | ||
| 174 | notify_window.height = MAX(requisition.height, window_height); | ||
| 175 | gtk_widget_set_size_request(window, window_width, notify_window.height); | ||
| 176 | y = rect.y + rect.height - notify_window.height - 2; | ||
| 177 | if (y < 0) y = 0; | ||
| 178 | gtk_window_move(GTK_WINDOW(window), x, y); | ||
| 179 | |||
| 180 | #if GTK_CHECK_VERSION(2, 12, 0) | ||
| 181 | notify_window.notify_tag = g_timeout_add(FADE_REFRESH_RATE, | ||
| 182 | notify_fadein_timeout_cb, | ||
| 183 | NULL); | ||
| 184 | #else | ||
| 185 | if (timeout > 0) { | ||
| 186 | notify_window.notify_tag = g_timeout_add(timeout * 1000, | ||
| 187 | notify_timeout_cb, NULL); | ||
| 188 | } | ||
| 189 | #endif | ||
| 190 | |||
| 191 | debug_print("notification window created\n"); | ||
| 192 | |||
| 193 | notify_window.window = window; | ||
| 194 | notify_window.msglabel = msglabel; | ||
| 195 | notify_window.sublabel = sublabel; | ||
| 196 | notify_window.x = x; | ||
| 197 | notify_window.y = y; | ||
| 198 | notify_window.fade_length = FADE_IN_LENGTH; | ||
| 199 | notify_window.fade_count = 0; | ||
| 200 | notify_window.notify_event_count = 0; | ||
| 201 | notify_window.timeout = timeout; | ||
| 202 | |||
| 203 | return 0; | ||
| 204 | } | ||
| 205 | |||
| 206 | void notification_window_set_message(const gchar *message, | ||
| 207 | const gchar *submessage) | ||
| 208 | { | ||
| 209 | if (notify_window.window) { | ||
| 210 | gtk_label_set_text(GTK_LABEL(notify_window.msglabel), message); | ||
| 211 | gtk_label_set_markup(GTK_LABEL(notify_window.sublabel), submessage); | ||
| 212 | } | ||
| 213 | } | ||
| 214 | |||
| 215 | void notification_window_close(void) | ||
| 216 | { | ||
| 217 | notification_window_destroy(); | ||
| 218 | } | ||
| 219 | |||
| 220 | static void notification_window_destroy(void) | ||
| 221 | { | ||
| 222 | if (notify_window.window) { | ||
| 223 | if (notify_window.notify_tag > 0) { | ||
| 224 | g_source_remove(notify_window.notify_tag); | ||
| 225 | notify_window.notify_tag = 0; | ||
| 226 | } | ||
| 227 | |||
| 228 | gtk_widget_destroy(notify_window.window); | ||
| 229 | |||
| 230 | notify_window.window = NULL; | ||
| 231 | notify_window.msglabel = NULL; | ||
| 232 | notify_window.sublabel = NULL; | ||
| 233 | |||
| 234 | debug_print("notification window removed\n"); | ||
| 235 | } | ||
| 236 | } | ||
| 237 | |||
| 238 | #if GTK_CHECK_VERSION(2, 12, 0) | ||
| 239 | static gboolean notify_fadein_timeout_cb(gpointer data) | ||
| 240 | { | ||
| 241 | gdk_threads_enter(); | ||
| 242 | notify_window.fade_length--; | ||
| 243 | notify_window.fade_count++; | ||
| 244 | |||
| 245 | gtk_window_set_opacity(GTK_WINDOW(notify_window.window), | ||
| 246 | (gdouble)notify_window.fade_count / FADE_IN_LENGTH); | ||
| 247 | |||
| 248 | if (notify_window.fade_length <= 0) { | ||
| 249 | notify_window.fade_length = 0; | ||
| 250 | notify_window.fade_count = 0; | ||
| 251 | if (notify_window.timeout > 0) { | ||
| 252 | notify_window.notify_tag = | ||
| 253 | g_timeout_add(notify_window.timeout * 1000, | ||
| 254 | notify_timeout_cb, NULL); | ||
| 255 | } | ||
| 256 | gdk_threads_leave(); | ||
| 257 | return FALSE; | ||
| 258 | } | ||
| 259 | gdk_threads_leave(); | ||
| 260 | return TRUE; | ||
| 261 | } | ||
| 262 | #endif | ||
| 263 | |||
| 264 | static gboolean notify_fadeout_timeout_cb(gpointer data) | ||
| 265 | { | ||
| 266 | gdk_threads_enter(); | ||
| 267 | notify_window.fade_length -= FADE_SPEED; | ||
| 268 | notify_window.fade_count++; | ||
| 269 | |||
| 270 | gtk_window_move(GTK_WINDOW(notify_window.window), | ||
| 271 | notify_window.x, notify_window.y + notify_window.fade_count * FADE_SPEED); | ||
| 272 | #if GTK_CHECK_VERSION(2, 12, 0) | ||
| 273 | gtk_window_set_opacity(GTK_WINDOW(notify_window.window), | ||
| 274 | (gdouble)notify_window.fade_length / (gdk_screen_height() - notify_window.y)); | ||
| 275 | #endif | ||
| 276 | |||
| 277 | if (notify_window.fade_length <= 0) { | ||
| 278 | notification_window_destroy(); | ||
| 279 | gdk_threads_leave(); | ||
| 280 | return FALSE; | ||
| 281 | } | ||
| 282 | gdk_threads_leave(); | ||
| 283 | return TRUE; | ||
| 284 | } | ||
| 285 | |||
| 286 | static gboolean notify_timeout_cb(gpointer data) | ||
| 287 | { | ||
| 288 | gdk_threads_enter(); | ||
| 289 | notify_window.fade_length = gdk_screen_height() - notify_window.y; | ||
| 290 | notify_window.notify_tag = g_timeout_add(FADE_REFRESH_RATE, | ||
| 291 | notify_fadeout_timeout_cb, | ||
| 292 | NULL); | ||
| 293 | gdk_threads_leave(); | ||
| 294 | |||
| 295 | return FALSE; | ||
| 296 | } | ||
| 297 | |||
| 298 | static gboolean nwin_button_pressed(GtkWidget *widget, GdkEventButton *event, | ||
| 299 | gpointer data) | ||
| 300 | { | ||
| 301 | if (!event) | ||
| 302 | return FALSE; | ||
| 303 | |||
| 304 | notification_window_destroy(); | ||
| 305 | main_window_popup(main_window_get()); | ||
| 306 | |||
| 307 | return TRUE; | ||
| 308 | } | ||
| 309 | |||
| 310 | static gboolean nwin_entered(GtkWidget *widget, GdkEventCrossing *event, | ||
| 311 | gpointer data) | ||
| 312 | { | ||
| 313 | return FALSE; | ||
| 314 | } | ||
| 315 | |||
| 316 | static gboolean nwin_motion_notify(GtkWidget *widget, GdkEventMotion *event, | ||
| 317 | gpointer data) | ||
| 318 | { | ||
| 319 | if (notify_window.fade_count > 0 && | ||
| 320 | notify_window.notify_event_count == 0) { | ||
| 321 | notify_window.notify_event_count++; | ||
| 322 | return FALSE; | ||
| 323 | } | ||
| 324 | |||
| 325 | if (notify_window.notify_tag > 0) { | ||
| 326 | g_source_remove(notify_window.notify_tag); | ||
| 327 | notify_window.notify_tag = 0; | ||
| 328 | } | ||
| 329 | notify_window.fade_count = 0; | ||
| 330 | notify_window.notify_event_count = 0; | ||
| 331 | #if GTK_CHECK_VERSION(2, 12, 0) | ||
| 332 | gtk_window_set_opacity(GTK_WINDOW(notify_window.window), 1.0); | ||
| 333 | #endif | ||
| 334 | gtk_window_move(GTK_WINDOW(notify_window.window), | ||
| 335 | notify_window.x, notify_window.y); | ||
| 336 | if (notify_window.timeout > 0) { | ||
| 337 | notify_window.notify_tag = | ||
| 338 | g_timeout_add(notify_window.timeout * 1000, | ||
| 339 | notify_timeout_cb, NULL); | ||
| 340 | } | ||
| 341 | |||
| 342 | return FALSE; | ||
| 343 | } | ||
| 344 | |||
| 345 | static void nwin_destroy_cb(GtkWidget *widget, gpointer data) | ||
| 346 | { | ||
| 347 | } | ||
