[Xfce4-commits] <orage:master> 4.7.4.7 Timezone selection improvments

Juha Kautto noreply at xfce.org
Fri Oct 2 12:54:01 CEST 2009


Updating branch refs/heads/master
         to 1b1ae3db8ff86378f647ef174a62161a36d2de10 (commit)
       from e41c58b12832936906baa2f87025b4758b8725f7 (commit)

commit 1b1ae3db8ff86378f647ef174a62161a36d2de10
Author: Juha Kautto <juha at xfce.org>
Date:   Fri Oct 2 13:50:52 2009 +0300

    4.7.4.7 Timezone selection improvments
    
    Extra information now shows next time and how clock changes. Also
    fixed how time change times are shown.
    Globaltime now has better timeadjust mode.

 NEWS                                   |   13 ++
 configure.in.in                        |    2 +-
 globaltime/globaltime.c                |   27 +++-
 globaltime/globaltime.h                |    3 +-
 globaltime/gt_prefs.c                  |  129 +++++++----------
 globaltime/timezone_selection.c        |  173 +++++++++++++++--------
 globaltime/timezone_selection.h        |    2 +-
 globaltime/tz_zoneinfo_read.c          |  245 ++++++++++++++++++++++++--------
 globaltime/tz_zoneinfo_read.h          |    4 +-
 panel-plugin/oc_config.c               |   44 +-----
 panel-plugin/timezone_selection.c      |  173 +++++++++++++++--------
 panel-plugin/timezone_selection.h      |    2 +-
 panel-plugin/tz_zoneinfo_read.c        |  247 ++++++++++++++++++++++++--------
 panel-plugin/tz_zoneinfo_read.h        |    4 +-
 panel-plugin/xfce4-orageclock-plugin.h |    1 -
 src/appointment.c                      |    6 +-
 src/parameters.c                       |    2 +-
 src/timezone_selection.c               |  176 ++++++++++++++---------
 src/timezone_selection.h               |    2 +-
 src/tz_zoneinfo_read.c                 |   42 +++++-
 src/tz_zoneinfo_read.h                 |    1 +
 21 files changed, 853 insertions(+), 445 deletions(-)

diff --git a/NEWS b/NEWS
index 682c15a..5591a0a 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,16 @@
+20091002: Globaltime and panel plugin updates
+---------
+        * Change timezone selection to be exactly the same than in Orage
+            - Removed the entry box and only include the button now. 
+            - It is not as flexible, but it is easier to use for non-gurus.
+        * Modified time adjust mode in globaltime to start from zero minute.
+            - By default it now changes 30 mins per click.
+            - Page Up and Page Down can be used to tune only 1 minute.
+            - Removed message informing time change mode is starting.
+        * Timezone extra information now shows correct time change time
+            - wel, it is 1 sec off, but close enough...
+        * Timezone extra information now shows also next time and how it changes
+
 20090906: Version 4.7.4 
 ---------
         * New timezone selection dialog (Bug 3876)
diff --git a/configure.in.in b/configure.in.in
index ac28494..9fe9134 100644
--- a/configure.in.in
+++ b/configure.in.in
@@ -9,7 +9,7 @@ dnl Written for Xfce by Juha Kautto <juha at xfce.org>
 dnl
 
 dnl Version information
-m4_define([orage_version], [4.7.4.6-test])
+m4_define([orage_version], [4.7.4.7-test])
 
 m4_define([gtk_minimum_version], [2.10.0])
 m4_define([xfce_minimum_version], [4.6.0])
diff --git a/globaltime/globaltime.c b/globaltime/globaltime.c
index 6c6f733..1c5377a 100644
--- a/globaltime/globaltime.c
+++ b/globaltime/globaltime.c
@@ -49,7 +49,7 @@
 #include "globaltime.h"
 
 
-#define NAME_VERSION "Global Time (2.0)"
+#define NAME_VERSION "Global Time (2.1)"
 
 
 global_times_struct clocks;
@@ -342,6 +342,8 @@ void show_clock(clock_struct *clockp, gint *pos)
 static gboolean preferences_button_pressed(GtkWidget *widget
         , GdkEventButton *event, gpointer dummy)
 {
+    struct tm *now;
+
     if (event->button == 2) { /* toggle spinbox sensitivity */
         if (clocks.time_adj_act) { /* end it */
             gtk_widget_hide(clocks.hdr_adj_mm);
@@ -355,15 +357,19 @@ static gboolean preferences_button_pressed(GtkWidget *widget
             clocks.mm_adj = 0;
             clocks.hh_adj = 0;
             clocks.previous_secs = 70; /* trick to refresh clocks once */
-            g_message(_("Ending time adjustment mode"));
             gtk_window_resize(GTK_WINDOW(clocks.window), 10, 10);
         }
         else { /* start hour adjusting mode */
+            /* let's try to guess good starting value = to get times to 
+             * 0 or 30 minutes. */
+            now = gmtime(&clocks.previous_t);
+            clocks.mm_adj = (now->tm_min < 30 ? 0 : 60) - now->tm_min;
+            gtk_spin_button_set_value(GTK_SPIN_BUTTON(clocks.hdr_adj_mm)
+                    , (gdouble)clocks.mm_adj);
             gtk_widget_show(clocks.hdr_adj_mm);
             gtk_widget_show(clocks.hdr_adj_sep);
             gtk_widget_show(clocks.hdr_adj_hh);
             clocks.time_adj_act = TRUE;
-            g_message(_("Starting time adjustment mode"));
         }
         return(FALSE);
     }
@@ -408,6 +414,9 @@ static gboolean upd_clocks(void)
     struct tm *now;
     gint secs_now;
 
+    if (clocks.no_update)
+        return(TRUE);
+
     now = get_time(clocks.local_tz->str); /* GMT is returmed if not found */
     if (!clocks.time_adj_act) {
         time(&clocks.previous_t);
@@ -424,7 +433,7 @@ static gboolean upd_clocks(void)
         /* minute changed => need to update visible clocks */
         g_list_foreach(clocks.clock_list, (GFunc) upd_clock, NULL);
     clocks.previous_secs = secs_now;
-    return (TRUE);
+    return(TRUE);
 }
 
 void adj_hh_changed(GtkSpinButton *cb, gpointer user_data)
@@ -462,7 +471,7 @@ static void init_hdr_button(void)
     gtk_widget_show(image);
     gtk_widget_show(clocks.hdr_button);
 
-    clocks.hdr_adj_hh = gtk_spin_button_new_with_range(-23, 23, 1);
+    clocks.hdr_adj_hh = gtk_spin_button_new_with_range(-24, 24, 1);
     gtk_box_pack_start(GTK_BOX(clocks.hdr_hbox)
             , clocks.hdr_adj_hh, FALSE, FALSE, 0);
     gtk_spin_button_set_value(GTK_SPIN_BUTTON(clocks.hdr_adj_hh), (gdouble)0);
@@ -477,14 +486,15 @@ static void init_hdr_button(void)
     gtk_box_pack_start(GTK_BOX(clocks.hdr_hbox)
             , clocks.hdr_adj_sep, FALSE, FALSE, 0);
 
-    clocks.hdr_adj_mm = gtk_spin_button_new_with_range(-59, 59, 1);
+    clocks.hdr_adj_mm = gtk_spin_button_new_with_range(-60, 60, 1);
     gtk_box_pack_start(GTK_BOX(clocks.hdr_hbox)
             , clocks.hdr_adj_mm, FALSE, FALSE, 0);
     gtk_spin_button_set_value(GTK_SPIN_BUTTON(clocks.hdr_adj_mm), (gdouble)0);
     gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(clocks.hdr_adj_mm), TRUE);
+    gtk_spin_button_set_increments(GTK_SPIN_BUTTON(clocks.hdr_adj_mm), 30, 1);
     gtk_widget_set_size_request(clocks.hdr_adj_mm, 40, -1);
     gtk_tooltips_set_tip(clocks.tips, clocks.hdr_adj_mm
-            , _("adjust to change minute"), NULL);
+            , _("adjust to change minute. Click arrows with button 2 to change only 1 minute."), NULL);
     g_signal_connect((gpointer) clocks.hdr_adj_mm, "changed"
             , G_CALLBACK(adj_mm_changed), NULL);
     /* We want it to be hidden initially, it is special thing to do...
@@ -507,6 +517,7 @@ static void initialize_clocks()
     strcpy(clocks.time_now, "88:88");
     clocks.previous_secs = 61;      
     clocks.time_adj_act = FALSE;      
+    clocks.no_update = FALSE;
     clocks.hh_adj = 0;      
     clocks.mm_adj = 0;      
     /* done in read_file
@@ -605,7 +616,7 @@ static void create_global_time(void)
 int main(int argc, char *argv[])
 {
     xfce_textdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR, "UTF-8");
-    gtk_init (&argc, &argv);
+    gtk_init(&argc, &argv);
     create_global_time();
     gtk_main();
 
diff --git a/globaltime/globaltime.h b/globaltime/globaltime.h
index af02942..4e8c51d 100644
--- a/globaltime/globaltime.h
+++ b/globaltime/globaltime.h
@@ -32,10 +32,11 @@ typedef struct
 typedef struct
 { /* all clocks */
     GList *clock_list;      /* list of clock_structs */
-    gchar time_now[16];     /* 88:88 null terminated */
+    gchar time_now[8];      /* 88:88+ null terminated */
     gint previous_secs;
     time_t previous_t;
     gboolean time_adj_act;  /* manual time adjustment active or not */
+    gboolean no_update;     /* do not update clocks */
     gint hh_adj;            /* adjustment hours */
     gint mm_adj;            /* adjustment hours */
     GString *local_tz;      /* local timezone. Used to set local_mday */
diff --git a/globaltime/gt_prefs.c b/globaltime/gt_prefs.c
index d8b3ecf..166c846 100644
--- a/globaltime/gt_prefs.c
+++ b/globaltime/gt_prefs.c
@@ -42,7 +42,7 @@ typedef struct modify
     clock_struct *clock;
     GtkWidget *window;
     GtkWidget *name_entry;
-    GtkWidget *tz_entry;
+    GtkWidget *button_tz;
     GtkWidget *button_clock_fg;
     GtkWidget *check_button_default_fg;
     GtkWidget *button_clock_bg;
@@ -66,9 +66,9 @@ static void create_parameter_formatting(GtkWidget *vbox
 static gboolean decoration_radio_button_pressed(GtkWidget *widget, gchar *label)
 {
     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
-        if (strcmp(label, _("Standard")) == 0)
+        if (strcmp(label, "Standard") == 0)
             clocks.decorations = TRUE;
-        else if (strcmp(label, _("None")) == 0)
+        else if (strcmp(label, "None") == 0)
             clocks.decorations = FALSE;
         else 
             g_warning("Unknown selection in decoration radio button\n");
@@ -81,9 +81,9 @@ static gboolean decoration_radio_button_pressed(GtkWidget *widget, gchar *label)
 static gboolean clocksize_radio_button_pressed(GtkWidget *widget, gchar *label)
 {
     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
-        if (strcmp(label, _("Equal")) == 0)
+        if (strcmp(label, "Equal") == 0)
             clocks.expand = TRUE;
-        else if (strcmp(label, _("Vary")) == 0)
+        else if (strcmp(label, "Vary") == 0)
             clocks.expand = FALSE;
         else 
             g_warning("Unknown selection in clock size radio button\n");
@@ -108,8 +108,6 @@ static void close_preferences_window(GtkWidget *widget, GtkWidget *window)
 static gboolean save_preferences(GtkWidget *widget
         , modify_struct *modify_default)
 {
-    g_string_assign(clocks.local_tz
-            , gtk_entry_get_text(GTK_ENTRY(modify_default->tz_entry)));
     write_file();
     return(FALSE);
 }
@@ -183,8 +181,6 @@ static gboolean save_clock(GtkWidget *widget
     g_string_assign(clockp->name
             , gtk_entry_get_text(GTK_ENTRY(modify_clock->name_entry)));
     gtk_label_set_text(GTK_LABEL(clockp->name_label), clockp->name->str);
-    g_string_assign(clockp->tz
-            , gtk_entry_get_text(GTK_ENTRY(modify_clock->tz_entry)));
     write_file();
     return(FALSE);
 }
@@ -202,7 +198,7 @@ static gboolean set_timezone_from_clock(GtkWidget *widget
         , modify_struct *modify_clock)
 {
     g_string_assign(clocks.local_tz
-            , gtk_entry_get_text(GTK_ENTRY(modify_clock->tz_entry)));
+            , gtk_button_get_label((GTK_BUTTON(modify_clock->button_tz))));
     write_file();
     return(FALSE);
 }
@@ -286,8 +282,8 @@ static void copy_clock(GtkWidget *widget, modify_struct *modify_clock)
 
     new_pos = g_list_index(clocks.clock_list, clockp_old)+1;
     clockp_new = g_new(clock_struct, 1);
-    clockp_new->tz = g_string_new(gtk_entry_get_text(
-            GTK_ENTRY(modify_clock->tz_entry)));
+    clockp_new->tz = g_string_new(gtk_button_get_label(
+            GTK_BUTTON(modify_clock->button_tz)));
     clockp_new->name = g_string_new(_("NEW COPY"));
     clockp_new->modified = FALSE;
     init_attr(&clockp_new->clock_attr);
@@ -357,54 +353,43 @@ static void move_clock(GtkWidget *widget, modify_struct *modify_clock)
     write_file();
 }
 
+/* We handle here timezone setting for individual clocks, but also
+ * the generic (default) setting. We know we are doing defaults when there is no
+ * clock pointer */
 static void ask_timezone(GtkButton *button, modify_struct *modify_clock)
-/* static void ask_timezone(GtkWidget *widget, modify_struct *modify_clock) */
 {
-    /* GtkWidget *dialog; */
-    gchar *filename = NULL;
+    gchar *tz_name = NULL;
     gchar *clockname = NULL;
-
+    char env_tz[256];
+
+    /* first stop all clocks and reset time to local timezone because
+     * timezone list needs to be shown in local timezone (details show time) */
+    clocks.no_update = TRUE; 
+    if (clocks.local_tz && clocks.local_tz->str && clocks.local_tz->len) {
+        g_snprintf(env_tz, 256, "TZ=%s", clocks.local_tz->str);
+        putenv(env_tz);
+        tzset();
+    }
     if (orage_timezone_button_clicked(button, GTK_WINDOW(modify_clock->window)
-                , &filename)) {
-        gtk_entry_set_text(GTK_ENTRY(modify_clock->tz_entry), filename);
-        if (strlen(gtk_entry_get_text(GTK_ENTRY(modify_clock->name_entry))) 
-                == 0) {
-            if ((clockname = strrchr(filename, (int)'/')))
-                gtk_entry_set_text(GTK_ENTRY(modify_clock->name_entry)
-                        , clockname+1);
-            else
-                gtk_entry_set_text(GTK_ENTRY(modify_clock->name_entry)
-                        , filename);
+                , &tz_name, FALSE, NULL)) {
+        if (modify_clock->clock) { /* individual, real clock */
+            g_string_assign(modify_clock->clock->tz, tz_name);
+            if (strlen(gtk_entry_get_text(GTK_ENTRY(modify_clock->name_entry))) 
+                    == 0) {
+                if ((clockname = strrchr(tz_name, (int)'/')))
+                    gtk_entry_set_text(GTK_ENTRY(modify_clock->name_entry)
+                            , clockname+1);
+                else
+                    gtk_entry_set_text(GTK_ENTRY(modify_clock->name_entry)
+                            , tz_name);
+            }
         }
-        g_free(filename);
-    }
-
-    /*
-    dialog = gtk_file_chooser_dialog_new(_("Select timezone"), NULL
-            , GTK_FILE_CHOOSER_ACTION_OPEN
-            , GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL
-            , GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
-/ * let's try to start on few standard positions * /
-    if (gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog)
-                , "/usr/share/zoneinfo/GMT") == FALSE)
-        gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog)
-                , "/usr/lib/zoneinfo/GMT");
-    if (gtk_dialog_run(GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
-        filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
-        gtk_entry_set_text(GTK_ENTRY(modify_clock->tz_entry), filename);
-        if (strlen(gtk_entry_get_text(GTK_ENTRY(modify_clock->name_entry))) 
-                == 0) {
-            if ((clockname = strrchr(filename, (int)'/')))
-                gtk_entry_set_text(GTK_ENTRY(modify_clock->name_entry)
-                        , clockname+1);
-            else
-                gtk_entry_set_text(GTK_ENTRY(modify_clock->name_entry)
-                        , filename);
+        else { /* default timezone in main setup */
+            g_string_assign(clocks.local_tz, tz_name);
         }
-        g_free(filename);
+        g_free(tz_name);
     }
-    gtk_widget_destroy(dialog);
-    */
+    clocks.no_update = FALSE; 
 }
 
 static void set_font(GtkWidget *widget, GString *font)
@@ -588,7 +573,6 @@ gboolean clock_parameters(GtkWidget *widget, clock_struct *clockp)
 {
     modify_struct *modify_clock;
     GtkWidget *vbox, *hbox;
-    GtkWidget *button;
     gchar     *window_name;
 
     if (clockp->modified)
@@ -628,15 +612,12 @@ gboolean clock_parameters(GtkWidget *widget, clock_struct *clockp)
 
     hbox = add_box(vbox, 'H');
     add_header(hbox, _("Timezone of the clock:"), TRUE);
-    modify_clock->tz_entry = gtk_entry_new();
-    gtk_entry_set_text(GTK_ENTRY(modify_clock->tz_entry), clockp->tz->str);
-    gtk_box_pack_start(GTK_BOX(hbox), modify_clock->tz_entry
-            , FALSE, FALSE, 5);
-    gtk_tooltips_set_tip(clocks.tips, modify_clock->tz_entry
-            , _("enter timezone of clock,\n(=any valid TZ value)"), NULL);
-    button = gtk_button_new_from_stock(GTK_STOCK_OPEN);
-    gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 2);
-    g_signal_connect(G_OBJECT(button), "clicked"
+    modify_clock->button_tz = gtk_button_new_from_stock(GTK_STOCK_OPEN);
+    if (clockp->tz->str && clockp->tz->len)
+         gtk_button_set_label(GTK_BUTTON(modify_clock->button_tz)
+                 , _(clockp->tz->str));
+    gtk_box_pack_start(GTK_BOX(hbox), modify_clock->button_tz, FALSE, FALSE, 2);
+    g_signal_connect(G_OBJECT(modify_clock->button_tz), "clicked"
             , G_CALLBACK(ask_timezone), modify_clock);
 
 /* ---------------------Text Formatting--------------------------------- */
@@ -1076,7 +1057,7 @@ gboolean default_preferences(GtkWidget *widget)
     modify_default = g_new0(modify_struct, 1);
     modify_default->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
     gtk_window_set_title(GTK_WINDOW (modify_default->window)
-            , _("Modify Preferences"));
+            , _("Modify Globaltime Preferences"));
     g_signal_connect(G_OBJECT(modify_default->window) , "destroy"
             , G_CALLBACK(release_preferences_window), modify_default);
                                                                                 
@@ -1137,20 +1118,14 @@ gboolean default_preferences(GtkWidget *widget)
 /* -----------------------Local timezone------------------------------ */
     hbox = add_box(vbox, 'H');
     add_header(hbox, _("Local timezone:"), TRUE);
-    modify_default->tz_entry = gtk_entry_new();
-    gtk_entry_set_text(GTK_ENTRY(modify_default->tz_entry)
-            , clocks.local_tz->str);
-    gtk_box_pack_start(GTK_BOX(hbox), modify_default->tz_entry
-            , FALSE, FALSE, 5);
-    gtk_tooltips_set_tip(clocks.tips, modify_default->tz_entry
-            , _("Enter local timezone. (it is used to show if time is on previous(-) or next date(+) by adding a +/- after the time)"), NULL);
-    button = gtk_button_new_from_stock(GTK_STOCK_OPEN);
-    gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 2);
-    g_signal_connect(G_OBJECT(button), "clicked"
+    modify_default->button_tz = gtk_button_new_from_stock(GTK_STOCK_OPEN);
+    if (clocks.local_tz->str)
+         gtk_button_set_label(GTK_BUTTON(modify_default->button_tz)
+                 , _(clocks.local_tz->str));
+    gtk_box_pack_start(GTK_BOX(hbox), modify_default->button_tz, FALSE, FALSE
+            , 2);
+    g_signal_connect(G_OBJECT(modify_default->button_tz), "clicked"
             , G_CALLBACK(ask_timezone), modify_default);
-    /* these are needed by ask_timezone */
-    modify_default->name_entry = gtk_entry_new();
-    gtk_entry_set_text(GTK_ENTRY(modify_default->name_entry), "dummy");
 
 /* ---------------------Text Formatting--------------------------------- */
     preferences_formatting(vbox, modify_default);
diff --git a/globaltime/timezone_selection.c b/globaltime/timezone_selection.c
index 8819d04..f82eaa1 100644
--- a/globaltime/timezone_selection.c
+++ b/globaltime/timezone_selection.c
@@ -67,12 +67,13 @@ enum {
     LOCATION,
     LOCATION_ENG,
     OFFSET,
+    CHANGES,
     COUNTRY,
-    NEXT_CHANGE,
     N_COLUMNS
 };
 
-static GtkTreeStore *tz_button_create_store(gboolean details)
+static GtkTreeStore *tz_button_create_store(gboolean details
+        , gboolean check_ical)
 {
 #undef P_N
 #define P_N "tz_button_create_store: "
@@ -82,14 +83,16 @@ static GtkTreeStore *tz_button_create_store(gboolean details)
     GtkTreeIter iter1, iter2, main;
     orage_timezone_array tz_a;
     char area_old[MAX_AREA_LENGTH+2]; /*+2 = / + null */
-    char s_offset[100], s_country[100], s_next[100];
-    gint i, j, offs_hour, offs_min;
+    char s_offset[100], s_country[100], s_changes[200], s_change[50]
+        , s_change_time[50];
+    gint i, j, offs_hour, offs_min, next_offs_hour, next_offs_min
+        , change_time, change_hour, change_min;
 
     store = gtk_tree_store_new(N_COLUMNS
             , G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING
             , G_TYPE_STRING, G_TYPE_STRING);
     strcpy(area_old, "S T a R T"); /* this never matches */
-    tz_a = get_orage_timezones(details);
+    tz_a = get_orage_timezones(details, check_ical ? 1 : 0);
     /*
     g_print(P_N "number of timezones %d\n", tz_a.count);
     */
@@ -100,8 +103,8 @@ static GtkTreeStore *tz_button_create_store(gboolean details)
             , LOCATION, _(" Other")
             , LOCATION_ENG, " Other"
             , OFFSET, " "
+            , CHANGES, " "
             , COUNTRY, " "
-            , NEXT_CHANGE, " "
             , -1);
     main = iter1; /* need to remember that */
 
@@ -124,8 +127,8 @@ static GtkTreeStore *tz_button_create_store(gboolean details)
                         , LOCATION, _(area_old)
                         , LOCATION_ENG, area_old
                         , OFFSET, " "
+                        , CHANGES, " "
                         , COUNTRY, " "
-                        , NEXT_CHANGE, " "
                         , -1);
                 /* let's make sure we do not match accidentally to those 
                  * plain names on main level. We do this by adding / */
@@ -144,26 +147,66 @@ static GtkTreeStore *tz_button_create_store(gboolean details)
         if (offs_min)
             g_print(P_N " %s offset %d hour %d minutes %d\n", tz_a.city[i], tz_a.utc_offset[i], offs_hour, offs_min);
         */
-        g_snprintf(s_offset, 100, "%+03d:%02d %s (%s)"
-                , offs_hour, offs_min
-                , (tz_a.dst[i]) ? "dst" : "std"
-                , (tz_a.tz[i]) ? tz_a.tz[i] : "-");
-        if (details && tz_a.country[i] && tz_a.cc[i])
-            g_snprintf(s_country, 100, "%s (%s)", tz_a.country[i], tz_a.cc[i]);
-        else
-            strcpy(s_country, " ");
-        if (details)
-            g_snprintf(s_next, 100, "%s"
+        if (details && tz_a.next[i]) {
+            next_offs_hour = tz_a.next_utc_offset[i] / (60*60);
+            next_offs_min = abs((tz_a.next_utc_offset[i]
+                    - next_offs_hour * (60*60)) / 60);
+            change_time = tz_a.next_utc_offset[i] - tz_a.utc_offset[i];
+            change_hour = change_time / (60*60);
+            change_min  = abs((change_time - change_hour * (60*60)) /60);
+            if (change_hour && change_min)
+                g_snprintf(s_change_time, 50, _("%d hour %d mins")
+                        , abs(change_hour), change_min);
+            else if (change_hour)
+                g_snprintf(s_change_time, 50, _("%d hour"), abs(change_hour));
+            else if (change_min)
+                g_snprintf(s_change_time, 50, _("%d mins"), change_min);
+            else
+                strcpy(s_change_time, " ");
+
+            if (change_time < 0)
+                g_snprintf(s_change, 50, "(%s %s)"
+                        , _("backward"), s_change_time);
+            else if (change_time > 0)
+                g_snprintf(s_change, 50, "(%s %s)"
+                        , _("forward"), s_change_time);
+            else
+                strcpy(s_change, " ");
+            g_snprintf(s_offset, 100
+                    , "%+03d:%02d %s (%s)\n   -> %+03d:%02d %s"
+                    , offs_hour, offs_min
+                    , (tz_a.dst[i]) ? "dst" : "std"
+                    , (tz_a.tz[i]) ? tz_a.tz[i] : "-"
+                    , next_offs_hour, next_offs_min
+                    , s_change);
+        }
+        else {
+            g_snprintf(s_offset, 100, "%+03d:%02d %s (%s)"
+                    , offs_hour, offs_min
+                    , (tz_a.dst[i]) ? "dst" : "std"
+                    , (tz_a.tz[i]) ? tz_a.tz[i] : "-");
+        }
+        if (details) {
+            if (tz_a.country[i] && tz_a.cc[i])
+                g_snprintf(s_country, 100, "%s (%s)"
+                        , tz_a.country[i], tz_a.cc[i]);
+            else
+                strcpy(s_country, " ");
+            g_snprintf(s_changes, 200, "%s\n%s"
+                    , (tz_a.prev[i]) ? tz_a.prev[i] : _("not changed")
                     , (tz_a.next[i]) ? tz_a.next[i] : _("not changing"));
-        else
-            strcpy(s_next, " ");
+        }
+        else {
+            strcpy(s_country, " ");
+            strcpy(s_changes, " ");
+        }
 
         gtk_tree_store_set(store, &iter2
                 , LOCATION, _(tz_a.city[i])
                 , LOCATION_ENG, tz_a.city[i]
                 , OFFSET, s_offset
+                , CHANGES, s_changes
                 , COUNTRY, s_country
-                , NEXT_CHANGE, s_next
                 , -1);
     }
     return(store);
@@ -220,20 +263,20 @@ GtkWidget *tz_button_create_view(gboolean details, GtkTreeStore *store)
 
     if (details) {
         rend = gtk_cell_renderer_text_new();
-        col  = gtk_tree_view_column_new_with_attributes(_("Country")
-                , rend, "text", COUNTRY, NULL);
+        col = gtk_tree_view_column_new_with_attributes(_("Previous/Next Change")
+                , rend, "text", CHANGES, NULL);
         gtk_tree_view_append_column(GTK_TREE_VIEW(tree), col);
 
         rend = gtk_cell_renderer_text_new();
-        col  = gtk_tree_view_column_new_with_attributes(_("Next Change")
-                , rend, "text", NEXT_CHANGE, NULL);
+        col = gtk_tree_view_column_new_with_attributes(_("Country")
+                , rend, "text", COUNTRY, NULL);
         gtk_tree_view_append_column(GTK_TREE_VIEW(tree), col);
     }
     return(tree);
 }
 
 gboolean orage_timezone_button_clicked(GtkButton *button, GtkWindow *parent
-        , gchar **tz)
+        , gchar **tz, gboolean check_ical, char *local_tz)
 {
 #undef P_N
 #define P_N "orage_timezone_button_clicked: "
@@ -243,30 +286,39 @@ gboolean orage_timezone_button_clicked(GtkButton *button, GtkWindow *parent
     GtkWidget *window;
     GtkWidget *sw;
     int result;
-    char *loc, *loc_eng;
+    char *loc = NULL, *loc_eng = NULL;
     GtkTreeSelection *sel;
     GtkTreeModel     *model;
     GtkTreeIter       iter;
     gboolean    changed = FALSE;
     gboolean    details = FALSE;
 
-    store = tz_button_create_store(details);
+    store = tz_button_create_store(details, check_ical);
     tree = tz_button_create_view(details, store);
 
     /* show it */
-    window =  gtk_dialog_new_with_buttons(_("Pick timezone")
-            , parent
-            , GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT
-            , _("Change mode"), 4
-            , _("UTC"), 1
-            /* , _("floating"), 2 */
-            /* , _(g_par.local_timezone), 3 */
-            , GTK_STOCK_OK, GTK_RESPONSE_ACCEPT
-            , NULL);
+    if (check_ical)
+        window =  gtk_dialog_new_with_buttons(_("Pick timezone")
+                , parent
+                , GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT
+                , _("Change mode"), 1
+                , _("UTC"), 2
+                , _("floating"), 3
+                , _(local_tz), 4
+                , GTK_STOCK_OK, GTK_RESPONSE_ACCEPT
+                , NULL);
+    else
+        window =  gtk_dialog_new_with_buttons(_("Pick timezone")
+                , parent
+                , GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT
+                , _("Change mode"), 1
+                , _("UTC"), 2
+                , GTK_STOCK_OK, GTK_RESPONSE_ACCEPT
+                , NULL);
     sw = gtk_scrolled_window_new(NULL, NULL);
     gtk_container_add(GTK_CONTAINER(sw), tree);
     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox), sw, TRUE, TRUE, 0);
-    gtk_window_set_default_size(GTK_WINDOW(window), 610, 500);
+    gtk_window_set_default_size(GTK_WINDOW(window), 750, 500);
 
     gtk_widget_show_all(window);
     do {
@@ -280,50 +332,53 @@ gboolean orage_timezone_button_clicked(GtkButton *button, GtkWindow *parent
                     else {
                         gtk_tree_model_get(model, &iter, LOCATION, &loc, -1);
                         gtk_tree_model_get(model, &iter, LOCATION_ENG, &loc_eng
-                                , -1);                     }
+                                , -1);
+                    }
                 else {
                     loc = g_strdup(_(*tz));
                     loc_eng = g_strdup(*tz);
                 }
                 break;
             case 1:
-                loc = g_strdup(_("UTC"));
-                loc_eng = g_strdup("UTC");
-                break;
-                /*
-            case 2:
-                loc = g_strdup(_("floating"));
-                loc_eng = g_strdup("floating");
-                break;
-            case 3:
-                loc = g_strdup(_(g_par.local_timezone));
-                loc_eng = g_strdup(g_par.local_timezone);
-                break;
-                */
-            case 4:
                 free_orage_timezones(details);
                 details = !details;
                 /* gtk_widget_destroy(GTK_WIDGET(store)); */
                 gtk_widget_destroy(tree);
-                store = tz_button_create_store(details);
+                store = tz_button_create_store(details, check_ical);
                 tree = tz_button_create_view(details, store);
                 gtk_container_add(GTK_CONTAINER(sw), tree);
                 gtk_widget_show_all(tree);
                 result = 0;
                 break;
+            case 2:
+                loc = g_strdup(_("UTC"));
+                loc_eng = g_strdup("UTC");
+                break;
+            case 3:
+                /* NOTE: this exists only if ical timezones are being used */
+                loc = g_strdup(_("floating"));
+                loc_eng = g_strdup("floating");
+                break;
+            case 4:
+                /* NOTE: this exists only if ical timezones are being used */
+                loc = g_strdup(_(local_tz));
+                loc_eng = g_strdup(local_tz);
+                break;
             default:
                 loc = g_strdup(_(*tz));
                 loc_eng = g_strdup(*tz);
                 break;
         }
     } while (result == 0);
-    if (g_ascii_strcasecmp(loc, (gchar *)gtk_button_get_label(button)) != 0)
+    if (loc && g_ascii_strcasecmp(loc, gtk_button_get_label(button))) {
         changed = TRUE;
-    gtk_button_set_label(button, loc);
-
-    if (*tz)
-        g_free(*tz);
-    *tz = g_strdup(loc_eng);
+        /* return the real timezone and update the button to show the 
+         * translated timezone name */
+        if (*tz)
+            g_free(*tz);
+        *tz = g_strdup(loc_eng);
+        gtk_button_set_label(button, loc);
+    }
     g_free(loc);
     g_free(loc_eng);
     gtk_widget_destroy(window);
diff --git a/globaltime/timezone_selection.h b/globaltime/timezone_selection.h
index 2f0e3af..e181a41 100644
--- a/globaltime/timezone_selection.h
+++ b/globaltime/timezone_selection.h
@@ -24,6 +24,6 @@
 #define __TIMEZONE_SELECTION_H__
 
 gboolean orage_timezone_button_clicked(GtkButton *button, GtkWindow *parent
-        , gchar **tz);
+        , gchar **tz, gboolean check_ical, char *local_tz);
 
 #endif /* !__TIMEZONE_SELECTION_H__ */
diff --git a/globaltime/tz_zoneinfo_read.c b/globaltime/tz_zoneinfo_read.c
index 1ff1330..508b427 100644
--- a/globaltime/tz_zoneinfo_read.c
+++ b/globaltime/tz_zoneinfo_read.c
@@ -59,63 +59,71 @@
 
 
 /** This is the toplevel directory where the timezone data is installed in. */
-#define ORAGE_ZONEINFO_DIRECTORY  PACKAGE_DATA_DIR "/orage/zoneinfo"
+#define ORAGE_ZONEINFO_DIRECTORY  PACKAGE_DATA_DIR "/orage/zoneinfo/"
 
 /** This is the filename of the file containing tz_convert parameters
  * This file contains the location of the os zoneinfo data.
  * the same than the above DEFAULT_OS_ZONEINFO_DIRECTORY */
 #define TZ_CONVERT_PAR_FILENAME  "tz_convert.par"
-#define TZ_CONVERT_PAR_FILE_LOC  ORAGE_ZONEINFO_DIRECTORY "/" TZ_CONVERT_PAR_FILENAME
+#define TZ_CONVERT_PAR_FILE_LOC  ORAGE_ZONEINFO_DIRECTORY TZ_CONVERT_PAR_FILENAME
+
+/** This is the filename of the file containing orage ical timezone names */
+#define ICAL_ZONES_TAB_FILENAME  "zones.tab"
+#define ICAL_ZONES_TAB_FILE_LOC  ORAGE_ZONEINFO_DIRECTORY ICAL_ZONES_TAB_FILENAME
 
 
 
 /* this contains all timezone data */
 orage_timezone_array tz_array={0, NULL, NULL, NULL, NULL, NULL, NULL};
 
-char *zone_tab_buf = NULL, *country_buf = NULL;
+static char *zone_tab_buf = NULL, *country_buf = NULL, *zones_tab_buf = NULL;
 
-int debug = 0; /* bigger number => more output */
-char version[] = "1.4.4";
-int file_cnt = 0; /* number of processed files */
+static int debug = 0; /* bigger number => more output */
+static char version[] = "1.4.4";
+static int file_cnt = 0; /* number of processed files */
 
-unsigned char *in_buf, *in_head, *in_tail;
-int in_file_base_offset = 0;
+static unsigned char *in_buf, *in_head, *in_tail;
+static int in_file_base_offset = 0;
 
-int details;
+static int details;     /* show extra data (country and next change time) */
+static int check_ical;  /* check that we have also the ical timezone data */
 
-char *in_file = NULL, *out_file = NULL;
-int in_file_is_dir = 0;
-int excl_dir_cnt = -1;
-char **excl_dir = NULL;
+static char *in_file = NULL, *out_file = NULL;
+static int in_file_is_dir = 0;
+static int excl_dir_cnt = -1;
+static char **excl_dir = NULL;
 
 /* in_timezone_name is the real timezone name from the infile 
  * we are processing.
- * in_timezone_name is the timezone we are writing. Usually it is the same
+ * timezone_name is the timezone we are writing. Usually it is the same
  * than in_timezone_name. 
- * timezone name is for example Europe/Helsinki */
-char *timezone_name = NULL;  
-char *in_timezone_name = NULL;
+ * timezone_name is for example Europe/Helsinki */
+/* FIXME: we do not need both timezone_name and in_timezone_name here.
+ * Remove one */
+static char *timezone_name = NULL;  
+static char *in_timezone_name = NULL;
 
-int ignore_older = 1970; /* Ignore rules which are older or equal than this */
+/* Ignore rules which are older or equal than this */
+static int ignore_older = 1970; 
 
 /* time change table starts here */
-unsigned char *begin_timechanges;           
+static unsigned char *begin_timechanges;           
 
 /* time change type index table starts here */
-unsigned char *begin_timechangetypeindexes; 
+static unsigned char *begin_timechangetypeindexes; 
 
 /* time change type table starts here */
-unsigned char *begin_timechangetypes;       
+static unsigned char *begin_timechangetypes;       
 
 /* timezone name table */
-unsigned char *begin_timezonenames;         
+static unsigned char *begin_timezonenames;         
 
-unsigned long gmtcnt;
-unsigned long stdcnt;
-unsigned long leapcnt;
-unsigned long timecnt;  /* points when time changes */
-unsigned long typecnt;  /* table of different time changes = types */
-unsigned long charcnt;  /* length of timezone name table */
+static unsigned long gmtcnt;
+static unsigned long stdcnt;
+static unsigned long leapcnt;
+static unsigned long timecnt;  /* points when time changes */
+static unsigned long typecnt;  /* table of different time changes = types */
+static unsigned long charcnt;  /* length of timezone name table */
 
 static void read_file(const char *file_name, const struct stat *file_stat)
 {
@@ -136,7 +144,7 @@ static void read_file(const char *file_name, const struct stat *file_stat)
         printf("read_file: end\n");
 }
 
-long get_long()
+static long get_long()
 {
     unsigned long tmp;
 
@@ -148,7 +156,7 @@ long get_long()
     return(tmp);
 }
 
-int process_header()
+static int process_header()
 {
     if (debug > 2)
         printf("file id: %s\n", in_head);
@@ -180,7 +188,7 @@ int process_header()
     return(0);
 }
 
-process_local_time_table()
+static process_local_time_table()
 { /* points when time changes */
     unsigned long tmp;
     int i;
@@ -199,7 +207,7 @@ process_local_time_table()
     }
 }
 
-process_local_time_type_table()
+static process_local_time_type_table()
 { /* pointers to table, which explain how time changes */
     unsigned char tmp;
     int i;
@@ -215,7 +223,7 @@ process_local_time_type_table()
     }
 }
 
-process_ttinfo_table()
+static process_ttinfo_table()
 { /* table of different time changes = types */
     long tmp;
     unsigned char tmp2, tmp3;
@@ -236,7 +244,7 @@ process_ttinfo_table()
     }
 }
 
-process_abbr_table()
+static process_abbr_table()
 {
     unsigned char *tmp;
     int i;
@@ -253,7 +261,7 @@ process_abbr_table()
     in_head += charcnt;
 }
 
-process_leap_table()
+static process_leap_table()
 {
     unsigned long tmp, tmp2;
     int i;
@@ -269,7 +277,7 @@ process_leap_table()
     }
 }
 
-process_std_table()
+static process_std_table()
 {
     unsigned char tmp;
     int i;
@@ -284,7 +292,7 @@ process_std_table()
     }
 }
 
-process_gmt_table()
+static process_gmt_table()
 {
     unsigned char tmp;
     int i;
@@ -301,7 +309,7 @@ process_gmt_table()
 
 /* go through the contents of the file and find the positions of 
  * needed data. Uses global pointer: in_head */
-int process_file(const char *file_name)
+static int process_file(const char *file_name)
 {
     if (debug > 1)
         printf("\n\nprocess_file: start\n");
@@ -323,10 +331,10 @@ int process_file(const char *file_name)
     return(0); /* ok */
 }
 
-void get_country()
+static void get_country()
 { /* tz_array.city[tz_array.count] contains the city name.
      We will find corresponding country and fill it to the table */
-    char *str, *str_nl;
+    char *str, *str_nl, cc[4];
 
     if (!(str = strstr(zone_tab_buf, tz_array.city[tz_array.count])))
         return; /* not found */
@@ -343,12 +351,19 @@ void get_country()
     strncpy(tz_array.cc[tz_array.count], ++str_nl, 2);
     tz_array.cc[tz_array.count][2] = '\0';
 
-    /* then search the country */
-    if (!(str = strstr(country_buf, tz_array.cc[tz_array.count])))
+    /********** then search the country **********/
+    /* Need to search line, which starts with country code.
+     * Note that it is not enough to search any country coed, but it really
+     * needs to be the first two chars in the line */
+    cc[0] = '\n';
+    cc[1] = tz_array.cc[tz_array.count][0];
+    cc[2] = tz_array.cc[tz_array.count][1];
+    cc[3] = '\0';
+    if (!(str = strstr(country_buf, cc)))
         return; /* not found */
     /* country name is after the country code and a single tab */
-    str += 3;
-    /* but w still need to find how long it is.
+    str += 4;
+    /* but we still need to find how long it is.
      * It ends in the line end. 
      * (There is a line end at the end of the file also.) */
     for (str_nl = str; str_nl[0] != '\n'; str_nl++)
@@ -358,16 +373,28 @@ void get_country()
     tz_array.country[tz_array.count][(str_nl - str)] = '\0';
 }
 
+static int timezone_exists_in_ical()
+{ /* in_timezone_name contains the timezone name.
+     We will search if it exists also in the ical zones.tab file */
+    char *str;
+
+    if (str = strstr(zones_tab_buf, in_timezone_name))
+        return(1); /* yes, it is there */
+    else
+        return(0); /* not found */
+}
+
 /* FIXME: need to check that if OUTFILE is given as a parameter,
  * INFILE is not a directory (or make outfile to act like directory also ? */
-int write_ical_file(const char *in_file_name, const struct stat *in_file_stat)
+static int write_ical_file(const char *in_file_name
+        , const struct stat *in_file_stat)
 {
     int i;
     unsigned int tct_i, abbr_i;
     struct tm cur_gm_time;
     time_t tt_now = time(NULL);
-    long tc_time = 0; /* TimeChange time */
-    char s_next[101];
+    long tc_time = 0, prev_tc_time; /* TimeChange times */
+    char s_next[101], s_prev[101];
 
     if (debug > 1)
         printf("***** write_ical_file: start *****\n\n");
@@ -384,6 +411,7 @@ int write_ical_file(const char *in_file_name, const struct stat *in_file_stat)
         /* search for current time setting.
          * timecnt tells how many changes we have in the tz file.
          * i points to the next value to read. */
+        prev_tc_time = tc_time;
         tc_time = get_long(); /* start time of this timechange */
     }
     /* i points to the next value to be read, so need to -- */
@@ -393,7 +421,9 @@ int write_ical_file(const char *in_file_name, const struct stat *in_file_stat)
         tz_array.utc_offset[tz_array.count] = 0;
         tz_array.dst[tz_array.count] = 0;
         tz_array.tz[tz_array.count] = "UTC";
+        tz_array.prev[tz_array.count] = NULL;
         tz_array.next[tz_array.count] = NULL;
+        tz_array.next_utc_offset[tz_array.count] = 0;
         tz_array.count++;
         return(1); /* done */
     }
@@ -401,16 +431,52 @@ int write_ical_file(const char *in_file_name, const struct stat *in_file_stat)
         /* we found previous and next value */
         /* tc_time has the next change time */
         if (details) {
+            /* NOTE: If the time change happens for example at 04:00
+             * and goes one hour backward, the new time is 03:00 and this
+             * is what localtime_r reports. In real life we want to show
+             * here 04:00, so let's subtract 1 sec to get close to that.
+             * This is a bit similar than 24:00 or 00:00. Summary:
+             * 04:00 is returned as 03:00 (change happened already) but
+             * 03:59 is returned as 03:59 (change did not yet happen) */
+            prev_tc_time -= 1;
+            localtime_r((const time_t *)&prev_tc_time, &cur_gm_time);
+            strftime(s_prev, 100, "%c", &cur_gm_time);
+            tz_array.prev[tz_array.count] = strdup(s_prev);
+
+            tc_time -= 1;
             localtime_r((const time_t *)&tc_time, &cur_gm_time);
             strftime(s_next, 100, "%c", &cur_gm_time);
             tz_array.next[tz_array.count] = strdup(s_next);
+            /* get timechange type index */
+            if (timecnt) {
+                in_head = begin_timechangetypeindexes;
+                tct_i = (unsigned int)in_head[i];
+            }
+            else
+                tct_i = 0;
+
+            /* get timechange type */
+            in_head = begin_timechangetypes;
+            in_head += 6*tct_i;
+            tz_array.next_utc_offset[tz_array.count] = (int)get_long();
         }
-        else 
+        else {
+            tz_array.prev[tz_array.count] = NULL;
             tz_array.next[tz_array.count] = NULL;
+        }
         i--; /* we need to take the previous value */
     }
-    else 
+    else { /* no next value, but previous may exist */
         tz_array.next[tz_array.count] = NULL;
+        if (details && prev_tc_time) {
+            prev_tc_time -= 1;
+            localtime_r((const time_t *)&prev_tc_time, &cur_gm_time);
+            strftime(s_prev, 100, "%c", &cur_gm_time);
+            tz_array.prev[tz_array.count] = strdup(s_prev);
+        }
+        else
+            tz_array.prev[tz_array.count] = NULL;
+    }
 
     /* i now points to latest time change and shows current time.
      * So we found our result and can start collecting real data: */
@@ -441,7 +507,7 @@ int write_ical_file(const char *in_file_name, const struct stat *in_file_stat)
 }
 
 /* The main code. This is called once per each file found */
-int file_call(const char *file_name, const struct stat *sb, int flags
+static int file_call(const char *file_name, const struct stat *sb, int flags
         , struct FTW *f)
 {
     int i;
@@ -453,14 +519,21 @@ int file_call(const char *file_name, const struct stat *sb, int flags
     if (flags == FTW_F) { /* we got file */
         if (debug > 0)
             printf("\t\tfile_call: processing file=(%s)\n", file_name);
+        in_timezone_name = strdup(&file_name[in_file_base_offset
+                + strlen("zoneinfo/")]);
+        timezone_name = strdup(in_timezone_name);
+        if (check_ical && !timezone_exists_in_ical()) {
+            free(in_timezone_name);
+            free(timezone_name);
+            return(FTW_CONTINUE);
+        }
         read_file(file_name, sb);
         if (process_file(file_name)) { /* we skipped this file */
+            free(in_timezone_name);
+            free(timezone_name);
             free(in_buf);
             return(FTW_CONTINUE);
         }
-        in_timezone_name = strdup(&file_name[in_file_base_offset 
-                + strlen("zoneinfo/")]);
-        timezone_name = strdup(in_timezone_name);
         write_ical_file(file_name, sb);
 
         free(in_buf);
@@ -499,7 +572,7 @@ int file_call(const char *file_name, const struct stat *sb, int flags
 }
 
 /* check the parameters and use defaults when possible */
-int check_parameters()
+static int check_parameters()
 {
     char *s_tz, *last_tz = NULL, tz[]="/zoneinfo", tz2[]="zoneinfo/";
     int tz_len, i;
@@ -630,7 +703,7 @@ int check_parameters()
     return(0); /* continue */
 }
 
-void read_countries()
+static void read_countries()
 {
     char *tz_dir, *zone_tab_file_name, *country_file_name;
     int zoneinfo_len=strlen("zoneinfo/");
@@ -730,7 +803,40 @@ void read_countries()
     fclose(country_file);
 }
 
-orage_timezone_array get_orage_timezones(int show_details)
+static void read_ical_timezones()
+{
+    FILE *zones_tab_file;
+    struct stat zones_tab_file_stat;
+
+    /****** zones.tab file ******/
+    if (!(zones_tab_file = fopen(ICAL_ZONES_TAB_FILE_LOC, "r"))) {
+        printf("read_ical_timezones: zones.tab file open failed (%s)\n"
+                , ICAL_ZONES_TAB_FILE_LOC);
+        perror("\tfopen");
+        return;
+    }
+    if (stat(ICAL_ZONES_TAB_FILE_LOC, &zones_tab_file_stat) == -1) {
+        printf("read_ical_timezones: zones.tab file stat failed (%s)\n"
+                , ICAL_ZONES_TAB_FILE_LOC);
+        fclose(zones_tab_file);
+        perror("\tstat");
+        return;
+    }
+    zones_tab_buf = malloc(zones_tab_file_stat.st_size+1);
+    fread(zones_tab_buf, 1, zones_tab_file_stat.st_size, zones_tab_file);
+    if (ferror(zones_tab_file)) {
+        printf("read_ical_timezones: zones.tab file read failed (%s)\n"
+                , ICAL_ZONES_TAB_FILE_LOC);
+        perror("\tfread");
+        return;
+    }
+    zones_tab_buf[zones_tab_file_stat.st_size] = '\0';
+    printf("read_ical_timezones: zones.tab file read (%s) (%d bytes)\n"
+                , ICAL_ZONES_TAB_FILE_LOC, strlen(zones_tab_buf));
+    fclose(zones_tab_file);
+}
+
+orage_timezone_array get_orage_timezones(int show_details, int ical)
 {
     int tz_array_size = 1000; /* FIXME: this needs to be counted */
     /*
@@ -740,12 +846,15 @@ orage_timezone_array get_orage_timezones(int show_details)
    */
 
     details = show_details;
+    check_ical = ical;
     if (tz_array.count == 0) {
         tz_array.city = (char **)malloc(sizeof(char *)*(tz_array_size+2));
         tz_array.utc_offset = (int *)malloc(sizeof(int)*(tz_array_size+2));
         tz_array.dst = (int *)malloc(sizeof(int)*(tz_array_size+2));
         tz_array.tz = (char **)malloc(sizeof(char *)*(tz_array_size+2));
+        tz_array.prev = (char **)malloc(sizeof(char *)*(tz_array_size+2));
         tz_array.next = (char **)malloc(sizeof(char *)*(tz_array_size+2));
+        tz_array.next_utc_offset = (int *)malloc(sizeof(int)*(tz_array_size+2));
         tz_array.country = (char **)malloc(sizeof(char *)*(tz_array_size+2));
         tz_array.cc = (char **)malloc(sizeof(char *)*(tz_array_size+2));
     /* nftw goes through the whole file structure and calls "file_call"
@@ -756,6 +865,8 @@ orage_timezone_array get_orage_timezones(int show_details)
             printf("Processing %s files\n", in_file);
         if (details)
             read_countries();
+        if (check_ical)
+            read_ical_timezones();
         if (nftw(in_file, file_call, 10, FTW_PHYS | FTW_ACTIONRETVAL) == -1) {
             perror("nftw error in file handling");
             exit(EXIT_FAILURE);
@@ -768,7 +879,9 @@ orage_timezone_array get_orage_timezones(int show_details)
         tz_array.utc_offset[tz_array.count] = 0;
         tz_array.dst[tz_array.count] = 0;
         tz_array.tz[tz_array.count] = strdup("UTC");
+        tz_array.prev[tz_array.count] = NULL;
         tz_array.next[tz_array.count] = NULL;
+        tz_array.next_utc_offset[tz_array.count] = 0;
         tz_array.country[tz_array.count] = NULL;
         tz_array.cc[tz_array.count] = NULL;
         tz_array.city[tz_array.count++] = strdup("UTC");
@@ -776,12 +889,14 @@ orage_timezone_array get_orage_timezones(int show_details)
         tz_array.utc_offset[tz_array.count] = 0;
         tz_array.dst[tz_array.count] = 0;
         tz_array.tz[tz_array.count] = NULL;
+        tz_array.prev[tz_array.count] = NULL;
         tz_array.next[tz_array.count] = NULL;
+        tz_array.next_utc_offset[tz_array.count] = 0;
         tz_array.country[tz_array.count] = NULL;
         tz_array.cc[tz_array.count] = NULL;
         tz_array.city[tz_array.count++] = strdup("floating");
     }
-    return (tz_array);
+    return(tz_array);
 }
 
 void free_orage_timezones(int show_details)
@@ -793,6 +908,8 @@ void free_orage_timezones(int show_details)
             free(tz_array.city[i]);
         if (tz_array.tz[i])
             free(tz_array.tz[i]);
+        if (tz_array.prev[i])
+            free(tz_array.prev[i]);
         if (tz_array.next[i])
             free(tz_array.next[i]);
         if (tz_array.country[i])
@@ -804,10 +921,24 @@ void free_orage_timezones(int show_details)
     free(tz_array.utc_offset);
     free(tz_array.dst);
     free(tz_array.tz);
+    free(tz_array.prev);
     free(tz_array.next);
+    free(tz_array.next_utc_offset);
     free(tz_array.country);
     free(tz_array.cc);
     tz_array.count = 0;
     timezone_name = NULL;
+    if (zone_tab_buf) {
+        free(zone_tab_buf);
+        zone_tab_buf = NULL;
+    }
+    if (country_buf) {
+        free(country_buf);
+        country_buf = NULL;
+    }
+    if (zones_tab_buf) {
+        free(zones_tab_buf);
+        zones_tab_buf = NULL;
+    }
     file_cnt = 0; /* number of processed files */
 }
diff --git a/globaltime/tz_zoneinfo_read.h b/globaltime/tz_zoneinfo_read.h
index 9002f44..7b5c634 100644
--- a/globaltime/tz_zoneinfo_read.h
+++ b/globaltime/tz_zoneinfo_read.h
@@ -25,10 +25,12 @@ typedef struct _orage_timezone_array
     int  *utc_offset; /* pointer to int array holding utc offsets */
     int  *dst;        /* pointer to int array holding dst settings */
     char **tz;        /* pointer to timezone name strings */
+    char **prev;      /* pointer to previous time change strings */
     char **next;      /* pointer to next time change strings */
+    int  *next_utc_offset; /* pointer to int array holding utc offsets */
     char **country;   /* pointer to country name strings */
     char **cc;        /* pointer to country code strings */
 } orage_timezone_array;
 
-orage_timezone_array get_orage_timezones(int details);
+orage_timezone_array get_orage_timezones(int details, int ical);
 void free_orage_timezones(int details);
diff --git a/panel-plugin/oc_config.c b/panel-plugin/oc_config.c
index f393719..38da945 100644
--- a/panel-plugin/oc_config.c
+++ b/panel-plugin/oc_config.c
@@ -100,45 +100,18 @@ static void oc_set_width_changed(GtkSpinButton *sb, Clock *clock)
     oc_size_set(clock);
 }
 
-static void oc_timezone_changed(GtkWidget *widget, GdkEventKey *key
-        , Clock *clock)
-{
-    /* is it better to change only with GDK_Tab GDK_Return GDK_KP_Enter ? */
-    g_string_assign(clock->timezone, gtk_entry_get_text(GTK_ENTRY(widget)));
-    oc_timezone_set(clock);
-}
-
 static void oc_timezone_selected(GtkButton *button, Clock *clock)
 {
     GtkWidget *dialog;
     gchar *filename = NULL;
 
     dialog = g_object_get_data(G_OBJECT(clock->plugin), "dialog");
-    if (orage_timezone_button_clicked(button, GTK_WINDOW(dialog), &filename)) {
-        gtk_entry_set_text(GTK_ENTRY(clock->tz_entry), filename);
-        g_string_assign(clock->timezone, filename);
-        oc_timezone_set(clock);
-        g_free(filename);
-    }
-/*
-    dialog = gtk_file_chooser_dialog_new(_("Select timezone"), NULL
-            , GTK_FILE_CHOOSER_ACTION_OPEN
-            , GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL
-            , GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
-/ * let's try to start on few standard positions * /
-    if (gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog)
-            , "/usr/share/zoneinfo/GMT") == FALSE)
-        gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog)
-                , "/usr/lib/zoneinfo/GMT");
-    if (gtk_dialog_run(GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
-        filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
-        gtk_entry_set_text(GTK_ENTRY(clock->tz_entry), filename);
+    if (orage_timezone_button_clicked(button, GTK_WINDOW(dialog)
+            , &filename, FALSE, NULL)) {
         g_string_assign(clock->timezone, filename);
         oc_timezone_set(clock);
         g_free(filename);
     }
-    gtk_widget_destroy(dialog);
-    */
 }
 
 static void oc_show1(GtkToggleButton *cb, Clock *clock)
@@ -303,17 +276,10 @@ static void oc_properties_options(GtkWidget *dlg, Clock *clock)
     label = gtk_label_new(_("set timezone to:"));
     oc_table_add(table, label, 0, 0);
 
-    clock->tz_entry = gtk_entry_new();
-    gtk_entry_set_text(GTK_ENTRY(clock->tz_entry), clock->timezone->str);
-    oc_table_add(table, clock->tz_entry, 1, 0);
-    g_signal_connect(clock->tz_entry, "key-release-event"
-            , G_CALLBACK(oc_timezone_changed), clock);
-    gtk_tooltips_set_tip(clock->tips, GTK_WIDGET(clock->tz_entry),
-            _("Set any valid timezone (=TZ) value or pick one from the list.")
-            , NULL);
-
     button = gtk_button_new_from_stock(GTK_STOCK_OPEN);
-    oc_table_add(table, button, 2, 0);
+    if (clock->timezone->str && clock->timezone->len)
+         gtk_button_set_label(GTK_BUTTON(button), _(clock->timezone->str));
+    oc_table_add(table, button, 1, 0);
     g_signal_connect(G_OBJECT(button), "clicked"
             , G_CALLBACK(oc_timezone_selected), clock);
 
diff --git a/panel-plugin/timezone_selection.c b/panel-plugin/timezone_selection.c
index 8819d04..f82eaa1 100644
--- a/panel-plugin/timezone_selection.c
+++ b/panel-plugin/timezone_selection.c
@@ -67,12 +67,13 @@ enum {
     LOCATION,
     LOCATION_ENG,
     OFFSET,
+    CHANGES,
     COUNTRY,
-    NEXT_CHANGE,
     N_COLUMNS
 };
 
-static GtkTreeStore *tz_button_create_store(gboolean details)
+static GtkTreeStore *tz_button_create_store(gboolean details
+        , gboolean check_ical)
 {
 #undef P_N
 #define P_N "tz_button_create_store: "
@@ -82,14 +83,16 @@ static GtkTreeStore *tz_button_create_store(gboolean details)
     GtkTreeIter iter1, iter2, main;
     orage_timezone_array tz_a;
     char area_old[MAX_AREA_LENGTH+2]; /*+2 = / + null */
-    char s_offset[100], s_country[100], s_next[100];
-    gint i, j, offs_hour, offs_min;
+    char s_offset[100], s_country[100], s_changes[200], s_change[50]
+        , s_change_time[50];
+    gint i, j, offs_hour, offs_min, next_offs_hour, next_offs_min
+        , change_time, change_hour, change_min;
 
     store = gtk_tree_store_new(N_COLUMNS
             , G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING
             , G_TYPE_STRING, G_TYPE_STRING);
     strcpy(area_old, "S T a R T"); /* this never matches */
-    tz_a = get_orage_timezones(details);
+    tz_a = get_orage_timezones(details, check_ical ? 1 : 0);
     /*
     g_print(P_N "number of timezones %d\n", tz_a.count);
     */
@@ -100,8 +103,8 @@ static GtkTreeStore *tz_button_create_store(gboolean details)
             , LOCATION, _(" Other")
             , LOCATION_ENG, " Other"
             , OFFSET, " "
+            , CHANGES, " "
             , COUNTRY, " "
-            , NEXT_CHANGE, " "
             , -1);
     main = iter1; /* need to remember that */
 
@@ -124,8 +127,8 @@ static GtkTreeStore *tz_button_create_store(gboolean details)
                         , LOCATION, _(area_old)
                         , LOCATION_ENG, area_old
                         , OFFSET, " "
+                        , CHANGES, " "
                         , COUNTRY, " "
-                        , NEXT_CHANGE, " "
                         , -1);
                 /* let's make sure we do not match accidentally to those 
                  * plain names on main level. We do this by adding / */
@@ -144,26 +147,66 @@ static GtkTreeStore *tz_button_create_store(gboolean details)
         if (offs_min)
             g_print(P_N " %s offset %d hour %d minutes %d\n", tz_a.city[i], tz_a.utc_offset[i], offs_hour, offs_min);
         */
-        g_snprintf(s_offset, 100, "%+03d:%02d %s (%s)"
-                , offs_hour, offs_min
-                , (tz_a.dst[i]) ? "dst" : "std"
-                , (tz_a.tz[i]) ? tz_a.tz[i] : "-");
-        if (details && tz_a.country[i] && tz_a.cc[i])
-            g_snprintf(s_country, 100, "%s (%s)", tz_a.country[i], tz_a.cc[i]);
-        else
-            strcpy(s_country, " ");
-        if (details)
-            g_snprintf(s_next, 100, "%s"
+        if (details && tz_a.next[i]) {
+            next_offs_hour = tz_a.next_utc_offset[i] / (60*60);
+            next_offs_min = abs((tz_a.next_utc_offset[i]
+                    - next_offs_hour * (60*60)) / 60);
+            change_time = tz_a.next_utc_offset[i] - tz_a.utc_offset[i];
+            change_hour = change_time / (60*60);
+            change_min  = abs((change_time - change_hour * (60*60)) /60);
+            if (change_hour && change_min)
+                g_snprintf(s_change_time, 50, _("%d hour %d mins")
+                        , abs(change_hour), change_min);
+            else if (change_hour)
+                g_snprintf(s_change_time, 50, _("%d hour"), abs(change_hour));
+            else if (change_min)
+                g_snprintf(s_change_time, 50, _("%d mins"), change_min);
+            else
+                strcpy(s_change_time, " ");
+
+            if (change_time < 0)
+                g_snprintf(s_change, 50, "(%s %s)"
+                        , _("backward"), s_change_time);
+            else if (change_time > 0)
+                g_snprintf(s_change, 50, "(%s %s)"
+                        , _("forward"), s_change_time);
+            else
+                strcpy(s_change, " ");
+            g_snprintf(s_offset, 100
+                    , "%+03d:%02d %s (%s)\n   -> %+03d:%02d %s"
+                    , offs_hour, offs_min
+                    , (tz_a.dst[i]) ? "dst" : "std"
+                    , (tz_a.tz[i]) ? tz_a.tz[i] : "-"
+                    , next_offs_hour, next_offs_min
+                    , s_change);
+        }
+        else {
+            g_snprintf(s_offset, 100, "%+03d:%02d %s (%s)"
+                    , offs_hour, offs_min
+                    , (tz_a.dst[i]) ? "dst" : "std"
+                    , (tz_a.tz[i]) ? tz_a.tz[i] : "-");
+        }
+        if (details) {
+            if (tz_a.country[i] && tz_a.cc[i])
+                g_snprintf(s_country, 100, "%s (%s)"
+                        , tz_a.country[i], tz_a.cc[i]);
+            else
+                strcpy(s_country, " ");
+            g_snprintf(s_changes, 200, "%s\n%s"
+                    , (tz_a.prev[i]) ? tz_a.prev[i] : _("not changed")
                     , (tz_a.next[i]) ? tz_a.next[i] : _("not changing"));
-        else
-            strcpy(s_next, " ");
+        }
+        else {
+            strcpy(s_country, " ");
+            strcpy(s_changes, " ");
+        }
 
         gtk_tree_store_set(store, &iter2
                 , LOCATION, _(tz_a.city[i])
                 , LOCATION_ENG, tz_a.city[i]
                 , OFFSET, s_offset
+                , CHANGES, s_changes
                 , COUNTRY, s_country
-                , NEXT_CHANGE, s_next
                 , -1);
     }
     return(store);
@@ -220,20 +263,20 @@ GtkWidget *tz_button_create_view(gboolean details, GtkTreeStore *store)
 
     if (details) {
         rend = gtk_cell_renderer_text_new();
-        col  = gtk_tree_view_column_new_with_attributes(_("Country")
-                , rend, "text", COUNTRY, NULL);
+        col = gtk_tree_view_column_new_with_attributes(_("Previous/Next Change")
+                , rend, "text", CHANGES, NULL);
         gtk_tree_view_append_column(GTK_TREE_VIEW(tree), col);
 
         rend = gtk_cell_renderer_text_new();
-        col  = gtk_tree_view_column_new_with_attributes(_("Next Change")
-                , rend, "text", NEXT_CHANGE, NULL);
+        col = gtk_tree_view_column_new_with_attributes(_("Country")
+                , rend, "text", COUNTRY, NULL);
         gtk_tree_view_append_column(GTK_TREE_VIEW(tree), col);
     }
     return(tree);
 }
 
 gboolean orage_timezone_button_clicked(GtkButton *button, GtkWindow *parent
-        , gchar **tz)
+        , gchar **tz, gboolean check_ical, char *local_tz)
 {
 #undef P_N
 #define P_N "orage_timezone_button_clicked: "
@@ -243,30 +286,39 @@ gboolean orage_timezone_button_clicked(GtkButton *button, GtkWindow *parent
     GtkWidget *window;
     GtkWidget *sw;
     int result;
-    char *loc, *loc_eng;
+    char *loc = NULL, *loc_eng = NULL;
     GtkTreeSelection *sel;
     GtkTreeModel     *model;
     GtkTreeIter       iter;
     gboolean    changed = FALSE;
     gboolean    details = FALSE;
 
-    store = tz_button_create_store(details);
+    store = tz_button_create_store(details, check_ical);
     tree = tz_button_create_view(details, store);
 
     /* show it */
-    window =  gtk_dialog_new_with_buttons(_("Pick timezone")
-            , parent
-            , GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT
-            , _("Change mode"), 4
-            , _("UTC"), 1
-            /* , _("floating"), 2 */
-            /* , _(g_par.local_timezone), 3 */
-            , GTK_STOCK_OK, GTK_RESPONSE_ACCEPT
-            , NULL);
+    if (check_ical)
+        window =  gtk_dialog_new_with_buttons(_("Pick timezone")
+                , parent
+                , GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT
+                , _("Change mode"), 1
+                , _("UTC"), 2
+                , _("floating"), 3
+                , _(local_tz), 4
+                , GTK_STOCK_OK, GTK_RESPONSE_ACCEPT
+                , NULL);
+    else
+        window =  gtk_dialog_new_with_buttons(_("Pick timezone")
+                , parent
+                , GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT
+                , _("Change mode"), 1
+                , _("UTC"), 2
+                , GTK_STOCK_OK, GTK_RESPONSE_ACCEPT
+                , NULL);
     sw = gtk_scrolled_window_new(NULL, NULL);
     gtk_container_add(GTK_CONTAINER(sw), tree);
     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox), sw, TRUE, TRUE, 0);
-    gtk_window_set_default_size(GTK_WINDOW(window), 610, 500);
+    gtk_window_set_default_size(GTK_WINDOW(window), 750, 500);
 
     gtk_widget_show_all(window);
     do {
@@ -280,50 +332,53 @@ gboolean orage_timezone_button_clicked(GtkButton *button, GtkWindow *parent
                     else {
                         gtk_tree_model_get(model, &iter, LOCATION, &loc, -1);
                         gtk_tree_model_get(model, &iter, LOCATION_ENG, &loc_eng
-                                , -1);                     }
+                                , -1);
+                    }
                 else {
                     loc = g_strdup(_(*tz));
                     loc_eng = g_strdup(*tz);
                 }
                 break;
             case 1:
-                loc = g_strdup(_("UTC"));
-                loc_eng = g_strdup("UTC");
-                break;
-                /*
-            case 2:
-                loc = g_strdup(_("floating"));
-                loc_eng = g_strdup("floating");
-                break;
-            case 3:
-                loc = g_strdup(_(g_par.local_timezone));
-                loc_eng = g_strdup(g_par.local_timezone);
-                break;
-                */
-            case 4:
                 free_orage_timezones(details);
                 details = !details;
                 /* gtk_widget_destroy(GTK_WIDGET(store)); */
                 gtk_widget_destroy(tree);
-                store = tz_button_create_store(details);
+                store = tz_button_create_store(details, check_ical);
                 tree = tz_button_create_view(details, store);
                 gtk_container_add(GTK_CONTAINER(sw), tree);
                 gtk_widget_show_all(tree);
                 result = 0;
                 break;
+            case 2:
+                loc = g_strdup(_("UTC"));
+                loc_eng = g_strdup("UTC");
+                break;
+            case 3:
+                /* NOTE: this exists only if ical timezones are being used */
+                loc = g_strdup(_("floating"));
+                loc_eng = g_strdup("floating");
+                break;
+            case 4:
+                /* NOTE: this exists only if ical timezones are being used */
+                loc = g_strdup(_(local_tz));
+                loc_eng = g_strdup(local_tz);
+                break;
             default:
                 loc = g_strdup(_(*tz));
                 loc_eng = g_strdup(*tz);
                 break;
         }
     } while (result == 0);
-    if (g_ascii_strcasecmp(loc, (gchar *)gtk_button_get_label(button)) != 0)
+    if (loc && g_ascii_strcasecmp(loc, gtk_button_get_label(button))) {
         changed = TRUE;
-    gtk_button_set_label(button, loc);
-
-    if (*tz)
-        g_free(*tz);
-    *tz = g_strdup(loc_eng);
+        /* return the real timezone and update the button to show the 
+         * translated timezone name */
+        if (*tz)
+            g_free(*tz);
+        *tz = g_strdup(loc_eng);
+        gtk_button_set_label(button, loc);
+    }
     g_free(loc);
     g_free(loc_eng);
     gtk_widget_destroy(window);
diff --git a/panel-plugin/timezone_selection.h b/panel-plugin/timezone_selection.h
index 2f0e3af..e181a41 100644
--- a/panel-plugin/timezone_selection.h
+++ b/panel-plugin/timezone_selection.h
@@ -24,6 +24,6 @@
 #define __TIMEZONE_SELECTION_H__
 
 gboolean orage_timezone_button_clicked(GtkButton *button, GtkWindow *parent
-        , gchar **tz);
+        , gchar **tz, gboolean check_ical, char *local_tz);
 
 #endif /* !__TIMEZONE_SELECTION_H__ */
diff --git a/panel-plugin/tz_zoneinfo_read.c b/panel-plugin/tz_zoneinfo_read.c
index b3a08d4..508b427 100644
--- a/panel-plugin/tz_zoneinfo_read.c
+++ b/panel-plugin/tz_zoneinfo_read.c
@@ -59,65 +59,73 @@
 
 
 /** This is the toplevel directory where the timezone data is installed in. */
-#define ORAGE_ZONEINFO_DIRECTORY  PACKAGE_DATA_DIR "/orage/zoneinfo"
+#define ORAGE_ZONEINFO_DIRECTORY  PACKAGE_DATA_DIR "/orage/zoneinfo/"
 
 /** This is the filename of the file containing tz_convert parameters
  * This file contains the location of the os zoneinfo data.
  * the same than the above DEFAULT_OS_ZONEINFO_DIRECTORY */
 #define TZ_CONVERT_PAR_FILENAME  "tz_convert.par"
-#define TZ_CONVERT_PAR_FILE_LOC  ORAGE_ZONEINFO_DIRECTORY "/" TZ_CONVERT_PAR_FILENAME
+#define TZ_CONVERT_PAR_FILE_LOC  ORAGE_ZONEINFO_DIRECTORY TZ_CONVERT_PAR_FILENAME
+
+/** This is the filename of the file containing orage ical timezone names */
+#define ICAL_ZONES_TAB_FILENAME  "zones.tab"
+#define ICAL_ZONES_TAB_FILE_LOC  ORAGE_ZONEINFO_DIRECTORY ICAL_ZONES_TAB_FILENAME
 
 
 
 /* this contains all timezone data */
 orage_timezone_array tz_array={0, NULL, NULL, NULL, NULL, NULL, NULL};
 
-char *zone_tab_buf = NULL, *country_buf = NULL;
+static char *zone_tab_buf = NULL, *country_buf = NULL, *zones_tab_buf = NULL;
 
-int debug = 0; /* bigger number => more output */
-char version[] = "1.4.4";
-int file_cnt = 0; /* number of processed files */
+static int debug = 0; /* bigger number => more output */
+static char version[] = "1.4.4";
+static int file_cnt = 0; /* number of processed files */
 
-unsigned char *in_buf, *in_head, *in_tail;
-int in_file_base_offset = 0;
+static unsigned char *in_buf, *in_head, *in_tail;
+static int in_file_base_offset = 0;
 
-int details;
+static int details;     /* show extra data (country and next change time) */
+static int check_ical;  /* check that we have also the ical timezone data */
 
-char *in_file = NULL, *out_file = NULL;
-int in_file_is_dir = 0;
-int excl_dir_cnt = -1;
-char **excl_dir = NULL;
+static char *in_file = NULL, *out_file = NULL;
+static int in_file_is_dir = 0;
+static int excl_dir_cnt = -1;
+static char **excl_dir = NULL;
 
 /* in_timezone_name is the real timezone name from the infile 
  * we are processing.
- * in_timezone_name is the timezone we are writing. Usually it is the same
+ * timezone_name is the timezone we are writing. Usually it is the same
  * than in_timezone_name. 
- * timezone name is for example Europe/Helsinki */
-char *timezone_name = NULL;  
-char *in_timezone_name = NULL;
+ * timezone_name is for example Europe/Helsinki */
+/* FIXME: we do not need both timezone_name and in_timezone_name here.
+ * Remove one */
+static char *timezone_name = NULL;  
+static char *in_timezone_name = NULL;
 
-int ignore_older = 1970; /* Ignore rules which are older or equal than this */
+/* Ignore rules which are older or equal than this */
+static int ignore_older = 1970; 
 
 /* time change table starts here */
-unsigned char *begin_timechanges;           
+static unsigned char *begin_timechanges;           
 
 /* time change type index table starts here */
-unsigned char *begin_timechangetypeindexes; 
+static unsigned char *begin_timechangetypeindexes; 
 
 /* time change type table starts here */
-unsigned char *begin_timechangetypes;       
+static unsigned char *begin_timechangetypes;       
 
 /* timezone name table */
-unsigned char *begin_timezonenames;         
+static unsigned char *begin_timezonenames;         
 
-unsigned long gmtcnt;
-unsigned long stdcnt;
-unsigned long leapcnt;
-unsigned long timecnt;  /* points when time changes */
-unsigned long typecnt;  /* table of different time changes = types */
-unsigned long charcnt;  /* length of timezone name table */
+static unsigned long gmtcnt;
+static unsigned long stdcnt;
+static unsigned long leapcnt;
+static unsigned long timecnt;  /* points when time changes */
+static unsigned long typecnt;  /* table of different time changes = types */
+static unsigned long charcnt;  /* length of timezone name table */
 
-void read_file(const char *file_name, const struct stat *file_stat)
+static void read_file(const char *file_name, const struct stat *file_stat)
 {
     FILE *file;
 
@@ -136,7 +144,7 @@ void read_file(const char *file_name, const struct stat *file_stat)
         printf("read_file: end\n");
 }
 
-long get_long()
+static long get_long()
 {
     unsigned long tmp;
 
@@ -148,7 +156,7 @@ long get_long()
     return(tmp);
 }
 
-int process_header()
+static int process_header()
 {
     if (debug > 2)
         printf("file id: %s\n", in_head);
@@ -180,7 +188,7 @@ int process_header()
     return(0);
 }
 
-process_local_time_table()
+static process_local_time_table()
 { /* points when time changes */
     unsigned long tmp;
     int i;
@@ -199,7 +207,7 @@ process_local_time_table()
     }
 }
 
-process_local_time_type_table()
+static process_local_time_type_table()
 { /* pointers to table, which explain how time changes */
     unsigned char tmp;
     int i;
@@ -215,7 +223,7 @@ process_local_time_type_table()
     }
 }
 
-process_ttinfo_table()
+static process_ttinfo_table()
 { /* table of different time changes = types */
     long tmp;
     unsigned char tmp2, tmp3;
@@ -236,7 +244,7 @@ process_ttinfo_table()
     }
 }
 
-process_abbr_table()
+static process_abbr_table()
 {
     unsigned char *tmp;
     int i;
@@ -253,7 +261,7 @@ process_abbr_table()
     in_head += charcnt;
 }
 
-process_leap_table()
+static process_leap_table()
 {
     unsigned long tmp, tmp2;
     int i;
@@ -269,7 +277,7 @@ process_leap_table()
     }
 }
 
-process_std_table()
+static process_std_table()
 {
     unsigned char tmp;
     int i;
@@ -284,7 +292,7 @@ process_std_table()
     }
 }
 
-process_gmt_table()
+static process_gmt_table()
 {
     unsigned char tmp;
     int i;
@@ -301,7 +309,7 @@ process_gmt_table()
 
 /* go through the contents of the file and find the positions of 
  * needed data. Uses global pointer: in_head */
-int process_file(const char *file_name)
+static int process_file(const char *file_name)
 {
     if (debug > 1)
         printf("\n\nprocess_file: start\n");
@@ -323,10 +331,10 @@ int process_file(const char *file_name)
     return(0); /* ok */
 }
 
-void get_country()
+static void get_country()
 { /* tz_array.city[tz_array.count] contains the city name.
      We will find corresponding country and fill it to the table */
-    char *str, *str_nl;
+    char *str, *str_nl, cc[4];
 
     if (!(str = strstr(zone_tab_buf, tz_array.city[tz_array.count])))
         return; /* not found */
@@ -343,12 +351,19 @@ void get_country()
     strncpy(tz_array.cc[tz_array.count], ++str_nl, 2);
     tz_array.cc[tz_array.count][2] = '\0';
 
-    /* then search the country */
-    if (!(str = strstr(country_buf, tz_array.cc[tz_array.count])))
+    /********** then search the country **********/
+    /* Need to search line, which starts with country code.
+     * Note that it is not enough to search any country coed, but it really
+     * needs to be the first two chars in the line */
+    cc[0] = '\n';
+    cc[1] = tz_array.cc[tz_array.count][0];
+    cc[2] = tz_array.cc[tz_array.count][1];
+    cc[3] = '\0';
+    if (!(str = strstr(country_buf, cc)))
         return; /* not found */
     /* country name is after the country code and a single tab */
-    str += 3;
-    /* but w still need to find how long it is.
+    str += 4;
+    /* but we still need to find how long it is.
      * It ends in the line end. 
      * (There is a line end at the end of the file also.) */
     for (str_nl = str; str_nl[0] != '\n'; str_nl++)
@@ -358,16 +373,28 @@ void get_country()
     tz_array.country[tz_array.count][(str_nl - str)] = '\0';
 }
 
+static int timezone_exists_in_ical()
+{ /* in_timezone_name contains the timezone name.
+     We will search if it exists also in the ical zones.tab file */
+    char *str;
+
+    if (str = strstr(zones_tab_buf, in_timezone_name))
+        return(1); /* yes, it is there */
+    else
+        return(0); /* not found */
+}
+
 /* FIXME: need to check that if OUTFILE is given as a parameter,
  * INFILE is not a directory (or make outfile to act like directory also ? */
-int write_ical_file(const char *in_file_name, const struct stat *in_file_stat)
+static int write_ical_file(const char *in_file_name
+        , const struct stat *in_file_stat)
 {
     int i;
     unsigned int tct_i, abbr_i;
     struct tm cur_gm_time;
     time_t tt_now = time(NULL);
-    long tc_time = 0; /* TimeChange time */
-    char s_next[101];
+    long tc_time = 0, prev_tc_time; /* TimeChange times */
+    char s_next[101], s_prev[101];
 
     if (debug > 1)
         printf("***** write_ical_file: start *****\n\n");
@@ -384,6 +411,7 @@ int write_ical_file(const char *in_file_name, const struct stat *in_file_stat)
         /* search for current time setting.
          * timecnt tells how many changes we have in the tz file.
          * i points to the next value to read. */
+        prev_tc_time = tc_time;
         tc_time = get_long(); /* start time of this timechange */
     }
     /* i points to the next value to be read, so need to -- */
@@ -393,7 +421,9 @@ int write_ical_file(const char *in_file_name, const struct stat *in_file_stat)
         tz_array.utc_offset[tz_array.count] = 0;
         tz_array.dst[tz_array.count] = 0;
         tz_array.tz[tz_array.count] = "UTC";
+        tz_array.prev[tz_array.count] = NULL;
         tz_array.next[tz_array.count] = NULL;
+        tz_array.next_utc_offset[tz_array.count] = 0;
         tz_array.count++;
         return(1); /* done */
     }
@@ -401,16 +431,52 @@ int write_ical_file(const char *in_file_name, const struct stat *in_file_stat)
         /* we found previous and next value */
         /* tc_time has the next change time */
         if (details) {
+            /* NOTE: If the time change happens for example at 04:00
+             * and goes one hour backward, the new time is 03:00 and this
+             * is what localtime_r reports. In real life we want to show
+             * here 04:00, so let's subtract 1 sec to get close to that.
+             * This is a bit similar than 24:00 or 00:00. Summary:
+             * 04:00 is returned as 03:00 (change happened already) but
+             * 03:59 is returned as 03:59 (change did not yet happen) */
+            prev_tc_time -= 1;
+            localtime_r((const time_t *)&prev_tc_time, &cur_gm_time);
+            strftime(s_prev, 100, "%c", &cur_gm_time);
+            tz_array.prev[tz_array.count] = strdup(s_prev);
+
+            tc_time -= 1;
             localtime_r((const time_t *)&tc_time, &cur_gm_time);
             strftime(s_next, 100, "%c", &cur_gm_time);
             tz_array.next[tz_array.count] = strdup(s_next);
+            /* get timechange type index */
+            if (timecnt) {
+                in_head = begin_timechangetypeindexes;
+                tct_i = (unsigned int)in_head[i];
+            }
+            else
+                tct_i = 0;
+
+            /* get timechange type */
+            in_head = begin_timechangetypes;
+            in_head += 6*tct_i;
+            tz_array.next_utc_offset[tz_array.count] = (int)get_long();
         }
-        else 
+        else {
+            tz_array.prev[tz_array.count] = NULL;
             tz_array.next[tz_array.count] = NULL;
+        }
         i--; /* we need to take the previous value */
     }
-    else 
+    else { /* no next value, but previous may exist */
         tz_array.next[tz_array.count] = NULL;
+        if (details && prev_tc_time) {
+            prev_tc_time -= 1;
+            localtime_r((const time_t *)&prev_tc_time, &cur_gm_time);
+            strftime(s_prev, 100, "%c", &cur_gm_time);
+            tz_array.prev[tz_array.count] = strdup(s_prev);
+        }
+        else
+            tz_array.prev[tz_array.count] = NULL;
+    }
 
     /* i now points to latest time change and shows current time.
      * So we found our result and can start collecting real data: */
@@ -441,7 +507,7 @@ int write_ical_file(const char *in_file_name, const struct stat *in_file_stat)
 }
 
 /* The main code. This is called once per each file found */
-int file_call(const char *file_name, const struct stat *sb, int flags
+static int file_call(const char *file_name, const struct stat *sb, int flags
         , struct FTW *f)
 {
     int i;
@@ -453,14 +519,21 @@ int file_call(const char *file_name, const struct stat *sb, int flags
     if (flags == FTW_F) { /* we got file */
         if (debug > 0)
             printf("\t\tfile_call: processing file=(%s)\n", file_name);
+        in_timezone_name = strdup(&file_name[in_file_base_offset
+                + strlen("zoneinfo/")]);
+        timezone_name = strdup(in_timezone_name);
+        if (check_ical && !timezone_exists_in_ical()) {
+            free(in_timezone_name);
+            free(timezone_name);
+            return(FTW_CONTINUE);
+        }
         read_file(file_name, sb);
         if (process_file(file_name)) { /* we skipped this file */
+            free(in_timezone_name);
+            free(timezone_name);
             free(in_buf);
             return(FTW_CONTINUE);
         }
-        in_timezone_name = strdup(&file_name[in_file_base_offset 
-                + strlen("zoneinfo/")]);
-        timezone_name = strdup(in_timezone_name);
         write_ical_file(file_name, sb);
 
         free(in_buf);
@@ -499,7 +572,7 @@ int file_call(const char *file_name, const struct stat *sb, int flags
 }
 
 /* check the parameters and use defaults when possible */
-int check_parameters()
+static int check_parameters()
 {
     char *s_tz, *last_tz = NULL, tz[]="/zoneinfo", tz2[]="zoneinfo/";
     int tz_len, i;
@@ -630,7 +703,7 @@ int check_parameters()
     return(0); /* continue */
 }
 
-void read_countries()
+static void read_countries()
 {
     char *tz_dir, *zone_tab_file_name, *country_file_name;
     int zoneinfo_len=strlen("zoneinfo/");
@@ -730,7 +803,40 @@ void read_countries()
     fclose(country_file);
 }
 
-orage_timezone_array get_orage_timezones(int show_details)
+static void read_ical_timezones()
+{
+    FILE *zones_tab_file;
+    struct stat zones_tab_file_stat;
+
+    /****** zones.tab file ******/
+    if (!(zones_tab_file = fopen(ICAL_ZONES_TAB_FILE_LOC, "r"))) {
+        printf("read_ical_timezones: zones.tab file open failed (%s)\n"
+                , ICAL_ZONES_TAB_FILE_LOC);
+        perror("\tfopen");
+        return;
+    }
+    if (stat(ICAL_ZONES_TAB_FILE_LOC, &zones_tab_file_stat) == -1) {
+        printf("read_ical_timezones: zones.tab file stat failed (%s)\n"
+                , ICAL_ZONES_TAB_FILE_LOC);
+        fclose(zones_tab_file);
+        perror("\tstat");
+        return;
+    }
+    zones_tab_buf = malloc(zones_tab_file_stat.st_size+1);
+    fread(zones_tab_buf, 1, zones_tab_file_stat.st_size, zones_tab_file);
+    if (ferror(zones_tab_file)) {
+        printf("read_ical_timezones: zones.tab file read failed (%s)\n"
+                , ICAL_ZONES_TAB_FILE_LOC);
+        perror("\tfread");
+        return;
+    }
+    zones_tab_buf[zones_tab_file_stat.st_size] = '\0';
+    printf("read_ical_timezones: zones.tab file read (%s) (%d bytes)\n"
+                , ICAL_ZONES_TAB_FILE_LOC, strlen(zones_tab_buf));
+    fclose(zones_tab_file);
+}
+
+orage_timezone_array get_orage_timezones(int show_details, int ical)
 {
     int tz_array_size = 1000; /* FIXME: this needs to be counted */
     /*
@@ -740,12 +846,15 @@ orage_timezone_array get_orage_timezones(int show_details)
    */
 
     details = show_details;
+    check_ical = ical;
     if (tz_array.count == 0) {
         tz_array.city = (char **)malloc(sizeof(char *)*(tz_array_size+2));
         tz_array.utc_offset = (int *)malloc(sizeof(int)*(tz_array_size+2));
         tz_array.dst = (int *)malloc(sizeof(int)*(tz_array_size+2));
         tz_array.tz = (char **)malloc(sizeof(char *)*(tz_array_size+2));
+        tz_array.prev = (char **)malloc(sizeof(char *)*(tz_array_size+2));
         tz_array.next = (char **)malloc(sizeof(char *)*(tz_array_size+2));
+        tz_array.next_utc_offset = (int *)malloc(sizeof(int)*(tz_array_size+2));
         tz_array.country = (char **)malloc(sizeof(char *)*(tz_array_size+2));
         tz_array.cc = (char **)malloc(sizeof(char *)*(tz_array_size+2));
     /* nftw goes through the whole file structure and calls "file_call"
@@ -756,6 +865,8 @@ orage_timezone_array get_orage_timezones(int show_details)
             printf("Processing %s files\n", in_file);
         if (details)
             read_countries();
+        if (check_ical)
+            read_ical_timezones();
         if (nftw(in_file, file_call, 10, FTW_PHYS | FTW_ACTIONRETVAL) == -1) {
             perror("nftw error in file handling");
             exit(EXIT_FAILURE);
@@ -768,7 +879,9 @@ orage_timezone_array get_orage_timezones(int show_details)
         tz_array.utc_offset[tz_array.count] = 0;
         tz_array.dst[tz_array.count] = 0;
         tz_array.tz[tz_array.count] = strdup("UTC");
+        tz_array.prev[tz_array.count] = NULL;
         tz_array.next[tz_array.count] = NULL;
+        tz_array.next_utc_offset[tz_array.count] = 0;
         tz_array.country[tz_array.count] = NULL;
         tz_array.cc[tz_array.count] = NULL;
         tz_array.city[tz_array.count++] = strdup("UTC");
@@ -776,12 +889,14 @@ orage_timezone_array get_orage_timezones(int show_details)
         tz_array.utc_offset[tz_array.count] = 0;
         tz_array.dst[tz_array.count] = 0;
         tz_array.tz[tz_array.count] = NULL;
+        tz_array.prev[tz_array.count] = NULL;
         tz_array.next[tz_array.count] = NULL;
+        tz_array.next_utc_offset[tz_array.count] = 0;
         tz_array.country[tz_array.count] = NULL;
         tz_array.cc[tz_array.count] = NULL;
         tz_array.city[tz_array.count++] = strdup("floating");
     }
-    return (tz_array);
+    return(tz_array);
 }
 
 void free_orage_timezones(int show_details)
@@ -793,6 +908,8 @@ void free_orage_timezones(int show_details)
             free(tz_array.city[i]);
         if (tz_array.tz[i])
             free(tz_array.tz[i]);
+        if (tz_array.prev[i])
+            free(tz_array.prev[i]);
         if (tz_array.next[i])
             free(tz_array.next[i]);
         if (tz_array.country[i])
@@ -804,10 +921,24 @@ void free_orage_timezones(int show_details)
     free(tz_array.utc_offset);
     free(tz_array.dst);
     free(tz_array.tz);
+    free(tz_array.prev);
     free(tz_array.next);
+    free(tz_array.next_utc_offset);
     free(tz_array.country);
     free(tz_array.cc);
     tz_array.count = 0;
     timezone_name = NULL;
+    if (zone_tab_buf) {
+        free(zone_tab_buf);
+        zone_tab_buf = NULL;
+    }
+    if (country_buf) {
+        free(country_buf);
+        country_buf = NULL;
+    }
+    if (zones_tab_buf) {
+        free(zones_tab_buf);
+        zones_tab_buf = NULL;
+    }
     file_cnt = 0; /* number of processed files */
 }
diff --git a/panel-plugin/tz_zoneinfo_read.h b/panel-plugin/tz_zoneinfo_read.h
index 9002f44..7b5c634 100644
--- a/panel-plugin/tz_zoneinfo_read.h
+++ b/panel-plugin/tz_zoneinfo_read.h
@@ -25,10 +25,12 @@ typedef struct _orage_timezone_array
     int  *utc_offset; /* pointer to int array holding utc offsets */
     int  *dst;        /* pointer to int array holding dst settings */
     char **tz;        /* pointer to timezone name strings */
+    char **prev;      /* pointer to previous time change strings */
     char **next;      /* pointer to next time change strings */
+    int  *next_utc_offset; /* pointer to int array holding utc offsets */
     char **country;   /* pointer to country name strings */
     char **cc;        /* pointer to country code strings */
 } orage_timezone_array;
 
-orage_timezone_array get_orage_timezones(int details);
+orage_timezone_array get_orage_timezones(int details, int ical);
 void free_orage_timezones(int details);
diff --git a/panel-plugin/xfce4-orageclock-plugin.h b/panel-plugin/xfce4-orageclock-plugin.h
index f325e77..85e7ffa 100644
--- a/panel-plugin/xfce4-orageclock-plugin.h
+++ b/panel-plugin/xfce4-orageclock-plugin.h
@@ -50,7 +50,6 @@ typedef struct _clock
     GdkColor   bg;
     GString   *timezone;
     gchar     *TZ_orig;
-    GtkWidget *tz_entry;
     ClockLine  line[OC_MAX_LINES];
     GString   *tooltip_data;
     gchar      tooltip_prev[OC_MAX_LINE_LENGTH+1];
diff --git a/src/appointment.c b/src/appointment.c
index 4a5c876..fe1d3c6 100644
--- a/src/appointment.c
+++ b/src/appointment.c
@@ -1305,7 +1305,7 @@ static void on_appStartTimezone_clicked_cb(GtkButton *button
 
     appt = (xfical_appt *)apptw->xf_appt;
     if (orage_timezone_button_clicked(button, GTK_WINDOW(apptw->Window)
-            , &appt->start_tz_loc))
+            , &appt->start_tz_loc, TRUE, g_par.local_timezone))
         mark_appointment_changed(apptw);
 }
 
@@ -1317,7 +1317,7 @@ static void on_appEndTimezone_clicked_cb(GtkButton *button
 
     appt = (xfical_appt *)apptw->xf_appt;
     if (orage_timezone_button_clicked(button, GTK_WINDOW(apptw->Window)
-            , &appt->end_tz_loc))
+            , &appt->end_tz_loc, TRUE, g_par.local_timezone))
         mark_appointment_changed(apptw);
 }
 
@@ -1329,7 +1329,7 @@ static void on_appCompletedTimezone_clicked_cb(GtkButton *button
 
     appt = (xfical_appt *)apptw->xf_appt;
     if (orage_timezone_button_clicked(button, GTK_WINDOW(apptw->Window)
-            , &appt->completed_tz_loc))
+            , &appt->completed_tz_loc, TRUE, g_par.local_timezone))
         mark_appointment_changed(apptw);
 }
 
diff --git a/src/parameters.c b/src/parameters.c
index 2c49725..89b14d3 100644
--- a/src/parameters.c
+++ b/src/parameters.c
@@ -444,7 +444,7 @@ static void timezone_button_clicked(GtkButton *button, gpointer user_data)
         g_par.local_timezone = g_strdup("floating");
     }
     if (orage_timezone_button_clicked(button, GTK_WINDOW(itf->orage_dialog)
-            , &g_par.local_timezone))
+            , &g_par.local_timezone, TRUE, g_par.local_timezone))
         xfical_set_local_timezone(FALSE);
 }
 
diff --git a/src/timezone_selection.c b/src/timezone_selection.c
index e5eafa7..44e18a9 100644
--- a/src/timezone_selection.c
+++ b/src/timezone_selection.c
@@ -66,13 +66,13 @@ enum {
     LOCATION,
     LOCATION_ENG,
     OFFSET,
+    CHANGES,
     COUNTRY,
-    PREV_CHANGE,
-    NEXT_CHANGE,
     N_COLUMNS
 };
 
-static GtkTreeStore *tz_button_create_store(gboolean details)
+static GtkTreeStore *tz_button_create_store(gboolean details
+        , gboolean check_ical)
 {
 #undef P_N
 #define P_N "tz_button_create_store: "
@@ -82,14 +82,16 @@ static GtkTreeStore *tz_button_create_store(gboolean details)
     GtkTreeIter iter1, iter2, main;
     orage_timezone_array tz_a;
     char area_old[MAX_AREA_LENGTH+2]; /*+2 = / + null */
-    char s_offset[100], s_country[100], s_next[100], s_prev[100];
-    gint i, j, offs_hour, offs_min;
+    char s_offset[100], s_country[100], s_changes[200], s_change[50]
+        , s_change_time[50];
+    gint i, j, offs_hour, offs_min, next_offs_hour, next_offs_min
+        , change_time, change_hour, change_min;
 
     store = gtk_tree_store_new(N_COLUMNS
             , G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING
-            , G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+            , G_TYPE_STRING, G_TYPE_STRING);
     strcpy(area_old, "S T a R T"); /* this never matches */
-    tz_a = get_orage_timezones(details, 1);
+    tz_a = get_orage_timezones(details, check_ical ? 1 : 0);
     /*
     g_print(P_N "number of timezones %d\n", tz_a.count);
     */
@@ -100,9 +102,8 @@ static GtkTreeStore *tz_button_create_store(gboolean details)
             , LOCATION, _(" Other")
             , LOCATION_ENG, " Other"
             , OFFSET, " "
+            , CHANGES, " "
             , COUNTRY, " "
-            , PREV_CHANGE, " "
-            , NEXT_CHANGE, " "
             , -1);
     main = iter1; /* need to remember that */
 
@@ -125,9 +126,8 @@ static GtkTreeStore *tz_button_create_store(gboolean details)
                         , LOCATION, _(area_old)
                         , LOCATION_ENG, area_old
                         , OFFSET, " "
+                        , CHANGES, " "
                         , COUNTRY, " "
-                        , PREV_CHANGE, " "
-                        , NEXT_CHANGE, " "
                         , -1);
                 /* let's make sure we do not match accidentally to those 
                  * plain names on main level. We do this by adding / */
@@ -135,7 +135,7 @@ static GtkTreeStore *tz_button_create_store(gboolean details)
                 area_old[j] = 0;
             }
             else {
-                orage_message(310, P_N "too long line in zones.tab %s", tz_a.city[i]);
+                g_print(P_N "too long line in zones.tab %s", tz_a.city[i]);
             }
         }
         /* then city translated and in base form used internally */
@@ -146,34 +146,66 @@ static GtkTreeStore *tz_button_create_store(gboolean details)
         if (offs_min)
             g_print(P_N " %s offset %d hour %d minutes %d\n", tz_a.city[i], tz_a.utc_offset[i], offs_hour, offs_min);
         */
-        g_snprintf(s_offset, 100, "%+03d:%02d %s (%s)"
-                , offs_hour, offs_min
-                , (tz_a.dst[i]) ? "dst" : "std"
-                , (tz_a.tz[i]) ? tz_a.tz[i] : "-");
+        if (details && tz_a.next[i]) {
+            next_offs_hour = tz_a.next_utc_offset[i] / (60*60);
+            next_offs_min = abs((tz_a.next_utc_offset[i]
+                    - next_offs_hour * (60*60)) / 60);
+            change_time = tz_a.next_utc_offset[i] - tz_a.utc_offset[i];
+            change_hour = change_time / (60*60);
+            change_min  = abs((change_time - change_hour * (60*60)) /60);
+            if (change_hour && change_min)
+                g_snprintf(s_change_time, 50, _("%d hour %d mins")
+                        , abs(change_hour), change_min);
+            else if (change_hour)
+                g_snprintf(s_change_time, 50, _("%d hour"), abs(change_hour));
+            else if (change_min)
+                g_snprintf(s_change_time, 50, _("%d mins"), change_min);
+            else
+                strcpy(s_change_time, " ");
+
+            if (change_time < 0)
+                g_snprintf(s_change, 50, "(%s %s)"
+                        , _("backward"), s_change_time);
+            else if (change_time > 0)
+                g_snprintf(s_change, 50, "(%s %s)"
+                        , _("forward"), s_change_time);
+            else
+                strcpy(s_change, " ");
+            g_snprintf(s_offset, 100
+                    , "%+03d:%02d %s (%s)\n   -> %+03d:%02d %s"
+                    , offs_hour, offs_min
+                    , (tz_a.dst[i]) ? "dst" : "std"
+                    , (tz_a.tz[i]) ? tz_a.tz[i] : "-"
+                    , next_offs_hour, next_offs_min
+                    , s_change);
+        }
+        else {
+            g_snprintf(s_offset, 100, "%+03d:%02d %s (%s)"
+                    , offs_hour, offs_min
+                    , (tz_a.dst[i]) ? "dst" : "std"
+                    , (tz_a.tz[i]) ? tz_a.tz[i] : "-");
+        }
         if (details) {
             if (tz_a.country[i] && tz_a.cc[i])
                 g_snprintf(s_country, 100, "%s (%s)"
                         , tz_a.country[i], tz_a.cc[i]);
-            else 
+            else
                 strcpy(s_country, " ");
-            g_snprintf(s_prev, 100, "%s"
-                    , (tz_a.prev[i]) ? tz_a.prev[i] : _("not changed"));
-            g_snprintf(s_next, 100, "%s"
+            g_snprintf(s_changes, 200, "%s\n%s"
+                    , (tz_a.prev[i]) ? tz_a.prev[i] : _("not changed")
                     , (tz_a.next[i]) ? tz_a.next[i] : _("not changing"));
         }
         else {
             strcpy(s_country, " ");
-            strcpy(s_prev, " ");
-            strcpy(s_next, " ");
+            strcpy(s_changes, " ");
         }
 
         gtk_tree_store_set(store, &iter2
                 , LOCATION, _(tz_a.city[i])
                 , LOCATION_ENG, tz_a.city[i]
                 , OFFSET, s_offset
+                , CHANGES, s_changes
                 , COUNTRY, s_country
-                , PREV_CHANGE, s_prev
-                , NEXT_CHANGE, s_next
                 , -1);
     }
     return(store);
@@ -230,25 +262,20 @@ GtkWidget *tz_button_create_view(gboolean details, GtkTreeStore *store)
 
     if (details) {
         rend = gtk_cell_renderer_text_new();
-        col  = gtk_tree_view_column_new_with_attributes(_("Country")
-                , rend, "text", COUNTRY, NULL);
-        gtk_tree_view_append_column(GTK_TREE_VIEW(tree), col);
-
-        rend = gtk_cell_renderer_text_new();
-        col  = gtk_tree_view_column_new_with_attributes(_("Previous Change")
-                , rend, "text", PREV_CHANGE, NULL);
+        col = gtk_tree_view_column_new_with_attributes(_("Previous/Next Change")
+                , rend, "text", CHANGES, NULL);
         gtk_tree_view_append_column(GTK_TREE_VIEW(tree), col);
 
         rend = gtk_cell_renderer_text_new();
-        col  = gtk_tree_view_column_new_with_attributes(_("Next Change")
-                , rend, "text", NEXT_CHANGE, NULL);
+        col = gtk_tree_view_column_new_with_attributes(_("Country")
+                , rend, "text", COUNTRY, NULL);
         gtk_tree_view_append_column(GTK_TREE_VIEW(tree), col);
     }
     return(tree);
 }
 
 gboolean orage_timezone_button_clicked(GtkButton *button, GtkWindow *parent
-        , gchar **tz)
+        , gchar **tz, gboolean check_ical, char *local_tz)
 {
 #undef P_N
 #define P_N "orage_timezone_button_clicked: "
@@ -258,33 +285,39 @@ gboolean orage_timezone_button_clicked(GtkButton *button, GtkWindow *parent
     GtkWidget *window;
     GtkWidget *sw;
     int result;
-    char *loc, *loc_eng;
+    char *loc = NULL, *loc_eng = NULL;
     GtkTreeSelection *sel;
     GtkTreeModel     *model;
     GtkTreeIter       iter;
     gboolean    changed = FALSE;
     gboolean    details = FALSE;
 
-#ifdef ORAGE_DEBUG
-    orage_message(-100, P_N);
-#endif
-    store = tz_button_create_store(details);
+    store = tz_button_create_store(details, check_ical);
     tree = tz_button_create_view(details, store);
 
     /* show it */
-    window =  gtk_dialog_new_with_buttons(_("Pick timezone")
-            , parent
-            , GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT
-            , _("Change mode"), 4
-            , _("UTC"), 1
-            , _("floating"), 2
-            , _(g_par.local_timezone), 3
-            , GTK_STOCK_OK, GTK_RESPONSE_ACCEPT
-            , NULL);
+    if (check_ical)
+        window =  gtk_dialog_new_with_buttons(_("Pick timezone")
+                , parent
+                , GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT
+                , _("Change mode"), 1
+                , _("UTC"), 2
+                , _("floating"), 3
+                , _(local_tz), 4
+                , GTK_STOCK_OK, GTK_RESPONSE_ACCEPT
+                , NULL);
+    else
+        window =  gtk_dialog_new_with_buttons(_("Pick timezone")
+                , parent
+                , GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT
+                , _("Change mode"), 1
+                , _("UTC"), 2
+                , GTK_STOCK_OK, GTK_RESPONSE_ACCEPT
+                , NULL);
     sw = gtk_scrolled_window_new(NULL, NULL);
     gtk_container_add(GTK_CONTAINER(sw), tree);
     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox), sw, TRUE, TRUE, 0);
-    gtk_window_set_default_size(GTK_WINDOW(window), 610, 500);
+    gtk_window_set_default_size(GTK_WINDOW(window), 750, 500);
 
     gtk_widget_show_all(window);
     do {
@@ -298,48 +331,53 @@ gboolean orage_timezone_button_clicked(GtkButton *button, GtkWindow *parent
                     else {
                         gtk_tree_model_get(model, &iter, LOCATION, &loc, -1);
                         gtk_tree_model_get(model, &iter, LOCATION_ENG, &loc_eng
-                                , -1);                     }
+                                , -1);
+                    }
                 else {
                     loc = g_strdup(_(*tz));
                     loc_eng = g_strdup(*tz);
                 }
                 break;
             case 1:
-                loc = g_strdup(_("UTC"));
-                loc_eng = g_strdup("UTC");
-                break;
-            case 2:
-                loc = g_strdup(_("floating"));
-                loc_eng = g_strdup("floating");
-                break;
-            case 3:
-                loc = g_strdup(_(g_par.local_timezone));
-                loc_eng = g_strdup(g_par.local_timezone);
-                break;
-            case 4:
                 free_orage_timezones(details);
                 details = !details;
                 /* gtk_widget_destroy(GTK_WIDGET(store)); */
                 gtk_widget_destroy(tree);
-                store = tz_button_create_store(details);
+                store = tz_button_create_store(details, check_ical);
                 tree = tz_button_create_view(details, store);
                 gtk_container_add(GTK_CONTAINER(sw), tree);
                 gtk_widget_show_all(tree);
                 result = 0;
                 break;
+            case 2:
+                loc = g_strdup(_("UTC"));
+                loc_eng = g_strdup("UTC");
+                break;
+            case 3:
+                /* NOTE: this exists only if ical timezones are being used */
+                loc = g_strdup(_("floating"));
+                loc_eng = g_strdup("floating");
+                break;
+            case 4:
+                /* NOTE: this exists only if ical timezones are being used */
+                loc = g_strdup(_(local_tz));
+                loc_eng = g_strdup(local_tz);
+                break;
             default:
                 loc = g_strdup(_(*tz));
                 loc_eng = g_strdup(*tz);
                 break;
         }
     } while (result == 0);
-    if (g_ascii_strcasecmp(loc, (gchar *)gtk_button_get_label(button)) != 0)
+    if (loc && g_ascii_strcasecmp(loc, gtk_button_get_label(button))) {
         changed = TRUE;
-    gtk_button_set_label(button, loc);
-
-    if (*tz)
-        g_free(*tz);
-    *tz = g_strdup(loc_eng);
+        /* return the real timezone and update the button to show the 
+         * translated timezone name */
+        if (*tz)
+            g_free(*tz);
+        *tz = g_strdup(loc_eng);
+        gtk_button_set_label(button, loc);
+    }
     g_free(loc);
     g_free(loc_eng);
     gtk_widget_destroy(window);
diff --git a/src/timezone_selection.h b/src/timezone_selection.h
index 2f0e3af..e181a41 100644
--- a/src/timezone_selection.h
+++ b/src/timezone_selection.h
@@ -24,6 +24,6 @@
 #define __TIMEZONE_SELECTION_H__
 
 gboolean orage_timezone_button_clicked(GtkButton *button, GtkWindow *parent
-        , gchar **tz);
+        , gchar **tz, gboolean check_ical, char *local_tz);
 
 #endif /* !__TIMEZONE_SELECTION_H__ */
diff --git a/src/tz_zoneinfo_read.c b/src/tz_zoneinfo_read.c
index 8a48d02..508b427 100644
--- a/src/tz_zoneinfo_read.c
+++ b/src/tz_zoneinfo_read.c
@@ -72,6 +72,7 @@
 #define ICAL_ZONES_TAB_FILE_LOC  ORAGE_ZONEINFO_DIRECTORY ICAL_ZONES_TAB_FILENAME
 
 
+
 /* this contains all timezone data */
 orage_timezone_array tz_array={0, NULL, NULL, NULL, NULL, NULL, NULL};
 
@@ -97,7 +98,7 @@ static char **excl_dir = NULL;
  * timezone_name is the timezone we are writing. Usually it is the same
  * than in_timezone_name. 
  * timezone_name is for example Europe/Helsinki */
-/* FIXME: we do nto need both timezone_name and in_timezone_name here.
+/* FIXME: we do not need both timezone_name and in_timezone_name here.
  * Remove one */
 static char *timezone_name = NULL;  
 static char *in_timezone_name = NULL;
@@ -354,14 +355,14 @@ static void get_country()
     /* Need to search line, which starts with country code.
      * Note that it is not enough to search any country coed, but it really
      * needs to be the first two chars in the line */
-    cc[0] = '\n'; 
+    cc[0] = '\n';
     cc[1] = tz_array.cc[tz_array.count][0];
     cc[2] = tz_array.cc[tz_array.count][1];
     cc[3] = '\0';
     if (!(str = strstr(country_buf, cc)))
         return; /* not found */
     /* country name is after the country code and a single tab */
-    str += 3;
+    str += 4;
     /* but we still need to find how long it is.
      * It ends in the line end. 
      * (There is a line end at the end of the file also.) */
@@ -379,7 +380,7 @@ static int timezone_exists_in_ical()
 
     if (str = strstr(zones_tab_buf, in_timezone_name))
         return(1); /* yes, it is there */
-    else 
+    else
         return(0); /* not found */
 }
 
@@ -422,6 +423,7 @@ static int write_ical_file(const char *in_file_name
         tz_array.tz[tz_array.count] = "UTC";
         tz_array.prev[tz_array.count] = NULL;
         tz_array.next[tz_array.count] = NULL;
+        tz_array.next_utc_offset[tz_array.count] = 0;
         tz_array.count++;
         return(1); /* done */
     }
@@ -429,22 +431,45 @@ static int write_ical_file(const char *in_file_name
         /* we found previous and next value */
         /* tc_time has the next change time */
         if (details) {
+            /* NOTE: If the time change happens for example at 04:00
+             * and goes one hour backward, the new time is 03:00 and this
+             * is what localtime_r reports. In real life we want to show
+             * here 04:00, so let's subtract 1 sec to get close to that.
+             * This is a bit similar than 24:00 or 00:00. Summary:
+             * 04:00 is returned as 03:00 (change happened already) but
+             * 03:59 is returned as 03:59 (change did not yet happen) */
+            prev_tc_time -= 1;
             localtime_r((const time_t *)&prev_tc_time, &cur_gm_time);
             strftime(s_prev, 100, "%c", &cur_gm_time);
             tz_array.prev[tz_array.count] = strdup(s_prev);
+
+            tc_time -= 1;
             localtime_r((const time_t *)&tc_time, &cur_gm_time);
             strftime(s_next, 100, "%c", &cur_gm_time);
             tz_array.next[tz_array.count] = strdup(s_next);
+            /* get timechange type index */
+            if (timecnt) {
+                in_head = begin_timechangetypeindexes;
+                tct_i = (unsigned int)in_head[i];
+            }
+            else
+                tct_i = 0;
+
+            /* get timechange type */
+            in_head = begin_timechangetypes;
+            in_head += 6*tct_i;
+            tz_array.next_utc_offset[tz_array.count] = (int)get_long();
         }
         else {
-            tz_array.next[tz_array.count] = NULL;
             tz_array.prev[tz_array.count] = NULL;
+            tz_array.next[tz_array.count] = NULL;
         }
         i--; /* we need to take the previous value */
     }
     else { /* no next value, but previous may exist */
         tz_array.next[tz_array.count] = NULL;
         if (details && prev_tc_time) {
+            prev_tc_time -= 1;
             localtime_r((const time_t *)&prev_tc_time, &cur_gm_time);
             strftime(s_prev, 100, "%c", &cur_gm_time);
             tz_array.prev[tz_array.count] = strdup(s_prev);
@@ -494,7 +519,7 @@ static int file_call(const char *file_name, const struct stat *sb, int flags
     if (flags == FTW_F) { /* we got file */
         if (debug > 0)
             printf("\t\tfile_call: processing file=(%s)\n", file_name);
-        in_timezone_name = strdup(&file_name[in_file_base_offset 
+        in_timezone_name = strdup(&file_name[in_file_base_offset
                 + strlen("zoneinfo/")]);
         timezone_name = strdup(in_timezone_name);
         if (check_ical && !timezone_exists_in_ical()) {
@@ -678,7 +703,6 @@ static int check_parameters()
     return(0); /* continue */
 }
 
-
 static void read_countries()
 {
     char *tz_dir, *zone_tab_file_name, *country_file_name;
@@ -830,6 +854,7 @@ orage_timezone_array get_orage_timezones(int show_details, int ical)
         tz_array.tz = (char **)malloc(sizeof(char *)*(tz_array_size+2));
         tz_array.prev = (char **)malloc(sizeof(char *)*(tz_array_size+2));
         tz_array.next = (char **)malloc(sizeof(char *)*(tz_array_size+2));
+        tz_array.next_utc_offset = (int *)malloc(sizeof(int)*(tz_array_size+2));
         tz_array.country = (char **)malloc(sizeof(char *)*(tz_array_size+2));
         tz_array.cc = (char **)malloc(sizeof(char *)*(tz_array_size+2));
     /* nftw goes through the whole file structure and calls "file_call"
@@ -856,6 +881,7 @@ orage_timezone_array get_orage_timezones(int show_details, int ical)
         tz_array.tz[tz_array.count] = strdup("UTC");
         tz_array.prev[tz_array.count] = NULL;
         tz_array.next[tz_array.count] = NULL;
+        tz_array.next_utc_offset[tz_array.count] = 0;
         tz_array.country[tz_array.count] = NULL;
         tz_array.cc[tz_array.count] = NULL;
         tz_array.city[tz_array.count++] = strdup("UTC");
@@ -865,6 +891,7 @@ orage_timezone_array get_orage_timezones(int show_details, int ical)
         tz_array.tz[tz_array.count] = NULL;
         tz_array.prev[tz_array.count] = NULL;
         tz_array.next[tz_array.count] = NULL;
+        tz_array.next_utc_offset[tz_array.count] = 0;
         tz_array.country[tz_array.count] = NULL;
         tz_array.cc[tz_array.count] = NULL;
         tz_array.city[tz_array.count++] = strdup("floating");
@@ -896,6 +923,7 @@ void free_orage_timezones(int show_details)
     free(tz_array.tz);
     free(tz_array.prev);
     free(tz_array.next);
+    free(tz_array.next_utc_offset);
     free(tz_array.country);
     free(tz_array.cc);
     tz_array.count = 0;
diff --git a/src/tz_zoneinfo_read.h b/src/tz_zoneinfo_read.h
index 1f70f73..7b5c634 100644
--- a/src/tz_zoneinfo_read.h
+++ b/src/tz_zoneinfo_read.h
@@ -27,6 +27,7 @@ typedef struct _orage_timezone_array
     char **tz;        /* pointer to timezone name strings */
     char **prev;      /* pointer to previous time change strings */
     char **next;      /* pointer to next time change strings */
+    int  *next_utc_offset; /* pointer to int array holding utc offsets */
     char **country;   /* pointer to country name strings */
     char **cc;        /* pointer to country code strings */
 } orage_timezone_array;



More information about the Xfce4-commits mailing list