[evolution-data-server] I#195 - alarm-notify: Show more details of the selected event



commit aafe73742da404858a967953f82be60b6f91c893
Author: Milan Crha <mcrha redhat com>
Date:   Fri Mar 6 09:34:59 2020 +0100

    I#195 - alarm-notify: Show more details of the selected event
    
    Closes https://gitlab.gnome.org/GNOME/evolution-data-server/issues/195

 ...e.evolution-data-server.calendar.gschema.xml.in |   4 +
 po/POTFILES.in                                     |   1 +
 src/libedataserverui/CMakeLists.txt                |   2 +
 src/libedataserverui/e-buffer-tagger.c             | 740 +++++++++++++++++++++
 src/libedataserverui/e-buffer-tagger.h             |  38 ++
 src/libedataserverui/e-reminders-widget.c          | 153 ++++-
 src/libedataserverui/e-reminders-widget.h          |   1 +
 src/libedataserverui/libedataserverui.h            |   1 +
 .../evolution-alarm-notify/e-alarm-notify.c        |  64 +-
 9 files changed, 996 insertions(+), 8 deletions(-)
---
diff --git a/data/org.gnome.evolution-data-server.calendar.gschema.xml.in 
b/data/org.gnome.evolution-data-server.calendar.gschema.xml.in
index 4878eb5b4..1ecd78be1 100644
--- a/data/org.gnome.evolution-data-server.calendar.gschema.xml.in
+++ b/data/org.gnome.evolution-data-server.calendar.gschema.xml.in
@@ -64,6 +64,10 @@
       <default>-1</default>
       <_summary>Height of the reminder notification dialog</_summary>
     </key>
+    <key name="notify-window-paned-position" type="i">
+      <default>-1</default>
+      <_summary>Paned position in the reminder notification dialog, the split size between list of the 
events and the description</_summary>
+    </key>
     <key name="notify-completed-tasks" type="b">
       <default>true</default>
       <_summary>Show reminder notification for completed tasks</_summary>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index d69bd9931..cc6e39487 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -209,6 +209,7 @@ src/libedataserver/e-source-webdav.c
 src/libedataserver/e-time-utils.c
 src/libedataserver/e-webdav-discover.c
 src/libedataserver/e-webdav-session.c
+src/libedataserverui/e-buffer-tagger.c
 src/libedataserverui/e-credentials-prompter.c
 src/libedataserverui/e-credentials-prompter-impl-oauth2.c
 src/libedataserverui/e-credentials-prompter-impl-password.c
diff --git a/src/libedataserverui/CMakeLists.txt b/src/libedataserverui/CMakeLists.txt
index d7a026e38..ee14d92b8 100644
--- a/src/libedataserverui/CMakeLists.txt
+++ b/src/libedataserverui/CMakeLists.txt
@@ -1,6 +1,7 @@
 add_pkgconfig_file(libedataserverui.pc.in libedataserverui-${API_VERSION}.pc)
 
 set(SOURCES
+       e-buffer-tagger.c
        e-cell-renderer-color.c
        e-credentials-prompter.c
        e-credentials-prompter-impl.c
@@ -15,6 +16,7 @@ set(SOURCES
 
 set(HEADERS
        libedataserverui.h
+       e-buffer-tagger.h
        e-cell-renderer-color.h
        e-credentials-prompter.h
        e-credentials-prompter-impl.h
diff --git a/src/libedataserverui/e-buffer-tagger.c b/src/libedataserverui/e-buffer-tagger.c
new file mode 100644
index 000000000..6af23d004
--- /dev/null
+++ b/src/libedataserverui/e-buffer-tagger.c
@@ -0,0 +1,740 @@
+/*
+ * e-buffer-tagger.c
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "evolution-data-server-config.h"
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <glib/gi18n.h>
+#include <regex.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "e-buffer-tagger.h"
+
+static void
+e_show_uri (GtkWindow *parent,
+           const gchar *uri)
+{
+       GtkWidget *dialog;
+       GdkScreen *screen = NULL;
+       gchar *scheme;
+       GError *error = NULL;
+       guint32 timestamp;
+       gboolean success;
+
+       g_return_if_fail (uri != NULL);
+
+       timestamp = gtk_get_current_event_time ();
+
+       if (parent != NULL)
+               screen = gtk_widget_get_screen (GTK_WIDGET (parent));
+
+       scheme = g_uri_parse_scheme (uri);
+
+       if (!scheme || !*scheme) {
+               gchar *schemed_uri;
+
+               schemed_uri = g_strconcat ("http://";, uri, NULL);
+               success = gtk_show_uri (screen, schemed_uri, timestamp, &error);
+               g_free (schemed_uri);
+       } else {
+               success = gtk_show_uri (screen, uri, timestamp, &error);
+       }
+
+       g_free (scheme);
+
+       if (success)
+               return;
+
+       dialog = gtk_message_dialog_new_with_markup (
+               parent, GTK_DIALOG_DESTROY_WITH_PARENT,
+               GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+               "<big><b>%s</b></big>",
+               _("Could not open the link."));
+
+       gtk_message_dialog_format_secondary_text (
+               GTK_MESSAGE_DIALOG (dialog), "%s", error->message);
+
+       gtk_dialog_run (GTK_DIALOG (dialog));
+
+       gtk_widget_destroy (dialog);
+       g_error_free (error);
+}
+
+enum EBufferTaggerState {
+       E_BUFFER_TAGGER_STATE_NONE = 0,
+       E_BUFFER_TAGGER_STATE_INSDEL = 1 << 0, /* set when was called insert or delete of a text */
+       E_BUFFER_TAGGER_STATE_CHANGED = 1 << 1, /* remark of the buffer is scheduled */
+       E_BUFFER_TAGGER_STATE_IS_HOVERING = 1 << 2, /* mouse is over the link */
+       E_BUFFER_TAGGER_STATE_IS_HOVERING_TOOLTIP = 1 << 3, /* mouse is over the link and the tooltip can be 
shown */
+       E_BUFFER_TAGGER_STATE_CTRL_DOWN = 1 << 4  /* Ctrl key is down */
+};
+
+#define E_BUFFER_TAGGER_DATA_STATE "EBufferTagger::state"
+#define E_BUFFER_TAGGER_LINK_TAG   "EBufferTagger::link"
+
+struct _MagicInsertMatch {
+       const gchar *regex;
+       regex_t *preg;
+       const gchar *prefix;
+};
+
+typedef struct _MagicInsertMatch MagicInsertMatch;
+
+static MagicInsertMatch mim[] = {
+       /* prefixed expressions */
+       { 
"(news|telnet|nntp|file|http|ftp|sftp|https|webcal)://([-a-z0-9]+(:[-a-z0-9]+)?@)?[-a-z0-9.]+[-a-z0-9](:[0-9]*)?(([.])?/[-a-z0-9_$.+!*(),;:@%&=?/~#']*[^]'.}>\\)
 \n\r\t,?!;:\"]?)?", NULL, NULL },
+       { 
"(sip|h323|callto|tel):([-_a-z0-9.'\\+]+(:[0-9]{1,5})?(/[-_a-z0-9.']+)?)(@([-_a-z0-9.%=?]+|([0-9]{1,3}.){3}[0-9]{1,3})?)?(:[0-9]{1,5})?",
 NULL, NULL },
+       { "mailto:[-_a-z0-9.'\\+]+@[-_a-z0-9.%=?]+", NULL, NULL },
+       /* not prefixed expression */
+       { "www\\.[-a-z0-9.]+[-a-z0-9](:[0-9]*)?(([.])?/[-A-Za-z0-9_$.+!*(),;:@%&=?/~#]*[^]'.}>\\) 
\n\r\t,?!;:\"]?)?", NULL, "http://"; },
+       { "ftp\\.[-a-z0-9.]+[-a-z0-9](:[0-9]*)?(([.])?/[-A-Za-z0-9_$.+!*(),;:@%&=?/~#]*[^]'.}>\\) 
\n\r\t,?!;:\"]?)?", NULL, "ftp://"; },
+       { "[-_a-z0-9.'\\+]+@[-_a-z0-9.%=?]+", NULL, "mailto:"; }
+};
+
+static void
+init_magic_links (void)
+{
+       static gboolean done = FALSE;
+       gint i;
+
+       if (done)
+               return;
+
+       done = TRUE;
+
+       for (i = 0; i < G_N_ELEMENTS (mim); i++) {
+               mim[i].preg = g_new0 (regex_t, 1);
+               if (regcomp (mim[i].preg, mim[i].regex, REG_EXTENDED | REG_ICASE)) {
+                       /* error */
+                       g_free (mim[i].preg);
+                       mim[i].preg = 0;
+               }
+       }
+}
+
+static void
+markup_text (GtkTextBuffer *buffer)
+{
+       GtkTextIter start, end;
+       gchar *text;
+       gint i;
+       regmatch_t pmatch[2];
+       gboolean any;
+       const gchar *str;
+       gint offset = 0;
+
+       g_return_if_fail (buffer != NULL);
+
+       gtk_text_buffer_get_start_iter (buffer, &start);
+       gtk_text_buffer_get_end_iter (buffer, &end);
+       gtk_text_buffer_remove_tag_by_name (buffer, E_BUFFER_TAGGER_LINK_TAG, &start, &end);
+       text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+
+       str = text;
+       any = TRUE;
+       while (any) {
+               any = FALSE;
+               for (i = 0; i < G_N_ELEMENTS (mim); i++) {
+                       if (mim[i].preg && !regexec (mim[i].preg, str, 2, pmatch, 0)) {
+                               gint char_so, char_eo;
+
+                               char_so = g_utf8_pointer_to_offset (str, str + pmatch[0].rm_so);
+                               char_eo = g_utf8_pointer_to_offset (str, str + pmatch[0].rm_eo);
+
+                               gtk_text_buffer_get_iter_at_offset (buffer, &start, offset + char_so);
+                               gtk_text_buffer_get_iter_at_offset (buffer, &end, offset + char_eo);
+                               gtk_text_buffer_apply_tag_by_name (buffer, E_BUFFER_TAGGER_LINK_TAG, &start, 
&end);
+
+                               any = TRUE;
+                               str += pmatch[0].rm_eo;
+                               offset += char_eo;
+                               break;
+                       }
+               }
+       }
+
+       g_free (text);
+}
+
+static void
+get_pointer_position (GtkTextView *text_view,
+                      gint *x,
+                      gint *y)
+{
+       GdkWindow *window;
+       GdkDisplay *display;
+       GdkDeviceManager *device_manager;
+       GdkDevice *device;
+
+       window = gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_WIDGET);
+       display = gdk_window_get_display (window);
+       device_manager = gdk_display_get_device_manager (display);
+       device = gdk_device_manager_get_client_pointer (device_manager);
+
+       gdk_window_get_device_position (window, device, x, y, NULL);
+}
+
+static guint32
+get_state (GtkTextBuffer *buffer)
+{
+       g_return_val_if_fail (buffer != NULL, E_BUFFER_TAGGER_STATE_NONE);
+       g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), E_BUFFER_TAGGER_STATE_NONE);
+
+       return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (buffer), E_BUFFER_TAGGER_DATA_STATE));
+}
+
+static void
+set_state (GtkTextBuffer *buffer,
+           guint32 state)
+{
+       g_object_set_data (G_OBJECT (buffer), E_BUFFER_TAGGER_DATA_STATE, GINT_TO_POINTER (state));
+}
+
+static void
+update_state (GtkTextBuffer *buffer,
+              guint32 value,
+              gboolean do_set)
+{
+       guint32 state;
+
+       g_return_if_fail (buffer != NULL);
+       g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
+
+       state = get_state (buffer);
+
+       if (do_set)
+               state = state | value;
+       else
+               state = state & (~value);
+
+       set_state (buffer, state);
+}
+
+static gboolean
+get_tag_bounds (GtkTextIter *iter,
+                GtkTextTag *tag,
+                GtkTextIter *start,
+                GtkTextIter *end)
+{
+       gboolean res = FALSE;
+
+       g_return_val_if_fail (iter != NULL, FALSE);
+       g_return_val_if_fail (tag != NULL, FALSE);
+       g_return_val_if_fail (start != NULL, FALSE);
+       g_return_val_if_fail (end != NULL, FALSE);
+
+       if (gtk_text_iter_has_tag (iter, tag)) {
+               *start = *iter;
+               *end = *iter;
+
+               if (!gtk_text_iter_begins_tag (start, tag))
+                       gtk_text_iter_backward_to_tag_toggle (start, tag);
+
+               if (!gtk_text_iter_ends_tag (end, tag))
+                       gtk_text_iter_forward_to_tag_toggle (end, tag);
+
+               res = TRUE;
+       }
+
+       return res;
+}
+
+static gchar *
+get_url_at_iter (GtkTextBuffer *buffer,
+                 GtkTextIter *iter)
+{
+       GtkTextTagTable *tag_table;
+       GtkTextTag *tag;
+       GtkTextIter start, end;
+       gchar *url = NULL;
+
+       g_return_val_if_fail (buffer != NULL, NULL);
+
+       tag_table = gtk_text_buffer_get_tag_table (buffer);
+       tag = gtk_text_tag_table_lookup (tag_table, E_BUFFER_TAGGER_LINK_TAG);
+       g_return_val_if_fail (tag != NULL, NULL);
+
+       if (get_tag_bounds (iter, tag, &start, &end))
+               url = gtk_text_iter_get_text (&start, &end);
+
+       return url;
+}
+
+static gboolean
+invoke_link_if_present (GtkTextBuffer *buffer,
+                        GtkTextIter *iter)
+{
+       gboolean res;
+       gchar *url;
+
+       g_return_val_if_fail (buffer != NULL, FALSE);
+
+       url = get_url_at_iter (buffer, iter);
+
+       res = url && *url;
+       if (res)
+               e_show_uri (NULL, url);
+
+       g_free (url);
+
+       return res;
+}
+
+static void
+remove_tag_if_present (GtkTextBuffer *buffer,
+                       GtkTextIter *where)
+{
+       GtkTextTagTable *tag_table;
+       GtkTextTag *tag;
+       GtkTextIter start, end;
+
+       g_return_if_fail (buffer != NULL);
+       g_return_if_fail (where != NULL);
+
+       tag_table = gtk_text_buffer_get_tag_table (buffer);
+       tag = gtk_text_tag_table_lookup (tag_table, E_BUFFER_TAGGER_LINK_TAG);
+       g_return_if_fail (tag != NULL);
+
+       if (get_tag_bounds (where, tag, &start, &end))
+               gtk_text_buffer_remove_tag (buffer, tag, &start, &end);
+}
+
+static void
+buffer_insert_text (GtkTextBuffer *buffer,
+                    GtkTextIter *location,
+                    gchar *text,
+                    gint len,
+                    gpointer user_data)
+{
+       update_state (buffer, E_BUFFER_TAGGER_STATE_INSDEL, TRUE);
+       remove_tag_if_present (buffer, location);
+}
+
+static void
+buffer_delete_range (GtkTextBuffer *buffer,
+                     GtkTextIter *start,
+                     GtkTextIter *end,
+                     gpointer user_data)
+{
+       update_state (buffer, E_BUFFER_TAGGER_STATE_INSDEL, TRUE);
+       remove_tag_if_present (buffer, start);
+       remove_tag_if_present (buffer, end);
+}
+
+static void
+buffer_cursor_position (GtkTextBuffer *buffer,
+                        gpointer user_data)
+{
+       guint32 state;
+
+       state = get_state (buffer);
+       if (state & E_BUFFER_TAGGER_STATE_INSDEL) {
+               state = (state & (~E_BUFFER_TAGGER_STATE_INSDEL)) | E_BUFFER_TAGGER_STATE_CHANGED;
+       } else {
+               if (state & E_BUFFER_TAGGER_STATE_CHANGED) {
+                       markup_text (buffer);
+               }
+
+               state = state & (~ (E_BUFFER_TAGGER_STATE_CHANGED | E_BUFFER_TAGGER_STATE_INSDEL));
+       }
+
+       set_state (buffer, state);
+}
+
+static void
+update_mouse_cursor (GtkTextView *text_view,
+                     gint x,
+                     gint y)
+{
+       static GdkCursor *hand_cursor = NULL;
+       static GdkCursor *regular_cursor = NULL;
+       gboolean hovering = FALSE, hovering_over_link = FALSE, hovering_real;
+       guint32 state;
+       GtkTextBuffer *buffer = gtk_text_view_get_buffer (text_view);
+       GtkTextTagTable *tag_table;
+       GtkTextTag *tag;
+       GtkTextIter iter;
+
+       if (!hand_cursor) {
+               hand_cursor = gdk_cursor_new (GDK_HAND2);
+               regular_cursor = gdk_cursor_new (GDK_XTERM);
+       }
+
+       g_return_if_fail (buffer != NULL);
+
+       tag_table = gtk_text_buffer_get_tag_table (buffer);
+       tag = gtk_text_tag_table_lookup (tag_table, E_BUFFER_TAGGER_LINK_TAG);
+       g_return_if_fail (tag != NULL);
+
+       state = get_state (buffer);
+
+       gtk_text_view_get_iter_at_location (text_view, &iter, x, y);
+       hovering_real = gtk_text_iter_has_tag (&iter, tag);
+
+       hovering_over_link = (state & E_BUFFER_TAGGER_STATE_IS_HOVERING) != 0;
+       if ((state & E_BUFFER_TAGGER_STATE_CTRL_DOWN) == 0) {
+               hovering = FALSE;
+       } else {
+               hovering = hovering_real;
+       }
+
+       if (hovering != hovering_over_link) {
+               update_state (buffer, E_BUFFER_TAGGER_STATE_IS_HOVERING, hovering);
+
+               if (hovering && gtk_widget_has_focus (GTK_WIDGET (text_view)))
+                       gdk_window_set_cursor (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT), 
hand_cursor);
+               else
+                       gdk_window_set_cursor (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT), 
regular_cursor);
+
+               /* XXX Is this necessary?  Appears to be a no-op. */
+               get_pointer_position (text_view, NULL, NULL);
+       }
+
+       hovering_over_link = (state & E_BUFFER_TAGGER_STATE_IS_HOVERING_TOOLTIP) != 0;
+
+       if (hovering_real != hovering_over_link) {
+               update_state (buffer, E_BUFFER_TAGGER_STATE_IS_HOVERING_TOOLTIP, hovering_real);
+
+               gtk_widget_trigger_tooltip_query (GTK_WIDGET (text_view));
+       }
+}
+
+static gboolean
+textview_query_tooltip (GtkTextView *text_view,
+                        gint x,
+                        gint y,
+                        gboolean keyboard_mode,
+                        GtkTooltip *tooltip,
+                        gpointer user_data)
+{
+       GtkTextBuffer *buffer;
+       guint32 state;
+       gboolean res = FALSE;
+
+       if (keyboard_mode)
+               return FALSE;
+
+       buffer = gtk_text_view_get_buffer (text_view);
+       g_return_val_if_fail (buffer != NULL, FALSE);
+
+       state = get_state (buffer);
+
+       if ((state & E_BUFFER_TAGGER_STATE_IS_HOVERING_TOOLTIP) != 0) {
+               gchar *url;
+               GtkTextIter iter;
+
+               gtk_text_view_window_to_buffer_coords (
+                       text_view,
+                       GTK_TEXT_WINDOW_WIDGET,
+                       x, y, &x, &y);
+               gtk_text_view_get_iter_at_location (text_view, &iter, x, y);
+
+               url = get_url_at_iter (buffer, &iter);
+               res = url && *url;
+
+               if (res) {
+                       gchar *str;
+
+                       /* To Translators: The text is concatenated to a form: "Ctrl-click to open a link 
http://www.example.com"; */
+                       str = g_strconcat (_("Ctrl-click to open a link"), " ", url, NULL);
+                       gtk_tooltip_set_text (tooltip, str);
+                       g_free (str);
+               }
+
+               g_free (url);
+       }
+
+       return res;
+}
+
+/* Links can be activated by pressing Enter. */
+static gboolean
+textview_key_press_event (GtkWidget *text_view,
+                          GdkEventKey *event)
+{
+       GtkTextIter iter;
+       GtkTextBuffer *buffer;
+
+       if ((event->state & GDK_CONTROL_MASK) == 0)
+               return FALSE;
+
+       switch (event->keyval) {
+       case GDK_KEY_Return:
+       case GDK_KEY_KP_Enter:
+               buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
+               gtk_text_buffer_get_iter_at_mark (buffer, &iter, gtk_text_buffer_get_insert (buffer));
+               if (invoke_link_if_present (buffer, &iter))
+                       return TRUE;
+               break;
+
+       default:
+               break;
+       }
+
+       return FALSE;
+}
+
+static void
+update_ctrl_state (GtkTextView *textview,
+                   gboolean ctrl_is_down)
+{
+       GtkTextBuffer *buffer;
+       gint x, y;
+
+       buffer = gtk_text_view_get_buffer (textview);
+       if (buffer) {
+               if (((get_state (buffer) & E_BUFFER_TAGGER_STATE_CTRL_DOWN) != 0) != (ctrl_is_down != FALSE)) 
{
+                       update_state (buffer, E_BUFFER_TAGGER_STATE_CTRL_DOWN, ctrl_is_down != FALSE);
+               }
+
+               get_pointer_position (textview, &x, &y);
+               gtk_text_view_window_to_buffer_coords (textview, GTK_TEXT_WINDOW_WIDGET, x, y, &x, &y);
+               update_mouse_cursor (textview, x, y);
+       }
+}
+
+/* Links can also be activated by clicking. */
+static gboolean
+textview_event_after (GtkTextView *textview,
+                      GdkEvent *event)
+{
+       GtkTextIter start, end, iter;
+       GtkTextBuffer *buffer;
+       gint x, y;
+       GdkModifierType mt = 0;
+       guint event_button = 0;
+       gdouble event_x_win = 0;
+       gdouble event_y_win = 0;
+
+       g_return_val_if_fail (GTK_IS_TEXT_VIEW (textview), FALSE);
+
+       if (event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE) {
+               guint event_keyval = 0;
+
+               gdk_event_get_keyval (event, &event_keyval);
+
+               switch (event_keyval) {
+                       case GDK_KEY_Control_L:
+                       case GDK_KEY_Control_R:
+                               update_ctrl_state (
+                                       textview,
+                                       event->type == GDK_KEY_PRESS);
+                               break;
+               }
+
+               return FALSE;
+       }
+
+       if (!gdk_event_get_state (event, &mt)) {
+               GdkWindow *window;
+               GdkDisplay *display;
+               GdkDeviceManager *device_manager;
+               GdkDevice *device;
+
+               window = gtk_widget_get_parent_window (GTK_WIDGET (textview));
+               display = gdk_window_get_display (window);
+               device_manager = gdk_display_get_device_manager (display);
+               device = gdk_device_manager_get_client_pointer (device_manager);
+
+               gdk_window_get_device_position (window, device, NULL, NULL, &mt);
+       }
+
+       update_ctrl_state (textview, (mt & GDK_CONTROL_MASK) != 0);
+
+       if (event->type != GDK_BUTTON_RELEASE)
+               return FALSE;
+
+       gdk_event_get_button (event, &event_button);
+       gdk_event_get_coords (event, &event_x_win, &event_y_win);
+
+       if (event_button != 1 || (mt & GDK_CONTROL_MASK) == 0)
+               return FALSE;
+
+       buffer = gtk_text_view_get_buffer (textview);
+
+       /* we shouldn't follow a link if the user has selected something */
+       gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
+       if (gtk_text_iter_get_offset (&start) != gtk_text_iter_get_offset (&end))
+               return FALSE;
+
+       gtk_text_view_window_to_buffer_coords (
+               textview,
+               GTK_TEXT_WINDOW_WIDGET,
+               event_x_win, event_y_win, &x, &y);
+
+       gtk_text_view_get_iter_at_location (textview, &iter, x, y);
+
+       invoke_link_if_present (buffer, &iter);
+       update_mouse_cursor (textview, x, y);
+
+       return FALSE;
+}
+
+static gboolean
+textview_motion_notify_event (GtkTextView *textview,
+                              GdkEventMotion *event)
+{
+       gint x, y;
+
+       g_return_val_if_fail (GTK_IS_TEXT_VIEW (textview), FALSE);
+
+       gtk_text_view_window_to_buffer_coords (
+               textview,
+               GTK_TEXT_WINDOW_WIDGET,
+               event->x, event->y, &x, &y);
+
+       update_mouse_cursor (textview, x, y);
+
+       return FALSE;
+}
+
+static gboolean
+textview_visibility_notify_event (GtkTextView *textview,
+                                  GdkEventVisibility *event)
+{
+       gint wx, wy, bx, by;
+
+       g_return_val_if_fail (GTK_IS_TEXT_VIEW (textview), FALSE);
+
+       get_pointer_position (textview, &wx, &wy);
+
+       gtk_text_view_window_to_buffer_coords (
+               textview,
+               GTK_TEXT_WINDOW_WIDGET,
+               wx, wy, &bx, &by);
+
+       update_mouse_cursor (textview, bx, by);
+
+       return FALSE;
+}
+
+void
+e_buffer_tagger_connect (GtkTextView *textview)
+{
+       GtkTextBuffer *buffer;
+       GtkTextTagTable *tag_table;
+       GtkTextTag *tag;
+
+       init_magic_links ();
+
+       g_return_if_fail (textview != NULL);
+       g_return_if_fail (GTK_IS_TEXT_VIEW (textview));
+
+       buffer = gtk_text_view_get_buffer (textview);
+       tag_table = gtk_text_buffer_get_tag_table (buffer);
+       tag = gtk_text_tag_table_lookup (tag_table, E_BUFFER_TAGGER_LINK_TAG);
+
+       /* if tag is there already, then it is connected, thus claim */
+       g_return_if_fail (tag == NULL);
+
+       gtk_text_buffer_create_tag (
+               buffer, E_BUFFER_TAGGER_LINK_TAG,
+               "foreground", "blue",
+               "underline", PANGO_UNDERLINE_SINGLE,
+               NULL);
+
+       set_state (buffer, E_BUFFER_TAGGER_STATE_NONE);
+
+       g_signal_connect (
+               buffer, "insert-text",
+               G_CALLBACK (buffer_insert_text), NULL);
+       g_signal_connect (
+               buffer, "delete-range",
+               G_CALLBACK (buffer_delete_range), NULL);
+       g_signal_connect (
+               buffer, "notify::cursor-position",
+               G_CALLBACK (buffer_cursor_position), NULL);
+
+       gtk_widget_set_has_tooltip (GTK_WIDGET (textview), TRUE);
+
+       g_signal_connect (
+               textview, "query-tooltip",
+               G_CALLBACK (textview_query_tooltip), NULL);
+       g_signal_connect (
+               textview, "key-press-event",
+               G_CALLBACK (textview_key_press_event), NULL);
+       g_signal_connect (
+               textview, "event-after",
+               G_CALLBACK (textview_event_after), NULL);
+       g_signal_connect (
+               textview, "motion-notify-event",
+               G_CALLBACK (textview_motion_notify_event), NULL);
+       g_signal_connect (
+               textview, "visibility-notify-event",
+               G_CALLBACK (textview_visibility_notify_event), NULL);
+}
+
+void
+e_buffer_tagger_disconnect (GtkTextView *textview)
+{
+       GtkTextBuffer *buffer;
+       GtkTextTagTable *tag_table;
+       GtkTextTag *tag;
+
+       g_return_if_fail (textview != NULL);
+       g_return_if_fail (GTK_IS_TEXT_VIEW (textview));
+
+       buffer = gtk_text_view_get_buffer (textview);
+       tag_table = gtk_text_buffer_get_tag_table (buffer);
+       tag = gtk_text_tag_table_lookup (tag_table, E_BUFFER_TAGGER_LINK_TAG);
+
+       /* if tag is not there, then it is not connected, thus claim */
+       g_return_if_fail (tag != NULL);
+
+       gtk_text_tag_table_remove (tag_table, tag);
+
+       set_state (buffer, E_BUFFER_TAGGER_STATE_NONE);
+
+       g_signal_handlers_disconnect_by_func (buffer, G_CALLBACK (buffer_insert_text), NULL);
+       g_signal_handlers_disconnect_by_func (buffer, G_CALLBACK (buffer_delete_range), NULL);
+       g_signal_handlers_disconnect_by_func (buffer, G_CALLBACK (buffer_cursor_position), NULL);
+
+       gtk_widget_set_has_tooltip (GTK_WIDGET (textview), FALSE);
+
+       g_signal_handlers_disconnect_by_func (textview, G_CALLBACK (textview_query_tooltip), NULL);
+       g_signal_handlers_disconnect_by_func (textview, G_CALLBACK (textview_key_press_event), NULL);
+       g_signal_handlers_disconnect_by_func (textview, G_CALLBACK (textview_event_after), NULL);
+       g_signal_handlers_disconnect_by_func (textview, G_CALLBACK (textview_motion_notify_event), NULL);
+       g_signal_handlers_disconnect_by_func (textview, G_CALLBACK (textview_visibility_notify_event), NULL);
+}
+
+void
+e_buffer_tagger_update_tags (GtkTextView *textview)
+{
+       GtkTextBuffer *buffer;
+       GtkTextTagTable *tag_table;
+       GtkTextTag *tag;
+
+       g_return_if_fail (textview != NULL);
+       g_return_if_fail (GTK_IS_TEXT_VIEW (textview));
+
+       buffer = gtk_text_view_get_buffer (textview);
+       tag_table = gtk_text_buffer_get_tag_table (buffer);
+       tag = gtk_text_tag_table_lookup (tag_table, E_BUFFER_TAGGER_LINK_TAG);
+
+       /* if tag is not there, then it is not connected, thus claim */
+       g_return_if_fail (tag != NULL);
+
+       update_state (buffer, E_BUFFER_TAGGER_STATE_INSDEL | E_BUFFER_TAGGER_STATE_CHANGED, FALSE);
+
+       markup_text (buffer);
+}
diff --git a/src/libedataserverui/e-buffer-tagger.h b/src/libedataserverui/e-buffer-tagger.h
new file mode 100644
index 000000000..c6cf386ac
--- /dev/null
+++ b/src/libedataserverui/e-buffer-tagger.h
@@ -0,0 +1,38 @@
+/*
+ * e-buffer-tagger.h
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#if !defined (__LIBEDATASERVERUI_H_INSIDE__) && !defined (LIBEDATASERVERUI_COMPILATION)
+#error "Only <libedataserverui/libedataserverui.h> should be included directly."
+#endif
+
+#ifndef E_BUFFER_TAGGER_H
+#define E_BUFFER_TAGGER_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+void e_buffer_tagger_connect     (GtkTextView *textview);
+void e_buffer_tagger_disconnect  (GtkTextView *textview);
+void e_buffer_tagger_update_tags (GtkTextView *textview);
+
+G_END_DECLS
+
+#endif /* E_BUFFER_TAGGER_H */
diff --git a/src/libedataserverui/e-reminders-widget.c b/src/libedataserverui/e-reminders-widget.c
index 9838398e9..d9ae6ef05 100644
--- a/src/libedataserverui/e-reminders-widget.c
+++ b/src/libedataserverui/e-reminders-widget.c
@@ -40,6 +40,7 @@
 
 #include "libedataserverui-private.h"
 
+#include "e-buffer-tagger.h"
 #include "e-reminders-widget.h"
 
 #define MAX_CUSTOM_SNOOZE_VALUES 7
@@ -49,7 +50,10 @@ struct _ERemindersWidgetPrivate {
        GSettings *settings;
        gboolean is_empty;
 
+       GtkPaned *paned;
+
        GtkTreeView *tree_view;
+       GtkTextView *details_text_view;
        GtkWidget *dismiss_button;
        GtkWidget *dismiss_all_button;
        GtkWidget *snooze_combo;
@@ -934,11 +938,30 @@ reminders_widget_row_activated_cb (GtkTreeView *tree_view,
        }
 }
 
+static void
+reminders_widget_set_text_buffer_markup (GtkTextBuffer *buffer,
+                                        const gchar *markup)
+{
+       GtkTextIter start, end;
+
+       g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
+       g_return_if_fail (markup != NULL);
+
+       gtk_text_buffer_get_start_iter (buffer, &start);
+       gtk_text_buffer_get_end_iter (buffer, &end);
+       gtk_text_buffer_delete (buffer, &start, &end);
+
+       gtk_text_buffer_get_start_iter (buffer, &start);
+
+       gtk_text_buffer_insert_markup (buffer, &start, markup, -1);
+}
+
 static void
 reminders_widget_selection_changed_cb (GtkTreeSelection *selection,
                                       gpointer user_data)
 {
        ERemindersWidget *reminders = user_data;
+       gchar *markup = NULL;
        gint nselected;
 
        g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
@@ -948,6 +971,77 @@ reminders_widget_selection_changed_cb (GtkTreeSelection *selection,
        gtk_widget_set_sensitive (reminders->priv->snooze_combo, nselected > 0);
        gtk_widget_set_sensitive (reminders->priv->snooze_button, nselected > 0);
        gtk_widget_set_sensitive (reminders->priv->dismiss_button, nselected > 0);
+
+       if (nselected == 0) {
+               markup = g_markup_printf_escaped ("<i>%s</i>", _("No reminder is selected."));
+       } else if (nselected == 1) {
+               GList *rows;
+               GtkTreeIter iter;
+               GtkTreeModel *model = NULL;
+
+               rows = gtk_tree_selection_get_selected_rows (selection, &model);
+               g_return_if_fail (rows != NULL);
+               g_return_if_fail (model != NULL);
+
+               if (gtk_tree_model_get_iter (model, &iter, rows->data)) {
+                       EReminderData *rd = NULL;
+                       gchar *description = NULL;
+
+                       gtk_tree_model_get (model, &iter,
+                               E_REMINDERS_WIDGET_COLUMN_DESCRIPTION, &description,
+                               E_REMINDERS_WIDGET_COLUMN_REMINDER_DATA, &rd,
+                               -1);
+
+                       if (rd) {
+                               ECalComponent *comp;
+
+                               comp = e_reminder_data_get_component (rd);
+
+                               if (comp) {
+                                       ICalComponent *icomp;
+
+                                       icomp = e_cal_component_get_icalcomponent (comp);
+
+                                       if (icomp) {
+                                               const gchar *icomp_description;
+
+                                               icomp_description = i_cal_component_get_description (icomp);
+
+                                               if (icomp_description && *icomp_description) {
+                                                       gchar *tmp;
+
+                                                       tmp = g_markup_escape_text (icomp_description, -1);
+
+                                                       markup = g_strconcat (description, "\n\n<tt>", tmp, 
"</tt>", NULL);
+
+                                                       g_free (tmp);
+                                               }
+                                       }
+                               }
+
+                               if (!markup) {
+                                       markup = description;
+                                       description = NULL;
+                               }
+                       }
+
+                       e_reminder_data_free (rd);
+                       g_free (description);
+               }
+
+               if (!markup)
+                       markup = g_markup_printf_escaped ("<i>%s</i>", _("No details are available."));
+
+               g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
+       } else {
+               markup = g_markup_printf_escaped ("<i>%s</i>", _("Multiple reminders are selected."));
+       }
+
+       reminders_widget_set_text_buffer_markup (gtk_text_view_get_buffer 
(reminders->priv->details_text_view), markup);
+
+       e_buffer_tagger_update_tags (reminders->priv->details_text_view);
+
+       g_free (markup);
 }
 
 static void
@@ -1379,6 +1473,10 @@ reminders_widget_constructed (GObject *object)
        /* Chain up to parent's method. */
        G_OBJECT_CLASS (e_reminders_widget_parent_class)->constructed (object);
 
+       reminders->priv->paned = GTK_PANED (gtk_paned_new (GTK_ORIENTATION_VERTICAL));
+
+       gtk_grid_attach (GTK_GRID (reminders), GTK_WIDGET (reminders->priv->paned), 0, 0, 1, 1);
+
        scrolled_window = gtk_scrolled_window_new (NULL, NULL);
        g_object_set (G_OBJECT (scrolled_window),
                "halign", GTK_ALIGN_FILL,
@@ -1390,7 +1488,7 @@ reminders_widget_constructed (GObject *object)
                "shadow-type", GTK_SHADOW_IN,
                NULL);
 
-       gtk_grid_attach (GTK_GRID (reminders), scrolled_window, 0, 0, 1, 1);
+       gtk_paned_pack1 (reminders->priv->paned, scrolled_window, FALSE, FALSE);
 
        list_store = gtk_list_store_new (E_REMINDERS_WIDGET_N_COLUMNS,
                G_TYPE_STRING, /* E_REMINDERS_WIDGET_COLUMN_OVERDUE */
@@ -1415,6 +1513,10 @@ reminders_widget_constructed (GObject *object)
 
        gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (reminders->priv->tree_view));
 
+       e_binding_bind_property (reminders, "empty",
+               scrolled_window, "sensitive",
+               G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
+
        gtk_tree_view_set_tooltip_column (reminders->priv->tree_view, E_REMINDERS_WIDGET_COLUMN_DESCRIPTION);
 
        /* Headers not visible, thus column's caption is not localized */
@@ -1435,6 +1537,34 @@ reminders_widget_constructed (GObject *object)
        column = gtk_tree_view_get_column (reminders->priv->tree_view, 1);
        gtk_tree_view_column_set_expand (column, TRUE);
 
+       scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+       g_object_set (G_OBJECT (scrolled_window),
+               "halign", GTK_ALIGN_FILL,
+               "hexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "hscrollbar-policy", GTK_POLICY_NEVER,
+               "vscrollbar-policy", GTK_POLICY_AUTOMATIC,
+               "shadow-type", GTK_SHADOW_IN,
+               NULL);
+
+       gtk_paned_pack2 (reminders->priv->paned, scrolled_window, TRUE, FALSE);
+
+       reminders->priv->details_text_view = GTK_TEXT_VIEW (gtk_text_view_new ());
+
+       g_object_set (G_OBJECT (reminders->priv->details_text_view),
+               "halign", GTK_ALIGN_FILL,
+               "hexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "editable", FALSE,
+               "wrap-mode", GTK_WRAP_WORD_CHAR,
+               NULL);
+
+       gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (reminders->priv->details_text_view));
+
+       e_buffer_tagger_connect (reminders->priv->details_text_view);
+
        reminders->priv->dismiss_button = gtk_button_new_with_mnemonic (_("_Dismiss"));
        reminders->priv->dismiss_all_button = gtk_button_new_with_mnemonic (_("Dismiss _All"));
        reminders->priv->snooze_combo = reminders_widget_new_snooze_combo ();
@@ -1498,10 +1628,6 @@ reminders_widget_constructed (GObject *object)
                reminders->priv->dismiss_all_button, "sensitive",
                G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
 
-       e_binding_bind_property (reminders, "empty",
-               scrolled_window, "sensitive",
-               G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
-
        _libedataserverui_load_modules ();
 
        e_extensible_load_extensions (E_EXTENSIBLE (object));
@@ -1744,6 +1870,23 @@ e_reminders_widget_get_tree_view (ERemindersWidget *reminders)
        return reminders->priv->tree_view;
 }
 
+/**
+ * e_reminders_widget_get_paned:
+ * @reminders: an #ERemindersWidget
+ *
+ * Returns: (transfer none): a #GtkPaned used to split list of events and
+ *    the description of the reminders. It's owned by the @reminders widget.
+ *
+ * Since: 3.38
+ **/
+GtkPaned *
+e_reminders_widget_get_paned (ERemindersWidget *reminders)
+{
+       g_return_val_if_fail (E_IS_REMINDERS_WIDGET (reminders), NULL);
+
+       return reminders->priv->paned;
+}
+
 static void
 reminders_widget_error_response_cb (GtkInfoBar *info_bar,
                                    gint response_id,
diff --git a/src/libedataserverui/e-reminders-widget.h b/src/libedataserverui/e-reminders-widget.h
index ea20c137d..d63f4fce6 100644
--- a/src/libedataserverui/e-reminders-widget.h
+++ b/src/libedataserverui/e-reminders-widget.h
@@ -100,6 +100,7 @@ EReminderWatcher *
 GSettings *    e_reminders_widget_get_settings (ERemindersWidget *reminders);
 gboolean       e_reminders_widget_is_empty     (ERemindersWidget *reminders);
 GtkTreeView *  e_reminders_widget_get_tree_view(ERemindersWidget *reminders);
+GtkPaned *     e_reminders_widget_get_paned    (ERemindersWidget *reminders);
 void           e_reminders_widget_report_error (ERemindersWidget *reminders,
                                                 const gchar *prefix,
                                                 const GError *error);
diff --git a/src/libedataserverui/libedataserverui.h b/src/libedataserverui/libedataserverui.h
index 30def6370..0fc2d03f0 100644
--- a/src/libedataserverui/libedataserverui.h
+++ b/src/libedataserverui/libedataserverui.h
@@ -20,6 +20,7 @@
 
 #define __LIBEDATASERVERUI_H_INSIDE__
 
+#include <libedataserverui/e-buffer-tagger.h>
 #include <libedataserverui/e-cell-renderer-color.h>
 #include <libedataserverui/e-credentials-prompter.h>
 #include <libedataserverui/e-credentials-prompter-impl.h>
diff --git a/src/services/evolution-alarm-notify/e-alarm-notify.c 
b/src/services/evolution-alarm-notify/e-alarm-notify.c
index 363765314..341a7bfa0 100644
--- a/src/services/evolution-alarm-notify/e-alarm-notify.c
+++ b/src/services/evolution-alarm-notify/e-alarm-notify.c
@@ -51,6 +51,7 @@ struct _EAlarmNotifyPrivate {
        gint window_y;
        gint window_width;
        gint window_height;
+       gint window_paned_position;
        gint window_geometry_save_id;
 
        GMutex dismiss_lock;
@@ -113,8 +114,21 @@ e_alarm_notify_show_window (EAlarmNotify *an,
 
        gtk_window_present (window);
 
-       if (!was_visible)
+       if (!was_visible) {
+               GtkTreeSelection *selection;
+
                gtk_window_move (window, an->priv->window_x, an->priv->window_y);
+
+               selection = gtk_tree_view_get_selection (e_reminders_widget_get_tree_view 
(an->priv->reminders));
+
+               if (!gtk_tree_selection_count_selected_rows (selection)) {
+                       GtkTreePath *path;
+
+                       path = gtk_tree_path_new_first ();
+                       gtk_tree_selection_select_path (selection, path);
+                       gtk_tree_path_free (path);
+               }
+       }
 }
 
 static gboolean
@@ -935,6 +949,7 @@ e_alarm_notify_window_geometry_save_cb (gpointer user_data)
                set_if_changed ("notify-window-y", an->priv->window_y);
                set_if_changed ("notify-window-width", an->priv->window_width);
                set_if_changed ("notify-window-height", an->priv->window_height);
+               set_if_changed ("notify-window-paned-position", an->priv->window_paned_position);
 
                #undef set_if_changed
        }
@@ -942,6 +957,32 @@ e_alarm_notify_window_geometry_save_cb (gpointer user_data)
        return FALSE;
 }
 
+static void
+e_alarm_notify_paned_position_changed_cb (GObject *paned,
+                                         GParamSpec *param,
+                                         gpointer user_data)
+{
+       EAlarmNotify *an = user_data;
+
+       g_return_if_fail (E_IS_ALARM_NOTIFY (an));
+
+       if (an->priv->reminders && an->priv->settings) {
+               gint paned_position;
+
+               paned_position = gtk_paned_get_position (e_reminders_widget_get_paned (an->priv->reminders));
+
+               if (paned_position != an->priv->window_paned_position) {
+                       an->priv->window_paned_position = paned_position;
+
+                       if (an->priv->window_geometry_save_id > 0)
+                               g_source_remove (an->priv->window_geometry_save_id);
+
+                       an->priv->window_geometry_save_id = e_named_timeout_add_seconds (1,
+                               e_alarm_notify_window_geometry_save_cb, an);
+               }
+       }
+}
+
 static gboolean
 e_alarm_notify_window_configure_event_cb (GtkWidget *widget,
                                          GdkEvent *event,
@@ -951,19 +992,24 @@ e_alarm_notify_window_configure_event_cb (GtkWidget *widget,
 
        g_return_val_if_fail (E_IS_ALARM_NOTIFY (an), FALSE);
 
-       if (an->priv->window && an->priv->settings && gtk_widget_get_visible (an->priv->window)) {
+       if (an->priv->window && an->priv->settings && an->priv->reminders && gtk_widget_get_visible 
(an->priv->window)) {
                gint pos_x = an->priv->window_x, pos_y = an->priv->window_y;
                gint width = an->priv->window_width, height = an->priv->window_height;
+               gint paned_position;
 
                gtk_window_get_position (GTK_WINDOW (an->priv->window), &pos_x, &pos_y);
                gtk_window_get_size (GTK_WINDOW (an->priv->window), &width, &height);
 
+               paned_position = gtk_paned_get_position (e_reminders_widget_get_paned (an->priv->reminders));
+
                if (pos_x != an->priv->window_x || pos_y != an->priv->window_y ||
-                   width != an->priv->window_width || height != an->priv->window_height) {
+                   width != an->priv->window_width || height != an->priv->window_height ||
+                   paned_position != an->priv->window_paned_position) {
                        an->priv->window_x = pos_x;
                        an->priv->window_y = pos_y;
                        an->priv->window_width = width;
                        an->priv->window_height = height;
+                       an->priv->window_paned_position = paned_position;
 
                        if (an->priv->window_geometry_save_id > 0)
                                g_source_remove (an->priv->window_geometry_save_id);
@@ -1017,6 +1063,7 @@ static void
 e_alarm_notify_activate (GApplication *application)
 {
        EAlarmNotify *an = E_ALARM_NOTIFY (application);
+       gint paned_position;
 
        if (g_application_get_is_remote (application)) {
                g_application_quit (application);
@@ -1045,6 +1092,14 @@ e_alarm_notify_activate (GApplication *application)
        an->priv->window_x = g_settings_get_int (an->priv->settings, "notify-window-x");
        an->priv->window_y = g_settings_get_int (an->priv->settings, "notify-window-y");
 
+       paned_position = g_settings_get_int (an->priv->settings, "notify-window-paned-position");
+
+       if (paned_position <= 0 && g_settings_get_int (an->priv->settings, "notify-window-height") > 0)
+               paned_position = g_settings_get_int (an->priv->settings, "notify-window-height") / 2;
+
+       if (paned_position > 0)
+               gtk_paned_set_position (e_reminders_widget_get_paned (an->priv->reminders), paned_position);
+
        gtk_container_add (GTK_CONTAINER (an->priv->window), GTK_WIDGET (an->priv->reminders));
 
        gtk_window_set_keep_above (GTK_WINDOW (an->priv->window), g_settings_get_boolean (an->priv->settings, 
"notify-window-on-top"));
@@ -1055,6 +1110,9 @@ e_alarm_notify_activate (GApplication *application)
        g_signal_connect (an->priv->reminders, "changed",
                G_CALLBACK (e_alarm_notify_reminders_changed_cb), an);
 
+       g_signal_connect (e_reminders_widget_get_paned (an->priv->reminders), "notify::position",
+               G_CALLBACK (e_alarm_notify_paned_position_changed_cb), an);
+
        g_signal_connect (an->priv->window, "configure-event",
                G_CALLBACK (e_alarm_notify_window_configure_event_cb), an);
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]