diff --git a/gnome-panel.spec b/gnome-panel.spec index 96ad150..888755f 100644 --- a/gnome-panel.spec +++ b/gnome-panel.spec @@ -22,7 +22,7 @@ Summary: GNOME panel Name: gnome-panel Version: 2.20.0.1 -Release: 4%{?dist} +Release: 5%{?dist} URL: http://www.gnome.org Source0: http://download.gnome.org/sources/gnome-panel/2.20/%{name}-%{version}.tar.bz2 # we are upstream for this @@ -30,7 +30,7 @@ Source1: timezone.tar.gz # Novell intlclock, I don't think this has a canonical upstream location Source2: intlclock-1.0.tar.gz Patch98: timezone-changes.patch -Patch99: intlclock-changes-20071005.patch +Patch99: intlclock-changes-20071009.patch Patch100: dont-build-clock.patch Patch101: intlclock-build-fixes.patch @@ -376,6 +376,9 @@ fi %{_datadir}/gtk-doc/html/* %changelog +* Tue Oct 9 2007 Matthias Clasen - 2.20.0.1-5 +- Some intlclock updates + * Fri Oct 5 2007 Matthias Clasen - 2.20.0.1-4 - Replace clock by intlclock diff --git a/intlclock-changes-20071009.patch b/intlclock-changes-20071009.patch new file mode 100644 index 0000000..911110a --- /dev/null +++ b/intlclock-changes-20071009.patch @@ -0,0 +1,10832 @@ +diff --git a/configure.in b/configure.in +index 4e818da..6c75db1 100644 +--- a/configure.in ++++ b/configure.in +@@ -59,7 +59,7 @@ if test -n "$LIBECAL_REQUIREMENT"; then + fi + AM_CONDITIONAL(HAVE_LIBECAL, test -n "$LIBECAL_REQUIREMENT") + +-PKG_CHECK_MODULES(INTLCLOCK, [ glib-2.0 gobject-2.0 gtk+-2.0 gdk-2.0 librsvg-2.0 libpanelapplet-2.0 libgnome-2.0 gconf-2.0 $LIBECAL_REQUIREMENT ]) ++PKG_CHECK_MODULES(INTLCLOCK, [ glib-2.0 gobject-2.0 gtk+-2.0 gdk-2.0 librsvg-2.0 libpanelapplet-2.0 libgnome-2.0 gconf-2.0 $LIBECAL_REQUIREMENT dbus-glib-1 gweather libxml-2.0 polkit polkit-dbus ]) + + AC_SUBST(INTLCLOCK_CFLAGS) + AC_SUBST(INTLCLOCK_LIBS) +diff --git a/data/GNOME_IntlClockApplet.xml b/data/GNOME_IntlClockApplet.xml +index dd2e8b8..a88dc3d 100644 +--- a/data/GNOME_IntlClockApplet.xml ++++ b/data/GNOME_IntlClockApplet.xml +@@ -1,9 +1,14 @@ + + + ++ ++ ++ + ++ _label="Ad_just Date & Time" ++ pixtype="stock" pixname="gtk-preferences"/> + + +diff --git a/data/GNOME_IntlClockApplet_Factory.server.in.in b/data/GNOME_IntlClockApplet_Factory.server.in.in +index 1446822..705fb76 100644 +--- a/data/GNOME_IntlClockApplet_Factory.server.in.in ++++ b/data/GNOME_IntlClockApplet_Factory.server.in.in +@@ -20,7 +20,7 @@ + + + +- ++ + + + + ++ 6 + True + False + 0 + + +- ++ + 6 + True + True +@@ -37,194 +38,35 @@ + False + + +- +- 6 ++ ++ 12 + True +- 4 +- 3 + False +- 6 +- 6 ++ 18 + + +- +- True +- Clock options: +- False +- False +- GTK_JUSTIFY_LEFT +- False +- False +- 0 +- 0.5 +- 0 +- 0 +- PANGO_ELLIPSIZE_NONE +- -1 +- False +- 0 +- +- +- 0 +- 1 +- 0 +- 1 +- fill +- +- +- +- +- +- +- True +- +- False +- False +- GTK_JUSTIFY_LEFT +- False +- False +- 0 +- 0.5 +- 0 +- 0 +- PANGO_ELLIPSIZE_NONE +- -1 +- False +- 0 +- +- +- 0 +- 1 +- 1 +- 2 +- fill +- +- +- +- +- +- +- True +- +- False +- False +- GTK_JUSTIFY_LEFT +- False +- False +- 0 +- 0.5 +- 0 +- 0 +- PANGO_ELLIPSIZE_NONE +- -1 +- False +- 0 +- +- +- 0 +- 1 +- 2 +- 3 +- fill +- +- +- +- +- +- +- True +- +- False +- False +- GTK_JUSTIFY_LEFT +- False +- False +- 0 +- 0.5 +- 0 +- 0 +- PANGO_ELLIPSIZE_NONE +- -1 +- False +- 0 +- +- +- 0 +- 1 +- 3 +- 4 +- fill +- +- +- +- +- +- +- True +- +- False +- False +- GTK_JUSTIFY_LEFT +- False +- False +- 0 +- 0.5 +- 0 +- 0 +- PANGO_ELLIPSIZE_NONE +- -1 +- False +- 0 +- +- +- 2 +- 3 +- 3 +- 4 +- fill +- +- +- +- +- +- +- True +- True +- Show s_econds +- True +- GTK_RELIEF_NORMAL +- True +- False +- False +- True +- +- +- 1 +- 3 +- 2 +- 3 +- fill +- +- +- +- +- +- ++ + True + False + 6 + + +- ++ + True +- True +- 12 _hour format +- True +- GTK_RELIEF_NORMAL +- True +- False +- False +- True ++ <b>Clock Options</b> ++ False ++ True ++ GTK_JUSTIFY_LEFT ++ False ++ False ++ 0 ++ 0.5 ++ 0 ++ 0 ++ PANGO_ELLIPSIZE_NONE ++ -1 ++ False ++ 0 + + + 0 +@@ -234,138 +76,150 @@ + + + +- ++ + True +- True +- 24 h_our format +- True +- GTK_RELIEF_NORMAL +- True +- False +- False +- True +- 12hr_radio ++ False ++ 0 ++ ++ ++ ++ True ++ ++ False ++ False ++ GTK_JUSTIFY_LEFT ++ False ++ False ++ 0.5 ++ 0.5 ++ 0 ++ 0 ++ PANGO_ELLIPSIZE_NONE ++ -1 ++ False ++ 0 ++ ++ ++ 0 ++ False ++ False ++ ++ ++ ++ ++ ++ True ++ False ++ 6 ++ ++ ++ ++ True ++ False ++ 13 ++ ++ ++ ++ True ++ True ++ 12 _hour format ++ True ++ GTK_RELIEF_NORMAL ++ True ++ False ++ False ++ True ++ ++ ++ 0 ++ False ++ False ++ ++ ++ ++ ++ ++ True ++ True ++ 24 h_our format ++ True ++ GTK_RELIEF_NORMAL ++ True ++ False ++ False ++ True ++ 12hr_radio ++ ++ ++ 0 ++ False ++ False ++ ++ ++ ++ ++ 0 ++ True ++ True ++ ++ ++ ++ ++ ++ True ++ True ++ Show the _date ++ True ++ GTK_RELIEF_NORMAL ++ True ++ False ++ False ++ True ++ ++ ++ 0 ++ False ++ False ++ ++ ++ ++ ++ ++ True ++ True ++ Show _seconds ++ True ++ GTK_RELIEF_NORMAL ++ True ++ False ++ False ++ True ++ ++ ++ 0 ++ False ++ False ++ ++ ++ ++ ++ 0 ++ True ++ True ++ ++ + + + 0 +- False +- False ++ True ++ True + + + + +- 1 +- 2 +- 0 +- 1 +- fill +- fill +- +- +- +- +- +- True +- True +- Show the _date +- True +- GTK_RELIEF_NORMAL +- True +- False +- False +- True +- +- +- 1 +- 2 +- 1 +- 2 +- fill +- +- +- +- +- +- +- True +- +- False +- False +- GTK_JUSTIFY_LEFT +- False +- False +- 0 +- 0.5 +- 0 +- 0 +- PANGO_ELLIPSIZE_NONE +- -1 +- False +- 0 +- +- +- 2 +- 3 +- 0 +- 1 +- fill +- +- +- +- +- +- +- True +- +- False +- False +- GTK_JUSTIFY_LEFT +- False +- False +- 0 +- 0.5 +- 0 +- 0 +- PANGO_ELLIPSIZE_NONE +- -1 +- False +- 0 +- +- +- 2 +- 3 +- 1 +- 2 +- fill +- +- +- +- +- +- +- True +- +- False +- False +- GTK_JUSTIFY_LEFT +- False +- False +- 0 +- 0.5 +- 0 +- 0 +- PANGO_ELLIPSIZE_NONE +- -1 +- False +- 0 +- +- +- 1 +- 2 +- 3 +- 4 +- fill +- ++ 0 ++ False ++ True + + + +@@ -376,7 +230,7 @@ + + + +- ++ + True + General + False +@@ -399,332 +253,105 @@ + + + +- +- 6 ++ ++ 12 + True +- 8 +- 4 + False +- 6 +- 6 +- +- +- +- True +- +- False +- False +- GTK_JUSTIFY_LEFT +- False +- False +- 0 +- 0.5 +- 0 +- 0 +- PANGO_ELLIPSIZE_NONE +- -1 +- False +- 0 +- +- +- 3 +- 4 +- 0 +- 1 +- fill +- +- +- +- +- +- +- True +- +- False +- False +- GTK_JUSTIFY_LEFT +- False +- False +- 0 +- 0.5 +- 0 +- 0 +- PANGO_ELLIPSIZE_NONE +- -1 +- False +- 0 +- +- +- 3 +- 4 +- 1 +- 2 +- fill +- +- +- ++ 18 + + +- ++ + True +- True +- GTK_POLICY_NEVER +- GTK_POLICY_NEVER +- GTK_SHADOW_IN +- GTK_CORNER_TOP_LEFT ++ False ++ 12 + + +- ++ + True + True +- False +- False +- False +- True +- False +- False +- True ++ GTK_POLICY_NEVER ++ GTK_POLICY_NEVER ++ GTK_SHADOW_IN ++ GTK_CORNER_TOP_LEFT ++ ++ ++ ++ True ++ True ++ False ++ False ++ False ++ True ++ False ++ False ++ True ++ ++ + ++ ++ 0 ++ True ++ True ++ + +- +- +- 0 +- 3 +- 2 +- 8 +- fill +- +- +- +- +- +- True +- 0 +- 0.5 +- 0 +- 1 +- 0 +- 0 +- 0 +- 0 + + +- ++ + True +- False ++ GTK_BUTTONBOX_START + 6 + + +- ++ ++ 24 ++ 25 + True +- +- False +- False +- GTK_JUSTIFY_LEFT +- False +- False +- 0.5 +- 0.5 +- 0 +- 0 +- PANGO_ELLIPSIZE_NONE +- -1 +- False +- 0 ++ True ++ True ++ gtk-add ++ True ++ GTK_RELIEF_NORMAL ++ True + +- +- 0 +- False +- False +- + + + +- ++ ++ 24 ++ 24 + True +- GTK_BUTTONBOX_START +- 6 +- +- +- +- 24 +- 25 +- True +- True +- True +- gtk-add +- True +- GTK_RELIEF_NORMAL +- True +- +- +- +- +- +- 24 +- 24 +- True +- True +- True +- gtk-edit +- True +- GTK_RELIEF_NORMAL +- True +- +- +- +- +- +- True +- True +- True +- gtk-remove +- True +- GTK_RELIEF_NORMAL +- True +- +- ++ True ++ True ++ gtk-edit ++ True ++ GTK_RELIEF_NORMAL ++ True + +- +- 0 +- False +- False +- + + + +- ++ + True +- +- False +- False +- GTK_JUSTIFY_LEFT +- False +- False +- 0.5 +- 0.5 +- 0 +- 0 +- PANGO_ELLIPSIZE_NONE +- -1 +- False +- 0 ++ True ++ True ++ gtk-remove ++ True ++ GTK_RELIEF_NORMAL ++ True + +- +- 0 +- False +- False +- + + ++ ++ 0 ++ False ++ False ++ + + + +- 3 +- 4 +- 2 +- 8 +- +- fill +- +- +- +- +- +- True +- True +- Show the world _map in the clock +- True +- GTK_RELIEF_NORMAL +- True +- False +- False +- True +- +- +- 0 +- 1 +- 1 +- 2 +- fill +- +- +- +- +- +- +- True +- +- False +- False +- GTK_JUSTIFY_LEFT +- False +- False +- 0.5 +- 0.5 +- 0 +- 0 +- PANGO_ELLIPSIZE_NONE +- -1 +- False +- 0 +- +- +- 2 +- 3 +- 1 +- 2 +- +- +- +- +- +- +- True +- +- False +- False +- GTK_JUSTIFY_LEFT +- False +- False +- 0 +- 0.5 +- 0 +- 0 +- PANGO_ELLIPSIZE_NONE +- -1 +- False +- 0 +- +- +- 1 +- 2 +- 1 +- 2 +- fill +- +- +- +- +- +- +- True +- True +- S_how locations in the clock +- True +- GTK_RELIEF_NORMAL +- True +- False +- False +- True +- +- +- 0 +- 3 +- 0 +- 1 +- fill +- ++ 0 ++ True ++ True + + + +@@ -735,7 +362,7 @@ + + + +- ++ + True + Locations + False +@@ -772,95 +399,20 @@ + 0 + + +- +- True +- GTK_BUTTONBOX_DEFAULT_STYLE +- 0 +- +- +- 0 +- False +- False +- +- +- +- +- ++ + True + GTK_BUTTONBOX_END + 6 + + +- ++ + True + True + True ++ Time Settings ++ True + GTK_RELIEF_NORMAL + True +- +- +- +- True +- 0.5 +- 0.5 +- 0 +- 0 +- 0 +- 0 +- 0 +- 0 +- +- +- +- True +- False +- 2 +- +- +- +- True +- gtk-properties +- 4 +- 0.5 +- 0.5 +- 0 +- 0 +- +- +- 0 +- False +- False +- +- +- +- +- +- True +- Time _Settings +- True +- False +- GTK_JUSTIFY_LEFT +- False +- False +- 0.5 +- 0.5 +- 0 +- 0 +- PANGO_ELLIPSIZE_NONE +- -1 +- False +- 0 +- +- +- 0 +- False +- False +- +- +- +- +- +- + + + +@@ -910,13 +462,13 @@ + + + ++ 12 + True + False +- 0 ++ 24 + + + +- 6 + True + 1 + 3 +@@ -926,7 +478,6 @@ + + + +- 6 + True + False + 6 +@@ -974,62 +525,6 @@ + 6 + + +- +- True +- Timezone: +- False +- False +- GTK_JUSTIFY_LEFT +- False +- False +- 0 +- 0.5 +- 0 +- 0 +- PANGO_ELLIPSIZE_NONE +- -1 +- False +- 0 +- +- +- 0 +- 1 +- 0 +- 1 +- fill +- +- +- +- +- +- +- True +- Location Name: +- False +- False +- GTK_JUSTIFY_LEFT +- False +- False +- 0 +- 0.5 +- 0 +- 0 +- PANGO_ELLIPSIZE_NONE +- -1 +- False +- 0 +- +- +- 0 +- 1 +- 1 +- 2 +- fill +- +- +- +- +- + + True + True +@@ -1070,45 +565,6 @@ + + + +- +- True +- Dummy Item +- False +- True +- True +- +- +- 1 +- 4 +- 0 +- 1 +- fill +- fill +- +- +- +- +- +- True +- True +- True +- True +- 0 +- +- True +- +- False +- +- +- 1 +- 4 +- 1 +- 2 +- +- +- +- +- + + True + Longitude: +@@ -1145,7 +601,7 @@ + 0 + + True +- ++ + False + + +@@ -1258,7 +714,7 @@ South + 0 + + True +- ++ + False + + +@@ -1269,6 +725,131 @@ South + + + ++ ++ ++ ++ True ++ Location Name: ++ False ++ False ++ GTK_JUSTIFY_LEFT ++ False ++ False ++ 0 ++ 0.5 ++ 0 ++ 0 ++ PANGO_ELLIPSIZE_NONE ++ -1 ++ False ++ 0 ++ ++ ++ 0 ++ 1 ++ 0 ++ 1 ++ fill ++ ++ ++ ++ ++ ++ ++ True ++ Timezone: ++ False ++ False ++ GTK_JUSTIFY_LEFT ++ False ++ False ++ 0 ++ 0.5 ++ 0 ++ 0 ++ PANGO_ELLIPSIZE_NONE ++ -1 ++ False ++ 0 ++ ++ ++ 0 ++ 1 ++ 1 ++ 2 ++ fill ++ ++ ++ ++ ++ ++ ++ True ++ False ++ 6 ++ ++ ++ ++ True ++ True ++ True ++ True ++ 0 ++ ++ True ++ ++ False ++ ++ ++ 0 ++ True ++ True ++ ++ ++ ++ ++ ++ True ++ True ++ Find... ++ True ++ GTK_RELIEF_NORMAL ++ True ++ ++ ++ 0 ++ False ++ False ++ ++ ++ ++ ++ 1 ++ 4 ++ 0 ++ 1 ++ fill ++ fill ++ ++ ++ ++ ++ ++ True ++ Dummy Item ++ False ++ True ++ True ++ ++ ++ 1 ++ 4 ++ 1 ++ 2 ++ fill ++ fill ++ ++ + + + 0 +@@ -1294,60 +875,33 @@ South + + + +- +- 6 ++ + True +- False +- 0 ++ GTK_BUTTONBOX_END ++ 6 + + +- ++ + True +- GTK_BUTTONBOX_DEFAULT_STYLE +- 0 ++ True ++ True ++ gtk-cancel ++ True ++ GTK_RELIEF_NORMAL ++ True + +- +- 0 +- False +- False +- + + + +- ++ + True +- GTK_BUTTONBOX_END +- 6 +- +- +- +- True +- True +- True +- gtk-cancel +- True +- GTK_RELIEF_NORMAL +- True +- +- +- +- +- +- True +- True +- True +- gtk-ok +- True +- GTK_RELIEF_NORMAL +- True +- +- ++ True ++ True ++ gtk-ok ++ True ++ GTK_RELIEF_NORMAL ++ True + +- +- 0 +- True +- True +- + + + +@@ -1723,4 +1277,580 @@ South + + + ++ ++ ++ GTK_WINDOW_TOPLEVEL ++ GTK_WIN_POS_NONE ++ False ++ True ++ False ++ True ++ False ++ False ++ GDK_WINDOW_TYPE_HINT_NORMAL ++ GDK_GRAVITY_NORTH_WEST ++ True ++ False ++ ++ ++ ++ 12 ++ True ++ False ++ 24 ++ ++ ++ ++ True ++ False ++ 6 ++ ++ ++ ++ True ++ True ++ GTK_POLICY_AUTOMATIC ++ GTK_POLICY_AUTOMATIC ++ GTK_SHADOW_IN ++ GTK_CORNER_TOP_LEFT ++ ++ ++ ++ True ++ True ++ False ++ False ++ False ++ False ++ False ++ False ++ False ++ ++ ++ ++ ++ 0 ++ True ++ True ++ ++ ++ ++ ++ ++ True ++ False ++ 6 ++ ++ ++ ++ True ++ _Find: ++ True ++ False ++ GTK_JUSTIFY_LEFT ++ False ++ False ++ 0.5 ++ 0.5 ++ 0 ++ 0 ++ find-location-entry ++ PANGO_ELLIPSIZE_NONE ++ -1 ++ False ++ 0 ++ ++ ++ 0 ++ False ++ False ++ ++ ++ ++ ++ ++ True ++ True ++ True ++ True ++ 0 ++ ++ True ++ ++ False ++ ++ ++ 0 ++ True ++ True ++ ++ ++ ++ ++ ++ True ++ True ++ GTK_RELIEF_NORMAL ++ True ++ ++ ++ ++ True ++ 0.5 ++ 0.5 ++ 0 ++ 0 ++ 0 ++ 0 ++ 0 ++ 0 ++ ++ ++ ++ True ++ False ++ 2 ++ ++ ++ ++ True ++ gtk-find ++ 4 ++ 0.5 ++ 0.5 ++ 0 ++ 0 ++ ++ ++ 0 ++ False ++ False ++ ++ ++ ++ ++ ++ True ++ Find _Next ++ True ++ False ++ GTK_JUSTIFY_LEFT ++ False ++ False ++ 0.5 ++ 0.5 ++ 0 ++ 0 ++ PANGO_ELLIPSIZE_NONE ++ -1 ++ False ++ 0 ++ ++ ++ 0 ++ False ++ False ++ ++ ++ ++ ++ ++ ++ ++ ++ 0 ++ False ++ False ++ ++ ++ ++ ++ 0 ++ False ++ False ++ ++ ++ ++ ++ 0 ++ True ++ True ++ ++ ++ ++ ++ ++ True ++ GTK_BUTTONBOX_END ++ 0 ++ ++ ++ ++ True ++ True ++ True ++ gtk-cancel ++ True ++ GTK_RELIEF_NORMAL ++ True ++ ++ ++ ++ ++ ++ True ++ True ++ True ++ gtk-ok ++ True ++ GTK_RELIEF_NORMAL ++ True ++ ++ ++ ++ ++ 0 ++ False ++ False ++ ++ ++ ++ ++ ++ ++ ++ 12 ++ Time Settings ++ GTK_WINDOW_TOPLEVEL ++ GTK_WIN_POS_NONE ++ False ++ True ++ False ++ True ++ False ++ False ++ GDK_WINDOW_TYPE_HINT_NORMAL ++ GDK_GRAVITY_NORTH_WEST ++ True ++ False ++ ++ ++ ++ True ++ False ++ 6 ++ ++ ++ ++ True ++ <b>Time Settings</b> ++ False ++ True ++ GTK_JUSTIFY_LEFT ++ False ++ False ++ 0 ++ 0.5 ++ 0 ++ 0 ++ PANGO_ELLIPSIZE_NONE ++ -1 ++ False ++ 0 ++ ++ ++ 0 ++ False ++ False ++ ++ ++ ++ ++ ++ True ++ False ++ 0 ++ ++ ++ ++ True ++ ++ False ++ False ++ GTK_JUSTIFY_LEFT ++ False ++ False ++ 0.5 ++ 0.5 ++ 0 ++ 0 ++ PANGO_ELLIPSIZE_NONE ++ -1 ++ False ++ 0 ++ ++ ++ 0 ++ False ++ False ++ ++ ++ ++ ++ ++ True ++ False ++ 12 ++ ++ ++ ++ True ++ True ++ GTK_CALENDAR_SHOW_HEADING ++ ++ ++ 0 ++ True ++ True ++ ++ ++ ++ ++ ++ True ++ False ++ 6 ++ ++ ++ ++ True ++ Current Time: ++ False ++ False ++ GTK_JUSTIFY_LEFT ++ False ++ False ++ 0 ++ 0.5 ++ 0 ++ 0 ++ PANGO_ELLIPSIZE_NONE ++ -1 ++ False ++ 0 ++ ++ ++ 0 ++ False ++ False ++ ++ ++ ++ ++ ++ True ++ Time: ++ False ++ False ++ GTK_JUSTIFY_LEFT ++ False ++ False ++ 0 ++ 0.5 ++ 0 ++ 0 ++ PANGO_ELLIPSIZE_NONE ++ -1 ++ False ++ 0 ++ ++ ++ 0 ++ False ++ False ++ ++ ++ ++ ++ 0 ++ True ++ True ++ ++ ++ ++ ++ ++ True ++ False ++ 6 ++ ++ ++ ++ True ++ 23:59:59 ++ False ++ False ++ GTK_JUSTIFY_LEFT ++ False ++ False ++ 0 ++ 0.5 ++ 0 ++ 0 ++ PANGO_ELLIPSIZE_NONE ++ -1 ++ False ++ 0 ++ ++ ++ 0 ++ False ++ False ++ ++ ++ ++ ++ ++ True ++ False ++ 0 ++ ++ ++ ++ True ++ True ++ 1 ++ 0 ++ True ++ GTK_UPDATE_ALWAYS ++ False ++ True ++ 23 0 23 1 12 12 ++ ++ ++ 0 ++ True ++ True ++ ++ ++ ++ ++ ++ True ++ True ++ 1 ++ 0 ++ True ++ GTK_UPDATE_ALWAYS ++ False ++ True ++ 59 0 59 1 30 30 ++ ++ ++ 0 ++ True ++ True ++ ++ ++ ++ ++ ++ True ++ True ++ 1 ++ 0 ++ True ++ GTK_UPDATE_ALWAYS ++ False ++ True ++ 59 0 59 1 30 30 ++ ++ ++ 0 ++ True ++ True ++ ++ ++ ++ ++ 0 ++ False ++ True ++ ++ ++ ++ ++ 0 ++ True ++ True ++ ++ ++ ++ ++ 0 ++ True ++ True ++ ++ ++ ++ ++ 0 ++ False ++ False ++ ++ ++ ++ ++ ++ True ++ False ++ 0 ++ ++ ++ ++ True ++ GTK_BUTTONBOX_END ++ 6 ++ ++ ++ ++ True ++ True ++ True ++ gtk-cancel ++ True ++ GTK_RELIEF_NORMAL ++ True ++ ++ ++ ++ ++ ++ True ++ True ++ Set System Time ++ True ++ GTK_RELIEF_NORMAL ++ True ++ ++ ++ ++ ++ 0 ++ True ++ True ++ ++ ++ ++ ++ 0 ++ False ++ False ++ GTK_PACK_END ++ ++ ++ ++ ++ ++ + +diff --git a/data/intlclock.schemas.in b/data/intlclock.schemas.in +index 2ab6772..00fee4f 100644 +--- a/data/intlclock.schemas.in ++++ b/data/intlclock.schemas.in +@@ -89,41 +89,54 @@ + + + +- /schemas/apps/intlclock_applet/prefs/show_locations ++ /schemas/apps/intlclock_applet/prefs/cities ++ clock-applet ++ list ++ string ++ [] ++ ++ List of cities for the clock. ++ ++ List of cities to display in the international clock. ++ ++ ++ ++ ++ ++ /schemas/apps/intlclock_applet/prefs/expand_locations + clock-applet + bool + true + +- Show other locations in clock ++ Expand the location section in clock + +- If true, show a list of locations in the international clock. ++ If true, expand the list of locations in the international clock. + + + + + +- /schemas/apps/intlclock_applet/prefs/show_map ++ /schemas/apps/intlclock_applet/prefs/expand_tasks + clock-applet + bool + true + +- Show world map in clock ++ Expand the tasks section in clock + +- If true, show the world map in the international clock. ++ If true, expand the list of tasks in the international clock. + + + + + +- /schemas/apps/intlclock_applet/prefs/cities ++ /schemas/apps/intlclock_applet/prefs/expand_appointments + clock-applet +- list +- string +- [] ++ bool ++ true + +- List of cities for the clock. ++ Expand the appointments section in clock + +- List of cities to display in the international clock. ++ If true, expand the list of appointments in the international clock. + + + +diff --git a/intltool-extract.in b/intltool-extract.in +deleted file mode 100755 +index bb6c368..340fc8d +--- a/intltool-extract.in ++++ /dev/null +@@ -1,841 +0,0 @@ +-#!@INTLTOOL_PERL@ -w +-# -*- Mode: perl; indent-tabs-mode: nil; c-basic-offset: 4 -*- +- +-# +-# The Intltool Message Extractor +-# +-# Copyright (C) 2000-2001, 2003 Free Software Foundation. +-# +-# Intltool is free software; you can redistribute it and/or +-# modify it under the terms of the GNU General Public License as +-# published by the Free Software Foundation; either version 2 of the +-# License, or (at your option) any later version. +-# +-# Intltool is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY; without even the implied warranty of +-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +-# General Public License for more details. +-# +-# You should have received a copy of the GNU General Public License +-# along with this program; if not, write to the Free Software +-# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-# +-# As a special exception to the GNU General Public License, if you +-# distribute this file as part of a program that contains a +-# configuration script generated by Autoconf, you may include it under +-# the same distribution terms that you use for the rest of that program. +-# +-# Authors: Kenneth Christiansen +-# Darin Adler +-# +- +-## Release information +-my $PROGRAM = "intltool-extract"; +-my $PACKAGE = "intltool"; +-my $VERSION = "0.35.0"; +- +-## Loaded modules +-use strict; +-use File::Basename; +-use Getopt::Long; +- +-## Scalars used by the option stuff +-my $TYPE_ARG = "0"; +-my $LOCAL_ARG = "0"; +-my $HELP_ARG = "0"; +-my $VERSION_ARG = "0"; +-my $UPDATE_ARG = "0"; +-my $QUIET_ARG = "0"; +-my $SRCDIR_ARG = "."; +- +-my $FILE; +-my $OUTFILE; +- +-my $gettext_type = ""; +-my $input; +-my %messages = (); +-my %loc = (); +-my %count = (); +-my %comments = (); +-my $strcount = 0; +- +-my $XMLCOMMENT = ""; +- +-## Use this instead of \w for XML files to handle more possible characters. +-my $w = "[-A-Za-z0-9._:]"; +- +-## Always print first +-$| = 1; +- +-## Handle options +-GetOptions ( +- "type=s" => \$TYPE_ARG, +- "local|l" => \$LOCAL_ARG, +- "help|h" => \$HELP_ARG, +- "version|v" => \$VERSION_ARG, +- "update" => \$UPDATE_ARG, +- "quiet|q" => \$QUIET_ARG, +- "srcdir=s" => \$SRCDIR_ARG, +- ) or &error; +- +-&split_on_argument; +- +- +-## Check for options. +-## This section will check for the different options. +- +-sub split_on_argument { +- +- if ($VERSION_ARG) { +- &version; +- +- } elsif ($HELP_ARG) { +- &help; +- +- } elsif ($LOCAL_ARG) { +- &place_local; +- &extract; +- +- } elsif ($UPDATE_ARG) { +- &place_normal; +- &extract; +- +- } elsif (@ARGV > 0) { +- &place_normal; +- &message; +- &extract; +- +- } else { +- &help; +- +- } +-} +- +-sub place_normal { +- $FILE = $ARGV[0]; +- $OUTFILE = "$FILE.h"; +-} +- +-sub place_local { +- $FILE = $ARGV[0]; +- $OUTFILE = fileparse($FILE, ()); +- if (!-e "tmp/") { +- system("mkdir tmp/"); +- } +- $OUTFILE = "./tmp/$OUTFILE.h" +-} +- +-sub determine_type { +- if ($TYPE_ARG =~ /^gettext\/(.*)/) { +- $gettext_type=$1 +- } +-} +- +-## Sub for printing release information +-sub version{ +- print <<_EOF_; +-${PROGRAM} (${PACKAGE}) $VERSION +-Copyright (C) 2000, 2003 Free Software Foundation, Inc. +-Written by Kenneth Christiansen, 2000. +- +-This is free software; see the source for copying conditions. There is NO +-warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +-_EOF_ +- exit; +-} +- +-## Sub for printing usage information +-sub help { +- print <<_EOF_; +-Usage: ${PROGRAM} [OPTION]... [FILENAME] +-Generates a header file from an XML source file. +- +-It grabs all strings between <_translatable_node> and its end tag in +-XML files. Read manpage (man ${PROGRAM}) for more info. +- +- --type=TYPE Specify the file type of FILENAME. Currently supports: +- "gettext/glade", "gettext/ini", "gettext/keys" +- "gettext/rfc822deb", "gettext/schemas", +- "gettext/scheme", "gettext/xml" +- -l, --local Writes output into current working directory +- (conflicts with --update) +- --update Writes output into the same directory the source file +- reside (conflicts with --local) +- --srcdir Root of the source tree +- -v, --version Output version information and exit +- -h, --help Display this help and exit +- -q, --quiet Quiet mode +- +-Report bugs to http://bugzilla.gnome.org/ (product name "$PACKAGE") +-or send email to . +-_EOF_ +- exit; +-} +- +-## Sub for printing error messages +-sub error{ +- print STDERR "Try `${PROGRAM} --help' for more information.\n"; +- exit; +-} +- +-sub message { +- print "Generating C format header file for translation.\n" unless $QUIET_ARG; +-} +- +-sub extract { +- &determine_type; +- +- &convert; +- +- open OUT, ">$OUTFILE"; +- binmode (OUT) if $^O eq 'MSWin32'; +- &msg_write; +- close OUT; +- +- print "Wrote $OUTFILE\n" unless $QUIET_ARG; +-} +- +-sub convert { +- +- ## Reading the file +- { +- local (*IN); +- local $/; #slurp mode +- open (IN, "<$SRCDIR_ARG/$FILE") || die "can't open $SRCDIR_ARG/$FILE: $!"; +- $input = ; +- } +- +- &type_ini if $gettext_type eq "ini"; +- &type_keys if $gettext_type eq "keys"; +- &type_xml if $gettext_type eq "xml"; +- &type_glade if $gettext_type eq "glade"; +- &type_scheme if $gettext_type eq "scheme"; +- &type_schemas if $gettext_type eq "schemas"; +- &type_rfc822deb if $gettext_type eq "rfc822deb"; +-} +- +-sub entity_decode_minimal +-{ +- local ($_) = @_; +- +- s/'/'/g; # ' +- s/"/"/g; # " +- s/&/&/g; +- +- return $_; +-} +- +-sub entity_decode +-{ +- local ($_) = @_; +- +- s/'/'/g; # ' +- s/"/"/g; # " +- s/&/&/g; +- s/<//g; +- +- return $_; +-} +- +-sub escape_char +-{ +- return '\"' if $_ eq '"'; +- return '\n' if $_ eq "\n"; +- return '\\' if $_ eq '\\'; +- +- return $_; +-} +- +-sub escape +-{ +- my ($string) = @_; +- return join "", map &escape_char, split //, $string; +-} +- +-sub type_ini { +- ### For generic translatable desktop files ### +- while ($input =~ /^_.*=(.*)$/mg) { +- $messages{$1} = []; +- } +-} +- +-sub type_keys { +- ### For generic translatable mime/keys files ### +- while ($input =~ /^\s*_\w+=(.*)$/mg) { +- $messages{$1} = []; +- } +-} +- +-sub type_xml { +- ### For generic translatable XML files ### +- my $tree = readXml($input); +- parseTree(0, $tree); +-} +- +-sub print_var { +- my $var = shift; +- my $vartype = ref $var; +- +- if ($vartype =~ /ARRAY/) { +- my @arr = @{$var}; +- print "[ "; +- foreach my $el (@arr) { +- print_var($el); +- print ", "; +- } +- print "] "; +- } elsif ($vartype =~ /HASH/) { +- my %hash = %{$var}; +- print "{ "; +- foreach my $key (keys %hash) { +- print "$key => "; +- print_var($hash{$key}); +- print ", "; +- } +- print "} "; +- } else { +- print $var; +- } +-} +- +-# Same syntax as getAttributeString in intltool-merge.in.in, similar logic (look for ## differences comment) +-sub getAttributeString +-{ +- my $sub = shift; +- my $do_translate = shift || 1; +- my $language = shift || ""; +- my $translate = shift; +- my $result = ""; +- foreach my $e (reverse(sort(keys %{ $sub }))) { +- my $key = $e; +- my $string = $sub->{$e}; +- my $quote = '"'; +- +- $string =~ s/^[\s]+//; +- $string =~ s/[\s]+$//; +- +- if ($string =~ /^'.*'$/) +- { +- $quote = "'"; +- } +- $string =~ s/^['"]//g; +- $string =~ s/['"]$//g; +- +- ## differences from intltool-merge.in.in +- if ($key =~ /^_/) { +- $comments{entity_decode($string)} = $XMLCOMMENT if $XMLCOMMENT; +- $messages{entity_decode($string)} = []; +- $$translate = 2; +- } +- ## differences end here from intltool-merge.in.in +- $result .= " $key=$quote$string$quote"; +- } +- return $result; +-} +- +-# Verbatim copy from intltool-merge.in.in +-sub getXMLstring +-{ +- my $ref = shift; +- my $spacepreserve = shift || 0; +- my @list = @{ $ref }; +- my $result = ""; +- +- my $count = scalar(@list); +- my $attrs = $list[0]; +- my $index = 1; +- +- $spacepreserve = 1 if ((exists $attrs->{"xml:space"}) && ($attrs->{"xml:space"} =~ /^["']?preserve["']?$/)); +- $spacepreserve = 0 if ((exists $attrs->{"xml:space"}) && ($attrs->{"xml:space"} =~ /^["']?default["']?$/)); +- +- while ($index < $count) { +- my $type = $list[$index]; +- my $content = $list[$index+1]; +- if (! $type ) { +- # We've got CDATA +- if ($content) { +- # lets strip the whitespace here, and *ONLY* here +- $content =~ s/\s+/ /gs if (!$spacepreserve); +- $result .= $content; +- } +- } elsif ( "$type" ne "1" ) { +- # We've got another element +- $result .= "<$type"; +- $result .= getAttributeString(@{$content}[0], 0); # no nested translatable elements +- if ($content) { +- my $subresult = getXMLstring($content, $spacepreserve); +- if ($subresult) { +- $result .= ">".$subresult . ""; +- } else { +- $result .= "/>"; +- } +- } else { +- $result .= "/>"; +- } +- } +- $index += 2; +- } +- return $result; +-} +- +-# Verbatim copy from intltool-merge.in.in, except for MULTIPLE_OUTPUT handling removed +-# Translate list of nodes if necessary +-sub translate_subnodes +-{ +- my $fh = shift; +- my $content = shift; +- my $language = shift || ""; +- my $singlelang = shift || 0; +- my $spacepreserve = shift || 0; +- +- my @nodes = @{ $content }; +- +- my $count = scalar(@nodes); +- my $index = 0; +- while ($index < $count) { +- my $type = $nodes[$index]; +- my $rest = $nodes[$index+1]; +- traverse($fh, $type, $rest, $language, $spacepreserve); +- $index += 2; +- } +-} +- +-# Based on traverse() in intltool-merge.in.in +-sub traverse +-{ +- my $fh = shift; # unused, to allow us to sync code between -merge and -extract +- my $nodename = shift; +- my $content = shift; +- my $language = shift || ""; +- my $spacepreserve = shift || 0; +- +- if ($nodename && "$nodename" eq "1") { +- $XMLCOMMENT = $content; +- } elsif ($nodename) { +- # element +- my @all = @{ $content }; +- my $attrs = shift @all; +- my $translate = 0; +- my $outattr = getAttributeString($attrs, 1, $language, \$translate); +- +- if ($nodename =~ /^_/) { +- $translate = 1; +- $nodename =~ s/^_//; +- } +- my $lookup = ''; +- +- $spacepreserve = 0 if ((exists $attrs->{"xml:space"}) && ($attrs->{"xml:space"} =~ /^["']?default["']?$/)); +- $spacepreserve = 1 if ((exists $attrs->{"xml:space"}) && ($attrs->{"xml:space"} =~ /^["']?preserve["']?$/)); +- +- if ($translate) { +- $lookup = getXMLstring($content, $spacepreserve); +- if (!$spacepreserve) { +- $lookup =~ s/^\s+//s; +- $lookup =~ s/\s+$//s; +- } +- +- if ($lookup && $translate != 2) { +- $comments{$lookup} = $XMLCOMMENT if $XMLCOMMENT; +- $messages{$lookup} = []; +- } elsif ($translate == 2) { +- translate_subnodes($fh, \@all, $language, 1, $spacepreserve); +- } +- } else { +- $XMLCOMMENT = ""; +- my $count = scalar(@all); +- if ($count > 0) { +- my $index = 0; +- while ($index < $count) { +- my $type = $all[$index]; +- my $rest = $all[$index+1]; +- traverse($fh, $type, $rest, $language, $spacepreserve); +- $index += 2; +- } +- } +- } +- $XMLCOMMENT = ""; +- } +-} +- +- +-# Verbatim copy from intltool-merge.in.in, $fh for compatibility +-sub parseTree +-{ +- my $fh = shift; +- my $ref = shift; +- my $language = shift || ""; +- +- my $name = shift @{ $ref }; +- my $cont = shift @{ $ref }; +- +- while (!$name || "$name" eq "1") { +- $name = shift @{ $ref }; +- $cont = shift @{ $ref }; +- } +- +- my $spacepreserve = 0; +- my $attrs = @{$cont}[0]; +- $spacepreserve = 1 if ((exists $attrs->{"xml:space"}) && ($attrs->{"xml:space"} =~ /^["']?preserve["']?$/)); +- +- traverse($fh, $name, $cont, $language, $spacepreserve); +-} +- +-# Verbatim copy from intltool-merge.in.in +-sub intltool_tree_comment +-{ +- my $expat = shift; +- my $data = $expat->original_string(); +- my $clist = $expat->{Curlist}; +- my $pos = $#$clist; +- +- $data =~ s/^$//s; +- push @$clist, 1 => $data; +-} +- +-# Verbatim copy from intltool-merge.in.in +-sub intltool_tree_cdatastart +-{ +- my $expat = shift; +- my $clist = $expat->{Curlist}; +- my $pos = $#$clist; +- +- push @$clist, 0 => $expat->original_string(); +-} +- +-# Verbatim copy from intltool-merge.in.in +-sub intltool_tree_cdataend +-{ +- my $expat = shift; +- my $clist = $expat->{Curlist}; +- my $pos = $#$clist; +- +- $clist->[$pos] .= $expat->original_string(); +-} +- +-# Verbatim copy from intltool-merge.in.in +-sub intltool_tree_char +-{ +- my $expat = shift; +- my $text = shift; +- my $clist = $expat->{Curlist}; +- my $pos = $#$clist; +- +- # Use original_string so that we retain escaped entities +- # in CDATA sections. +- # +- if ($pos > 0 and $clist->[$pos - 1] eq '0') { +- $clist->[$pos] .= $expat->original_string(); +- } else { +- push @$clist, 0 => $expat->original_string(); +- } +-} +- +-# Verbatim copy from intltool-merge.in.in +-sub intltool_tree_start +-{ +- my $expat = shift; +- my $tag = shift; +- my @origlist = (); +- +- # Use original_string so that we retain escaped entities +- # in attribute values. We must convert the string to an +- # @origlist array to conform to the structure of the Tree +- # Style. +- # +- my @original_array = split /\x/, $expat->original_string(); +- my $source = $expat->original_string(); +- +- # Remove leading tag. +- # +- $source =~ s|^\s*<\s*(\S+)||s; +- +- # Grab attribute key/value pairs and push onto @origlist array. +- # +- while ($source) +- { +- if ($source =~ /^\s*([\w:-]+)\s*[=]\s*["]/) +- { +- $source =~ s|^\s*([\w:-]+)\s*[=]\s*["]([^"]*)["]||s; +- push @origlist, $1; +- push @origlist, '"' . $2 . '"'; +- } +- elsif ($source =~ /^\s*([\w:-]+)\s*[=]\s*[']/) +- { +- $source =~ s|^\s*([\w:-]+)\s*[=]\s*[']([^']*)[']||s; +- push @origlist, $1; +- push @origlist, "'" . $2 . "'"; +- } +- else +- { +- last; +- } +- } +- +- my $ol = [ { @origlist } ]; +- +- push @{ $expat->{Lists} }, $expat->{Curlist}; +- push @{ $expat->{Curlist} }, $tag => $ol; +- $expat->{Curlist} = $ol; +-} +- +-# Copied from intltool-merge.in.in and added comment handler. +-sub readXml +-{ +- my $xmldoc = shift || return; +- my $ret = eval 'require XML::Parser'; +- if(!$ret) { +- die "You must have XML::Parser installed to run $0\n\n"; +- } +- my $xp = new XML::Parser(Style => 'Tree'); +- $xp->setHandlers(Char => \&intltool_tree_char); +- $xp->setHandlers(Start => \&intltool_tree_start); +- $xp->setHandlers(CdataStart => \&intltool_tree_cdatastart); +- $xp->setHandlers(CdataEnd => \&intltool_tree_cdataend); +- +- ## differences from intltool-merge.in.in +- $xp->setHandlers(Comment => \&intltool_tree_comment); +- ## differences end here from intltool-merge.in.in +- +- my $tree = $xp->parse($xmldoc); +- #print_var($tree); +- +-# Hello thereHowdydo +-# would be: +-# [foo, [{}, 1, "comment", head, [{id => "a"}, 0, "Hello ", em, [{}, 0, "there"]], bar, +-# [{}, 0, "Howdy", ref, [{}]], 0, "do" ] ] +- +- return $tree; +-} +- +-sub type_schemas { +- ### For schemas XML files ### +- +- # FIXME: We should handle escaped < (less than) +- while ($input =~ / +- \s* +- (\s*(?:\s*)?(.*?)\s*<\/default>\s*)? +- (\s*(?:\s*)?(.*?)\s*<\/short>\s*)? +- (\s*(?:\s*)?(.*?)\s*<\/long>\s*)? +- <\/locale> +- /sgx) { +- my @totranslate = ($3,$6,$9); +- my @eachcomment = ($2,$5,$8); +- foreach (@totranslate) { +- my $currentcomment = shift @eachcomment; +- next if !$_; +- s/\s+/ /g; +- $messages{entity_decode_minimal($_)} = []; +- $comments{entity_decode_minimal($_)} = $currentcomment if (defined($currentcomment)); +- } +- } +-} +- +-sub type_rfc822deb { +- ### For rfc822-style Debian configuration files ### +- +- my $lineno = 1; +- my $type = ''; +- while ($input =~ /\G(.*?)(^|\n)(_+)([^:]+):[ \t]*(.*?)(?=\n\S|$)/sg) +- { +- my ($pre, $newline, $underscore, $tag, $text) = ($1, $2, $3, $4, $5); +- while ($pre =~ m/\n/g) +- { +- $lineno ++; +- } +- $lineno += length($newline); +- my @str_list = rfc822deb_split(length($underscore), $text); +- for my $str (@str_list) +- { +- $strcount++; +- $messages{$str} = []; +- $loc{$str} = $lineno; +- $count{$str} = $strcount; +- my $usercomment = ''; +- while($pre =~ s/(^|\n)#([^\n]*)$//s) +- { +- $usercomment = "\n" . $2 . $usercomment; +- } +- $comments{$str} = $tag . $usercomment; +- } +- $lineno += ($text =~ s/\n//g); +- } +-} +- +-sub rfc822deb_split { +- # Debian defines a special way to deal with rfc822-style files: +- # when a value contain newlines, it consists of +- # 1. a short form (first line) +- # 2. a long description, all lines begin with a space, +- # and paragraphs are separated by a single dot on a line +- # This routine returns an array of all paragraphs, and reformat +- # them. +- # When first argument is 2, the string is a comma separated list of +- # values. +- my $type = shift; +- my $text = shift; +- $text =~ s/^[ \t]//mg; +- return (split(/, */, $text, 0)) if $type ne 1; +- return ($text) if $text !~ /\n/; +- +- $text =~ s/([^\n]*)\n//; +- my @list = ($1); +- my $str = ''; +- for my $line (split (/\n/, $text)) +- { +- chomp $line; +- if ($line =~ /^\.\s*$/) +- { +- # New paragraph +- $str =~ s/\s*$//; +- push(@list, $str); +- $str = ''; +- } +- elsif ($line =~ /^\s/) +- { +- # Line which must not be reformatted +- $str .= "\n" if length ($str) && $str !~ /\n$/; +- $line =~ s/\s+$//; +- $str .= $line."\n"; +- } +- else +- { +- # Continuation line, remove newline +- $str .= " " if length ($str) && $str !~ /\n$/; +- $str .= $line; +- } +- } +- $str =~ s/\s*$//; +- push(@list, $str) if length ($str); +- return @list; +-} +- +-sub type_glade { +- ### For translatable Glade XML files ### +- +- my $tags = "label|title|text|format|copyright|comments|preview_text|tooltip|message"; +- +- while ($input =~ /<($tags)>([^<]+)<\/($tags)>/sg) { +- # Glade sometimes uses tags that normally mark translatable things for +- # little bits of non-translatable content. We work around this by not +- # translating strings that only includes something like label4 or window1. +- $messages{entity_decode($2)} = [] unless $2 =~ /^(window|label|dialog)[0-9]+$/; +- } +- +- while ($input =~ /(..[^<]*)<\/items>/sg) { +- for my $item (split (/\n/, $1)) { +- $messages{entity_decode($item)} = []; +- } +- } +- +- ## handle new glade files +- while ($input =~ /<(property|atkproperty)\s+[^>]*translatable\s*=\s*"yes"(?:\s+[^>]*comments\s*=\s*"([^"]*)")?[^>]*>([^<]+)<\/\1>/sg) { +- $messages{entity_decode($3)} = [] unless $3 =~ /^(window|label)[0-9]+$/; +- if (defined($2) and !($3 =~ /^(window|label)[0-9]+$/)) { +- $comments{entity_decode($3)} = entity_decode($2) ; +- } +- } +- while ($input =~ /]*)"\s+description="([^>]+)"\/>/sg) { +- $messages{entity_decode_minimal($2)} = []; +- } +-} +- +-sub type_scheme { +- my ($line, $i, $state, $str, $trcomment, $char); +- for $line (split(/\n/, $input)) { +- $i = 0; +- $state = 0; # 0 - nothing, 1 - string, 2 - translatable string +- while ($i < length($line)) { +- if (substr($line,$i,1) eq "\"") { +- if ($state == 2) { +- $comments{$str} = $trcomment if ($trcomment); +- $messages{$str} = []; +- $str = ''; +- $state = 0; $trcomment = ""; +- } elsif ($state == 1) { +- $str = ''; +- $state = 0; $trcomment = ""; +- } else { +- $state = 1; +- $str = ''; +- if ($i>0 && substr($line,$i-1,1) eq '_') { +- $state = 2; +- } +- } +- } elsif (!$state) { +- if (substr($line,$i,1) eq ";") { +- $trcomment = substr($line,$i+1); +- $trcomment =~ s/^;*\s*//; +- $i = length($line); +- } elsif ($trcomment && substr($line,$i,1) !~ /\s|\(|\)|_/) { +- $trcomment = ""; +- } +- } else { +- if (substr($line,$i,1) eq "\\") { +- $char = substr($line,$i+1,1); +- if ($char ne "\"" && $char ne "\\") { +- $str = $str . "\\"; +- } +- $i++; +- } +- $str = $str . substr($line,$i,1); +- } +- $i++; +- } +- } +-} +- +-sub msg_write { +- my @msgids; +- if (%count) +- { +- @msgids = sort { $count{$a} <=> $count{$b} } keys %count; +- } +- else +- { +- @msgids = sort keys %messages; +- } +- for my $message (@msgids) +- { +- my $offsetlines = 1; +- $offsetlines++ if $message =~ /%/; +- if (defined ($comments{$message})) +- { +- while ($comments{$message} =~ m/\n/g) +- { +- $offsetlines++; +- } +- } +- print OUT "# ".($loc{$message} - $offsetlines). " \"$FILE\"\n" +- if defined $loc{$message}; +- print OUT "/* ".$comments{$message}." */\n" +- if defined $comments{$message}; +- print OUT "/* xgettext:no-c-format */\n" if $message =~ /%/; +- +- my @lines = split (/\n/, $message, -1); +- for (my $n = 0; $n < @lines; $n++) +- { +- if ($n == 0) +- { +- print OUT "char *s = N_(\""; +- } +- else +- { +- print OUT " \""; +- } +- +- print OUT escape($lines[$n]); +- +- if ($n < @lines - 1) +- { +- print OUT "\\n\"\n"; +- } +- else +- { +- print OUT "\");\n"; +- } +- } +- } +-} +- +diff --git a/intltool-extract.in b/intltool-extract.in +new file mode 120000 +index bb6c368..340fc8d +--- /dev/null ++++ b/intltool-extract.in +@@ -0,0 +1 @@ ++/usr/share/intltool/intltool-extract.in +\ No newline at end of file +diff --git a/intltool-merge.in b/intltool-merge.in +deleted file mode 100755 +index d0535ab..2238bbd +--- a/intltool-merge.in ++++ /dev/null +@@ -1,1356 +0,0 @@ +-#!@INTLTOOL_PERL@ -w +-# -*- Mode: perl; indent-tabs-mode: nil; c-basic-offset: 4 -*- +- +-# +-# The Intltool Message Merger +-# +-# Copyright (C) 2000, 2003 Free Software Foundation. +-# Copyright (C) 2000, 2001 Eazel, Inc +-# +-# Intltool is free software; you can redistribute it and/or +-# modify it under the terms of the GNU General Public License +-# version 2 published by the Free Software Foundation. +-# +-# Intltool is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY; without even the implied warranty of +-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +-# General Public License for more details. +-# +-# You should have received a copy of the GNU General Public License +-# along with this program; if not, write to the Free Software +-# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-# +-# As a special exception to the GNU General Public License, if you +-# distribute this file as part of a program that contains a +-# configuration script generated by Autoconf, you may include it under +-# the same distribution terms that you use for the rest of that program. +-# +-# Authors: Maciej Stachowiak +-# Kenneth Christiansen +-# Darin Adler +-# +-# Proper XML UTF-8'ification written by Cyrille Chepelov +-# +- +-## Release information +-my $PROGRAM = "intltool-merge"; +-my $PACKAGE = "intltool"; +-my $VERSION = "0.35.0"; +- +-## Loaded modules +-use strict; +-use Getopt::Long; +-use Text::Wrap; +-use File::Basename; +- +-my $must_end_tag = -1; +-my $last_depth = -1; +-my $translation_depth = -1; +-my @tag_stack = (); +-my @entered_tag = (); +-my @translation_strings = (); +-my $leading_space = ""; +- +-## Scalars used by the option stuff +-my $HELP_ARG = 0; +-my $VERSION_ARG = 0; +-my $BA_STYLE_ARG = 0; +-my $XML_STYLE_ARG = 0; +-my $KEYS_STYLE_ARG = 0; +-my $DESKTOP_STYLE_ARG = 0; +-my $SCHEMAS_STYLE_ARG = 0; +-my $RFC822DEB_STYLE_ARG = 0; +-my $QUIET_ARG = 0; +-my $PASS_THROUGH_ARG = 0; +-my $UTF8_ARG = 0; +-my $MULTIPLE_OUTPUT = 0; +-my $cache_file; +- +-## Handle options +-GetOptions +-( +- "help" => \$HELP_ARG, +- "version" => \$VERSION_ARG, +- "quiet|q" => \$QUIET_ARG, +- "oaf-style|o" => \$BA_STYLE_ARG, ## for compatibility +- "ba-style|b" => \$BA_STYLE_ARG, +- "xml-style|x" => \$XML_STYLE_ARG, +- "keys-style|k" => \$KEYS_STYLE_ARG, +- "desktop-style|d" => \$DESKTOP_STYLE_ARG, +- "schemas-style|s" => \$SCHEMAS_STYLE_ARG, +- "rfc822deb-style|r" => \$RFC822DEB_STYLE_ARG, +- "pass-through|p" => \$PASS_THROUGH_ARG, +- "utf8|u" => \$UTF8_ARG, +- "multiple-output|m" => \$MULTIPLE_OUTPUT, +- "cache|c=s" => \$cache_file +- ) or &error; +- +-my $PO_DIR; +-my $FILE; +-my $OUTFILE; +- +-my %po_files_by_lang = (); +-my %translations = (); +-my $iconv = $ENV{"ICONV"} || $ENV{"INTLTOOL_ICONV"} || "@INTLTOOL_ICONV@"; +-my $devnull = ($^O eq 'MSWin32' ? 'NUL:' : '/dev/null'); +- +-# Use this instead of \w for XML files to handle more possible characters. +-my $w = "[-A-Za-z0-9._:]"; +- +-# XML quoted string contents +-my $q = "[^\\\"]*"; +- +-## Check for options. +- +-if ($VERSION_ARG) +-{ +- &print_version; +-} +-elsif ($HELP_ARG) +-{ +- &print_help; +-} +-elsif ($BA_STYLE_ARG && @ARGV > 2) +-{ +- &utf8_sanity_check; +- &preparation; +- &print_message; +- &ba_merge_translations; +- &finalize; +-} +-elsif ($XML_STYLE_ARG && @ARGV > 2) +-{ +- &utf8_sanity_check; +- &preparation; +- &print_message; +- &xml_merge_output; +- &finalize; +-} +-elsif ($KEYS_STYLE_ARG && @ARGV > 2) +-{ +- &utf8_sanity_check; +- &preparation; +- &print_message; +- &keys_merge_translations; +- &finalize; +-} +-elsif ($DESKTOP_STYLE_ARG && @ARGV > 2) +-{ +- &utf8_sanity_check; +- &preparation; +- &print_message; +- &desktop_merge_translations; +- &finalize; +-} +-elsif ($SCHEMAS_STYLE_ARG && @ARGV > 2) +-{ +- &utf8_sanity_check; +- &preparation; +- &print_message; +- &schemas_merge_translations; +- &finalize; +-} +-elsif ($RFC822DEB_STYLE_ARG && @ARGV > 2) +-{ +- &preparation; +- &print_message; +- &rfc822deb_merge_translations; +- &finalize; +-} +-else +-{ +- &print_help; +-} +- +-exit; +- +-## Sub for printing release information +-sub print_version +-{ +- print <<_EOF_; +-${PROGRAM} (${PACKAGE}) ${VERSION} +-Written by Maciej Stachowiak, Darin Adler and Kenneth Christiansen. +- +-Copyright (C) 2000-2003 Free Software Foundation, Inc. +-Copyright (C) 2000-2001 Eazel, Inc. +-This is free software; see the source for copying conditions. There is NO +-warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +-_EOF_ +- exit; +-} +- +-## Sub for printing usage information +-sub print_help +-{ +- print <<_EOF_; +-Usage: ${PROGRAM} [OPTION]... PO_DIRECTORY FILENAME OUTPUT_FILE +-Generates an output file that includes some localized attributes from an +-untranslated source file. +- +-Mandatory options: (exactly one must be specified) +- -b, --ba-style includes translations in the bonobo-activation style +- -d, --desktop-style includes translations in the desktop style +- -k, --keys-style includes translations in the keys style +- -s, --schemas-style includes translations in the schemas style +- -r, --rfc822deb-style includes translations in the RFC822 style +- -x, --xml-style includes translations in the standard xml style +- +-Other options: +- -u, --utf8 convert all strings to UTF-8 before merging +- (default for everything except RFC822 style) +- -p, --pass-through deprecated, does nothing and issues a warning +- -m, --multiple-output output one localized file per locale, instead of +- a single file containing all localized elements +- -c, --cache=FILE specify cache file name +- (usually \$top_builddir/po/.intltool-merge-cache) +- -q, --quiet suppress most messages +- --help display this help and exit +- --version output version information and exit +- +-Report bugs to http://bugzilla.gnome.org/ (product name "$PACKAGE") +-or send email to . +-_EOF_ +- exit; +-} +- +- +-## Sub for printing error messages +-sub print_error +-{ +- print STDERR "Try `${PROGRAM} --help' for more information.\n"; +- exit; +-} +- +- +-sub print_message +-{ +- print "Merging translations into $OUTFILE.\n" unless $QUIET_ARG; +-} +- +- +-sub preparation +-{ +- $PO_DIR = $ARGV[0]; +- $FILE = $ARGV[1]; +- $OUTFILE = $ARGV[2]; +- +- &gather_po_files; +- &get_translation_database; +-} +- +-# General-purpose code for looking up translations in .po files +- +-sub po_file2lang +-{ +- my ($tmp) = @_; +- $tmp =~ s/^.*\/(.*)\.po$/$1/; +- return $tmp; +-} +- +-sub gather_po_files +-{ +- for my $po_file (glob "$PO_DIR/*.po") { +- $po_files_by_lang{po_file2lang($po_file)} = $po_file; +- } +-} +- +-sub get_local_charset +-{ +- my ($encoding) = @_; +- my $alias_file = $ENV{"G_CHARSET_ALIAS"} || "@INTLTOOL_LIBDIR@/charset.alias"; +- +- # seek character encoding aliases in charset.alias (glib) +- +- if (open CHARSET_ALIAS, $alias_file) +- { +- while () +- { +- next if /^\#/; +- return $1 if (/^\s*([-._a-zA-Z0-9]+)\s+$encoding\b/i) +- } +- +- close CHARSET_ALIAS; +- } +- +- # if not found, return input string +- +- return $encoding; +-} +- +-sub get_po_encoding +-{ +- my ($in_po_file) = @_; +- my $encoding = ""; +- +- open IN_PO_FILE, $in_po_file or die; +- while () +- { +- ## example: "Content-Type: text/plain; charset=ISO-8859-1\n" +- if (/Content-Type\:.*charset=([-a-zA-Z0-9]+)\\n/) +- { +- $encoding = $1; +- last; +- } +- } +- close IN_PO_FILE; +- +- if (!$encoding) +- { +- print STDERR "Warning: no encoding found in $in_po_file. Assuming ISO-8859-1\n" unless $QUIET_ARG; +- $encoding = "ISO-8859-1"; +- } +- +- system ("$iconv -f $encoding -t UTF-8 <$devnull 2>$devnull"); +- if ($?) { +- $encoding = get_local_charset($encoding); +- } +- +- return $encoding +-} +- +-sub utf8_sanity_check +-{ +- print STDERR "Warning: option --pass-through has been removed.\n" if $PASS_THROUGH_ARG; +- $UTF8_ARG = 1; +-} +- +-sub get_translation_database +-{ +- if ($cache_file) { +- &get_cached_translation_database; +- } else { +- &create_translation_database; +- } +-} +- +-sub get_newest_po_age +-{ +- my $newest_age; +- +- foreach my $file (values %po_files_by_lang) +- { +- my $file_age = -M $file; +- $newest_age = $file_age if !$newest_age || $file_age < $newest_age; +- } +- +- $newest_age = 0 if !$newest_age; +- +- return $newest_age; +-} +- +-sub create_cache +-{ +- print "Generating and caching the translation database\n" unless $QUIET_ARG; +- +- &create_translation_database; +- +- open CACHE, ">$cache_file" || die; +- print CACHE join "\x01", %translations; +- close CACHE; +-} +- +-sub load_cache +-{ +- print "Found cached translation database\n" unless $QUIET_ARG; +- +- my $contents; +- open CACHE, "<$cache_file" || die; +- { +- local $/; +- $contents = ; +- } +- close CACHE; +- %translations = split "\x01", $contents; +-} +- +-sub get_cached_translation_database +-{ +- my $cache_file_age = -M $cache_file; +- if (defined $cache_file_age) +- { +- if ($cache_file_age <= &get_newest_po_age) +- { +- &load_cache; +- return; +- } +- print "Found too-old cached translation database\n" unless $QUIET_ARG; +- } +- +- &create_cache; +-} +- +-sub create_translation_database +-{ +- for my $lang (keys %po_files_by_lang) +- { +- my $po_file = $po_files_by_lang{$lang}; +- +- if ($UTF8_ARG) +- { +- my $encoding = get_po_encoding ($po_file); +- +- if (lc $encoding eq "utf-8") +- { +- open PO_FILE, "<$po_file"; +- } +- else +- { +- print "NOTICE: $po_file is not in UTF-8 but $encoding, converting...\n" unless $QUIET_ARG;; +- +- open PO_FILE, "$iconv -f $encoding -t UTF-8 $po_file|"; +- } +- } +- else +- { +- open PO_FILE, "<$po_file"; +- } +- +- my $nextfuzzy = 0; +- my $inmsgid = 0; +- my $inmsgstr = 0; +- my $msgid = ""; +- my $msgstr = ""; +- +- while () +- { +- $nextfuzzy = 1 if /^#, fuzzy/; +- +- if (/^msgid "((\\.|[^\\])*)"/ ) +- { +- $translations{$lang, $msgid} = $msgstr if $inmsgstr && $msgid && $msgstr; +- $msgid = ""; +- $msgstr = ""; +- +- if ($nextfuzzy) { +- $inmsgid = 0; +- } else { +- $msgid = unescape_po_string($1); +- $inmsgid = 1; +- } +- $inmsgstr = 0; +- $nextfuzzy = 0; +- } +- +- if (/^msgstr "((\\.|[^\\])*)"/) +- { +- $msgstr = unescape_po_string($1); +- $inmsgstr = 1; +- $inmsgid = 0; +- } +- +- if (/^"((\\.|[^\\])*)"/) +- { +- $msgid .= unescape_po_string($1) if $inmsgid; +- $msgstr .= unescape_po_string($1) if $inmsgstr; +- } +- } +- $translations{$lang, $msgid} = $msgstr if $inmsgstr && $msgid && $msgstr; +- } +-} +- +-sub finalize +-{ +-} +- +-sub unescape_one_sequence +-{ +- my ($sequence) = @_; +- +- return "\\" if $sequence eq "\\\\"; +- return "\"" if $sequence eq "\\\""; +- return "\n" if $sequence eq "\\n"; +- return "\r" if $sequence eq "\\r"; +- return "\t" if $sequence eq "\\t"; +- return "\b" if $sequence eq "\\b"; +- return "\f" if $sequence eq "\\f"; +- return "\a" if $sequence eq "\\a"; +- return chr(11) if $sequence eq "\\v"; # vertical tab, see ascii(7) +- +- return chr(hex($1)) if ($sequence =~ /\\x([0-9a-fA-F]{2})/); +- return chr(oct($1)) if ($sequence =~ /\\([0-7]{3})/); +- +- # FIXME: Is \0 supported as well? Kenneth and Rodney don't want it, see bug #48489 +- +- return $sequence; +-} +- +-sub unescape_po_string +-{ +- my ($string) = @_; +- +- $string =~ s/(\\x[0-9a-fA-F]{2}|\\[0-7]{3}|\\.)/unescape_one_sequence($1)/eg; +- +- return $string; +-} +- +-## NOTE: deal with < - < but not > - > because it seems its ok to have +-## > in the entity. For further info please look at #84738. +-sub entity_decode +-{ +- local ($_) = @_; +- +- s/'/'/g; # ' +- s/"/"/g; # " +- s/&/&/g; +- s/</; +- close INPUT; +- } +- +- open OUTPUT, ">$OUTFILE" or die "can't open $OUTFILE: $!"; +- # Binmode so that selftest works ok if using a native Win32 Perl... +- binmode (OUTPUT) if $^O eq 'MSWin32'; +- +- while ($source =~ s|^(.*?)([ \t]*<\s*$w+\s+($w+\s*=\s*"$q"\s*)+/?>)([ \t]*\n)?||s) +- { +- print OUTPUT $1; +- +- my $node = $2 . "\n"; +- +- my @strings = (); +- $_ = $node; +- while (s/(\s)_($w+\s*=\s*"($q)")/$1$2/s) { +- push @strings, entity_decode($3); +- } +- print OUTPUT; +- +- my %langs; +- for my $string (@strings) +- { +- for my $lang (keys %po_files_by_lang) +- { +- $langs{$lang} = 1 if $translations{$lang, $string}; +- } +- } +- +- for my $lang (sort keys %langs) +- { +- $_ = $node; +- s/(\sname\s*=\s*)"($q)"/$1"$2-$lang"/s; +- s/(\s)_($w+\s*=\s*")($q)"/$1 . $2 . entity_encoded_translation($lang, $3) . '"'/seg; +- print OUTPUT; +- } +- } +- +- print OUTPUT $source; +- +- close OUTPUT; +-} +- +- +-## XML (non-bonobo-activation) merge code +- +- +-# Process tag attributes +-# Only parameter is a HASH containing attributes -> values mapping +-sub getAttributeString +-{ +- my $sub = shift; +- my $do_translate = shift || 0; +- my $language = shift || ""; +- my $result = ""; +- my $translate = shift; +- foreach my $e (reverse(sort(keys %{ $sub }))) { +- my $key = $e; +- my $string = $sub->{$e}; +- my $quote = '"'; +- +- $string =~ s/^[\s]+//; +- $string =~ s/[\s]+$//; +- +- if ($string =~ /^'.*'$/) +- { +- $quote = "'"; +- } +- $string =~ s/^['"]//g; +- $string =~ s/['"]$//g; +- +- if ($do_translate && $key =~ /^_/) { +- $key =~ s|^_||g; +- if ($language) { +- # Handle translation +- my $decode_string = entity_decode($string); +- my $translation = $translations{$language, $decode_string}; +- if ($translation) { +- $translation = entity_encode($translation); +- $string = $translation; +- } +- $$translate = 2; +- } else { +- $$translate = 2 if ($translate && (!$$translate)); # watch not to "overwrite" $translate +- } +- } +- +- $result .= " $key=$quote$string$quote"; +- } +- return $result; +-} +- +-# Returns a translatable string from XML node, it works on contents of every node in XML::Parser tree +-sub getXMLstring +-{ +- my $ref = shift; +- my $spacepreserve = shift || 0; +- my @list = @{ $ref }; +- my $result = ""; +- +- my $count = scalar(@list); +- my $attrs = $list[0]; +- my $index = 1; +- +- $spacepreserve = 1 if ((exists $attrs->{"xml:space"}) && ($attrs->{"xml:space"} =~ /^["']?preserve["']?$/)); +- $spacepreserve = 0 if ((exists $attrs->{"xml:space"}) && ($attrs->{"xml:space"} =~ /^["']?default["']?$/)); +- +- while ($index < $count) { +- my $type = $list[$index]; +- my $content = $list[$index+1]; +- if (! $type ) { +- # We've got CDATA +- if ($content) { +- # lets strip the whitespace here, and *ONLY* here +- $content =~ s/\s+/ /gs if (!$spacepreserve); +- $result .= $content; +- } +- } elsif ( "$type" ne "1" ) { +- # We've got another element +- $result .= "<$type"; +- $result .= getAttributeString(@{$content}[0], 0); # no nested translatable elements +- if ($content) { +- my $subresult = getXMLstring($content, $spacepreserve); +- if ($subresult) { +- $result .= ">".$subresult . ""; +- } else { +- $result .= "/>"; +- } +- } else { +- $result .= "/>"; +- } +- } +- $index += 2; +- } +- return $result; +-} +- +-# Translate list of nodes if necessary +-sub translate_subnodes +-{ +- my $fh = shift; +- my $content = shift; +- my $language = shift || ""; +- my $singlelang = shift || 0; +- my $spacepreserve = shift || 0; +- +- my @nodes = @{ $content }; +- +- my $count = scalar(@nodes); +- my $index = 0; +- while ($index < $count) { +- my $type = $nodes[$index]; +- my $rest = $nodes[$index+1]; +- if ($singlelang) { +- my $oldMO = $MULTIPLE_OUTPUT; +- $MULTIPLE_OUTPUT = 1; +- traverse($fh, $type, $rest, $language, $spacepreserve); +- $MULTIPLE_OUTPUT = $oldMO; +- } else { +- traverse($fh, $type, $rest, $language, $spacepreserve); +- } +- $index += 2; +- } +-} +- +-sub isWellFormedXmlFragment +-{ +- my $ret = eval 'require XML::Parser'; +- if(!$ret) { +- die "You must have XML::Parser installed to run $0\n\n"; +- } +- +- my $fragment = shift; +- return 0 if (!$fragment); +- +- $fragment = "$fragment"; +- my $xp = new XML::Parser(Style => 'Tree'); +- my $tree = 0; +- eval { $tree = $xp->parse($fragment); }; +- return $tree; +-} +- +-sub traverse +-{ +- my $fh = shift; +- my $nodename = shift; +- my $content = shift; +- my $language = shift || ""; +- my $spacepreserve = shift || 0; +- +- if (!$nodename) { +- if ($content =~ /^[\s]*$/) { +- $leading_space .= $content; +- } +- print $fh $content; +- } else { +- # element +- my @all = @{ $content }; +- my $attrs = shift @all; +- my $translate = 0; +- my $outattr = getAttributeString($attrs, 1, $language, \$translate); +- +- if ($nodename =~ /^_/) { +- $translate = 1; +- $nodename =~ s/^_//; +- } +- my $lookup = ''; +- +- $spacepreserve = 0 if ((exists $attrs->{"xml:space"}) && ($attrs->{"xml:space"} =~ /^["']?default["']?$/)); +- $spacepreserve = 1 if ((exists $attrs->{"xml:space"}) && ($attrs->{"xml:space"} =~ /^["']?preserve["']?$/)); +- +- print $fh "<$nodename", $outattr; +- if ($translate) { +- $lookup = getXMLstring($content, $spacepreserve); +- if (!$spacepreserve) { +- $lookup =~ s/^\s+//s; +- $lookup =~ s/\s+$//s; +- } +- +- if ($lookup || $translate == 2) { +- my $translation = $translations{$language, $lookup} if isWellFormedXmlFragment($translations{$language, $lookup}); +- if ($MULTIPLE_OUTPUT && ($translation || $translate == 2)) { +- $translation = $lookup if (!$translation); +- print $fh " xml:lang=\"", $language, "\"" if $language; +- print $fh ">"; +- if ($translate == 2) { +- translate_subnodes($fh, \@all, $language, 1, $spacepreserve); +- } else { +- print $fh $translation; +- } +- print $fh ""; +- +- return; # this means there will be no same translation with xml:lang="$language"... +- # if we want them both, just remove this "return" +- } else { +- print $fh ">"; +- if ($translate == 2) { +- translate_subnodes($fh, \@all, $language, 1, $spacepreserve); +- } else { +- print $fh $lookup; +- } +- print $fh ""; +- } +- } else { +- print $fh "/>"; +- } +- +- for my $lang (sort keys %po_files_by_lang) { +- if ($MULTIPLE_OUTPUT && $lang ne "$language") { +- next; +- } +- if ($lang) { +- # Handle translation +- # +- my $translate = 0; +- my $localattrs = getAttributeString($attrs, 1, $lang, \$translate); +- my $translation = $translations{$lang, $lookup} if isWellFormedXmlFragment($translations{$lang, $lookup}); +- if ($translate && !$translation) { +- $translation = $lookup; +- } +- +- if ($translation || $translate) { +- print $fh "\n"; +- $leading_space =~ s/.*\n//g; +- print $fh $leading_space; +- print $fh "<", $nodename, " xml:lang=\"", $lang, "\"", $localattrs, ">"; +- if ($translate == 2) { +- translate_subnodes($fh, \@all, $lang, 1, $spacepreserve); +- } else { +- print $fh $translation; +- } +- print $fh ""; +- } +- } +- } +- +- } else { +- my $count = scalar(@all); +- if ($count > 0) { +- print $fh ">"; +- my $index = 0; +- while ($index < $count) { +- my $type = $all[$index]; +- my $rest = $all[$index+1]; +- traverse($fh, $type, $rest, $language, $spacepreserve); +- $index += 2; +- } +- print $fh ""; +- } else { +- print $fh "/>"; +- } +- } +- } +-} +- +-sub intltool_tree_comment +-{ +- my $expat = shift; +- my $data = shift; +- my $clist = $expat->{Curlist}; +- my $pos = $#$clist; +- +- push @$clist, 1 => $data; +-} +- +-sub intltool_tree_cdatastart +-{ +- my $expat = shift; +- my $clist = $expat->{Curlist}; +- my $pos = $#$clist; +- +- push @$clist, 0 => $expat->original_string(); +-} +- +-sub intltool_tree_cdataend +-{ +- my $expat = shift; +- my $clist = $expat->{Curlist}; +- my $pos = $#$clist; +- +- $clist->[$pos] .= $expat->original_string(); +-} +- +-sub intltool_tree_char +-{ +- my $expat = shift; +- my $text = shift; +- my $clist = $expat->{Curlist}; +- my $pos = $#$clist; +- +- # Use original_string so that we retain escaped entities +- # in CDATA sections. +- # +- if ($pos > 0 and $clist->[$pos - 1] eq '0') { +- $clist->[$pos] .= $expat->original_string(); +- } else { +- push @$clist, 0 => $expat->original_string(); +- } +-} +- +-sub intltool_tree_start +-{ +- my $expat = shift; +- my $tag = shift; +- my @origlist = (); +- +- # Use original_string so that we retain escaped entities +- # in attribute values. We must convert the string to an +- # @origlist array to conform to the structure of the Tree +- # Style. +- # +- my @original_array = split /\x/, $expat->original_string(); +- my $source = $expat->original_string(); +- +- # Remove leading tag. +- # +- $source =~ s|^\s*<\s*(\S+)||s; +- +- # Grab attribute key/value pairs and push onto @origlist array. +- # +- while ($source) +- { +- if ($source =~ /^\s*([\w:-]+)\s*[=]\s*["]/) +- { +- $source =~ s|^\s*([\w:-]+)\s*[=]\s*["]([^"]*)["]||s; +- push @origlist, $1; +- push @origlist, '"' . $2 . '"'; +- } +- elsif ($source =~ /^\s*([\w:-]+)\s*[=]\s*[']/) +- { +- $source =~ s|^\s*([\w:-]+)\s*[=]\s*[']([^']*)[']||s; +- push @origlist, $1; +- push @origlist, "'" . $2 . "'"; +- } +- else +- { +- last; +- } +- } +- +- my $ol = [ { @origlist } ]; +- +- push @{ $expat->{Lists} }, $expat->{Curlist}; +- push @{ $expat->{Curlist} }, $tag => $ol; +- $expat->{Curlist} = $ol; +-} +- +-sub readXml +-{ +- my $filename = shift || return; +- if(!-f $filename) { +- die "ERROR Cannot find filename: $filename\n"; +- } +- +- my $ret = eval 'require XML::Parser'; +- if(!$ret) { +- die "You must have XML::Parser installed to run $0\n\n"; +- } +- my $xp = new XML::Parser(Style => 'Tree'); +- $xp->setHandlers(Char => \&intltool_tree_char); +- $xp->setHandlers(Start => \&intltool_tree_start); +- $xp->setHandlers(CdataStart => \&intltool_tree_cdatastart); +- $xp->setHandlers(CdataEnd => \&intltool_tree_cdataend); +- my $tree = $xp->parsefile($filename); +- +-# Hello thereHowdydo +-# would be: +-# [foo, [{}, head, [{id => "a"}, 0, "Hello ", em, [{}, 0, "there"]], bar, [{}, +-# 0, "Howdy", ref, [{}]], 0, "do" ] ] +- +- return $tree; +-} +- +-sub print_header +-{ +- my $infile = shift; +- my $fh = shift; +- my $source; +- +- if(!-f $infile) { +- die "ERROR Cannot find filename: $infile\n"; +- } +- +- print $fh qq{\n}; +- { +- local $/; +- open DOCINPUT, "<${FILE}" or die; +- $source = ; +- close DOCINPUT; +- } +- if ($source =~ /()/s) +- { +- print $fh "$1\n"; +- } +- elsif ($source =~ /(]*>)/s) +- { +- print $fh "$1\n"; +- } +-} +- +-sub parseTree +-{ +- my $fh = shift; +- my $ref = shift; +- my $language = shift || ""; +- +- my $name = shift @{ $ref }; +- my $cont = shift @{ $ref }; +- +- while (!$name || "$name" eq "1") { +- $name = shift @{ $ref }; +- $cont = shift @{ $ref }; +- } +- +- my $spacepreserve = 0; +- my $attrs = @{$cont}[0]; +- $spacepreserve = 1 if ((exists $attrs->{"xml:space"}) && ($attrs->{"xml:space"} =~ /^["']?preserve["']?$/)); +- +- traverse($fh, $name, $cont, $language, $spacepreserve); +-} +- +-sub xml_merge_output +-{ +- my $source; +- +- if ($MULTIPLE_OUTPUT) { +- for my $lang (sort keys %po_files_by_lang) { +- if ( ! -e $lang ) { +- mkdir $lang or die "Cannot create subdirectory $lang: $!\n"; +- } +- open OUTPUT, ">$lang/$OUTFILE" or die "Cannot open $lang/$OUTFILE: $!\n"; +- binmode (OUTPUT) if $^O eq 'MSWin32'; +- my $tree = readXml($FILE); +- print_header($FILE, \*OUTPUT); +- parseTree(\*OUTPUT, $tree, $lang); +- close OUTPUT; +- print "CREATED $lang/$OUTFILE\n" unless $QUIET_ARG; +- } +- } +- open OUTPUT, ">$OUTFILE" or die "Cannot open $OUTFILE: $!\n"; +- binmode (OUTPUT) if $^O eq 'MSWin32'; +- my $tree = readXml($FILE); +- print_header($FILE, \*OUTPUT); +- parseTree(\*OUTPUT, $tree); +- close OUTPUT; +- print "CREATED $OUTFILE\n" unless $QUIET_ARG; +-} +- +-sub keys_merge_translations +-{ +- open INPUT, "<${FILE}" or die; +- open OUTPUT, ">${OUTFILE}" or die; +- binmode (OUTPUT) if $^O eq 'MSWin32'; +- +- while () +- { +- if (s/^(\s*)_(\w+=(.*))/$1$2/) +- { +- my $string = $3; +- +- print OUTPUT; +- +- my $non_translated_line = $_; +- +- for my $lang (sort keys %po_files_by_lang) +- { +- my $translation = $translations{$lang, $string}; +- next if !$translation; +- +- $_ = $non_translated_line; +- s/(\w+)=.*/[$lang]$1=$translation/; +- print OUTPUT; +- } +- } +- else +- { +- print OUTPUT; +- } +- } +- +- close OUTPUT; +- close INPUT; +-} +- +-sub desktop_merge_translations +-{ +- open INPUT, "<${FILE}" or die; +- open OUTPUT, ">${OUTFILE}" or die; +- binmode (OUTPUT) if $^O eq 'MSWin32'; +- +- while () +- { +- if (s/^(\s*)_(\w+=(.*))/$1$2/) +- { +- my $string = $3; +- +- print OUTPUT; +- +- my $non_translated_line = $_; +- +- for my $lang (sort keys %po_files_by_lang) +- { +- my $translation = $translations{$lang, $string}; +- next if !$translation; +- +- $_ = $non_translated_line; +- s/(\w+)=.*/${1}[$lang]=$translation/; +- print OUTPUT; +- } +- } +- else +- { +- print OUTPUT; +- } +- } +- +- close OUTPUT; +- close INPUT; +-} +- +-sub schemas_merge_translations +-{ +- my $source; +- +- { +- local $/; # slurp mode +- open INPUT, "<$FILE" or die "can't open $FILE: $!"; +- $source = ; +- close INPUT; +- } +- +- open OUTPUT, ">$OUTFILE" or die; +- binmode (OUTPUT) if $^O eq 'MSWin32'; +- +- # FIXME: support attribute translations +- +- # Empty nodes never need translation, so unmark all of them. +- # For example, <_foo/> is just replaced by . +- $source =~ s|<\s*_($w+)\s*/>|<$1/>|g; +- +- while ($source =~ s/ +- (.*?) +- (\s+)((\s*) +- (\s*(?:\s*)?(.*?)\s*<\/default>)?(\s*) +- (\s*(?:\s*)?(.*?)\s*<\/short>)?(\s*) +- (\s*(?:\s*)?(.*?)\s*<\/long>)?(\s*) +- <\/locale>) +- //sx) +- { +- print OUTPUT $1; +- +- my $locale_start_spaces = $2 ? $2 : ''; +- my $default_spaces = $4 ? $4 : ''; +- my $short_spaces = $7 ? $7 : ''; +- my $long_spaces = $10 ? $10 : ''; +- my $locale_end_spaces = $13 ? $13 : ''; +- my $c_default_block = $3 ? $3 : ''; +- my $default_string = $6 ? $6 : ''; +- my $short_string = $9 ? $9 : ''; +- my $long_string = $12 ? $12 : ''; +- +- print OUTPUT "$locale_start_spaces$c_default_block"; +- +- $default_string =~ s/\s+/ /g; +- $default_string = entity_decode($default_string); +- $short_string =~ s/\s+/ /g; +- $short_string = entity_decode($short_string); +- $long_string =~ s/\s+/ /g; +- $long_string = entity_decode($long_string); +- +- for my $lang (sort keys %po_files_by_lang) +- { +- my $default_translation = $translations{$lang, $default_string}; +- my $short_translation = $translations{$lang, $short_string}; +- my $long_translation = $translations{$lang, $long_string}; +- +- next if (!$default_translation && !$short_translation && +- !$long_translation); +- +- print OUTPUT "\n$locale_start_spaces"; +- +- print OUTPUT "$default_spaces"; +- +- if ($default_translation) +- { +- $default_translation = entity_encode($default_translation); +- print OUTPUT "$default_translation"; +- } +- +- print OUTPUT "$short_spaces"; +- +- if ($short_translation) +- { +- $short_translation = entity_encode($short_translation); +- print OUTPUT "$short_translation"; +- } +- +- print OUTPUT "$long_spaces"; +- +- if ($long_translation) +- { +- $long_translation = entity_encode($long_translation); +- print OUTPUT "$long_translation"; +- } +- +- print OUTPUT "$locale_end_spaces"; +- } +- } +- +- print OUTPUT $source; +- +- close OUTPUT; +-} +- +-sub rfc822deb_merge_translations +-{ +- my %encodings = (); +- for my $lang (keys %po_files_by_lang) { +- $encodings{$lang} = ($UTF8_ARG ? 'UTF-8' : get_po_encoding($po_files_by_lang{$lang})); +- } +- +- my $source; +- +- $Text::Wrap::huge = 'overflow'; +- $Text::Wrap::break = qr/\n|\s(?=\S)/; +- +- { +- local $/; # slurp mode +- open INPUT, "<$FILE" or die "can't open $FILE: $!"; +- $source = ; +- close INPUT; +- } +- +- open OUTPUT, ">${OUTFILE}" or die; +- binmode (OUTPUT) if $^O eq 'MSWin32'; +- +- while ($source =~ /(^|\n+)(_*)([^:\s]+)(:[ \t]*)(.*?)(?=\n[\S\n]|$)/sg) +- { +- my $sep = $1; +- my $non_translated_line = $3.$4; +- my $string = $5; +- my $underscore = length($2); +- next if $underscore eq 0 && $non_translated_line =~ /^#/; +- # Remove [] dummy strings +- my $stripped = $string; +- $stripped =~ s/\[\s[^\[\]]*\],/,/g if $underscore eq 2; +- $stripped =~ s/\[\s[^\[\]]*\]$//; +- $non_translated_line .= $stripped; +- +- print OUTPUT $sep.$non_translated_line; +- +- if ($underscore) +- { +- my @str_list = rfc822deb_split($underscore, $string); +- +- for my $lang (sort keys %po_files_by_lang) +- { +- my $is_translated = 1; +- my $str_translated = ''; +- my $first = 1; +- +- for my $str (@str_list) +- { +- my $translation = $translations{$lang, $str}; +- +- if (!$translation) +- { +- $is_translated = 0; +- last; +- } +- +- # $translation may also contain [] dummy +- # strings, mostly to indicate an empty string +- $translation =~ s/\[\s[^\[\]]*\]$//; +- +- if ($first) +- { +- if ($underscore eq 2) +- { +- $str_translated .= $translation; +- } +- else +- { +- $str_translated .= +- Text::Tabs::expand($translation) . +- "\n"; +- } +- } +- else +- { +- if ($underscore eq 2) +- { +- $str_translated .= ', ' . $translation; +- } +- else +- { +- $str_translated .= Text::Tabs::expand( +- Text::Wrap::wrap(' ', ' ', $translation)) . +- "\n .\n"; +- } +- } +- $first = 0; +- +- # To fix some problems with Text::Wrap::wrap +- $str_translated =~ s/(\n )+\n/\n .\n/g; +- } +- next unless $is_translated; +- +- $str_translated =~ s/\n \.\n$//; +- $str_translated =~ s/\s+$//; +- +- $_ = $non_translated_line; +- s/^(\w+):\s*.*/$sep${1}-$lang.$encodings{$lang}: $str_translated/s; +- print OUTPUT; +- } +- } +- } +- print OUTPUT "\n"; +- +- close OUTPUT; +- close INPUT; +-} +- +-sub rfc822deb_split +-{ +- # Debian defines a special way to deal with rfc822-style files: +- # when a value contain newlines, it consists of +- # 1. a short form (first line) +- # 2. a long description, all lines begin with a space, +- # and paragraphs are separated by a single dot on a line +- # This routine returns an array of all paragraphs, and reformat +- # them. +- # When first argument is 2, the string is a comma separated list of +- # values. +- my $type = shift; +- my $text = shift; +- $text =~ s/^[ \t]//mg; +- return (split(/, */, $text, 0)) if $type ne 1; +- return ($text) if $text !~ /\n/; +- +- $text =~ s/([^\n]*)\n//; +- my @list = ($1); +- my $str = ''; +- +- for my $line (split (/\n/, $text)) +- { +- chomp $line; +- if ($line =~ /^\.\s*$/) +- { +- # New paragraph +- $str =~ s/\s*$//; +- push(@list, $str); +- $str = ''; +- } +- elsif ($line =~ /^\s/) +- { +- # Line which must not be reformatted +- $str .= "\n" if length ($str) && $str !~ /\n$/; +- $line =~ s/\s+$//; +- $str .= $line."\n"; +- } +- else +- { +- # Continuation line, remove newline +- $str .= " " if length ($str) && $str !~ /\n$/; +- $str .= $line; +- } +- } +- +- $str =~ s/\s*$//; +- push(@list, $str) if length ($str); +- +- return @list; +-} +- +diff --git a/intltool-merge.in b/intltool-merge.in +new file mode 120000 +index d0535ab..2238bbd +--- /dev/null ++++ b/intltool-merge.in +@@ -0,0 +1 @@ ++/usr/share/intltool/intltool-merge.in +\ No newline at end of file +diff --git a/intltool-update.in b/intltool-update.in +deleted file mode 100755 +index 661d8fe..0b1800f +--- a/intltool-update.in ++++ /dev/null +@@ -1,1089 +0,0 @@ +-#!@INTLTOOL_PERL@ -w +-# -*- Mode: perl; indent-tabs-mode: nil; c-basic-offset: 4 -*- +- +-# +-# The Intltool Message Updater +-# +-# Copyright (C) 2000-2003 Free Software Foundation. +-# +-# Intltool is free software; you can redistribute it and/or +-# modify it under the terms of the GNU General Public License +-# version 2 published by the Free Software Foundation. +-# +-# Intltool is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY; without even the implied warranty of +-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +-# General Public License for more details. +-# +-# You should have received a copy of the GNU General Public License +-# along with this program; if not, write to the Free Software +-# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-# +-# As a special exception to the GNU General Public License, if you +-# distribute this file as part of a program that contains a +-# configuration script generated by Autoconf, you may include it under +-# the same distribution terms that you use for the rest of that program. +-# +-# Authors: Kenneth Christiansen +-# Maciej Stachowiak +-# Darin Adler +- +-## Release information +-my $PROGRAM = "intltool-update"; +-my $VERSION = "0.35.0"; +-my $PACKAGE = "intltool"; +- +-## Loaded modules +-use strict; +-use Getopt::Long; +-use Cwd; +-use File::Copy; +-use File::Find; +- +-## Scalars used by the option stuff +-my $HELP_ARG = 0; +-my $VERSION_ARG = 0; +-my $DIST_ARG = 0; +-my $POT_ARG = 0; +-my $HEADERS_ARG = 0; +-my $MAINTAIN_ARG = 0; +-my $REPORT_ARG = 0; +-my $VERBOSE = 0; +-my $GETTEXT_PACKAGE = ""; +-my $OUTPUT_FILE = ""; +- +-my @languages; +-my %varhash = (); +-my %po_files_by_lang = (); +- +-# Regular expressions to categorize file types. +-# FIXME: Please check if the following is correct +- +-my $xml_support = +-"xml(?:\\.in)*|". # http://www.w3.org/XML/ (Note: .in is not required) +-"ui|". # Bonobo specific - User Interface desc. files +-"lang|". # ? +-"glade2?(?:\\.in)*|". # Glade specific - User Interface desc. files (Note: .in is not required) +-"scm(?:\\.in)*|". # ? (Note: .in is not required) +-"oaf(?:\\.in)+|". # DEPRECATED: Replaces by Bonobo .server files +-"etspec|". # ? +-"server(?:\\.in)+|". # Bonobo specific +-"sheet(?:\\.in)+|". # ? +-"schemas(?:\\.in)+|". # GConf specific +-"pong(?:\\.in)+|". # DEPRECATED: PONG is not used [by GNOME] any longer. +-"kbd(?:\\.in)+"; # GOK specific. +- +-my $ini_support = +-"icon(?:\\.in)+|". # http://www.freedesktop.org/Standards/icon-theme-spec +-"desktop(?:\\.in)+|". # http://www.freedesktop.org/Standards/menu-spec +-"caves(?:\\.in)+|". # GNOME Games specific +-"directory(?:\\.in)+|". # http://www.freedesktop.org/Standards/menu-spec +-"soundlist(?:\\.in)+|". # GNOME specific +-"keys(?:\\.in)+|". # GNOME Mime database specific +-"theme(?:\\.in)+|". # http://www.freedesktop.org/Standards/icon-theme-spec +-"service(?:\\.in)+"; # DBus specific +- +-my $buildin_gettext_support = +-"c|y|cs|cc|cpp|c\\+\\+|h|hh|gob|py"; +- +-## Always flush buffer when printing +-$| = 1; +- +-## Sometimes the source tree will be rooted somewhere else. +-my $SRCDIR = "."; +-my $POTFILES_in; +- +-$SRCDIR = $ENV{"srcdir"} if $ENV{"srcdir"}; +-$POTFILES_in = "<$SRCDIR/POTFILES.in"; +- +-my $devnull = ($^O eq 'MSWin32' ? 'NUL:' : '/dev/null'); +- +-## Handle options +-GetOptions +-( +- "help" => \$HELP_ARG, +- "version" => \$VERSION_ARG, +- "dist|d" => \$DIST_ARG, +- "pot|p" => \$POT_ARG, +- "headers|s" => \$HEADERS_ARG, +- "maintain|m" => \$MAINTAIN_ARG, +- "report|r" => \$REPORT_ARG, +- "verbose|x" => \$VERBOSE, +- "gettext-package|g=s" => \$GETTEXT_PACKAGE, +- "output-file|o=s" => \$OUTPUT_FILE, +- ) or &Console_WriteError_InvalidOption; +- +-&Console_Write_IntltoolHelp if $HELP_ARG; +-&Console_Write_IntltoolVersion if $VERSION_ARG; +- +-my $arg_count = ($DIST_ARG > 0) +- + ($POT_ARG > 0) +- + ($HEADERS_ARG > 0) +- + ($MAINTAIN_ARG > 0) +- + ($REPORT_ARG > 0); +- +-&Console_Write_IntltoolHelp if $arg_count > 1; +- +-# --version and --help don't require a module name +-my $MODULE = $GETTEXT_PACKAGE || &FindPackageName || "unknown"; +- +-if ($POT_ARG) +-{ +- &GenerateHeaders; +- &GeneratePOTemplate; +-} +-elsif ($HEADERS_ARG) +-{ +- &GenerateHeaders; +-} +-elsif ($MAINTAIN_ARG) +-{ +- &FindLeftoutFiles; +-} +-elsif ($REPORT_ARG) +-{ +- &GenerateHeaders; +- &GeneratePOTemplate; +- &Console_Write_CoverageReport; +-} +-elsif ((defined $ARGV[0]) && $ARGV[0] =~ /^[a-z]/) +-{ +- my $lang = $ARGV[0]; +- +- ## Report error if the language file supplied +- ## to the command line is non-existent +- &Console_WriteError_NotExisting("$SRCDIR/$lang.po") +- if ! -s "$SRCDIR/$lang.po"; +- +- if (!$DIST_ARG) +- { +- print "Working, please wait..." if $VERBOSE; +- &GenerateHeaders; +- &GeneratePOTemplate; +- } +- &POFile_Update ($lang, $OUTPUT_FILE); +- &Console_Write_TranslationStatus ($lang, $OUTPUT_FILE); +-} +-else +-{ +- &Console_Write_IntltoolHelp; +-} +- +-exit; +- +-######### +- +-sub Console_Write_IntltoolVersion +-{ +- print <<_EOF_; +-${PROGRAM} (${PACKAGE}) $VERSION +-Written by Kenneth Christiansen, Maciej Stachowiak, and Darin Adler. +- +-Copyright (C) 2000-2003 Free Software Foundation, Inc. +-This is free software; see the source for copying conditions. There is NO +-warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +-_EOF_ +- exit; +-} +- +-sub Console_Write_IntltoolHelp +-{ +- print <<_EOF_; +-Usage: ${PROGRAM} [OPTION]... LANGCODE +-Updates PO template files and merge them with the translations. +- +-Mode of operation (only one is allowed): +- -p, --pot generate the PO template only +- -s, --headers generate the header files in POTFILES.in +- -m, --maintain search for left out files from POTFILES.in +- -r, --report display a status report for the module +- -d, --dist merge LANGCODE.po with existing PO template +- +-Extra options: +- -g, --gettext-package=NAME override PO template name, useful with --pot +- -o, --output-file=FILE write merged translation to FILE +- -x, --verbose display lots of feedback +- --help display this help and exit +- --version output version information and exit +- +-Examples of use: +-${PROGRAM} --pot just create a new PO template +-${PROGRAM} xy create new PO template and merge xy.po with it +- +-Report bugs to http://bugzilla.gnome.org/ (product name "$PACKAGE") +-or send email to . +-_EOF_ +- exit; +-} +- +-sub echo_n +-{ +- my $str = shift; +- my $ret = `echo "$str"`; +- +- $ret =~ s/\n$//; # do we need the "s" flag? +- +- return $ret; +-} +- +-sub POFile_DetermineType ($) +-{ +- my $type = $_; +- my $gettext_type; +- +- my $xml_regex = "(?:" . $xml_support . ")"; +- my $ini_regex = "(?:" . $ini_support . ")"; +- my $buildin_regex = "(?:" . $buildin_gettext_support . ")"; +- +- if ($type =~ /\[type: gettext\/([^\]].*)]/) +- { +- $gettext_type=$1; +- } +- elsif ($type =~ /schemas(\.in)+$/) +- { +- $gettext_type="schemas"; +- } +- elsif ($type =~ /glade2?(\.in)*$/) +- { +- $gettext_type="glade"; +- } +- elsif ($type =~ /scm(\.in)*$/) +- { +- $gettext_type="scheme"; +- } +- elsif ($type =~ /keys(\.in)+$/) +- { +- $gettext_type="keys"; +- } +- +- # bucket types +- +- elsif ($type =~ /$xml_regex$/) +- { +- $gettext_type="xml"; +- } +- elsif ($type =~ /$ini_regex$/) +- { +- $gettext_type="ini"; +- } +- elsif ($type =~ /$buildin_regex$/) +- { +- $gettext_type="buildin"; +- } +- else +- { +- $gettext_type="unknown"; +- } +- +- return "gettext\/$gettext_type"; +-} +- +-sub TextFile_DetermineEncoding ($) +-{ +- my $gettext_code="ASCII"; # All files are ASCII by default +- my $filetype=`file $_ | cut -d ' ' -f 2`; +- +- if ($? eq "0") +- { +- if ($filetype =~ /^(ISO|UTF)/) +- { +- chomp ($gettext_code = $filetype); +- } +- elsif ($filetype =~ /^XML/) +- { +- $gettext_code="UTF-8"; # We asume that .glade and other .xml files are UTF-8 +- } +- } +- +- return $gettext_code; +-} +- +-sub isNotValidMissing +-{ +- my ($file) = @_; +- +- return if $file =~ /^\{arch\}\/.*$/; +- return if $file =~ /^$varhash{"PACKAGE"}-$varhash{"VERSION"}\/.*$/; +-} +- +-sub FindLeftoutFiles +-{ +- my (@buf_i18n_plain, +- @buf_i18n_xml, +- @buf_i18n_xml_unmarked, +- @buf_i18n_ini, +- @buf_potfiles, +- @buf_potfiles_ignore, +- @buf_allfiles, +- @buf_allfiles_sorted, +- @buf_potfiles_sorted +- ); +- +- ## Search and find all translatable files +- find sub { +- push @buf_i18n_plain, "$File::Find::name" if /\.($buildin_gettext_support)$/; +- push @buf_i18n_xml, "$File::Find::name" if /\.($xml_support)$/; +- push @buf_i18n_ini, "$File::Find::name" if /\.($ini_support)$/; +- push @buf_i18n_xml_unmarked, "$File::Find::name" if /\.(schemas(\.in)+)$/; +- }, ".."; +- +- +- open POTFILES, $POTFILES_in or die "$PROGRAM: there's no POTFILES.in!\n"; +- @buf_potfiles = grep !/^(#|\s*$)/, ; +- close POTFILES; +- +- foreach (@buf_potfiles) { +- s/^\[.*]\s*//; +- } +- +- print "Searching for missing translatable files...\n" if $VERBOSE; +- +- ## Check if we should ignore some found files, when +- ## comparing with POTFILES.in +- foreach my $ignore ("POTFILES.skip", "POTFILES.ignore") +- { +- (-s $ignore) or next; +- +- if ("$ignore" eq "POTFILES.ignore") +- { +- print "The usage of POTFILES.ignore is deprecated. Please consider moving the\n". +- "content of this file to POTFILES.skip.\n"; +- } +- +- print "Found $ignore: Ignoring files...\n" if $VERBOSE; +- open FILE, "<$ignore" or die "ERROR: Failed to open $ignore!\n"; +- +- while () +- { +- push @buf_potfiles_ignore, $_ unless /^(#|\s*$)/; +- } +- close FILE; +- +- @buf_potfiles = (@buf_potfiles_ignore, @buf_potfiles); +- } +- +- foreach my $file (@buf_i18n_plain) +- { +- my $in_comment = 0; +- my $in_macro = 0; +- +- open FILE, "<$file"; +- while () +- { +- # Handle continued multi-line comment. +- if ($in_comment) +- { +- next unless s-.*\*/--; +- $in_comment = 0; +- } +- +- # Handle continued macro. +- if ($in_macro) +- { +- $in_macro = 0 unless /\\$/; +- next; +- } +- +- # Handle start of macro (or any preprocessor directive). +- if (/^\s*\#/) +- { +- $in_macro = 1 if /^([^\\]|\\.)*\\$/; +- next; +- } +- +- # Handle comments and quoted text. +- while (m-(/\*|//|\'|\")-) # \' and \" keep emacs perl mode happy +- { +- my $match = $1; +- if ($match eq "/*") +- { +- if (!s-/\*.*?\*/--) +- { +- s-/\*.*--; +- $in_comment = 1; +- } +- } +- elsif ($match eq "//") +- { +- s-//.*--; +- } +- else # ' or " +- { +- if (!s-$match([^\\]|\\.)*?$match-QUOTEDTEXT-) +- { +- warn "mismatched quotes at line $. in $file\n"; +- s-$match.*--; +- } +- } +- } +- +- if (/\.GetString ?\(QUOTEDTEXT/) +- { +- if (defined isNotValidMissing (unpack("x3 A*", $file))) { +- ## Remove the first 3 chars and add newline +- push @buf_allfiles, unpack("x3 A*", $file) . "\n"; +- } +- last; +- } +- +- if (/_\(QUOTEDTEXT/) +- { +- if (defined isNotValidMissing (unpack("x3 A*", $file))) { +- ## Remove the first 3 chars and add newline +- push @buf_allfiles, unpack("x3 A*", $file) . "\n"; +- } +- last; +- } +- } +- close FILE; +- } +- +- foreach my $file (@buf_i18n_xml) +- { +- open FILE, "<$file"; +- +- while () +- { +- # FIXME: share the pattern matching code with intltool-extract +- if (/\s_[-A-Za-z0-9._:]+\s*=\s*\"([^"]+)\"/ || /<_[^>]+>/ || /translatable=\"yes\"/) +- { +- if (defined isNotValidMissing (unpack("x3 A*", $file))) { +- push @buf_allfiles, unpack("x3 A*", $file) . "\n"; +- } +- last; +- } +- } +- close FILE; +- } +- +- foreach my $file (@buf_i18n_ini) +- { +- open FILE, "<$file"; +- while () +- { +- if (/_(.*)=/) +- { +- if (defined isNotValidMissing (unpack("x3 A*", $file))) { +- push @buf_allfiles, unpack("x3 A*", $file) . "\n"; +- } +- last; +- } +- } +- close FILE; +- } +- +- foreach my $file (@buf_i18n_xml_unmarked) +- { +- if (defined isNotValidMissing (unpack("x3 A*", $file))) { +- push @buf_allfiles, unpack("x3 A*", $file) . "\n"; +- } +- } +- +- +- @buf_allfiles_sorted = sort (@buf_allfiles); +- @buf_potfiles_sorted = sort (@buf_potfiles); +- +- my %in2; +- foreach (@buf_potfiles_sorted) +- { +- $in2{$_} = 1; +- } +- +- my @result; +- +- foreach (@buf_allfiles_sorted) +- { +- if (!exists($in2{$_})) +- { +- push @result, $_ +- } +- } +- +- my @buf_potfiles_notexist; +- +- foreach (@buf_potfiles_sorted) +- { +- chomp (my $dummy = $_); +- if ("$dummy" ne "" and ! -f "../$dummy") +- { +- push @buf_potfiles_notexist, $_; +- } +- } +- +- ## Save file with information about the files missing +- ## if any, and give information about this procedure. +- if (@result + @buf_potfiles_notexist > 0) +- { +- if (@result) +- { +- print "\n" if $VERBOSE; +- unlink "missing"; +- open OUT, ">missing"; +- print OUT @result; +- close OUT; +- warn "\e[1mThe following files contain translations and are currently not in use. Please\e[0m\n". +- "\e[1mconsider adding these to the POTFILES.in file, located in the po/ directory.\e[0m\n\n"; +- print STDERR @result, "\n"; +- warn "If some of these files are left out on purpose then please add them to\n". +- "POTFILES.skip instead of POTFILES.in. A file \e[1m'missing'\e[0m containing this list\n". +- "of left out files has been written in the current directory.\n"; +- } +- if (@buf_potfiles_notexist) +- { +- unlink "notexist"; +- open OUT, ">notexist"; +- print OUT @buf_potfiles_notexist; +- close OUT; +- warn "\n" if ($VERBOSE or @result); +- warn "\e[1mThe following files do not exist anymore:\e[0m\n\n"; +- warn @buf_potfiles_notexist, "\n"; +- warn "Please remove them from POTFILES.in or POTFILES.skip. A file \e[1m'notexist'\e[0m\n". +- "containing this list of absent files has been written in the current directory.\n"; +- } +- } +- +- ## If there is nothing to complain about, notify the user +- else { +- print "\nAll files containing translations are present in POTFILES.in.\n" if $VERBOSE; +- } +-} +- +-sub Console_WriteError_InvalidOption +-{ +- ## Handle invalid arguments +- print STDERR "Try `${PROGRAM} --help' for more information.\n"; +- exit 1; +-} +- +-sub GenerateHeaders +-{ +- my $EXTRACT = "@INTLTOOL_EXTRACT@"; +- chomp $EXTRACT; +- +- $EXTRACT = $ENV{"INTLTOOL_EXTRACT"} if $ENV{"INTLTOOL_EXTRACT"}; +- +- ## Generate the .h header files, so we can allow glade and +- ## xml translation support +- if (! -x "$EXTRACT") +- { +- print STDERR "\n *** The intltool-extract script wasn't found!" +- ."\n *** Without it, intltool-update can not generate files.\n"; +- exit; +- } +- else +- { +- open (FILE, $POTFILES_in) or die "$PROGRAM: POTFILES.in not found.\n"; +- +- while () +- { +- chomp; +- next if /^\[\s*encoding/; +- +- ## Find xml files in POTFILES.in and generate the +- ## files with help from the extract script +- +- my $gettext_type= &POFile_DetermineType ($1); +- +- if (/\.($xml_support|$ini_support)$/ || /^\[/) +- { +- s/^\[[^\[].*]\s*//; +- +- my $filename = "../$_"; +- +- if ($VERBOSE) +- { +- system ($EXTRACT, "--update", "--srcdir=$SRCDIR", +- "--type=$gettext_type", $filename); +- } +- else +- { +- system ($EXTRACT, "--update", "--type=$gettext_type", +- "--srcdir=$SRCDIR", "--quiet", $filename); +- } +- } +- } +- close FILE; +- } +-} +- +-# +-# Generate .pot file from POTFILES.in +-# +-sub GeneratePOTemplate +-{ +- my $XGETTEXT = $ENV{"XGETTEXT"} || "@INTLTOOL_XGETTEXT@"; +- my $XGETTEXT_ARGS = $ENV{"XGETTEXT_ARGS"} || ''; +- chomp $XGETTEXT; +- +- if (! -x $XGETTEXT) +- { +- print STDERR " *** xgettext is not found on this system!\n". +- " *** Without it, intltool-update can not extract strings.\n"; +- exit; +- } +- +- print "Building $MODULE.pot...\n" if $VERBOSE; +- +- open INFILE, $POTFILES_in; +- unlink "POTFILES.in.temp"; +- open OUTFILE, ">POTFILES.in.temp" or die("Cannot open POTFILES.in.temp for writing"); +- +- my $gettext_support_nonascii = 0; +- +- # checks for GNU gettext >= 0.12 +- my $dummy = `$XGETTEXT --version --from-code=UTF-8 >$devnull 2>$devnull`; +- if ($? == 0) +- { +- $gettext_support_nonascii = 1; +- } +- else +- { +- # urge everybody to upgrade gettext +- print STDERR "WARNING: This version of gettext does not support extracting non-ASCII\n". +- " strings. That means you should install a version of gettext\n". +- " that supports non-ASCII strings (such as GNU gettext >= 0.12),\n". +- " or have to let non-ASCII strings untranslated. (If there is any)\n"; +- } +- +- my $encoding = "ASCII"; +- my $forced_gettext_code; +- my @temp_headers; +- my $encoding_problem_is_reported = 0; +- +- while () +- { +- next if (/^#/ or /^\s*$/); +- +- chomp; +- +- my $gettext_code; +- +- if (/^\[\s*encoding:\s*(.*)\s*\]/) +- { +- $forced_gettext_code=$1; +- } +- elsif (/\.($xml_support|$ini_support)$/ || /^\[/) +- { +- s/^\[.*]\s*//; +- print OUTFILE "../$_.h\n"; +- push @temp_headers, "../$_.h"; +- $gettext_code = &TextFile_DetermineEncoding ("../$_.h") if ($gettext_support_nonascii and not defined $forced_gettext_code); +- } +- else +- { +- if ($SRCDIR eq ".") { +- print OUTFILE "../$_\n"; +- } else { +- print OUTFILE "$SRCDIR/../$_\n"; +- } +- $gettext_code = &TextFile_DetermineEncoding ("../$_") if ($gettext_support_nonascii and not defined $forced_gettext_code); +- } +- +- next if (! $gettext_support_nonascii); +- +- if (defined $forced_gettext_code) +- { +- $encoding=$forced_gettext_code; +- } +- elsif (defined $gettext_code and "$encoding" ne "$gettext_code") +- { +- if ($encoding eq "ASCII") +- { +- $encoding=$gettext_code; +- } +- elsif ($gettext_code ne "ASCII") +- { +- # Only report once because the message is quite long +- if (! $encoding_problem_is_reported) +- { +- print STDERR "WARNING: You should use the same file encoding for all your project files,\n". +- " but $PROGRAM thinks that most of the source files are in\n". +- " $encoding encoding, while \"$_\" is (likely) in\n". +- " $gettext_code encoding. If you are sure that all translatable strings\n". +- " are in same encoding (say UTF-8), please \e[1m*prepend*\e[0m the following\n". +- " line to POTFILES.in:\n\n". +- " [encoding: UTF-8]\n\n". +- " and make sure that configure.in/ac checks for $PACKAGE >= 0.27 .\n". +- "(such warning message will only be reported once.)\n"; +- $encoding_problem_is_reported = 1; +- } +- } +- } +- } +- +- close OUTFILE; +- close INFILE; +- +- unlink "$MODULE.pot"; +- my @xgettext_argument=("$XGETTEXT", +- "--add-comments", +- "--directory\=\.", +- "--output\=$MODULE\.pot", +- "--files-from\=\.\/POTFILES\.in\.temp"); +- my $XGETTEXT_KEYWORDS = &FindPOTKeywords; +- push @xgettext_argument, $XGETTEXT_KEYWORDS; +- my $MSGID_BUGS_ADDRESS = &FindMakevarsBugAddress; +- push @xgettext_argument, "--msgid-bugs-address\=$MSGID_BUGS_ADDRESS" if $MSGID_BUGS_ADDRESS; +- push @xgettext_argument, "--from-code\=$encoding" if ($gettext_support_nonascii); +- push @xgettext_argument, $XGETTEXT_ARGS if $XGETTEXT_ARGS; +- my $xgettext_command = join ' ', @xgettext_argument; +- +- # intercept xgettext error message +- print "Running $xgettext_command\n" if $VERBOSE; +- my $xgettext_error_msg = `$xgettext_command 2>\&1`; +- my $command_failed = $?; +- +- unlink "POTFILES.in.temp"; +- +- print "Removing generated header (.h) files..." if $VERBOSE; +- unlink foreach (@temp_headers); +- print "done.\n" if $VERBOSE; +- +- if (! $command_failed) +- { +- if (! -e "$MODULE.pot") +- { +- print "None of the files in POTFILES.in contain strings marked for translation.\n" if $VERBOSE; +- } +- else +- { +- print "Wrote $MODULE.pot\n" if $VERBOSE; +- } +- } +- else +- { +- if ($xgettext_error_msg =~ /--from-code/) +- { +- # replace non-ASCII error message with a more useful one. +- print STDERR "ERROR: xgettext failed to generate PO template file because there is non-ASCII\n". +- " string marked for translation. Please make sure that all strings marked\n". +- " for translation are in uniform encoding (say UTF-8), then \e[1m*prepend*\e[0m the\n". +- " following line to POTFILES.in and rerun $PROGRAM:\n\n". +- " [encoding: UTF-8]\n\n"; +- } +- else +- { +- print STDERR "$xgettext_error_msg"; +- if (-e "$MODULE.pot") +- { +- # is this possible? +- print STDERR "ERROR: xgettext failed but still managed to generate PO template file.\n". +- " Please consult error message above if there is any.\n"; +- } +- else +- { +- print STDERR "ERROR: xgettext failed to generate PO template file. Please consult\n". +- " error message above if there is any.\n"; +- } +- } +- exit (1); +- } +-} +- +-sub POFile_Update +-{ +- -f "$MODULE.pot" or die "$PROGRAM: $MODULE.pot does not exist.\n"; +- +- my $MSGMERGE = $ENV{"MSGMERGE"} || "@INTLTOOL_MSGMERGE@"; +- my ($lang, $outfile) = @_; +- +- print "Merging $SRCDIR/$lang.po with $MODULE.pot..." if $VERBOSE; +- +- my $infile = "$SRCDIR/$lang.po"; +- $outfile = "$SRCDIR/$lang.po" if ($outfile eq ""); +- +- # I think msgmerge won't overwrite old file if merge is not successful +- system ("$MSGMERGE", "-o", $outfile, $infile, "$MODULE.pot"); +-} +- +-sub Console_WriteError_NotExisting +-{ +- my ($file) = @_; +- +- ## Report error if supplied language file is non-existing +- print STDERR "$PROGRAM: $file does not exist!\n"; +- print STDERR "Try '$PROGRAM --help' for more information.\n"; +- exit; +-} +- +-sub GatherPOFiles +-{ +- my @po_files = glob ("./*.po"); +- +- @languages = map (&POFile_GetLanguage, @po_files); +- +- foreach my $lang (@languages) +- { +- $po_files_by_lang{$lang} = shift (@po_files); +- } +-} +- +-sub POFile_GetLanguage ($) +-{ +- s/^(.*\/)?(.+)\.po$/$2/; +- return $_; +-} +- +-sub Console_Write_TranslationStatus +-{ +- my ($lang, $output_file) = @_; +- my $MSGFMT = $ENV{"MSGFMT"} || "@INTLTOOL_MSGFMT@"; +- +- $output_file = "$SRCDIR/$lang.po" if ($output_file eq ""); +- +- system ("$MSGFMT", "-o", "$devnull", "--verbose", $output_file); +-} +- +-sub Console_Write_CoverageReport +-{ +- my $MSGFMT = $ENV{"MSGFMT"} || "@INTLTOOL_MSGFMT@"; +- +- &GatherPOFiles; +- +- foreach my $lang (@languages) +- { +- print "$lang: "; +- &POFile_Update ($lang, ""); +- } +- +- print "\n\n * Current translation support in $MODULE \n\n"; +- +- foreach my $lang (@languages) +- { +- print "$lang: "; +- system ("$MSGFMT", "-o", "$devnull", "--verbose", "$SRCDIR/$lang.po"); +- } +-} +- +-sub SubstituteVariable +-{ +- my ($str) = @_; +- +- # always need to rewind file whenever it has been accessed +- seek (CONF, 0, 0); +- +- # cache each variable. varhash is global to we can add +- # variables elsewhere. +- while () +- { +- if (/^(\w+)=(.*)$/) +- { +- ($varhash{$1} = $2) =~ s/^["'](.*)["']$/$1/; +- } +- } +- +- if ($str =~ /^(.*)\${?([A-Z_]+)}?(.*)$/) +- { +- my $rest = $3; +- my $untouched = $1; +- my $sub = ""; +- # Ignore recursive definitions of variables +- $sub = $varhash{$2} if defined $varhash{$2} and $varhash{$2} !~ /\${?$2}?/; +- +- return SubstituteVariable ("$untouched$sub$rest"); +- } +- +- # We're using Perl backticks ` and "echo -n" here in order to +- # expand any shell escapes (such as backticks themselves) in every variable +- return echo_n ($str); +-} +- +-sub CONF_Handle_Open +-{ +- my $base_dirname = getcwd(); +- $base_dirname =~ s@.*/@@; +- +- my ($conf_in, $src_dir); +- +- if ($base_dirname =~ /^po(-.+)?$/) +- { +- if (-f "Makevars") +- { +- my $makefile_source; +- +- local (*IN); +- open (IN, ") +- { +- if (/^top_builddir[ \t]*=/) +- { +- $src_dir = $_; +- $src_dir =~ s/^top_builddir[ \t]*=[ \t]*([^ \t\n\r]*)/$1/; +- +- chomp $src_dir; +- if (-f "$src_dir" . "/configure.ac") { +- $conf_in = "$src_dir" . "/configure.ac" . "\n"; +- } else { +- $conf_in = "$src_dir" . "/configure.in" . "\n"; +- } +- last; +- } +- } +- close IN; +- +- $conf_in || die "Cannot find top_builddir in Makevars."; +- } +- elsif (-f "../configure.ac") +- { +- $conf_in = "../configure.ac"; +- } +- elsif (-f "../configure.in") +- { +- $conf_in = "../configure.in"; +- } +- else +- { +- my $makefile_source; +- +- local (*IN); +- open (IN, ") +- { +- if (/^top_srcdir[ \t]*=/) +- { +- $src_dir = $_; +- $src_dir =~ s/^top_srcdir[ \t]*=[ \t]*([^ \t\n\r]*)/$1/; +- +- chomp $src_dir; +- $conf_in = "$src_dir" . "/configure.in" . "\n"; +- +- last; +- } +- } +- close IN; +- +- $conf_in || die "Cannot find top_srcdir in Makefile."; +- } +- +- open (CONF, "<$conf_in"); +- } +- else +- { +- print STDERR "$PROGRAM: Unable to proceed.\n" . +- "Make sure to run this script inside the po directory.\n"; +- exit; +- } +-} +- +-sub FindPackageName +-{ +- my $version; +- my $domain = &FindMakevarsDomain; +- my $name = $domain || "untitled"; +- +- &CONF_Handle_Open; +- +- my $conf_source; { +- local (*IN); +- open (IN, "<&CONF") || return $name; +- seek (IN, 0, 0); +- local $/; # slurp mode +- $conf_source = ; +- close IN; +- } +- +- # priority for getting package name: +- # 1. GETTEXT_PACKAGE +- # 2. first argument of AC_INIT (with >= 2 arguments) +- # 3. first argument of AM_INIT_AUTOMAKE (with >= 2 argument) +- +- # /^AM_INIT_AUTOMAKE\([\s\[]*([^,\)\s\]]+)/m +- # the \s makes this not work, why? +- if ($conf_source =~ /^AM_INIT_AUTOMAKE\(([^,\)]+),([^,\)]+)/m) +- { +- ($name, $version) = ($1, $2); +- $name =~ s/[\[\]\s]//g; +- $version =~ s/[\[\]\s]//g; +- $varhash{"PACKAGE_NAME"} = $name if (not $name =~ /\${?AC_PACKAGE_NAME}?/); +- $varhash{"PACKAGE"} = $name if (not $name =~ /\${?PACKAGE}?/); +- $varhash{"PACKAGE_VERSION"} = $version if (not $name =~ /\${?AC_PACKAGE_VERSION}?/); +- $varhash{"VERSION"} = $version if (not $name =~ /\${?VERSION}?/); +- } +- +- if ($conf_source =~ /^AC_INIT\(([^,\)]+),([^,\)]+)/m) +- { +- ($name, $version) = ($1, $2); +- $name =~ s/[\[\]\s]//g; +- $version =~ s/[\[\]\s]//g; +- $varhash{"PACKAGE_NAME"} = $name if (not $name =~ /\${?AC_PACKAGE_NAME}?/); +- $varhash{"PACKAGE"} = $name if (not $name =~ /\${?PACKAGE}?/); +- $varhash{"PACKAGE_VERSION"} = $version if (not $name =~ /\${?AC_PACKAGE_VERSION}?/); +- $varhash{"VERSION"} = $version if (not $name =~ /\${?VERSION}?/); +- } +- +- # \s makes this not work, why? +- $name = $1 if $conf_source =~ /^GETTEXT_PACKAGE=\[?([^\n\]]+)/m; +- +- # m4 macros AC_PACKAGE_NAME, AC_PACKAGE_VERSION etc. have same value +- # as corresponding $PACKAGE_NAME, $PACKAGE_VERSION etc. shell variables. +- $name =~ s/\bAC_PACKAGE_/\$PACKAGE_/g; +- +- $name = $domain if $domain; +- +- $name = SubstituteVariable ($name); +- $name =~ s/^["'](.*)["']$/$1/; +- +- return $name if $name; +-} +- +- +-sub FindPOTKeywords +-{ +- +- my $keywords = "--keyword\=\_ --keyword\=N\_ --keyword\=U\_ --keyword\=Q\_"; +- my $varname = "XGETTEXT_OPTIONS"; +- my $make_source; { +- local (*IN); +- open (IN, "; +- close IN; +- } +- +- $keywords = $1 if $make_source =~ /^$varname[ ]*=\[?([^\n\]]+)/m; +- +- return $keywords; +-} +- +-sub FindMakevarsDomain +-{ +- +- my $domain = ""; +- my $makevars_source; { +- local (*IN); +- open (IN, "; +- close IN; +- } +- +- $domain = $1 if $makevars_source =~ /^DOMAIN[ ]*=\[?([^\n\]\$]+)/m; +- $domain =~ s/^\s+//; +- $domain =~ s/\s+$//; +- +- return $domain; +-} +- +-sub FindMakevarsBugAddress +-{ +- +- my $address = ""; +- my $makevars_source; { +- local (*IN); +- open (IN, "; +- close IN; +- } +- +- $address = $1 if $makevars_source =~ /^MSGID_BUGS_ADDRESS[ ]*=\[?([^\n\]\$]+)/m; +- $address =~ s/^\s+//; +- $address =~ s/\s+$//; +- +- return $address; +-} +diff --git a/intltool-update.in b/intltool-update.in +new file mode 120000 +index 661d8fe..0b1800f +--- /dev/null ++++ b/intltool-update.in +@@ -0,0 +1 @@ ++/usr/share/intltool/intltool-update.in +\ No newline at end of file +diff --git a/pixmaps/intlclock-map-location-current.png b/pixmaps/intlclock-map-location-current.png +new file mode 100644 +index 0000000..5c505d1 +Binary files /dev/null and b/pixmaps/intlclock-map-location-current.png differ +diff --git a/pixmaps/intlclock-map-location-current.svg b/pixmaps/intlclock-map-location-current.svg +new file mode 100644 +index 0000000..93b5188 +--- /dev/null ++++ b/pixmaps/intlclock-map-location-current.svg +@@ -0,0 +1,76 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ image/svg+xml ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/pixmaps/intlclock-map-location-hilight.png b/pixmaps/intlclock-map-location-hilight.png +new file mode 100644 +index 0000000..d7de5b7 +Binary files /dev/null and b/pixmaps/intlclock-map-location-hilight.png differ +diff --git a/pixmaps/intlclock-map-location-hilight.svg b/pixmaps/intlclock-map-location-hilight.svg +new file mode 100644 +index 0000000..4a245e0 +--- /dev/null ++++ b/pixmaps/intlclock-map-location-hilight.svg +@@ -0,0 +1,90 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ image/svg+xml ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/pixmaps/intlclock-map-location-marker.png b/pixmaps/intlclock-map-location-marker.png +new file mode 100644 +index 0000000..48d2184 +Binary files /dev/null and b/pixmaps/intlclock-map-location-marker.png differ +diff --git a/po/Makefile.in.in b/po/Makefile.in.in +deleted file mode 100755 +index d2d4e4c..e4713cf +--- a/po/Makefile.in.in ++++ /dev/null +@@ -1,221 +0,0 @@ +-# Makefile for program source directory in GNU NLS utilities package. +-# Copyright (C) 1995, 1996, 1997 by Ulrich Drepper +-# +-# This file file be copied and used freely without restrictions. It can +-# be used in projects which are not available under the GNU Public License +-# but which still want to provide support for the GNU gettext functionality. +-# Please note that the actual code is *not* freely available. +-# +-# - Modified by Owen Taylor to use GETTEXT_PACKAGE +-# instead of PACKAGE and to look for po2tbl in ./ not in intl/ +-# +-# - Modified by jacob berkman to install +-# Makefile.in.in and po2tbl.sed.in for use with glib-gettextize +-# +-# - Modified by Rodney Dawes for use with intltool +-# +-# We have the following line for use by intltoolize: +-# INTLTOOL_MAKEFILE +- +-GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +-PACKAGE = @PACKAGE@ +-VERSION = @VERSION@ +- +-SHELL = /bin/sh +- +-srcdir = @srcdir@ +-top_srcdir = @top_srcdir@ +-top_builddir = .. +-VPATH = @srcdir@ +- +-prefix = @prefix@ +-exec_prefix = @exec_prefix@ +-datadir = @datadir@ +-datarootdir = @datarootdir@ +-libdir = @libdir@ +-DATADIRNAME = @DATADIRNAME@ +-itlocaledir = $(prefix)/$(DATADIRNAME)/locale +-subdir = po +-install_sh = @install_sh@ +-# Automake >= 1.8 provides @mkdir_p@. +-# Until it can be supposed, use the safe fallback: +-mkdir_p = $(install_sh) -d +- +-INSTALL = @INSTALL@ +-INSTALL_DATA = @INSTALL_DATA@ +- +-GMSGFMT = @GMSGFMT@ +-MSGFMT = @MSGFMT@ +-XGETTEXT = @XGETTEXT@ +-INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +-INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +-MSGMERGE = INTLTOOL_EXTRACT=$(INTLTOOL_EXTRACT) srcdir=$(srcdir) $(INTLTOOL_UPDATE) --gettext-package $(GETTEXT_PACKAGE) --dist +-GENPOT = INTLTOOL_EXTRACT=$(INTLTOOL_EXTRACT) srcdir=$(srcdir) $(INTLTOOL_UPDATE) --gettext-package $(GETTEXT_PACKAGE) --pot +- +-ALL_LINGUAS = @ALL_LINGUAS@ +- +-PO_LINGUAS=$(shell if test -r $(srcdir)/LINGUAS; then grep -v "^\#" $(srcdir)/LINGUAS; fi) +- +-POFILES=$(shell if test -n "$(PO_LINGUAS)"; then LINGUAS="$(PO_LINGUAS)"; else LINGUAS="$(ALL_LINGUAS)"; fi; for lang in $$LINGUAS; do printf "$$lang.po "; done) +- +-DISTFILES = ChangeLog Makefile.in.in POTFILES.in $(POFILES) +-EXTRA_DISTFILES = POTFILES.skip Makevars LINGUAS +- +-POTFILES = \ +-#This Gets Replace for some reason +- +-CATALOGS=$(shell if test -n "$(PO_LINGUAS)"; then LINGUAS="$(PO_LINGUAS)"; else LINGUAS="$(ALL_LINGUAS)"; fi; for lang in $$LINGUAS; do printf "$$lang.gmo "; done) +- +-.SUFFIXES: +-.SUFFIXES: .po .pox .gmo .mo .msg .cat +- +-.po.pox: +- $(MAKE) $(GETTEXT_PACKAGE).pot +- $(MSGMERGE) $< $(GETTEXT_PACKAGE).pot -o $*.pox +- +-.po.mo: +- $(MSGFMT) -o $@ $< +- +-.po.gmo: +- file=`echo $* | sed 's,.*/,,'`.gmo \ +- && rm -f $$file && $(GMSGFMT) -o $$file $< +- +-.po.cat: +- sed -f ../intl/po2msg.sed < $< > $*.msg \ +- && rm -f $@ && gencat $@ $*.msg +- +- +-all: all-@USE_NLS@ +- +-all-yes: $(CATALOGS) +-all-no: +- +-$(GETTEXT_PACKAGE).pot: $(POTFILES) +- $(GENPOT) +- +-install: install-data +-install-data: install-data-@USE_NLS@ +-install-data-no: all +-install-data-yes: all +- $(mkdir_p) $(DESTDIR)$(itlocaledir) +- if test -n "$(PO_LINGUAS)"; then \ +- linguas="$(PO_LINGUAS)"; \ +- else \ +- linguas="$(ALL_LINGUAS)"; \ +- fi; \ +- for lang in $$linguas; do \ +- dir=$(DESTDIR)$(itlocaledir)/$$lang/LC_MESSAGES; \ +- $(mkdir_p) $$dir; \ +- if test -r $$lang.gmo; then \ +- $(INSTALL_DATA) $$lang.gmo $$dir/$(GETTEXT_PACKAGE).mo; \ +- echo "installing $$lang.gmo as $$dir/$(GETTEXT_PACKAGE).mo"; \ +- else \ +- $(INSTALL_DATA) $(srcdir)/$$lang.gmo $$dir/$(GETTEXT_PACKAGE).mo; \ +- echo "installing $(srcdir)/$$lang.gmo as" \ +- "$$dir/$(GETTEXT_PACKAGE).mo"; \ +- fi; \ +- if test -r $$lang.gmo.m; then \ +- $(INSTALL_DATA) $$lang.gmo.m $$dir/$(GETTEXT_PACKAGE).mo.m; \ +- echo "installing $$lang.gmo.m as $$dir/$(GETTEXT_PACKAGE).mo.m"; \ +- else \ +- if test -r $(srcdir)/$$lang.gmo.m ; then \ +- $(INSTALL_DATA) $(srcdir)/$$lang.gmo.m \ +- $$dir/$(GETTEXT_PACKAGE).mo.m; \ +- echo "installing $(srcdir)/$$lang.gmo.m as" \ +- "$$dir/$(GETTEXT_PACKAGE).mo.m"; \ +- else \ +- true; \ +- fi; \ +- fi; \ +- done +- +-# Empty stubs to satisfy archaic automake needs +-dvi info tags TAGS ID: +- +-# Define this as empty until I found a useful application. +-installcheck: +- +-uninstall: +- if test -n "$(PO_LINGUAS)"; then \ +- linguas="$(PO_LINGUAS)"; \ +- else \ +- linguas="$(ALL_LINGUAS)"; \ +- fi; \ +- for lang in $$linguas; do \ +- rm -f $(DESTDIR)$(itlocaledir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE).mo; \ +- rm -f $(DESTDIR)$(itlocaledir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE).mo.m; \ +- done +- +-check: all $(GETTEXT_PACKAGE).pot +- +-mostlyclean: +- rm -f *.pox $(GETTEXT_PACKAGE).pot *.old.po cat-id-tbl.tmp +- rm -f .intltool-merge-cache +- +-clean: mostlyclean +- +-distclean: clean +- rm -f Makefile Makefile.in POTFILES stamp-it +- rm -f *.mo *.msg *.cat *.cat.m *.gmo +- +-maintainer-clean: distclean +- @echo "This command is intended for maintainers to use;" +- @echo "it deletes files that may require special tools to rebuild." +- rm -f Makefile.in.in +- +-distdir = ../$(PACKAGE)-$(VERSION)/$(subdir) +-dist distdir: $(DISTFILES) +- dists="$(DISTFILES)"; \ +- extra_dists="$(EXTRA_DISTFILES)"; \ +- for file in $$extra_dists; do \ +- test -f $(srcdir)/$$file && dists="$$dists $(srcdir)/$$file"; \ +- done; \ +- for file in $$dists; do \ +- test -f $$file || file="$(srcdir)/$$file"; \ +- ln $$file $(distdir) 2> /dev/null \ +- || cp -p $$file $(distdir); \ +- done +- +-update-po: Makefile +- $(MAKE) $(GETTEXT_PACKAGE).pot +- tmpdir=`pwd`; \ +- if test -n "$(PO_LINGUAS)"; then \ +- linguas="$(PO_LINGUAS)"; \ +- else \ +- linguas="$(ALL_LINGUAS)"; \ +- fi; \ +- for lang in $$linguas; do \ +- echo "$$lang:"; \ +- result="`$(MSGMERGE) -o $$tmpdir/$$lang.new.po $$lang`"; \ +- if $$result; then \ +- if cmp $(srcdir)/$$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \ +- rm -f $$tmpdir/$$lang.new.po; \ +- else \ +- if mv -f $$tmpdir/$$lang.new.po $$lang.po; then \ +- :; \ +- else \ +- echo "msgmerge for $$lang.po failed: cannot move $$tmpdir/$$lang.new.po to $$lang.po" 1>&2; \ +- rm -f $$tmpdir/$$lang.new.po; \ +- exit 1; \ +- fi; \ +- fi; \ +- else \ +- echo "msgmerge for $$lang.gmo failed!"; \ +- rm -f $$tmpdir/$$lang.new.po; \ +- fi; \ +- done +- +-Makefile POTFILES: stamp-it +- @if test ! -f $@; then \ +- rm -f stamp-it; \ +- $(MAKE) stamp-it; \ +- fi +- +-stamp-it: Makefile.in.in ../config.status POTFILES.in +- cd .. \ +- && CONFIG_FILES=$(subdir)/Makefile.in CONFIG_HEADERS= CONFIG_LINKS= \ +- $(SHELL) ./config.status +- +-# Tell versions [3.59,3.63) of GNU make not to export all variables. +-# Otherwise a system limit (for SysV at least) may be exceeded. +-.NOEXPORT: +diff --git a/po/Makefile.in.in b/po/Makefile.in.in +new file mode 120000 +index d2d4e4c..e4713cf +--- /dev/null ++++ b/po/Makefile.in.in +@@ -0,0 +1 @@ ++/usr/share/intltool/Makefile.in.in +\ No newline at end of file +diff --git a/src/Makefile.am b/src/Makefile.am +index 80d1419..d681d39 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -3,7 +3,7 @@ + INCLUDES = \ + $(INTLCLOCK_CFLAGS) \ + -DGNOMELOCALEDIR=\"$(prefix)/$(DATADIRNAME)/locale\" \ +- -DEVOLUTION_TEXTDOMAIN=\"evolution-2.6\" \ ++ -DEVOLUTION_TEXTDOMAIN=\"evolution-2.12\" \ + -DINTLCLOCK_TEXTDOMAIN=\"gnome-panel-2.0\" \ + -DINTLCLOCK_ICONDIR=\"$(pkgdatadir)\" \ + -DINTLCLOCK_DATADIR=\"$(pkgdatadir)\" \ +@@ -60,7 +60,11 @@ COMMON_SOURCES = \ + intlclock-zoneinfo.c \ + intlclock-zoneinfo.h \ + intlclock-zonetable.c \ +- intlclock-zonetable.h ++ intlclock-zonetable.h \ ++ set-timezone.c \ ++ set-timezone.h \ ++ gweather-xml.c \ ++ gweather-xml.h + + + intlclock_applet_SOURCES = \ +diff --git a/src/gweather-xml.c b/src/gweather-xml.c +new file mode 100644 +index 0000000..94b275d +--- /dev/null ++++ b/src/gweather-xml.c +@@ -0,0 +1,490 @@ ++/* gweather-xml.c - Locations.xml parsing code ++ * ++ * Copyright (C) 2005 Ryan Lortie, 2004 Gareth Owen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++ ++/* There is very little error checking in the parsing code below, it relies ++ * heavily on the locations file being in the correct format. If you have ++ * elements within a parent element, they must come first and be ++ * grouped together. ++ * The format is as follows: ++ * ++ * ++ * ++ * Name of the region ++ * Translated Name ++ * Another Translated Name ++ * ++ * Name of the country ++ * ++ * Name of the location ++ * IWIN code ++ * Forecast code (North America, Australia, UK only) ++ * Weather.com radar map code (North America only) ++ * Latitude and longitude as DD-MM[-SS][H] pair ++ * ++ * ++ * ++ * .... ++ * ++ * ++ * Name of city with multiple locations ++ * Forecast code ++ * Radar Map code ++ * ++ * ... ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * The thing to note is that each country can either contain different locations ++ * or be split into "states" which in turn contain a list of locations. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "gweather-xml.h" ++ ++static gint ++gweather_xml_location_sort_func( GtkTreeModel *model, GtkTreeIter *a, ++ GtkTreeIter *b, gpointer user_data ) ++{ ++ gint res; ++ gchar *name_a, *name_b; ++ gchar *fold_a, *fold_b; ++ ++ gtk_tree_model_get (model, a, GWEATHER_XML_COL_LOC, &name_a, -1); ++ gtk_tree_model_get (model, b, GWEATHER_XML_COL_LOC, &name_b, -1); ++ ++ fold_a = g_utf8_casefold(name_a, -1); ++ fold_b = g_utf8_casefold(name_b, -1); ++ ++ res = g_utf8_collate(fold_a, fold_b); ++ ++ g_free(name_a); ++ g_free(name_b); ++ g_free(fold_a); ++ g_free(fold_b); ++ ++ return res; ++} ++ ++static char* ++gweather_xml_get_value( xmlTextReaderPtr xml ) ++{ ++ char* value; ++ ++ /* check for null node */ ++ if ( xmlTextReaderIsEmptyElement( xml ) ) ++ return NULL; ++ ++ /* the next "node" is the text node containing the value we want to get */ ++ if( xmlTextReaderRead( xml ) != 1 ) ++ return NULL; ++ ++ value = (char *) xmlTextReaderValue( xml ); ++ ++ /* move on to the end of this node */ ++ while( xmlTextReaderNodeType( xml ) != XML_READER_TYPE_END_ELEMENT ) ++ if( xmlTextReaderRead( xml ) != 1 ) ++ { ++ xmlFree( value ); ++ return NULL; ++ } ++ ++ /* consume the end element too */ ++ if( xmlTextReaderRead( xml ) != 1 ) ++ { ++ xmlFree( value ); ++ return NULL; ++ } ++ ++ return value; ++} ++ ++static char * ++gweather_xml_parse_name( xmlTextReaderPtr xml ) ++{ ++ const char * const *locales; ++ const char *this_language; ++ int best_match = INT_MAX; ++ char *lang, *tagname; ++ gboolean keep_going; ++ char *name = NULL; ++ int i; ++ ++ locales = g_get_language_names(); ++ ++ do ++ { ++ /* First let's get the language */ ++ lang = (char *) xmlTextReaderXmlLang( xml ); ++ ++ if( lang == NULL ) ++ this_language = "C"; ++ else ++ this_language = lang; ++ ++ /* the next "node" is text node containing the actual name */ ++ if( xmlTextReaderRead( xml ) != 1 ) ++ { ++ xmlFree( lang ); ++ return NULL; ++ } ++ ++ for( i = 0; locales[i] && i < best_match; i++ ) ++ if( !strcmp( locales[i], this_language ) ) ++ { ++ /* if we've already encounted a less accurate ++ translation, then free it */ ++ xmlFree( name ); ++ ++ name = (char *) xmlTextReaderValue( xml ); ++ best_match = i; ++ ++ break; ++ } ++ ++ xmlFree( lang ); ++ ++ while( xmlTextReaderNodeType( xml ) != XML_READER_TYPE_ELEMENT ) ++ if( xmlTextReaderRead( xml ) != 1 ) ++ { ++ xmlFree( name ); ++ return NULL; ++ } ++ ++ /* if the next tag is another then keep going */ ++ tagname = (char *) xmlTextReaderName( xml ); ++ keep_going = !strcmp( tagname, "name" ); ++ xmlFree( tagname ); ++ ++ } while( keep_going ); ++ ++ return name; ++} ++ ++static int ++gweather_xml_parse_node (GtkTreeView *view, GtkTreeIter *parent, ++ xmlTextReaderPtr xml, WeatherLocation *current, ++ const char *dflt_radar, const char *dflt_zone, ++ const char *cityname) ++{ ++ GtkTreeStore *store = GTK_TREE_STORE( gtk_tree_view_get_model( view ) ); ++ char *name, *code, *zone, *radar, *coordinates; ++ char **city, *nocity = NULL; ++ GtkTreeIter iter, *self; ++ gboolean is_location; ++ char *tagname; ++ int ret = -1; ++ int tagtype; ++ ++ if( (tagname = (char *) xmlTextReaderName( xml )) == NULL ) ++ return -1; ++ ++ if( !strcmp( tagname, "city" ) ) ++ city = &name; ++ else ++ city = &nocity; ++ ++ is_location = !strcmp( tagname, "location" ); ++ ++ /* if we're processing the top-level, then don't create a new iter */ ++ if( !strcmp( tagname, "gweather" ) ) ++ self = NULL; ++ else ++ { ++ self = &iter; ++ /* insert this node into the tree */ ++ gtk_tree_store_append( store, self, parent ); ++ } ++ ++ xmlFree( tagname ); ++ ++ coordinates = NULL; ++ radar = NULL; ++ zone = NULL; ++ code = NULL; ++ name = NULL; ++ ++ /* absorb the start tag */ ++ if( xmlTextReaderRead( xml ) != 1 ) ++ goto error_out; ++ ++ /* start parsing the actual contents of the node */ ++ while( (tagtype = xmlTextReaderNodeType( xml )) != ++ XML_READER_TYPE_END_ELEMENT ) ++ { ++ ++ /* skip non-element types */ ++ if( tagtype != XML_READER_TYPE_ELEMENT ) ++ { ++ if( xmlTextReaderRead( xml ) != 1 ) ++ goto error_out; ++ ++ continue; ++ } ++ ++ tagname = (char *) xmlTextReaderName( xml ); ++ ++ if( !strcmp( tagname, "region" ) || !strcmp( tagname, "country" ) || ++ !strcmp( tagname, "state" ) || !strcmp( tagname, "city" ) || ++ !strcmp( tagname, "location" ) ) ++ { ++ /* recursively handle sub-sections */ ++ if( gweather_xml_parse_node( view, self, xml, current, ++ radar, zone, *city ) ) ++ goto error_out; ++ } ++ else if ( !strcmp( tagname, "name" ) ) ++ { ++ xmlFree( name ); ++ if( (name = gweather_xml_parse_name( xml )) == NULL ) ++ goto error_out; ++ } ++ else if ( !strcmp( tagname, "code" ) ) ++ { ++ xmlFree( code ); ++ if( (code = gweather_xml_get_value( xml )) == NULL ) ++ goto error_out; ++ } ++ else if ( !strcmp( tagname, "zone" ) ) ++ { ++ xmlFree( zone ); ++ if( (zone = gweather_xml_get_value( xml )) == NULL ) ++ goto error_out; ++ } ++ else if ( !strcmp( tagname, "radar" ) ) ++ { ++ xmlFree( radar ); ++ if( (radar = gweather_xml_get_value( xml )) == NULL ) ++ goto error_out; ++ } ++ else if ( !strcmp( tagname, "coordinates" ) ) ++ { ++ xmlFree( coordinates ); ++ if( (coordinates = gweather_xml_get_value( xml )) == NULL ) ++ goto error_out; ++ } ++ else /* some strange tag */ ++ { ++ /* skip past it */ ++ if( xmlTextReaderRead( xml ) != 1 ) ++ goto error_out; ++ } ++ ++ xmlFree( tagname ); ++ } ++ ++ if( self ) ++ gtk_tree_store_set( store, self, GWEATHER_XML_COL_LOC, name, -1 ); ++ ++ /* absorb the end tag. in the case of processing a then 'self' ++ is NULL. In this case, we let this fail since we might be at EOF */ ++ if( xmlTextReaderRead( xml ) != 1 && self ) ++ goto error_out; ++ ++ /* if this is an actual location, setup the WeatherLocation for it */ ++ if( is_location ) ++ { ++ WeatherLocation *new_loc; ++ ++ if( cityname == NULL ) ++ cityname = name; ++ ++ if( radar != NULL ) ++ dflt_radar = radar; ++ ++ if( zone != NULL ) ++ dflt_zone = zone; ++ ++ new_loc = weather_location_new( cityname, code, dflt_zone, ++ dflt_radar, coordinates ); ++ ++ gtk_tree_store_set( store, &iter, GWEATHER_XML_COL_POINTER, new_loc, -1 ); ++ ++ /* If this location is actually the currently selected one, select it */ ++ if( current && weather_location_equal( new_loc, current ) ) ++ { ++ GtkTreePath *path; ++ ++ path = gtk_tree_model_get_path( GTK_TREE_MODEL (store), &iter ); ++ gtk_tree_view_expand_to_path( view, path ); ++ gtk_tree_view_set_cursor( view, path, NULL, FALSE ); ++ gtk_tree_view_scroll_to_cell( view, path, NULL, TRUE, 0.5, 0.5 ); ++ gtk_tree_path_free( path ); ++ } ++ } ++ ++ ret = 0; ++ ++error_out: ++ xmlFree( name ); ++ xmlFree( code ); ++ xmlFree( zone ); ++ xmlFree( radar ); ++ xmlFree( coordinates ); ++ ++ return ret; ++} ++ ++/***************************************************************************** ++ * Func: gweather_xml_load_locations() ++ * Desc: Main entry point for loading the locations from the XML file ++ * Parm: ++ * *tree: tree to view locations ++ * *current: currently selected location ++ */ ++int ++gweather_xml_load_locations( GtkTreeView *tree, WeatherLocation *current ) ++{ ++ char *tagname, *format; ++ GtkTreeSortable *sortable; ++ xmlTextReaderPtr xml; ++ int keep_going; ++ int ret = -1; ++ ++ /* Open the xml file containing the different locations */ ++#ifdef GWEATHER_XML_LOCATION ++ xml = xmlNewTextReaderFilename (GWEATHER_XML_LOCATION "Locations.xml"); ++#else ++ xml = xmlNewTextReaderFilename ("/usr/share/gnome-applets/gweather/Locations.xml"); ++#endif ++ if( xml == NULL ) ++ goto error_out; ++ ++ /* fast forward to the first element */ ++ do ++ { ++ /* if we encounter a problem here, exit right away */ ++ if( xmlTextReaderRead( xml ) != 1 ) ++ goto error_out; ++ } while( xmlTextReaderNodeType( xml ) != XML_READER_TYPE_ELEMENT ); ++ ++ /* check the name and format */ ++ tagname = (char *) xmlTextReaderName( xml ); ++ keep_going = tagname && !strcmp( tagname, "gweather" ); ++ xmlFree( tagname ); ++ ++ if( !keep_going ) ++ goto error_out; ++ ++ format = (char *) xmlTextReaderGetAttribute( xml, (xmlChar *) "format" ); ++ keep_going = format && !strcmp( format, "1.0" ); ++ xmlFree( format ); ++ ++ if( !keep_going ) ++ goto error_out; ++ ++ ret = gweather_xml_parse_node( tree, NULL, xml, current, NULL, NULL, NULL ); ++ ++ if( ret ) ++ goto error_out; ++ ++ /* Sort the tree */ ++ sortable = GTK_TREE_SORTABLE (gtk_tree_view_get_model( tree )); ++ gtk_tree_sortable_set_default_sort_func( sortable, ++ &gweather_xml_location_sort_func, ++ NULL, NULL); ++ gtk_tree_sortable_set_sort_column_id( sortable, ++ GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, ++ GTK_SORT_ASCENDING ); ++error_out: ++ xmlFreeTextReader( xml ); ++ ++ return ret; ++} ++ ++typedef struct { ++ const gchar *name; ++ gdouble latitude; ++ gdouble longitude; ++ gdouble distance; ++ WeatherLocation *location; ++} SearchData; ++ ++ ++static gdouble ++distance (gdouble lat1, gdouble lon1, ++ gdouble lat2, gdouble lon2) ++{ ++ gdouble radius = 6372.795; ++ ++ return acos (cos (lat1) * cos (lat2) * cos (lon1 - lon2) + sin (lat1) * sin (lat2)) * radius; ++} ++ ++gboolean ++compare_location (GtkTreeModel *model, ++ GtkTreePath *path, ++ GtkTreeIter *iter, ++ gpointer user_data) ++{ ++ SearchData *data = user_data; ++ WeatherLocation *loc; ++ gdouble d; ++ ++ gtk_tree_model_get (model, iter, GWEATHER_XML_COL_POINTER, &loc, -1); ++ ++ if (!loc) ++ return FALSE; ++ ++ d = distance (data->latitude, data->longitude, loc->latitude, loc->longitude); ++ ++ if (d < data->distance) { ++ data->distance = d; ++ data->location = loc; ++ } ++ ++ return FALSE; ++} ++ ++gchar * ++find_weather_code (GtkTreeModel *model, ++ const gchar *name, ++ gdouble lat, ++ gdouble lon) ++{ ++ SearchData data; ++ gchar *code; ++ ++ data.name = name; ++ data.latitude = lat; ++ data.longitude = lon; ++ data.distance = 1e6; ++ data.location = NULL; ++ ++ gtk_tree_model_foreach (GTK_TREE_MODEL (model), compare_location, &data); ++ ++ if (data.distance < 50) ++ code = g_strdup (data.location->code); ++ else ++ code = g_strdup ("-"); ++ ++ g_debug ("distance: %f\nin: %s\nlat, lon: %f, %f\nout: %s\nDMS: %s\ncode: %s\n", ++ data.distance, name, lat, lon, data.location->name, data.location->coordinates, code); ++ ++ return code; ++} +diff --git a/src/gweather-xml.h b/src/gweather-xml.h +new file mode 100644 +index 0000000..d65b651 +--- /dev/null ++++ b/src/gweather-xml.h +@@ -0,0 +1,37 @@ ++/* gweather-xml.h ++ * ++ * Copyright (C) 2004 Gareth Owen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef __GWEATHER_XML_H__ ++#define __GWEATHER_XML_H__ ++ ++#include ++#include ++ ++enum ++{ ++ GWEATHER_XML_COL_LOC = 0, ++ GWEATHER_XML_COL_POINTER, ++ GWEATHER_XML_NUM_COLUMNS ++}; ++ ++int gweather_xml_load_locations( GtkTreeView *tree, WeatherLocation *current ); ++gchar *find_weather_code (GtkTreeModel *model, const gchar *name, gdouble lat, gdouble lon); ++ ++ ++#endif /* __GWEATHER_XML_H__ */ +diff --git a/src/intlclock-events-popup.c b/src/intlclock-events-popup.c +index 4276f81..a54f6e3 100644 +--- a/src/intlclock-events-popup.c ++++ b/src/intlclock-events-popup.c +@@ -1,6 +1,8 @@ + #include + #include + #include ++#include ++#include + + #ifdef HAVE_CONFIG_H + #include "config.h" +@@ -11,12 +13,18 @@ + #endif + + #include "intlclock-events-popup.h" ++#include "intlclock-ui.h" ++ ++#define KEY_EXPAND_LOCATIONS "expand_locations" ++#define KEY_EXPAND_TASKS "expand_tasks" ++#define KEY_EXPAND_APPOINTMENTS "expand_appointments" + + G_DEFINE_TYPE (IntlClockEventsPopup, intlclock_events_popup, GTK_TYPE_WINDOW) + + typedef struct { + IntlClock *clock; + PanelApplet *panel_applet; ++ IntlClockUI *ui; + + GtkWidget *calendar; + GtkWidget *main_section; +@@ -126,7 +134,7 @@ intlclock_events_popup_size_allocate_cb (GtkWidget *widget, GtkAllocation *alloc + } + + IntlClockEventsPopup * +-intlclock_events_popup_new (IntlClock *clock, PanelApplet *panel_applet) ++intlclock_events_popup_new (IntlClock *clock, PanelApplet *panel_applet, IntlClockUI *ui) + { + IntlClockEventsPopup *this; + IntlClockEventsPopupPrivate *priv; +@@ -138,6 +146,7 @@ intlclock_events_popup_new (IntlClock *clock, PanelApplet *panel_applet) + + priv->clock = g_object_ref (clock); + priv->panel_applet = panel_applet; ++ priv->ui = ui; + + gtk_widget_set_name (GTK_WIDGET (this), "intlclock-events-window"); + gtk_container_set_border_width (GTK_CONTAINER (this), 0); +@@ -275,6 +284,21 @@ intlclock_events_popup_tick (IntlClock *clock, IntlClockEventsPopup *this) + gmtime (&priv->current_time); + } + ++static GtkWidget * create_hig_frame (IntlClockEventsPopup *this, ++ const char *title, ++ const char *button_label, ++ const char *key, ++ GCallback callback); ++ ++ ++static void ++edit_locations (IntlClockEventsPopup *this) ++{ ++ IntlClockEventsPopupPrivate *priv = PRIVATE (this); ++ ++ intlclock_ui_edit_locations (priv->ui); ++} ++ + static void + intlclock_events_popup_fill (IntlClockEventsPopup *this) + { +@@ -296,7 +320,10 @@ intlclock_events_popup_fill (IntlClockEventsPopup *this) + + orient = panel_applet_get_orient (priv->panel_applet); + +- priv->clock_container = gtk_vbox_new (FALSE, 0); ++ priv->clock_container = create_hig_frame (this, ++ _("Locations"), _("Edit"), ++ KEY_EXPAND_LOCATIONS, ++ G_CALLBACK (edit_locations)); + + switch (orient) { + case PANEL_APPLET_ORIENT_UP: +@@ -642,28 +669,167 @@ modify_task_text_attributes (GtkTreeModel *model, + g_value_take_boxed (value, attr_list); + } + ++static void ++expand_collapse_child (GtkWidget *child, ++ gpointer data) ++{ ++ gboolean expanded; ++ ++ if (data == child || gtk_widget_is_ancestor (data, child)) ++ return; ++ ++ expanded = gtk_expander_get_expanded (GTK_EXPANDER (data)); ++ g_object_set (child, "visible", expanded, NULL); ++} ++ ++static void ++expand_collapse (GtkWidget *expander, ++ GParamSpec *pspec, ++ gpointer data) ++{ ++ GtkWidget *box = data; ++ ++ gtk_container_foreach (GTK_CONTAINER (box), ++ (GtkCallback)expand_collapse_child, ++ expander); ++} ++ ++static void ++expander_activated (GtkExpander *expander, ++ gpointer data) ++{ ++ IntlClockEventsPopup *this = data; ++ IntlClockEventsPopupPrivate *priv = PRIVATE (this); ++ const gchar *key; ++ gboolean expanded; ++ ++ key = (const gchar*)g_object_get_data (G_OBJECT (expander), "gconf-key"); ++ expanded = gtk_expander_get_expanded (expander); ++ ++ panel_applet_gconf_set_bool (priv->panel_applet, key, expanded, NULL); ++} ++ ++static void ++expanded_changed (GConfClient *client, ++ guint cnxn_id, ++ GConfEntry *entry, ++ GtkExpander *expander) ++{ ++ if (!entry->value || entry->value->type != GCONF_VALUE_BOOL) ++ return; ++ ++ gtk_expander_set_expanded (expander, ++ gconf_value_get_bool (entry->value)); ++} ++ ++static void ++remove_listener (gpointer data) ++{ ++ GConfClient *client; ++ ++ client = gconf_client_get_default (); ++ gconf_client_notify_remove (client, GPOINTER_TO_UINT (data)); ++ g_object_unref (client); ++} ++ ++static void ++connect_expander_with_gconf (IntlClockEventsPopup *this, ++ GtkWidget *expander, ++ const gchar *key) ++{ ++ IntlClockEventsPopupPrivate *priv = PRIVATE (this); ++ GConfClient *client; ++ gboolean expanded; ++ guint listener; ++ gchar *full_key; ++ ++ g_object_set_data (G_OBJECT (expander), "gconf-key", (gpointer)key); ++ ++ expanded = panel_applet_gconf_get_bool (priv->panel_applet, key, NULL); ++ gtk_expander_set_expanded (GTK_EXPANDER (expander), expanded); ++ ++ g_signal_connect_after (expander, "activate", ++ G_CALLBACK (expander_activated), ++ this); ++ ++ client = gconf_client_get_default (); ++ full_key = panel_applet_gconf_get_full_key (priv->panel_applet, key); ++ listener = gconf_client_notify_add (client, full_key, ++ (GConfClientNotifyFunc)expanded_changed, ++ expander, NULL, NULL); ++ g_object_set_data_full (G_OBJECT (expander), "listener-id", ++ GUINT_TO_POINTER (listener), remove_listener); ++ g_object_unref (client); ++} ++ ++static void add_child (GtkContainer *container, ++ GtkWidget *child, ++ GtkExpander *expander) ++{ ++ gboolean expanded; ++ ++ expanded = gtk_expander_get_expanded (expander); ++ g_object_set (child, "visible", expanded, NULL); ++} ++ + static GtkWidget * +-create_hig_frame (const char *title) ++create_hig_frame (IntlClockEventsPopup *this, ++ const char *title, ++ const char *button_label, ++ const char *key, ++ GCallback callback) + { + GtkWidget *vbox; + GtkWidget *alignment; + GtkWidget *label; ++ GtkWidget *hbox; ++ GtkWidget *button; + char *bold_title; ++ char *text; ++ GtkWidget *expander; + + vbox = gtk_vbox_new (FALSE, 6); + + bold_title = g_strdup_printf ("%s", title); +- +- alignment = gtk_alignment_new (0, 0.5, 0, 0); +- gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0); +- gtk_widget_show (alignment); +- +- label = gtk_label_new (bold_title); +- gtk_label_set_use_markup (GTK_LABEL (label), TRUE); +- gtk_container_add (GTK_CONTAINER (alignment), label); +- gtk_widget_show (label); +- ++ expander = gtk_expander_new (""); ++ gtk_expander_set_label (GTK_EXPANDER (expander), bold_title); + g_free (bold_title); ++ gtk_expander_set_use_markup (GTK_EXPANDER (expander), TRUE); ++ ++ hbox = gtk_hbox_new (FALSE, 0); ++ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); ++ gtk_box_pack_start (GTK_BOX (hbox), expander, FALSE, FALSE, 0); ++ gtk_widget_show_all (vbox); ++ ++ g_signal_connect (expander, "notify::expanded", ++ G_CALLBACK (expand_collapse), hbox); ++ g_signal_connect (expander, "notify::expanded", ++ G_CALLBACK (expand_collapse), vbox); ++ ++ /* FIXME: this doesn't really work, since "add" does not ++ * get emitted for e.g. gtk_box_pack_start ++ */ ++ g_signal_connect (vbox, "add", G_CALLBACK (add_child), expander); ++ g_signal_connect (hbox, "add", G_CALLBACK (add_child), expander); ++ ++ if (button_label) { ++ button = gtk_button_new (); ++ text = g_markup_printf_escaped ("%s", button_label); ++ label = gtk_label_new (text); ++ g_free (text); ++ gtk_label_set_use_markup (GTK_LABEL (label), TRUE); ++ gtk_container_add (GTK_CONTAINER (button), label); ++ ++ alignment = gtk_alignment_new (1, 0, 0, 0); ++ gtk_container_add (GTK_CONTAINER (alignment), button); ++ gtk_widget_show_all (alignment); ++ ++ gtk_container_add (GTK_CONTAINER (hbox), alignment); ++ ++ g_signal_connect_swapped (button, "clicked", callback, this); ++ } ++ ++ connect_expander_with_gconf (this, expander, key); + + return vbox; + } +@@ -791,6 +957,12 @@ compare_tasks (GtkTreeModel *model, + } + } + ++static void ++edit_tasks (IntlClockEventsPopup *this) ++{ ++ clock_launch_evolution (this, "--component=tasks"); ++} ++ + static GtkWidget * + create_task_list (IntlClockEventsPopup *this, + GtkWidget **tree_view, +@@ -804,7 +976,9 @@ create_task_list (IntlClockEventsPopup *this, + GtkCellRenderer *cell; + GtkTreeViewColumn *column; + +- vbox = create_hig_frame (_("Tasks")); ++ vbox = create_hig_frame (this, _("Tasks"), _("Edit"), ++ KEY_EXPAND_TASKS, ++ G_CALLBACK (edit_tasks)); + + *scrolled_window = scrolled = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled), +@@ -812,8 +986,8 @@ create_task_list (IntlClockEventsPopup *this, + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); +- gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0); + gtk_widget_show (scrolled); ++ gtk_container_add (GTK_CONTAINER (vbox), scrolled); + + if (!priv->tasks_model) { + GType column_types [N_TASK_COLUMNS] = { +@@ -984,6 +1158,12 @@ handle_appointments_changed (IntlClockEventsPopup *this) + update_frame_visibility (priv->appointment_list, GTK_TREE_MODEL (priv->appointments_model)); + } + ++static void ++edit_appointments (IntlClockEventsPopup *this) ++{ ++ clock_launch_evolution (this, "--component=calendar"); ++} ++ + static GtkWidget * + create_appointment_list (IntlClockEventsPopup *this, + GtkWidget **tree_view, +@@ -997,7 +1177,9 @@ create_appointment_list (IntlClockEventsPopup *this, + GtkCellRenderer *cell; + GtkTreeViewColumn *column; + +- vbox = create_hig_frame ( _("Appointments")); ++ vbox = create_hig_frame (this, _("Appointments"), _("Edit"), ++ KEY_EXPAND_APPOINTMENTS, ++ G_CALLBACK (edit_appointments)); + + *scrolled_window = scrolled = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled), +@@ -1005,8 +1187,8 @@ create_appointment_list (IntlClockEventsPopup *this, + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); +- gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0); + gtk_widget_show (scrolled); ++ gtk_container_add (GTK_CONTAINER (vbox), scrolled); + + if (!priv->appointments_model) { + priv->appointments_model = +diff --git a/src/intlclock-events-popup.h b/src/intlclock-events-popup.h +index 84a5183..b88e4c7 100644 +--- a/src/intlclock-events-popup.h ++++ b/src/intlclock-events-popup.h +@@ -2,6 +2,7 @@ + #define __INTLCLOCK_EVENTS_POPUP_H__ + + #include "intlclock.h" ++#include "intlclock-ui.h" + + #include + #include +@@ -29,7 +30,8 @@ typedef struct + GType intlclock_events_popup_get_type (void); + + IntlClockEventsPopup *intlclock_events_popup_new (IntlClock *clock, +- PanelApplet *panel_applet); ++ PanelApplet *panel_applet, ++ IntlClockUI *ui); + void intlclock_events_popup_set_date (IntlClockEventsPopup *this, + guint year, guint month, guint mday); + GtkWidget *intlclock_events_popup_get_clock_container (IntlClockEventsPopup *this); +diff --git a/src/intlclock-location-tile.c b/src/intlclock-location-tile.c +index 40d96e3..7f64fee 100644 +--- a/src/intlclock-location-tile.c ++++ b/src/intlclock-location-tile.c +@@ -23,9 +23,18 @@ typedef struct { + + IntlClockFaceSize size; + ++ GtkWidget *box; + GtkWidget *clock_face; + GtkWidget *city_label; + GtkWidget *time_label; ++ ++ GtkWidget *current_button; ++ GtkWidget *current_label; ++ GtkWidget *current_marker; ++ GtkSizeGroup *button_group; ++ ++ GtkWidget *weather_icon; ++ + } IntlClockLocationTilePrivate; + + static void intlclock_location_tile_finalize (GObject *); +@@ -36,6 +45,12 @@ static void intlclock_location_tile_refresh (IntlClockLocationTile *this); + #define PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), INTLCLOCK_LOCATION_TILE_TYPE, IntlClockLocationTilePrivate)) + + static void intlclock_location_tile_fill (IntlClockLocationTile *this); ++static void update_weather_icon (IntlClockLocation *loc, WeatherInfo *info, gpointer data); ++static gboolean weather_tooltip (GtkWidget *widget, ++ gint x, gint y, ++ gboolean keyboard_mode, ++ GtkTooltip *tooltip, ++ gpointer data); + + IntlClockLocationTile * + intlclock_location_tile_new (IntlClock *clock, IntlClockUI *ui, +@@ -56,13 +71,17 @@ intlclock_location_tile_new (IntlClock *clock, IntlClockUI *ui, + priv->location = g_object_ref (loc); + priv->size = size; + +- gtk_alignment_set (GTK_ALIGNMENT (this), 0.0, 0.0, 0.0, 0.0); +- gtk_alignment_set_padding (GTK_ALIGNMENT (this), 3, 3, 3, 3); +- + intlclock_location_tile_fill (this); + +- g_signal_connect (G_OBJECT (priv->clock), "tick", +- G_CALLBACK (intlclock_location_tile_tick), this); ++ update_weather_icon (loc, intlclock_location_get_weather_info (loc), this); ++ gtk_widget_set_has_tooltip (priv->weather_icon, TRUE); ++ ++ g_signal_connect (priv->weather_icon, "query-tooltip", ++ G_CALLBACK (weather_tooltip), this); ++ g_signal_connect (G_OBJECT (loc), "weather-updated", ++ G_CALLBACK (update_weather_icon), this); ++ g_signal_connect (G_OBJECT (priv->clock), "tick", ++ G_CALLBACK (intlclock_location_tile_tick), this); + + return this; + } +@@ -133,13 +152,99 @@ intlclock_location_tile_finalize (GObject *g_obj) + priv->location = NULL; + } + ++ if (priv->button_group) { ++ g_object_unref (priv->button_group); ++ priv->button_group = NULL; ++ } ++ + G_OBJECT_CLASS (intlclock_location_tile_parent_class)->finalize (g_obj); + } + ++static gboolean ++press_on_tile (GtkWidget *widget, ++ GdkEventButton *event, ++ IntlClockLocationTile *tile) ++{ ++ IntlClockLocationTilePrivate *priv = PRIVATE (tile); ++ ++ intlclock_blink_location (priv->clock, priv->location); ++ ++ return TRUE; ++} ++ ++static void ++make_current (GtkWidget *widget, IntlClockLocationTile *tile) ++{ ++ IntlClockLocationTilePrivate *priv = PRIVATE (tile); ++ GError *error = NULL; ++ GtkWidget *dialog; ++ ++ if (intlclock_location_make_current (priv->location, &error)) { ++ g_signal_emit_by_name (priv->clock, "current-timezone-changed", 0); ++ } ++ else if (error) { ++ dialog = gtk_message_dialog_new (NULL, ++ 0, ++ GTK_MESSAGE_ERROR, ++ GTK_BUTTONS_OK, ++ _("Failed to set the system timezone")); ++ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), error->message); ++ g_signal_connect (dialog, "response", ++ G_CALLBACK (gtk_widget_destroy), NULL); ++ gtk_window_present (GTK_WINDOW (dialog)); ++ ++ g_error_free (error); ++ } ++} ++ ++static gboolean ++enter_or_leave_tile (GtkWidget *widget, ++ GdkEventCrossing *event, ++ IntlClockLocationTile *tile) ++{ ++ IntlClockLocationTilePrivate *priv = PRIVATE (tile); ++ ++ if (event->type == GDK_ENTER_NOTIFY) { ++ gint can_set; ++ ++ can_set = can_set_system_timezone (); ++ if (!intlclock_location_is_current (priv->location) && ++ can_set != 0) { ++ gtk_label_set_markup (GTK_LABEL (priv->current_label), ++ can_set == 1 ? ++ _("Set...") : ++ _("Set")); ++ gtk_widget_show (priv->current_button); ++ } ++ } ++ else { ++ if (event->detail != GDK_NOTIFY_INFERIOR) ++ gtk_widget_hide (priv->current_button); ++ } ++ ++ return TRUE; ++} ++ + static void + intlclock_location_tile_fill (IntlClockLocationTile *this) + { + IntlClockLocationTilePrivate *priv = PRIVATE (this); ++ GtkWidget *align; ++ GtkWidget *strut; ++ GtkWidget *box; ++ ++ priv->box = gtk_event_box_new (); ++ ++ gtk_widget_add_events (priv->box, GDK_BUTTON_PRESS_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); ++ g_signal_connect (priv->box, "button-press-event", ++ G_CALLBACK (press_on_tile), this); ++ g_signal_connect (priv->box, "enter-notify-event", ++ G_CALLBACK (enter_or_leave_tile), this); ++ g_signal_connect (priv->box, "leave-notify-event", ++ G_CALLBACK (enter_or_leave_tile), this); ++ ++ GtkWidget *alignment = gtk_alignment_new (0, 0, 1, 0); ++ gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 3, 3, 3, 0); + + GtkWidget *tile = gtk_hbox_new (FALSE, 6); + GtkWidget *head_section = gtk_vbox_new (FALSE, 0); +@@ -147,21 +252,55 @@ intlclock_location_tile_fill (IntlClockLocationTile *this) + priv->city_label = gtk_label_new (NULL); + gtk_misc_set_alignment (GTK_MISC (priv->city_label), 0, 0); + ++ align = gtk_alignment_new (0, 0, 0, 0); ++ gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 0, 0, 3); ++ gtk_container_add (GTK_CONTAINER (align), priv->city_label); ++ gtk_box_pack_start (GTK_BOX (head_section), align, FALSE, FALSE, 0); ++ + priv->time_label = gtk_label_new (NULL); + gtk_misc_set_alignment (GTK_MISC (priv->time_label), 0, 0); + +- gtk_box_pack_start (GTK_BOX (head_section), priv->city_label, +- FALSE, FALSE, 0); +- gtk_box_pack_start (GTK_BOX (head_section), priv->time_label, +- FALSE, FALSE, 0); ++ priv->weather_icon = gtk_image_new (); ++ align = gtk_alignment_new (0, 0, 0, 0); ++ gtk_container_add (GTK_CONTAINER (align), priv->weather_icon); ++ ++ box = gtk_hbox_new (FALSE, 0); ++ gtk_box_pack_start (GTK_BOX (head_section), box, FALSE, FALSE, 0); ++ gtk_box_pack_start (GTK_BOX (box), align, FALSE, FALSE, 0); ++ gtk_box_pack_start (GTK_BOX (box), priv->time_label, FALSE, FALSE, 0); ++ ++ priv->current_button = gtk_button_new (); ++ priv->current_label = gtk_label_new (""); ++ gtk_widget_show (priv->current_label); ++ gtk_widget_set_no_show_all (priv->current_button, TRUE); ++ gtk_container_add (GTK_CONTAINER (priv->current_button), priv->current_label); ++ gtk_widget_set_tooltip_text (priv->current_button, _("Set as current timezone for this computer")); ++ ++ priv->current_marker = gtk_image_new_from_icon_name ("go-home", GTK_ICON_SIZE_BUTTON); ++ gtk_widget_set_no_show_all (priv->current_marker, TRUE); ++ ++ align = gtk_alignment_new (1, 1, 0, 0); ++ strut = gtk_event_box_new (); ++ gtk_box_pack_start (GTK_BOX (box), strut, TRUE, TRUE, 0); ++ gtk_box_pack_start (GTK_BOX (box), priv->current_button, FALSE, FALSE, 0); ++ gtk_box_pack_start (GTK_BOX (box), priv->current_marker, FALSE, FALSE, 0); ++ priv->button_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL); ++ gtk_size_group_set_ignore_hidden (priv->button_group, FALSE); ++ gtk_size_group_add_widget (priv->button_group, strut); ++ gtk_size_group_add_widget (priv->button_group, priv->current_button); ++ ++ g_signal_connect (priv->current_button, "clicked", ++ G_CALLBACK (make_current), this); + + priv->clock_face = intlclock_face_new_with_location ( + priv->size, priv->location, head_section); + + gtk_box_pack_start (GTK_BOX (tile), priv->clock_face, FALSE, FALSE, 0); +- gtk_box_pack_start (GTK_BOX (tile), head_section, FALSE, FALSE, 0); ++ gtk_box_pack_start (GTK_BOX (tile), head_section, TRUE, TRUE, 0); + +- gtk_container_add (GTK_CONTAINER (this), tile); ++ gtk_container_add (GTK_CONTAINER (alignment), tile); ++ gtk_container_add (GTK_CONTAINER (priv->box), alignment); ++ gtk_container_add (GTK_CONTAINER (this), priv->box); + + intlclock_location_tile_refresh (this); + } +@@ -231,6 +370,21 @@ intlclock_location_tile_refresh (IntlClockLocationTile *this) + char buf[256]; + struct tm now; + ++ if (intlclock_location_is_current (priv->location)) { ++ if (!GTK_WIDGET_VISIBLE (priv->current_marker)) { ++ GdkPixbuf *pixbuf; ++ ++ pixbuf = gtk_image_get_pixbuf (GTK_IMAGE (priv->weather_icon)); ++ intlclock_ui_update_weather_icon (priv->ui, pixbuf); ++ } ++ ++ gtk_widget_hide (priv->current_button); ++ gtk_widget_show (priv->current_marker); ++ } ++ else { ++ gtk_widget_hide (priv->current_marker); ++ } ++ + if (intlclock_needs_face_refresh (this)) { + intlclock_face_refresh (INTLCLOCK_FACE (priv->clock_face)); + } +@@ -271,3 +425,87 @@ intlclock_location_tile_tick (IntlClock *clock, + + intlclock_location_tile_refresh (this); + } ++ ++static gboolean ++weather_tooltip (GtkWidget *widget, ++ gint x, ++ gint y, ++ gboolean keyboard_mode, ++ GtkTooltip *tooltip, ++ gpointer data) ++{ ++ IntlClockLocationTile *tile = data; ++ IntlClockLocationTilePrivate *priv = PRIVATE (tile); ++ WeatherInfo *info; ++ GdkPixbuf *pixbuf = NULL; ++ gchar *conditions, *temp, *apparent, *wind; ++ gchar *line1, *line2, *line3, *line4, *tip; ++ ++ info = intlclock_location_get_weather_info (priv->location); ++ ++ if (!info || !weather_info_is_valid (info)) ++ return FALSE; ++ ++ weather_info_get_pixbuf (info, &pixbuf); ++ if (pixbuf) ++ gtk_tooltip_set_icon (tooltip, pixbuf); ++ ++ conditions = weather_info_get_conditions (info); ++ if (strcmp (conditions, "-") != 0) ++ line1 = g_strdup_printf (_("%s, %s"), ++ conditions, ++ weather_info_get_sky (info)); ++ else ++ line1 = g_strdup (weather_info_get_sky (info)); ++ ++ temp = weather_info_get_temp (info); ++ apparent = weather_info_get_apparent (info); ++ if (strcmp (apparent, temp) != 0 && ++ /* FIXME libgweather needs some real api */ ++ strcmp (apparent, dgettext ("gnome-applets-2.0", "Unknown")) != 0) ++ line2 = g_strdup_printf (_("%s, feels like %s"), temp, apparent); ++ else ++ line2 = g_strdup (temp); ++ ++ wind = weather_info_get_wind (info); ++ if (strcmp (apparent, dgettext ("gnome-applets-2.0", "Unknown")) != 0) ++ line3 = g_strdup_printf ("%s\n", wind); ++ else ++ line3 = g_strdup (""); ++ ++ line4 = g_strdup_printf (_("Sunrise: %s / Sunset: %s"), ++ weather_info_get_sunrise (info), ++ weather_info_get_sunset (info)); ++ ++ tip = g_strdup_printf ("%s\n%s\n%s%s", line1, line2, line3, line4); ++ gtk_tooltip_set_markup (tooltip, tip); ++ g_free (line1); ++ g_free (line2); ++ g_free (line3); ++ g_free (line4); ++ g_free (tip); ++ ++ return TRUE; ++} ++ ++ ++static void ++update_weather_icon (IntlClockLocation *loc, WeatherInfo *info, gpointer data) ++{ ++ IntlClockLocationTile *tile = data; ++ IntlClockLocationTilePrivate *priv = PRIVATE (tile); ++ GdkPixbuf *pixbuf = NULL; ++ ++ if (!info || !weather_info_is_valid (info)) ++ return; ++ ++ weather_info_get_pixbuf_mini (info, &pixbuf); ++ ++ if (pixbuf) { ++ gtk_image_set_from_pixbuf (GTK_IMAGE (priv->weather_icon), pixbuf); ++ gtk_alignment_set_padding (GTK_ALIGNMENT (gtk_widget_get_parent (priv->weather_icon)), 0, 0, 0, 6); ++ if (intlclock_location_is_current (loc)) ++ intlclock_ui_update_weather_icon (priv->ui, pixbuf); ++ } ++} ++ +diff --git a/src/intlclock-location.c b/src/intlclock-location.c +index 384b2aa..0a37b48 100644 +--- a/src/intlclock-location.c ++++ b/src/intlclock-location.c +@@ -1,4 +1,3 @@ +-#include "intlclock-location.h" + + #ifdef HAVE_CONFIG_H + #include +@@ -13,9 +12,16 @@ + #include + #include + #include ++#include + + #include + #include ++#include ++ ++#include "intlclock-location.h" ++#include "intlclock-marshallers.h" ++#include "set-timezone.h" ++#include "gweather-xml.h" + + G_DEFINE_TYPE (IntlClockLocation, intlclock_location, G_TYPE_OBJECT) + +@@ -29,17 +35,31 @@ typedef struct { + + gfloat latitude; + gfloat longitude; ++ ++ gchar *weather_code; ++ WeatherInfo *weather_info; ++ guint weather_timeout; ++ + } IntlClockLocationPrivate; + ++enum { ++ WEATHER_UPDATED, ++ LAST_SIGNAL ++}; ++ ++static guint location_signals[LAST_SIGNAL] = { 0 }; ++ + static void intlclock_location_finalize (GObject *); + static void intlclock_location_set_tz (IntlClockLocation *this); + static void intlclock_location_unset_tz (IntlClockLocation *this); ++static void setup_weather_updates (IntlClockLocation *loc); + + #define PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), INTLCLOCK_LOCATION_TYPE, IntlClockLocationPrivate)) + + IntlClockLocation * + intlclock_location_new (const gchar *name, const gchar *timezone, +- gfloat latitude, gfloat longitude) ++ gfloat latitude, gfloat longitude, ++ const gchar *code) + { + IntlClockLocation *this; + IntlClockLocationPrivate *priv; +@@ -62,6 +82,9 @@ intlclock_location_new (const gchar *name, const gchar *timezone, + priv->latitude = latitude; + priv->longitude = longitude; + ++ priv->weather_code = g_strdup (code); ++ setup_weather_updates (this); ++ + return this; + } + +@@ -193,6 +216,77 @@ guess_zone_from_tree (const gchar *localtime, IntlClockZoneTable *zones) + return ret; + } + ++static const gchar *current_zone = NULL; ++static GnomeVFSMonitorHandle *monitor = NULL; ++ ++static void ++parse_etc_sysconfig_clock (void) ++{ ++ gchar *data; ++ gsize len; ++ gchar **lines; ++ gchar *res; ++ gint i; ++ gchar *p, *q; ++ ++ lines = NULL; ++ res = NULL; ++ if (g_file_test ("/etc/sysconfig/clock", ++ G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) { ++ if (!g_file_get_contents ("/etc/sysconfig/clock", ++ &data, &len, NULL)) ++ goto out; ++ ++ lines = g_strsplit (data, "\n", 0); ++ g_free (data); ++ ++ for (i = 0; lines[i] && !res; i++) { ++ if (g_str_has_prefix (lines[i], "ZONE=")) { ++ p = lines[i] + strlen ("ZONE="); ++ if (p[0] != '\"') ++ goto out; ++ p++; ++ q = strchr (p, '\"'); ++ q[0] = '\0'; ++ res = g_strdup (p); ++ } ++ } ++ } ++ ++out: ++ if (lines) ++ g_strfreev (lines); ++ ++ g_free (current_zone); ++ current_zone = res; ++} ++ ++static void ++monitor_etc_sysconfig_clock (GnomeVFSMonitorHandle *handle, ++ const gchar *monitor_uri, ++ const gchar *info_uri, ++ GnomeVFSMonitorEventType event_type, ++ gpointer user_data) ++{ ++ parse_etc_sysconfig_clock (); ++} ++ ++static const gchar * ++zone_from_etc_sysconfig_clock (void) ++{ ++ if (monitor == NULL) { ++ parse_etc_sysconfig_clock (); ++ ++ gnome_vfs_monitor_add (&monitor, ++ "/etc/sysconfig/clock", ++ GNOME_VFS_MONITOR_FILE, ++ monitor_etc_sysconfig_clock, ++ NULL); ++ } ++ ++ return current_zone; ++} ++ + static gchar * + intlclock_location_guess_zone (IntlClockZoneTable *zones) + { +@@ -200,6 +294,12 @@ intlclock_location_guess_zone (IntlClockZoneTable *zones) + const char *localtime = "/etc/localtime"; + gchar *linkfile = NULL; + GError *err = NULL; ++ gchar *zone; ++ ++ /* look for /etc/sysconfig/clock */ ++ if ((zone = zone_from_etc_sysconfig_clock ())) { ++ return g_strdup (zone); ++ } + + /* guess the current time zone by readlink() on /etc/localtime */ + linkfile = g_file_read_link (localtime, &err); +@@ -235,14 +335,14 @@ intlclock_location_new_from_env (IntlClockZoneTable *zones) + + if (zone == NULL) { + /* make a fake location with a null TZ */ +- return intlclock_location_new (_("Unknown Location"), NULL, 0.0, 0.0); ++ return intlclock_location_new (_("Unknown Location"), NULL, 0.0, 0.0, NULL); + } + + info = intlclock_zonetable_get_zone (zones, zone); + + if (info == NULL) { + /* make a fake location with the current TZ */ +- return intlclock_location_new (_("Unknown Location"), zone, 0.0, 0.0); ++ return intlclock_location_new (_("Unknown Location"), zone, 0.0, 0.0, NULL); + } + + g_free (zone); +@@ -260,7 +360,7 @@ intlclock_location_new_from_env (IntlClockZoneTable *zones) + } + + ret = intlclock_location_new (name, intlclock_zoneinfo_get_name (info), +- lat, lon); ++ lat, lon, NULL); + + g_free (name); + +@@ -274,6 +374,15 @@ intlclock_location_class_init (IntlClockLocationClass *this_class) + + g_obj_class->finalize = intlclock_location_finalize; + ++ location_signals[WEATHER_UPDATED] = ++ g_signal_new ("weather-updated", ++ G_OBJECT_CLASS_TYPE (g_obj_class), ++ G_SIGNAL_RUN_FIRST, ++ G_STRUCT_OFFSET (IntlClockLocationClass, weather_updated), ++ NULL, NULL, ++ _intlclock_marshal_VOID__POINTER, ++ G_TYPE_NONE, 1, G_TYPE_POINTER); ++ + g_type_class_add_private (this_class, sizeof (IntlClockLocationPrivate)); + } + +@@ -318,6 +427,21 @@ intlclock_location_finalize (GObject *g_obj) + priv->tzname = NULL; + } + ++ if (priv->weather_code) { ++ g_free (priv->weather_code); ++ priv->weather_code = NULL; ++ } ++ ++ if (priv->weather_info) { ++ weather_info_free (priv->weather_info); ++ priv->weather_info = NULL; ++ } ++ ++ if (priv->weather_timeout) { ++ g_source_remove (priv->weather_timeout); ++ priv->weather_timeout = 0; ++ } ++ + G_OBJECT_CLASS (intlclock_location_parent_class)->finalize (g_obj); + } + +@@ -467,3 +591,179 @@ intlclock_location_localtime (IntlClockLocation *loc, struct tm *tm) + + intlclock_location_unset_tz (loc); + } ++ ++gboolean ++intlclock_location_is_current (IntlClockLocation *loc) ++{ ++ IntlClockLocationPrivate *priv = PRIVATE (loc); ++ gboolean current; ++ long sys_timezone; ++ const char *zone; ++ ++ if ((zone = zone_from_etc_sysconfig_clock ())) ++ return strcmp (zone, priv->timezone) == 0; ++ ++ unsetenv ("TZ"); ++ tzset (); ++ sys_timezone = timezone; ++ ++ setenv ("TZ", priv->timezone, 1); ++ tzset(); ++ ++ current = sys_timezone == timezone; ++ ++ if (priv->sys_timezone) { ++ setenv ("TZ", priv->sys_timezone, 1); ++ } else { ++ unsetenv ("TZ"); ++ } ++ tzset(); ++ ++ return current; ++} ++ ++gboolean ++intlclock_location_make_current (IntlClockLocation *loc, GError **error) ++{ ++ IntlClockLocationPrivate *priv = PRIVATE (loc); ++ gchar *filename; ++ gboolean ret; ++ ++ filename = g_build_filename (SYSTEM_ZONEINFODIR, priv->timezone, NULL); ++ ret = set_system_timezone (filename, error); ++ g_free (filename); ++ ++ if (ret) { ++ /* FIXME this ugly shortcut is necessary until we move the ++ * current timezone tracking to intlclock.c and emit the ++ * signal from there ++ */ ++ g_free (current_zone); ++ current_zone = g_strdup (priv->timezone); ++ } ++ ++ return ret; ++} ++ ++const gchar * ++intlclock_location_get_weather_code (IntlClockLocation *loc) ++{ ++ IntlClockLocationPrivate *priv = PRIVATE (loc); ++ ++ return priv->weather_code; ++} ++ ++void ++intlclock_location_set_weather_code (IntlClockLocation *loc, const gchar *code) ++{ ++ IntlClockLocationPrivate *priv = PRIVATE (loc); ++ ++ g_free (priv->weather_code); ++ priv->weather_code = g_strdup (code); ++ ++ setup_weather_updates (loc); ++} ++ ++WeatherInfo * ++intlclock_location_get_weather_info (IntlClockLocation *loc) ++{ ++ IntlClockLocationPrivate *priv = PRIVATE (loc); ++ ++ return priv->weather_info; ++} ++ ++static void ++weather_info_updated (WeatherInfo *info, gpointer data) ++{ ++ IntlClockLocation *loc = data; ++ IntlClockLocationPrivate *priv = PRIVATE (loc); ++ ++ g_signal_emit (loc, location_signals[WEATHER_UPDATED], ++ 0, priv->weather_info); ++} ++ ++static gboolean ++update_weather_info (gpointer data) ++{ ++ IntlClockLocation *loc = data; ++ IntlClockLocationPrivate *priv = PRIVATE (loc); ++ WeatherPrefs prefs = { ++ FORECAST_STATE, ++ FALSE, ++ NULL, ++ TEMP_UNIT_CENTIGRADE, ++ SPEED_UNIT_MS, ++ PRESSURE_UNIT_MB, ++ DISTANCE_UNIT_KM ++ }; ++ ++ weather_info_update (priv->weather_info, ++ &prefs, weather_info_updated, loc); ++ ++ return TRUE; ++} ++ ++static gchar * ++rad2dms (gfloat lat, gfloat lon) ++{ ++ gchar h, h2; ++ gfloat d, deg, min, d2, deg2, min2; ++ ++ h = lat > 0 ? 'N' : 'S'; ++ d = fabs (lat); ++ deg = floor (d); ++ min = floor (60 * (d - deg)); ++ h2 = lon > 0 ? 'E' : 'W'; ++ d2 = fabs (lon); ++ deg2 = floor (d2); ++ min2 = floor (60 * (d2 - deg2)); ++ return g_strdup_printf ("%02d-%02d%c %02d-%02d%c", ++ (int)deg, (int)min, h, ++ (int)deg2, (int)min2, h2); ++} ++ ++static void ++setup_weather_updates (IntlClockLocation *loc) ++{ ++ IntlClockLocationPrivate *priv = PRIVATE (loc); ++ const gchar *code; ++ WeatherLocation *wl; ++ WeatherPrefs prefs = { ++ FORECAST_STATE, ++ FALSE, ++ NULL, ++ TEMP_UNIT_CENTIGRADE, ++ SPEED_UNIT_MS, ++ PRESSURE_UNIT_MB, ++ DISTANCE_UNIT_KM ++ }; ++ gfloat lat, lon; ++ gchar *dms; ++ ++ if (priv->weather_info) { ++ weather_info_free (priv->weather_info); ++ priv->weather_info = NULL; ++ } ++ ++ if (priv->weather_timeout) { ++ g_source_remove (priv->weather_timeout); ++ priv->weather_timeout = 0; ++ } ++ ++ if (!priv->weather_code || strcmp (priv->weather_code, "-") == 0) ++ return; ++ ++ dms = rad2dms (priv->latitude, priv->longitude); ++ wl = weather_location_new (priv->name, priv->weather_code, ++ NULL, NULL, dms); ++ ++ priv->weather_info = ++ weather_info_new (wl, &prefs, weather_info_updated, loc); ++ ++ priv->weather_timeout = ++ g_timeout_add_seconds (1800, update_weather_info, loc); ++ ++ weather_location_free (wl); ++ g_free (dms); ++} ++ +diff --git a/src/intlclock-location.h b/src/intlclock-location.h +index 824ee3c..deb7074 100644 +--- a/src/intlclock-location.h ++++ b/src/intlclock-location.h +@@ -4,6 +4,7 @@ + #include + #include + #include ++#include + + #include "intlclock-zonetable.h" + +@@ -24,12 +25,16 @@ typedef struct + typedef struct + { + GObjectClass g_object_class; ++ ++ void (* weather_updated) (IntlClockLocation *location, WeatherInfo *info); ++ + } IntlClockLocationClass; + + GType intlclock_location_get_type (void); + + IntlClockLocation *intlclock_location_new (const gchar *name, const gchar *timezone, +- gfloat latitude, gfloat longitude); ++ gfloat latitude, gfloat longitude, ++ const gchar *code); + + IntlClockLocation *intlclock_location_new_from_env (IntlClockZoneTable *zones); + +@@ -46,5 +51,12 @@ void intlclock_location_set_coords (IntlClockLocation *loc, gfloat latitude, gfl + + void intlclock_location_localtime (IntlClockLocation *loc, struct tm *tm); + ++gboolean intlclock_location_is_current (IntlClockLocation *loc); ++gboolean intlclock_location_make_current (IntlClockLocation *loc, GError **error); ++ ++const gchar *intlclock_location_get_weather_code (IntlClockLocation *loc); ++void intlclock_location_set_weather_code (IntlClockLocation *loc, const gchar *code); ++WeatherInfo *intlclock_location_get_weather_info (IntlClockLocation *loc); ++ + G_END_DECLS + #endif /* __INTLCLOCK_LOCATION_H__ */ +diff --git a/src/intlclock-map.c b/src/intlclock-map.c +index 2d72975..9ded127 100644 +--- a/src/intlclock-map.c ++++ b/src/intlclock-map.c +@@ -25,7 +25,7 @@ typedef struct { + gint height; + + GdkPixbuf *stock_map_pixbuf; +- GdkPixbuf *location_marker_pixbuf; ++ GdkPixbuf *location_marker_pixbuf[3]; + + GdkPixbuf *location_map_pixbuf; + +@@ -54,6 +54,7 @@ static void intlclock_map_display (IntlClockMap *this); + + + static void intlclock_map_locations_changed (IntlClock *clock, IntlClockMap *this); ++static void intlclock_map_blink_location (IntlClock *clock, IntlClockLocation *loc, IntlClockMap *this); + static void intlclock_map_tick (IntlClock *clock, IntlClockMap *this); + + #define PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), INTLCLOCK_MAP_TYPE, IntlClockMapPrivate)) +@@ -69,8 +70,12 @@ intlclock_map_new (IntlClock *clock) + + priv->clock = g_object_ref (clock); + +- priv->location_marker_pixbuf = rsvg_pixbuf_from_file +- (INTLCLOCK_ICONDIR "/intlclock-map-location-marker.svg", NULL); ++ priv->location_marker_pixbuf[0] = gdk_pixbuf_new_from_file ++ (INTLCLOCK_ICONDIR "/intlclock-map-location-marker.png", NULL); ++ priv->location_marker_pixbuf[1] = gdk_pixbuf_new_from_file ++ (INTLCLOCK_ICONDIR "/intlclock-map-location-hilight.png", NULL); ++ priv->location_marker_pixbuf[2] = gdk_pixbuf_new_from_file ++ (INTLCLOCK_ICONDIR "/intlclock-map-location-current.png", NULL); + + g_signal_connect (G_OBJECT (priv->clock), "tick", + G_CALLBACK (intlclock_map_tick), this); +@@ -78,6 +83,12 @@ intlclock_map_new (IntlClock *clock) + g_signal_connect (G_OBJECT (priv->clock), "locations_changed", + G_CALLBACK (intlclock_map_locations_changed), this); + ++ g_signal_connect (G_OBJECT (priv->clock), "current_timezone_changed", ++ G_CALLBACK (intlclock_map_locations_changed), this); ++ ++ g_signal_connect (G_OBJECT (priv->clock), "blink_location", ++ G_CALLBACK (intlclock_map_blink_location), this); ++ + intlclock_map_refresh (this); + + return this; +@@ -109,13 +120,16 @@ intlclock_map_init (IntlClockMap *this) + priv->clock = NULL; + + priv->stock_map_pixbuf = NULL; +- priv->location_marker_pixbuf = NULL; ++ priv->location_marker_pixbuf[0] = NULL; ++ priv->location_marker_pixbuf[1] = NULL; ++ priv->location_marker_pixbuf[2] = NULL; + } + + static void + intlclock_map_finalize (GObject *g_obj) + { + IntlClockMapPrivate *priv = PRIVATE (g_obj); ++ int i; + + g_signal_handlers_disconnect_by_func + (priv->clock, G_CALLBACK (intlclock_map_tick), g_obj); +@@ -130,10 +144,12 @@ intlclock_map_finalize (GObject *g_obj) + priv->stock_map_pixbuf = NULL; + } + +- if (priv->location_marker_pixbuf) { +- gdk_pixbuf_unref (priv->location_marker_pixbuf); +- priv->location_marker_pixbuf = NULL; +- } ++ for (i = 0; i < 3; i++) { ++ if (priv->location_marker_pixbuf[i]) { ++ gdk_pixbuf_unref (priv->location_marker_pixbuf[i]); ++ priv->location_marker_pixbuf[i] = NULL; ++ } ++ } + + if (priv->location_map_pixbuf) { + gdk_pixbuf_unref (priv->location_map_pixbuf); +@@ -164,12 +180,13 @@ intlclock_map_refresh (IntlClockMap *this) + IntlClockMapPrivate *priv = PRIVATE (this); + GtkWidget *widget = GTK_WIDGET (this); + GtkWidget *parent = gtk_widget_get_parent (widget); ++ GtkRequisition req; + gint width; + gint height; + +- gtk_widget_size_request (widget, &(widget->requisition)); +- width = widget->requisition.width; +- height = widget->requisition.height; ++ gtk_widget_size_request (widget, &req); ++ width = req.width; ++ height = req.height; + + if (parent) { + if (widget->allocation.width != 1) { +@@ -276,10 +293,10 @@ intlclock_map_size_allocate (GtkWidget *this, GtkAllocation *allocation) + } + + static void +-intlclock_map_mark (IntlClockMap *this, gfloat latitude, gfloat longitude) ++intlclock_map_mark (IntlClockMap *this, gfloat latitude, gfloat longitude, gint mark) + { + IntlClockMapPrivate *priv = PRIVATE (this); +- GdkPixbuf *marker = priv->location_marker_pixbuf; ++ GdkPixbuf *marker = priv->location_marker_pixbuf[mark]; + GdkPixbuf *partial = NULL; + + int x, y; +@@ -395,14 +412,22 @@ intlclock_map_mark (IntlClockMap *this, gfloat latitude, gfloat longitude) + } + + static void +-intlclock_map_place_location (IntlClockMap *this, IntlClockLocation *loc) ++intlclock_map_place_location (IntlClockMap *this, IntlClockLocation *loc, gboolean hilight) + { + IntlClockMapPrivate *priv = PRIVATE (this); + gfloat latitude, longitude; ++ gint marker; + + intlclock_location_get_coords (loc, &latitude, &longitude); + +- intlclock_map_mark (this, latitude, longitude); ++ if (hilight) ++ marker = 1; ++ else if (intlclock_location_is_current (loc)) ++ marker = 2; ++ else ++ marker = 0; ++ ++ intlclock_map_mark (this, latitude, longitude, marker); + } + + static void +@@ -428,7 +453,7 @@ intlclock_map_place_locations (IntlClockMap *this) + while (locs) { + loc = INTLCLOCK_LOCATION (locs->data); + +- intlclock_map_place_location (this, loc); ++ intlclock_map_place_location (this, loc, FALSE); + + locs = locs->next; + } +@@ -633,16 +658,12 @@ intlclock_map_rotate (IntlClockMap *this) + static void + intlclock_map_display (IntlClockMap *this) + { +- IntlClockMapPrivate *priv = PRIVATE (this); +- + gtk_widget_queue_draw (GTK_WIDGET (this)); + } + + static void + intlclock_map_locations_changed (IntlClock *clock, IntlClockMap *this) + { +- IntlClockMapPrivate *priv = PRIVATE (this); +- + intlclock_map_place_locations (this); + + intlclock_map_render_shadow (this); +@@ -651,6 +672,49 @@ intlclock_map_locations_changed (IntlClock *clock, IntlClockMap *this) + intlclock_map_display (this); + } + ++typedef struct { ++ IntlClockMap *map; ++ IntlClockLocation *location; ++ int count; ++} BlinkData; ++ ++static gboolean ++highlight (gpointer user_data) ++{ ++ BlinkData *data = user_data; ++ ++ if (data->count == 6) { ++ g_free (data); ++ return FALSE; ++ } ++ ++ if (data->count % 2 == 0) ++ intlclock_map_place_location (data->map, data->location, TRUE); ++ else ++ intlclock_map_place_locations (data->map); ++ intlclock_map_render_shadow (data->map); ++ intlclock_map_rotate (data->map); ++ intlclock_map_display (data->map); ++ ++ data->count++; ++ ++ return TRUE; ++} ++ ++static void ++intlclock_map_blink_location (IntlClock *clock, IntlClockLocation *loc, IntlClockMap *this) ++{ ++ BlinkData *data; ++ ++ data = g_new0 (BlinkData, 1); ++ data->map = this; ++ data->location = loc; ++ ++ highlight (data); ++ ++ g_timeout_add (300, highlight, data); ++} ++ + static gboolean + intlclock_map_needs_refresh (IntlClockMap *this) + { +diff --git a/src/intlclock-marshallers.c b/src/intlclock-marshallers.c +deleted file mode 100644 +index 8f01ab8..0000000 +--- a/src/intlclock-marshallers.c ++++ /dev/null +@@ -1,51 +0,0 @@ +- +-#include +- +- +-#ifdef G_ENABLE_DEBUG +-#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) +-#define g_marshal_value_peek_char(v) g_value_get_char (v) +-#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) +-#define g_marshal_value_peek_int(v) g_value_get_int (v) +-#define g_marshal_value_peek_uint(v) g_value_get_uint (v) +-#define g_marshal_value_peek_long(v) g_value_get_long (v) +-#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) +-#define g_marshal_value_peek_int64(v) g_value_get_int64 (v) +-#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) +-#define g_marshal_value_peek_enum(v) g_value_get_enum (v) +-#define g_marshal_value_peek_flags(v) g_value_get_flags (v) +-#define g_marshal_value_peek_float(v) g_value_get_float (v) +-#define g_marshal_value_peek_double(v) g_value_get_double (v) +-#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) +-#define g_marshal_value_peek_param(v) g_value_get_param (v) +-#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) +-#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) +-#define g_marshal_value_peek_object(v) g_value_get_object (v) +-#else /* !G_ENABLE_DEBUG */ +-/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. +- * Do not access GValues directly in your code. Instead, use the +- * g_value_get_*() functions +- */ +-#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int +-#define g_marshal_value_peek_char(v) (v)->data[0].v_int +-#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint +-#define g_marshal_value_peek_int(v) (v)->data[0].v_int +-#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint +-#define g_marshal_value_peek_long(v) (v)->data[0].v_long +-#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong +-#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 +-#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 +-#define g_marshal_value_peek_enum(v) (v)->data[0].v_long +-#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong +-#define g_marshal_value_peek_float(v) (v)->data[0].v_float +-#define g_marshal_value_peek_double(v) (v)->data[0].v_double +-#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer +-#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer +-#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer +-#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer +-#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer +-#endif /* !G_ENABLE_DEBUG */ +- +- +-/* VOID:VOID (intlclock-marshallers.list:1) */ +- +diff --git a/src/intlclock-marshallers.h b/src/intlclock-marshallers.h +deleted file mode 100644 +index c022a3e..0000000 +--- a/src/intlclock-marshallers.h ++++ /dev/null +@@ -1,15 +0,0 @@ +- +-#ifndef ___intlclock_marshal_MARSHAL_H__ +-#define ___intlclock_marshal_MARSHAL_H__ +- +-#include +- +-G_BEGIN_DECLS +- +-/* VOID:VOID (intlclock-marshallers.list:1) */ +-#define _intlclock_marshal_VOID__VOID g_cclosure_marshal_VOID__VOID +- +-G_END_DECLS +- +-#endif /* ___intlclock_marshal_MARSHAL_H__ */ +- +diff --git a/src/intlclock-marshallers.list b/src/intlclock-marshallers.list +index 5b76282..e737cac 100644 +--- a/src/intlclock-marshallers.list ++++ b/src/intlclock-marshallers.list +@@ -1 +1,3 @@ + VOID:VOID ++VOID:OBJECT ++VOID:POINTER +diff --git a/src/intlclock-sunpos.c b/src/intlclock-sunpos.c +index 942617b..89d4cc1 100644 +--- a/src/intlclock-sunpos.c ++++ b/src/intlclock-sunpos.c +@@ -1,348 +1,196 @@ + /* +- * sunpos.c +- * kirk johnson +- * july 1993 ++ * Copyright (C) 2007 Red Hat, Inc. + * +- * includes revisions from Frank T. Solensky, february 1999 ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. + * +- * code for calculating the position on the earth's surface for which +- * the sun is directly overhead (adapted from _practical astronomy +- * with your calculator, third edition_, peter duffett-smith, +- * cambridge university press, 1988.) ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. + * +- * Copyright (C) 1989, 1990, 1993-1995, 1999 Kirk Lauritz Johnson ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA ++ * 02111-1307, USA. + * +- * Parts of the source code (as marked) are: +- * Copyright (C) 1989, 1990, 1991 by Jim Frost +- * Copyright (C) 1992 by Jamie Zawinski +- * +- * Permission to use, copy, modify and freely distribute xearth for +- * non-commercial and not-for-profit purposes is hereby granted +- * without fee, provided that both the above copyright notice and this +- * permission notice appear in all copies and in supporting +- * documentation. +- * +- * Unisys Corporation holds worldwide patent rights on the Lempel Zev +- * Welch (LZW) compression technique employed in the CompuServe GIF +- * image file format as well as in other formats. Unisys has made it +- * clear, however, that it does not require licensing or fees to be +- * paid for freely distributed, non-commercial applications (such as +- * xearth) that employ LZW/GIF technology. Those wishing further +- * information about licensing the LZW patent should contact Unisys +- * directly at (lzw_info@unisys.com) or by writing to +- * +- * Unisys Corporation +- * Welch Licensing Department +- * M/S-C1SW19 +- * P.O. Box 500 +- * Blue Bell, PA 19424 +- * +- * The author makes no representations about the suitability of this +- * software for any purpose. It is provided "as is" without express or +- * implied warranty. +- * +- * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +- * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT +- * OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +- * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +- * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ * Authors: ++ * Jonathan Blandford ++ * Matthias Clasen + */ +- +-#include +-#include ++ + #include ++#include ++#include + +-#include "intlclock-sunpos.h" +- +-#define TWOPI (2*M_PI) +-#define DegsToRads(x) ((x)*(TWOPI/360)) +- +-/* +- * the epoch upon which these astronomical calculations are based is +- * 1990 january 0.0, 631065600 seconds since the beginning of the +- * "unix epoch" (00:00:00 GMT, Jan. 1, 1970) +- * +- * given a number of seconds since the start of the unix epoch, +- * DaysSinceEpoch() computes the number of days since the start of the +- * astronomical epoch (1990 january 0.0) +- */ +- +-#define EpochStart (631065600) +-#define DaysSinceEpoch(secs) (((secs)-EpochStart)*(1.0/(24*3600))) +- +-/* +- * assuming the apparent orbit of the sun about the earth is circular, +- * the rate at which the orbit progresses is given by RadsPerDay -- +- * TWOPI radians per orbit divided by 365.242191 days per year: +- */ +- +-#define RadsPerDay (TWOPI/365.242191) +- +-/* +- * details of sun's apparent orbit at epoch 1990.0 (after +- * duffett-smith, table 6, section 46) +- * +- * Epsilon_g (ecliptic longitude at epoch 1990.0) 279.403303 degrees +- * OmegaBar_g (ecliptic longitude of perigee) 282.768422 degrees +- * Eccentricity (eccentricity of orbit) 0.016713 ++/* Calculated with the methods and figures from "Practical Astronomy With Your ++ * Calculator, version 3" by Peter Duffet-Smith. + */ ++/* Table 6. Details of the Sun's apparent orbit at epoch 1990.0 */ + +-#define Epsilon_g (DegsToRads(279.403303)) +-#define OmegaBar_g (DegsToRads(282.768422)) +-#define Eccentricity (0.016713) ++#define EPOCH 2447891.5 /* days */ /* epoch 1990 */ ++#define UNIX_EPOCH 2440586.5 /* days */ /* epoch 1970 */ ++#define EPSILON_G 279.403303 /* degrees */ /* ecliptic longitude at epoch 1990.0 */ ++#define MU_G 282.768422 /* degrees */ /* ecliptic longitude at perigree */ ++#define ECCENTRICITY 0.016713 /* eccentricity of orbit */ ++#define R_0 149598500 /* km */ /* semi-major access */ ++#define THETA_0 0.533128 /* degrees */ /* angular diameter at r = r_0 */ ++#define MEAN_OBLIQUITY 23.440592 /* degrees */ /* mean obliquity of earths axis at epoch 1990.0 */ + +-/* +- * MeanObliquity gives the mean obliquity of the earth's axis at epoch +- * 1990.0 (computed as 23.440592 degrees according to the method given +- * in duffett-smith, section 27) +- */ +-#define MeanObliquity (23.440592*(TWOPI/360)) ++#define NORMALIZE(x) \ ++ while (x>360) x-=360; while (x<0) x+= 360; + +-/* +- * Lunar parameters, epoch January 0, 1990.0 +- */ +-#define MoonMeanLongitude DegsToRads(318.351648) +-#define MoonMeanLongitudePerigee DegsToRads( 36.340410) +-#define MoonMeanLongitudeNode DegsToRads(318.510107) +-#define MoonInclination DegsToRads( 5.145396) ++#define DEG_TO_RADS(x) \ ++ (x * G_PI/180.0) + +-#define SideralMonth (27.3217) ++#define RADS_TO_DEG(x) \ ++ (x * 180.0/G_PI) + +-/* +- * Force an angular value into the range [-PI, +PI] ++/* Calculate number of days since 4713BC. + */ +-#define Normalize(x) \ +- do { \ +- if ((x) < -M_PI) \ +- do (x) += TWOPI; while ((x) < -M_PI); \ +- else if ((x) > M_PI) \ +- do (x) -= TWOPI; while ((x) > M_PI); \ +- } while (0) +- +-static double solve_keplers_equation (double); +-static double mean_sun (double); +-static double sun_ecliptic_longitude (time_t); +-static void ecliptic_to_equatorial (double, double, double *, double *); +-static double julian_date (int, int, int); +-static double GST (time_t); +- +-/* +- * solve Kepler's equation via Newton's method +- * (after duffett-smith, section 47) +- */ +-static double solve_keplers_equation(M) +- double M; ++static gdouble ++unix_time_to_julian_date (gint unix_time) + { +- double E; +- double delta; +- +- E = M; +- while (1) +- { +- delta = E - Eccentricity*sin(E) - M; +- if (fabs(delta) <= 1e-10) break; +- E -= delta / (1 - Eccentricity*cos(E)); +- } +- +- return E; ++ return UNIX_EPOCH + (double) unix_time / (60 * 60 * 24); + } + ++/* Finds an iterative solution for [ E - e sin (E) = M ] for values of e less ++ than 0.1. Page 90 */ + +-/* +- * Calculate the position of the mean sun: where the sun would +- * be if the earth's orbit were circular instead of ellipictal. +- */ ++#define ERROR_ACCURACY 1e-6 /* radians */ + +-static double mean_sun (D) +- double D; /* days since ephemeris epoch */ ++static gdouble ++solve_keplers_equation (gdouble e, ++ gdouble M) + { +- double N, M; ++ gdouble d, E; + +- N = RadsPerDay * D; +- N = fmod(N, TWOPI); +- if (N < 0) N += TWOPI; ++ /* start with an initial estimate */ ++ E = M; ++ ++ d = E - e * sin (E) - M; ++ ++ while (ABS (d) > ERROR_ACCURACY) ++ { ++ E = E - (d / (1 - e * cos (E))); ++ d = E - e * sin (E) - M; ++ } + +- M = N + Epsilon_g - OmegaBar_g; +- if (M < 0) M += TWOPI; +- return M; ++ return E; + } + +-/* +- * compute ecliptic longitude of sun (in radians) +- * (after duffett-smith, section 47) +- */ +-static double sun_ecliptic_longitude(ssue) +- time_t ssue; /* seconds since unix epoch */ ++ /* convert the ecliptic longitude to right ascension and declination. Section 27. */ ++static void ++ecliptic_to_equatorial (gdouble lambda, ++ gdouble beta, ++ gdouble *ra, ++ gdouble *dec) + { +- double D; +- double M_sun, E; +- double v; ++ gdouble cos_mo; ++ gdouble sin_mo; + +- D = DaysSinceEpoch(ssue); +- M_sun = mean_sun(D); ++ g_assert (ra != NULL); ++ g_assert (dec != NULL); + +- E = solve_keplers_equation(M_sun); +- v = 2 * atan(sqrt((1+Eccentricity)/(1-Eccentricity)) * tan(E/2)); ++ sin_mo = sin (DEG_TO_RADS (MEAN_OBLIQUITY)); ++ cos_mo = cos (DEG_TO_RADS (MEAN_OBLIQUITY)); + +- return (v + OmegaBar_g); ++ *ra = atan2 (sin (lambda) * cos_mo - tan (beta) * sin_mo, cos (lambda)); ++ *dec = asin (sin (beta) * cos_mo + cos (beta) * sin_mo * sin (lambda)); + } + +- +-/* +- * convert from ecliptic to equatorial coordinates +- * (after duffett-smith, section 27) +- */ +-static void ecliptic_to_equatorial(lambda, beta, alpha, delta) +- double lambda; /* ecliptic longitude */ +- double beta; /* ecliptic latitude */ +- double *alpha; /* (return) right ascension */ +- double *delta; /* (return) declination */ ++/* calculate GST. Section 12 */ ++static gdouble ++greenwich_sidereal_time (gdouble unix_time) + { +- double sin_e, cos_e; ++ gdouble u, JD, T, T0, UT; + +- sin_e = sin(MeanObliquity); +- cos_e = cos(MeanObliquity); ++ u = fmod (unix_time, 24 * 60 * 60); ++ JD = unix_time_to_julian_date (unix_time - u); ++ T = (JD - 2451545) / 36525; ++ T0 = 6.697374558 + (2400.051336 * T) + (0.000025862 * T * T); ++ T0 = fmod (T0, 24); ++ UT = u / (60 * 60); ++ T0 = T0 + UT * 1.002737909; ++ T0 = fmod (T0, 24); + +- *alpha = atan2(sin(lambda)*cos_e - tan(beta)*sin_e, cos(lambda)); +- *delta = asin(sin(beta)*cos_e + cos(beta)*sin_e*sin(lambda)); ++ return T0; + } + +- +-/* +- * computing julian dates (assuming gregorian calendar, thus this is +- * only valid for dates of 1582 oct 15 or later) +- * (after duffett-smith, section 4) +- */ +-static double julian_date(y, m, d) +- int y; /* year (e.g. 19xx) */ +- int m; /* month (jan=1, feb=2, ...) */ +- int d; /* day of month */ ++/* Calculate the position of the sun at a given time. pages 89-91 */ ++void ++sun_position (gint unix_time, gdouble *lat, gdouble *lon) + { +- int A, B, C, D; +- double JD; ++ gdouble jd, D, N, M, E, x, v, lambda; ++ gdouble ra, dec; ++ jd = unix_time_to_julian_date (unix_time); + +- /* lazy test to ensure gregorian calendar */ +- assert(y >= 1583); ++ /* Calculate number of days since the epoch */ ++ D = jd - EPOCH; + +- if ((m == 1) || (m == 2)) +- { +- y -= 1; +- m += 12; +- } ++ N = D*360/365.242191; + +- A = y / 100; +- B = 2 - A + (A / 4); +- C = 365.25 * y; +- D = 30.6001 * (m + 1); ++ /* normalize to 0 - 360 degrees */ ++ NORMALIZE (N); + +- JD = B + C + D + d + 1720994.5; ++ /* Step 4: */ ++ M = N + EPSILON_G - MU_G; ++ NORMALIZE (M); + +- return JD; +-} +- +- +-/* +- * compute greenwich mean sidereal time (GST) corresponding to a given +- * number of seconds since the unix epoch +- * (after duffett-smith, section 12) +- */ +-static double GST(ssue) +- time_t ssue; /* seconds since unix epoch */ +-{ +- double JD; +- double T, T0; +- double UT; +- struct tm *tm; ++ /* Step 5: convert to radians */ ++ M = DEG_TO_RADS (M); + +- tm = gmtime(&ssue); ++ /* Step 6: */ ++ E = solve_keplers_equation (ECCENTRICITY, M); + +- JD = julian_date(tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday); +- T = (JD - 2451545) / 36525; ++ /* Step 7: */ ++ x = sqrt ((1 + ECCENTRICITY)/(1 - ECCENTRICITY)) * tan (E/2); + +- T0 = ((T + 2.5862e-5) * T + 2400.051336) * T + 6.697374558; ++ /* Step 8, 9 */ ++ v = 2 * RADS_TO_DEG (atan (x)); ++ NORMALIZE (v); + +- T0 = fmod(T0, 24.0); +- if (T0 < 0) T0 += 24; ++ /* Step 10 */ ++ lambda = v + MU_G; ++ NORMALIZE (lambda); + +- UT = tm->tm_hour + (tm->tm_min + tm->tm_sec / 60.0) / 60.0; ++ /* convert the ecliptic longitude to right ascension and declination */ ++ ecliptic_to_equatorial (DEG_TO_RADS (lambda), 0.0, &ra, &dec); + +- T0 += UT * 1.002737909; +- T0 = fmod(T0, 24.0); +- if (T0 < 0) T0 += 24; ++ ra = ra - (G_PI/12) * greenwich_sidereal_time (unix_time); ++ ra = RADS_TO_DEG (ra); ++ dec = RADS_TO_DEG (dec); ++ NORMALIZE (ra); ++ NORMALIZE (dec); + +- return T0; ++ *lat = dec; ++ *lon = ra; + } + + +-/* +- * given a particular time (expressed in seconds since the unix +- * epoch), compute position on the earth (lat, lon) such that sun is +- * directly overhead. +- */ +-void sun_position(ssue, lat, lon) +- time_t ssue; /* seconds since unix epoch */ +- double *lat; /* (return) latitude */ +- double *lon; /* (return) longitude */ ++#if 0 ++int ++main (int argc, char *argv[]) + { +- double lambda; +- double alpha, delta; +- double tmp; ++ gint i; ++ gint now; ++ GTimeVal timeval; ++ gdouble lat, lon; + +- lambda = sun_ecliptic_longitude(ssue); +- ecliptic_to_equatorial(lambda, 0.0, &alpha, &delta); ++ gtk_init (&argc, &argv); + +- tmp = alpha - (TWOPI/24)*GST(ssue); +- Normalize(tmp); +- *lon = tmp * (360/TWOPI); +- *lat = delta * (360/TWOPI); +-} ++ g_get_current_time (&timeval); ++ now = timeval.tv_sec; + ++ for (i = 0; i < now; i += 15 * 60) ++ { ++ sun_position (i, &lat, &lon); ++ g_print ("%d: %f %f\n", lat, lon); ++ } + +-/* +- * given a particular time (expressed in seconds since the unix +- * epoch), compute position on the earth (lat, lon) such that the +- * moon is directly overhead. +- * +- * Based on duffett-smith **2nd ed** section 61; combines some steps +- * into single expressions to reduce the number of extra variables. +- */ +-void moon_position(ssue, lat, lon) +- time_t ssue; /* seconds since unix epoch */ +- double *lat; /* (return) latitude */ +- double *lon; /* (return) longitude */ +-{ +- double lambda, beta; +- double D, L, Ms, Mm, N, Ev, Ae, Ec, alpha, delta; +- +- D = DaysSinceEpoch(ssue); +- lambda = sun_ecliptic_longitude(ssue); +- Ms = mean_sun(D); +- +- L = fmod(D/SideralMonth, 1.0)*TWOPI + MoonMeanLongitude; +- Normalize(L); +- Mm = L - DegsToRads(0.1114041*D) - MoonMeanLongitudePerigee; +- Normalize(Mm); +- N = MoonMeanLongitudeNode - DegsToRads(0.0529539*D); +- Normalize(N); +- Ev = DegsToRads(1.2739) * sin(2.0*(L-lambda)-Mm); +- Ae = DegsToRads(0.1858) * sin(Ms); +- Mm += Ev - Ae - DegsToRads(0.37)*sin(Ms); +- Ec = DegsToRads(6.2886) * sin(Mm); +- L += Ev + Ec - Ae + DegsToRads(0.214) * sin(2.0*Mm); +- L += DegsToRads(0.6583) * sin(2.0*(L-lambda)); +- N -= DegsToRads(0.16) * sin(Ms); +- +- L -= N; +- lambda =(fabs(cos(L)) < 1e-12) ? +- (N + sin(L) * cos(MoonInclination) * M_PI/2) : +- (N + atan2(sin(L) * cos(MoonInclination), cos(L))); +- Normalize(lambda); +- beta = asin(sin(L) * sin(MoonInclination)); +- ecliptic_to_equatorial(lambda, beta, &alpha, &delta); +- alpha -= (TWOPI/24)*GST(ssue); +- Normalize(alpha); +- *lon = alpha * (360/TWOPI); +- *lat = delta * (360/TWOPI); ++ return 0; + } ++ ++#endif +diff --git a/src/intlclock-ui.c b/src/intlclock-ui.c +index f51e4de..f0d70bd 100644 +--- a/src/intlclock-ui.c ++++ b/src/intlclock-ui.c +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -26,18 +27,18 @@ + #include "intlclock-map.h" + #include "intlclock-zoneinfo.h" + #include "intlclock-zonetable.h" ++#include "set-timezone.h" ++#include "gweather-xml.h" + + G_DEFINE_TYPE (IntlClockUI, intlclock_ui, G_TYPE_OBJECT) + + /* GConf keys for compatibility with the older GNOME clock */ +-#define N_GCONF_PREFS 7 ++#define N_GCONF_PREFS 5 + static const char *KEY_CITIES = "cities"; + static const char *KEY_FORMAT = "format"; + static const char *KEY_SHOW_SECONDS = "show_seconds"; + static const char *KEY_SHOW_DATE = "show_date"; + static const char *KEY_SHOW_WEEK = "show_week_numbers"; +-static const char *KEY_SHOW_LOCATIONS = "show_locations"; +-static const char *KEY_SHOW_MAP = "show_map"; + + /* Needs to match the indices in the combo */ + typedef enum { +@@ -78,12 +79,14 @@ typedef struct { + GtkWidget *panel_box; + GtkWidget *panel_button; + GtkWidget *panel_label; ++ GtkWidget *panel_weather_icon; + + GtkTooltips *panel_tips; + + GtkWidget *panel_button_popup; + + GtkWidget *clock_vbox; ++ GtkSizeGroup *clock_group; + + GtkWidget *main_section; + GtkWidget *clock_calendar; +@@ -97,17 +100,34 @@ typedef struct { + + GtkListStore *cities_store; + +- gboolean show_locations; +- gboolean show_map; +- + GtkWidget *prefs_window; + GtkTreeView *prefs_locations; + ++ GtkWidget *prefs_location_add_button; ++ GtkWidget *prefs_location_edit_button; ++ GtkWidget *prefs_location_remove_button; ++ ++ GtkWidget *location_tree; ++ GtkWidget *find_next_location_button; ++ GtkWidget *find_location_entry; ++ GtkWidget *find_location_ok_button; ++ ++ GtkWidget *set_time_window; ++ GtkWidget *hours_spin; ++ GtkWidget *minutes_spin; ++ GtkWidget *seconds_spin; ++ GtkWidget *calendar; ++ GtkWidget *current_time_label; ++ GtkWidget *set_time_button; ++ GtkWidget *time_settings_button; ++ + gboolean format_12hr; + gboolean format_show_seconds; + gboolean format_show_date; + gboolean format_show_week; + ++ gulong zone_combo_changed; ++ + guint listeners [N_GCONF_PREFS]; + } IntlClockUIPrivate; + +@@ -118,51 +138,51 @@ static gboolean update_panel_label (gpointer this); + static void setup_gconf (IntlClockUI *this); + static void load_gconf_settings (IntlClockUI *this); + +-static void bonobo_run_time_configuration (BonoboUIComponent *uic, +- IntlClockUI *this, +- const gchar *verbname); + static void bonobo_display_properties_dialog (BonoboUIComponent *uic, + IntlClockUI *this, + const gchar *verbname); +-static void display_help_dialog (BonoboUIComponent *uic, +- IntlClockUI *this, +- const gchar *verbname); +-static void display_about_dialog (BonoboUIComponent *uic, +- IntlClockUI *this, +- const gchar *verbname); +- +-static void config_date (BonoboUIComponent *uic, +- IntlClockUI *this, +- const gchar *verbname); + + static void intlclock_reposition_events_window (IntlClockUI *this); +- + static void position_popup_window (IntlClockUI *this, + GtkWindow *window, + GtkWidget *origin); + +-static void display_prefs_window_cb (GtkButton *button, gpointer this); +-static void run_time_configuration (IntlClockUI *this); +-static void run_time_configuration_cb (GtkButton *button, gpointer this); ++static void display_prefs_window (IntlClockUI *this, gboolean locations); + static void run_prefs_locations_add (GtkButton *button, gpointer this); + static void run_prefs_locations_edit (GtkButton *button, gpointer this); + static void run_prefs_locations_remove (GtkButton *button, gpointer this); + static void run_prefs_edit_save (GtkButton *button, gpointer this); +- ++static void run_find_location (GtkButton *button, gpointer this); ++static void run_find_location_save (GtkButton *button, gpointer this); ++static void intlclock_edit_hide (GtkWidget *widget, IntlClockUI *this); ++static void intlclock_find_hide (GtkWidget *widget, IntlClockUI *this); + static void intlclock_ui_save_cities_store (IntlClockUI *this); + + static void intlclock_ui_locations_changed (IntlClock *clock, IntlClockUI *this); + + static void create_cities_section (IntlClockUI *this); + static void create_events_window (IntlClockUI *this); +-static void create_main_section (IntlClockUI *this); + static void create_map_section (IntlClockUI *this); + + static void zone_combo_changed (GtkComboBox *widget, gpointer this); ++static void update_coords (IntlClockUI *this, gboolean valid, gfloat lat, gfloat lon); ++static void fill_location_tree (IntlClockUI *this); ++static void copy_time (BonoboUIComponent *uic, ++ IntlClockUI *this, ++ const gchar *verbname); ++static void copy_date (BonoboUIComponent *uic, ++ IntlClockUI *this, ++ const gchar *verbname); ++static void config_date (BonoboUIComponent *uic, ++ IntlClockUI *this, ++ const gchar *verbname); ++ + + static const BonoboUIVerb intlclock_menu_verbs [] = { + BONOBO_UI_UNSAFE_VERB ("IntlClockPreferences", bonobo_display_properties_dialog), +- BONOBO_UI_UNSAFE_VERB ("IntlClockConfig", bonobo_run_time_configuration), ++ BONOBO_UI_UNSAFE_VERB ("ClockCopyTime", copy_time), ++ BONOBO_UI_UNSAFE_VERB ("ClockCopyDate", copy_date), ++ BONOBO_UI_UNSAFE_VERB ("ClockConfig", config_date), + BONOBO_UI_VERB_END + }; + +@@ -190,11 +210,6 @@ intlclock_ui_is_12hr (IntlClockUI *this) + static gboolean + panel_button_press_cb (GtkWidget *button, GdkEventButton *event, gpointer data) + { +- IntlClockUI *this = INTLCLOCK_UI (data); +- IntlClockUIPrivate *priv = PRIVATE (this); +- +- GtkToggleButton *toggle = GTK_TOGGLE_BUTTON (button); +- + if (event->button != 1) + g_signal_stop_emission_by_name (button, "button_press_event"); + +@@ -206,7 +221,9 @@ intlclock_ui_reset_timeout (IntlClockUI *this) + { + IntlClockUIPrivate *priv = PRIVATE (this); + +- if (GTK_WIDGET_VISIBLE (priv->events_window) || priv->format_show_seconds) { ++ if (GTK_WIDGET_VISIBLE (priv->events_window) || ++ (priv->set_time_window && GTK_WIDGET_VISIBLE (priv->set_time_window)) || ++ priv->format_show_seconds) { + intlclock_set_tick_timeout (INTLCLOCK (priv->clock), 1); + } else { + intlclock_set_tick_timeout (INTLCLOCK (priv->clock), 60); +@@ -244,20 +261,6 @@ panel_button_clicked_cb (GtkButton *button, gpointer data) + intlclock_ui_reset_timeout (this); + } + +-static gboolean +-panel_events_button_press_cb (GtkWidget *button, GdkEventButton *event, gpointer data) +-{ +- IntlClockUI *this = INTLCLOCK_UI (data); +- IntlClockUIPrivate *priv = PRIVATE (this); +- +- GtkToggleButton *toggle = GTK_TOGGLE_BUTTON (button); +- +- if (event->button != 1) +- g_signal_stop_emission_by_name (button, "button_press_event"); +- +- return FALSE; +-} +- + static void + position_popup_window (IntlClockUI *this, + GtkWindow *window, +@@ -375,147 +378,6 @@ intlclock_events_window_size_allocate_cb (GtkWidget *widget, GtkAllocation *allo + intlclock_reposition_events_window (this); + } + +-static gboolean +-intlclock_window_expose_cb (GtkWidget *widget, GdkEventExpose *event, gpointer user_data) +-{ +- IntlClockUIPrivate *priv = PRIVATE (user_data); +- +- cairo_t *cr; +- +- cr = gdk_cairo_create (widget->window); +- +- cairo_rectangle ( +- cr, +- event->area.x, event->area.y, +- event->area.width, event->area.height); +- +- cairo_clip (cr); +- +-/* draw window background */ +- +- cairo_rectangle ( +- cr, +- widget->allocation.x + 0.5, widget->allocation.y + 0.5, +- widget->allocation.width - 1, widget->allocation.height - 1); +- +- cairo_set_source_rgb ( +- cr, +- widget->style->bg [GTK_STATE_ACTIVE].red / 65535.0, +- widget->style->bg [GTK_STATE_ACTIVE].green / 65535.0, +- widget->style->bg [GTK_STATE_ACTIVE].blue / 65535.0); +- +- cairo_fill_preserve (cr); +- +-/* draw window outline */ +- +- cairo_set_source_rgb ( +- cr, +- widget->style->dark [GTK_STATE_ACTIVE].red / 65535.0, +- widget->style->dark [GTK_STATE_ACTIVE].green / 65535.0, +- widget->style->dark [GTK_STATE_ACTIVE].blue / 65535.0); +- +- cairo_set_line_width (cr, 1.0); +- cairo_stroke (cr); +- +-/* draw main pane background */ +- +- cairo_rectangle ( +- cr, +- priv->main_section->allocation.x + 0.5, priv->main_section->allocation +-.y + 0.5, +- priv->main_section->allocation.width - 1, priv->main_section->allocation.height - 1); +- +- cairo_set_source_rgb ( +- cr, +- widget->style->bg [GTK_STATE_NORMAL].red / 65535.0, +- widget->style->bg [GTK_STATE_NORMAL].green / 65535.0, +- widget->style->bg [GTK_STATE_NORMAL].blue / 65535.0); +- +- cairo_fill (cr); +- +-/* draw map pane background */ +- +- if (priv->show_map) { +- cairo_rectangle ( +- cr, +- priv->map_section->allocation.x + 0.5, +- priv->map_section->allocation.y + 0.5, +- priv->map_section->allocation.width - 1, +- priv->map_section->allocation.height - 1); +- +- cairo_set_source_rgb ( +- cr, +- widget->style->bg [GTK_STATE_NORMAL].red / 65535.0, +- widget->style->bg [GTK_STATE_NORMAL].green / 65535.0, +- widget->style->bg [GTK_STATE_NORMAL].blue / 65535.0); +- +- cairo_fill (cr); +- } +- +-/* draw internal window outline */ +- +- cairo_rectangle ( +- cr, +- priv->clock_vbox->allocation.x + 0.5, priv->clock_vbox->allocation.y + 0.5, +- priv->clock_vbox->allocation.width - 1, priv->clock_vbox->allocation.height - 1); +- +- cairo_set_source_rgb ( +- cr, +- widget->style->dark [GTK_STATE_ACTIVE].red / 65535.0, +- widget->style->dark [GTK_STATE_ACTIVE].green / 65535.0, +- widget->style->dark [GTK_STATE_ACTIVE].blue / 65535.0); +- +- cairo_stroke (cr); +- +-/* draw map/cities pane separator */ +- +- if (priv->show_map) { +- cairo_move_to ( +- cr, +- priv->map_section->allocation.x + 0.5, +- priv->map_section->allocation.y + priv->map_section->allocation.height - 0.5); +- +- cairo_line_to ( +- cr, +- priv->map_section->allocation.x + priv->map_section->allocation.width - 0.5, +- priv->map_section->allocation.y + priv->map_section->allocation.height - 0.5); +- +- cairo_set_source_rgb ( +- cr, +- widget->style->dark [GTK_STATE_ACTIVE].red / 65535.0, +- widget->style->dark [GTK_STATE_ACTIVE].green / 65535.0, +- widget->style->dark [GTK_STATE_ACTIVE].blue / 65535.0); +- +- cairo_stroke (cr); +- } +- +-/* draw cities/main pane separator */ +- +- if (priv->show_locations) { +- cairo_move_to ( +- cr, +- priv->cities_section->allocation.x + 0.5, +- priv->cities_section->allocation.y + priv->cities_section->allocation.height - 0.5); +- +- cairo_line_to ( +- cr, +- priv->cities_section->allocation.x + priv->cities_section->allocation.width - 0.5, +- priv->cities_section->allocation.y + priv->cities_section->allocation.height - 0.5); +- +- cairo_set_source_rgb ( +- cr, +- widget->style->dark [GTK_STATE_ACTIVE].red / 65535.0, +- widget->style->dark [GTK_STATE_ACTIVE].green / 65535.0, +- widget->style->dark [GTK_STATE_ACTIVE].blue / 65535.0); +- +- cairo_stroke (cr); +- } +- +- cairo_destroy (cr); +- +- return FALSE; +-} +- + static void + intlclock_ui_tick (IntlClock *clock, IntlClockUI *this) + { +@@ -529,10 +391,7 @@ intlclock_ui_tick (IntlClock *clock, IntlClockUI *this) + static void + intlclock_ui_locations_changed (IntlClock *clock, IntlClockUI *this) + { +- IntlClockUIPrivate *priv = PRIVATE (this); +- +- create_cities_section (this); +- intlclock_map_refresh (INTLCLOCK_MAP (priv->map_widget)); ++ create_cities_section (this); + } + + static gboolean +@@ -550,12 +409,20 @@ update_panel_label (gpointer this) + time (&now_t); + localtime_r (&now_t, &now); + ++ if (priv->current_time_label && ++ GTK_WIDGET_VISIBLE (priv->current_time_label)) { ++ date = intlclock_format_time (priv->clock, &now, ++ FALSE, FALSE, TRUE, ++ FALSE, FALSE, NULL, TRUE); ++ gtk_label_set_markup (GTK_LABEL (priv->current_time_label), date); ++ g_free (date); ++ } ++ + date = intlclock_format_time (priv->clock, &now, + priv->format_show_date, + priv->format_12hr, + priv->format_show_seconds, + FALSE, priv->format_show_date, NULL, TRUE); +- + gtk_label_set_markup (GTK_LABEL (priv->panel_label), date); + g_free (date); + +@@ -583,6 +450,7 @@ create_panel_buttons (IntlClockUI *this) + { + IntlClockUIPrivate *priv = PRIVATE (this); + PanelAppletOrient orient; ++ GtkWidget *box; + + orient = panel_applet_get_orient (priv->panel_applet); + +@@ -590,13 +458,18 @@ create_panel_buttons (IntlClockUI *this) + case PANEL_APPLET_ORIENT_UP: + case PANEL_APPLET_ORIENT_DOWN: + priv->panel_box = gtk_hbox_new (FALSE, 0); ++ box = gtk_hbox_new (FALSE, 6); + break; + case PANEL_APPLET_ORIENT_RIGHT: + case PANEL_APPLET_ORIENT_LEFT: + priv->panel_box = gtk_vbox_new (FALSE, 0); ++ box = gtk_vbox_new (FALSE, 6); + break; ++ default: ++ g_assert_not_reached (); + } + priv->panel_label = gtk_label_new (NULL); ++ priv->panel_weather_icon = gtk_image_new (); + priv->panel_button = gtk_toggle_button_new (); + priv->panel_tips = gtk_tooltips_new (); + +@@ -626,8 +499,9 @@ create_panel_buttons (IntlClockUI *this) + gtk_button_set_relief (GTK_BUTTON (priv->panel_button), + GTK_RELIEF_NONE); + +- gtk_container_add (GTK_CONTAINER (priv->panel_button), +- priv->panel_label); ++ gtk_container_add (GTK_CONTAINER (priv->panel_button), box); ++ gtk_container_add (GTK_CONTAINER (box), priv->panel_weather_icon); ++ gtk_container_add (GTK_CONTAINER (box), priv->panel_label); + + g_signal_connect ( + G_OBJECT (priv->panel_button), "clicked", +@@ -655,12 +529,13 @@ create_panel_buttons (IntlClockUI *this) + ); + + gtk_widget_show (priv->panel_label); ++ gtk_widget_show (priv->panel_weather_icon); ++ gtk_widget_show (box); + + gtk_box_pack_start (GTK_BOX (priv->panel_box), priv->panel_button, + FALSE, FALSE, 0); + +- gtk_container_add (GTK_CONTAINER (priv->panel_applet), +- priv->panel_box); ++ gtk_container_add (GTK_CONTAINER (priv->panel_applet), priv->panel_box); + + gtk_widget_show_all (priv->panel_box); + } +@@ -671,10 +546,16 @@ intlclock_ui_change_orient (PanelApplet *applet, + IntlClockUI *this) + { + IntlClockUIPrivate *priv = PRIVATE (this); +- +- if (GTK_IS_WIDGET (priv->panel_box)) +- gtk_widget_destroy (priv->panel_box); ++ GdkPixbuf *pixbuf = NULL; ++ ++ if (GTK_IS_WIDGET (priv->panel_box)) { ++ pixbuf = g_object_ref (gtk_image_get_pixbuf (GTK_IMAGE (priv->panel_weather_icon))); ++ gtk_widget_destroy (priv->panel_box); ++ } + create_panel_buttons (this); ++ intlclock_ui_update_weather_icon (this, pixbuf); ++ if (pixbuf) ++ g_object_unref (pixbuf); + } + + static void +@@ -706,6 +587,16 @@ create_panel_button_popup (IntlClockUI *this) + "hidden", "1", + NULL); + } ++ ++ if (!can_set_system_time ()) { ++ popup_component = panel_applet_get_popup_component ++ (PANEL_APPLET (priv->panel_applet)); ++ ++ bonobo_ui_component_set_prop (popup_component, ++ "/commands/IntlClockConfig", ++ "sensitive", "0", ++ NULL); ++ } + } + + static void +@@ -714,7 +605,7 @@ create_events_window (IntlClockUI *this) + IntlClockUIPrivate *priv = PRIVATE (this); + + priv->events_window = +- GTK_WIDGET (intlclock_events_popup_new (priv->clock, priv->panel_applet)); ++ GTK_WIDGET (intlclock_events_popup_new (priv->clock, priv->panel_applet, this)); + + g_signal_connect (G_OBJECT (priv->events_window), "size-allocate", + G_CALLBACK (intlclock_events_window_size_allocate_cb), this); +@@ -723,41 +614,32 @@ create_events_window (IntlClockUI *this) + } + + static void +-create_clock_window (IntlClockUI *this) ++add_to_group (GtkWidget *child, gpointer data) + { +- IntlClockUIPrivate *priv = PRIVATE (this); +- GtkSettings *settings; +- GtkStyle *style; ++ GtkSizeGroup *group = data; + +- priv->clock_vbox = intlclock_events_popup_get_clock_container +- (INTLCLOCK_EVENTS_POPUP (priv->events_window)); +- gtk_widget_show (priv->clock_vbox); ++ gtk_size_group_add_widget (group, child); + } + + static void +-create_main_section (IntlClockUI *this) ++create_clock_window (IntlClockUI *this) + { + IntlClockUIPrivate *priv = PRIVATE (this); +- IntlClockLocation *loc; +- GtkWidget *header, *subheader; +- GtkWidget *prefs_button; +- GtkWidget *prefs_button_box; +- +- if (!priv->main_section) { +- priv->main_section = gtk_vbox_new (FALSE, 6); +- gtk_container_set_border_width +- (GTK_CONTAINER (priv->main_section), +- MAIN_SECTION_PADDING); +- gtk_box_pack_end (GTK_BOX (priv->clock_vbox), +- priv->main_section, FALSE, FALSE, 0); ++ GtkWidget *clock_container; + +- } else { +- gtk_container_foreach (GTK_CONTAINER (priv->main_section), +- (GtkCallback)gtk_widget_destroy, +- NULL); +- } ++ clock_container = intlclock_events_popup_get_clock_container ++ (INTLCLOCK_EVENTS_POPUP (priv->events_window)); ++ gtk_widget_show (clock_container); ++ ++ priv->clock_vbox = gtk_vbox_new (FALSE, 6); ++ gtk_container_add (GTK_CONTAINER (clock_container), priv->clock_vbox); + +- gtk_widget_show_all (priv->main_section); ++ priv->clock_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); ++ gtk_size_group_set_ignore_hidden (priv->clock_group, FALSE); ++ ++ gtk_container_foreach (GTK_CONTAINER (clock_container), ++ (GtkCallback)add_to_group, ++ priv->clock_group); + } + + static gint +@@ -824,7 +706,6 @@ create_cities_section (IntlClockUI *this) + IntlClockUIPrivate *priv = PRIVATE (this); + GList *node; + IntlClockLocationTile *city; +- GtkWidget *header, *subheader, *image;; + GList *cities; + + if (priv->cities_section) { +@@ -832,12 +713,8 @@ create_cities_section (IntlClockUI *this) + priv->cities_section = NULL; + } + +- if (!priv->show_locations) { +- return; +- } +- + priv->cities_section = gtk_vbox_new (FALSE, 6); +- gtk_container_set_border_width (GTK_CONTAINER (priv->cities_section), 8); ++ gtk_container_set_border_width (GTK_CONTAINER (priv->cities_section), 0); + + cities = intlclock_get_locations (priv->clock); + if (g_list_length (cities) == 0) { +@@ -884,10 +761,6 @@ create_map_section (IntlClockUI *this) + priv->map_widget = NULL; + } + +- if (!priv->show_map) { +- return; +- } +- + map = intlclock_map_new (priv->clock); + + priv->map_section = gtk_alignment_new (0, 0, 1, 1); +@@ -895,11 +768,10 @@ create_map_section (IntlClockUI *this) + + gtk_container_add (GTK_CONTAINER (priv->map_section), priv->map_widget); + +- gtk_box_pack_start (GTK_BOX (priv->clock_vbox), +- priv->map_section, FALSE, FALSE, 0); +- + gtk_alignment_set_padding (GTK_ALIGNMENT (priv->map_section), + 1, 1, 1, 1); ++ gtk_box_pack_start (GTK_BOX (priv->clock_vbox), ++ priv->map_section, FALSE, FALSE, 0); + + gtk_widget_show (priv->map_widget); + gtk_widget_show (priv->map_section); +@@ -927,7 +799,6 @@ create_cities_store (IntlClockUI *this) + + while (list) { + IntlClockLocation *loc = INTLCLOCK_LOCATION (list->data); +- gfloat latitude, longitude; + + gtk_list_store_append (priv->cities_store, &iter); + gtk_list_store_set (priv->cities_store, &iter, +@@ -953,12 +824,16 @@ intlclock_prefs_hide (GtkWidget *widget, IntlClockUI *this) + IntlClockUIPrivate *priv = PRIVATE (this); + GtkWidget *tree; + ++ intlclock_edit_hide (widget, this); ++ + gtk_widget_hide (priv->prefs_window); + + tree = glade_xml_get_widget (priv->glade_xml, "cities_list"); + + gtk_tree_selection_unselect_all + (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree))); ++ ++ intlclock_ui_reset_timeout (this); + } + + static gboolean +@@ -974,8 +849,6 @@ intlclock_edit_clear (GtkWidget *widget, IntlClockUI *this) + { + IntlClockUIPrivate *priv = PRIVATE (this); + +- GtkWidget *edit_window = glade_xml_get_widget (priv->glade_xml, "edit-location-window"); +- + GtkWidget *zone_combo = glade_xml_get_widget (priv->glade_xml, "edit-location-timezone-combo"); + GtkWidget *name_entry = glade_xml_get_widget (priv->glade_xml, "edit-location-name-entry"); + +@@ -1003,6 +876,8 @@ intlclock_edit_hide (GtkWidget *widget, IntlClockUI *this) + + GtkWidget *edit_window = glade_xml_get_widget (priv->glade_xml, "edit-location-window"); + ++ intlclock_find_hide (widget, this); ++ + gtk_widget_hide (edit_window); + + intlclock_edit_clear (widget, this); +@@ -1016,6 +891,23 @@ intlclock_edit_hide_event (GtkWidget *widget, GdkEvent *event, IntlClockUI *this + return TRUE; + } + ++static void ++intlclock_find_hide (GtkWidget *widget, IntlClockUI *this) ++{ ++ IntlClockUIPrivate *priv = PRIVATE (this); ++ ++ GtkWidget *find_window = glade_xml_get_widget (priv->glade_xml, "find-location-window"); ++ ++ gtk_widget_hide (find_window); ++} ++ ++static gboolean ++intlclock_find_hide_event (GtkWidget *widget, GdkEvent *event, IntlClockUI *this) ++{ ++ intlclock_find_hide (widget, this); ++ ++ return TRUE; ++} + + static void + set_12hr_format_radio_cb (GtkWidget *widget, IntlClockUI *this) +@@ -1057,28 +949,6 @@ set_seconds_check_cb (GtkWidget *widget, IntlClockUI *this) + } + + static void +-set_locations_check_cb (GtkWidget *widget, IntlClockUI *this) +-{ +- IntlClockUIPrivate *priv = PRIVATE (this); +- +- panel_applet_gconf_set_bool (PANEL_APPLET (priv->panel_applet), +- KEY_SHOW_LOCATIONS, +- GTK_TOGGLE_BUTTON (widget)->active, +- NULL); +-} +- +-static void +-set_map_check_cb (GtkWidget *widget, IntlClockUI *this) +-{ +- IntlClockUIPrivate *priv = PRIVATE (this); +- +- panel_applet_gconf_set_bool (PANEL_APPLET (priv->panel_applet), +- KEY_SHOW_MAP, +- GTK_TOGGLE_BUTTON (widget)->active, +- NULL); +-} +- +-static void + fill_prefs_window (IntlClockUI *this) + { + IntlClockUIPrivate *priv = PRIVATE (this); +@@ -1087,6 +957,9 @@ fill_prefs_window (IntlClockUI *this) + GtkCellRenderer *renderer; + GtkTreeViewColumn *col; + ++ time_t now_t; ++ struct tm now; ++ + /* Set the 12 hour / 24 hour widget */ + widget = glade_xml_get_widget (priv->glade_xml, "12hr_radio"); + g_signal_connect (widget, "toggled", +@@ -1109,18 +982,6 @@ fill_prefs_window (IntlClockUI *this) + g_signal_connect (widget, "toggled", G_CALLBACK (set_seconds_check_cb), + this); + +- /* Set the "Show Locations" checkbox */ +- widget = glade_xml_get_widget (priv->glade_xml, "show_locations_check"); +- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), priv->show_locations); +- g_signal_connect (widget, "toggled", G_CALLBACK (set_locations_check_cb), +- this); +- +- /* Set the "Show Map" checkbox */ +- widget = glade_xml_get_widget (priv->glade_xml, "show_map_check"); +- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), priv->show_map); +- g_signal_connect (widget, "toggled", G_CALLBACK (set_map_check_cb), +- this); +- + /* Fill the Cities list */ + widget = glade_xml_get_widget (priv->glade_xml, "cities_list"); + +@@ -1136,6 +997,15 @@ fill_prefs_window (IntlClockUI *this) + + gtk_tree_view_set_model (GTK_TREE_VIEW (widget), + GTK_TREE_MODEL (priv->cities_store)); ++ ++ /* Fill the time settings */ ++ tzset (); ++ time (&now_t); ++ localtime_r (&now_t, &now); ++ ++ gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->seconds_spin), now.tm_sec); ++ gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->minutes_spin), now.tm_min); ++ gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->hours_spin), now.tm_hour); + } + + static gint +@@ -1150,19 +1020,334 @@ sort_zoneinfo_by_l10n_name (gconstpointer a, gconstpointer b) + return strcmp (name_a, name_b); + } + ++static void ++intlclock_prefs_locations_changed (GtkTreeSelection *selection, ++ IntlClockUI *this) ++{ ++ IntlClockUIPrivate *priv = PRIVATE (this); ++ gint n; ++ ++ n = gtk_tree_selection_count_selected_rows (selection); ++ gtk_widget_set_sensitive (priv->prefs_location_edit_button, n > 0); ++ gtk_widget_set_sensitive (priv->prefs_location_remove_button, n > 0); ++} ++ + static void +-display_prefs_window (IntlClockUI *this) ++location_tree_selection_changed (GtkTreeSelection *selection, ++ IntlClockUI *this) ++{ ++ IntlClockUIPrivate *priv = PRIVATE (this); ++ GtkTreeModel *model; ++ GtkTreeIter iter; ++ WeatherLocation *loc = NULL; ++ gboolean can_save = FALSE; ++ ++ if (gtk_tree_selection_get_selected (selection, &model, &iter)) { ++ gtk_tree_model_get (model, &iter, GWEATHER_XML_COL_POINTER, &loc, -1); ++ if (loc != NULL) ++ can_save = TRUE; ++ } ++ ++ gtk_widget_set_sensitive (priv->find_location_ok_button, can_save); ++} ++ ++static void ++wrap_cb (GtkSpinButton *spin, ++ IntlClockUI *this) ++{ ++ IntlClockUIPrivate *priv = PRIVATE (this); ++ gdouble value; ++ gdouble min, max; ++ GtkSpinType direction; ++ ++ value = gtk_spin_button_get_value (spin); ++ gtk_spin_button_get_range (spin, &min, &max); ++ ++ if (value == min) ++ direction = GTK_SPIN_STEP_FORWARD; ++ else ++ direction = GTK_SPIN_STEP_BACKWARD; ++ ++ if (spin == (GtkSpinButton *)priv->seconds_spin) ++ gtk_spin_button_spin (GTK_SPIN_BUTTON (priv->minutes_spin), ++ direction, 1.0); ++ else if (spin == (GtkSpinButton *)priv->minutes_spin) ++ gtk_spin_button_spin (GTK_SPIN_BUTTON (priv->hours_spin), ++ direction, 1.0); ++ else { ++ guint year, month, day; ++ GDate *date; ++ ++ gtk_calendar_get_date (GTK_CALENDAR (priv->calendar), ++ &year, &month, &day); ++ ++ date = g_date_new_dmy (day, month + 1, year); ++ ++ if (direction == GTK_SPIN_STEP_FORWARD) ++ g_date_add_days (date, 1); ++ else ++ g_date_subtract_days (date, 1); ++ ++ year = g_date_get_year (date); ++ month = g_date_get_month (date) - 1; ++ day = g_date_get_day (date); ++ ++ gtk_calendar_select_month (GTK_CALENDAR (priv->calendar), ++ month, year); ++ gtk_calendar_select_day (GTK_CALENDAR (priv->calendar), ++ day); ++ ++ g_date_free (date); ++ } ++} ++ ++static void ++update_set_time_button (IntlClockUI *this) ++{ ++ IntlClockUIPrivate *priv = PRIVATE (this); ++ gint can_set; ++ ++ can_set = can_set_system_time (); ++ ++ if (priv->time_settings_button) ++ gtk_widget_set_sensitive (priv->time_settings_button, can_set != 0); ++ if (priv->set_time_button) ++ gtk_button_set_label (GTK_BUTTON (priv->set_time_button), ++ can_set == 1 ? ++ _("Set System Time...") : ++ _("Set System Time")); ++} ++ ++static void ++set_time_callback (IntlClockUI *this, GError *error) ++{ ++ IntlClockUIPrivate *priv = PRIVATE (this); ++ GtkWidget *window; ++ GtkWidget *dialog; ++ ++ if (error) { ++ dialog = gtk_message_dialog_new (NULL, ++ 0, ++ GTK_MESSAGE_ERROR, ++ GTK_BUTTONS_OK, ++ _("Failed to set the system time")); ++ ++ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), error->message); ++ g_signal_connect (dialog, "response", ++ G_CALLBACK (gtk_widget_destroy), NULL); ++ gtk_window_present (GTK_WINDOW (dialog)); ++ ++ g_error_free (error); ++ } ++ else ++ update_set_time_button (this); ++ ++ window = glade_xml_get_widget (priv->glade_xml, "set-time-window"); ++ gtk_widget_hide (window); ++} ++ ++static void ++set_time (GtkWidget *widget, IntlClockUI *this) ++{ ++ IntlClockUIPrivate *priv = PRIVATE (this); ++ struct tm t; ++ gint64 time; ++ guint year, month, day; ++ ++ t.tm_sec = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (priv->seconds_spin)); ++ t.tm_min = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (priv->minutes_spin)); ++ t.tm_hour = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (priv->hours_spin)); ++ gtk_calendar_get_date (GTK_CALENDAR (priv->calendar), &year, &month, &day); ++ t.tm_year = year - 1900; ++ t.tm_mon = month; ++ t.tm_mday = day; ++ ++ time = mktime (&t); ++ ++ set_system_time_async (time, (GFunc)set_time_callback, this, NULL); ++} ++ ++static gboolean ++find_location (GtkTreeModel *model, ++ GtkTreeIter *iter, ++ const gchar *location, ++ gboolean go_parent) ++{ ++ GtkTreeIter iter_child; ++ GtkTreeIter iter_parent; ++ gchar *aux_loc; ++ gboolean valid; ++ int len; ++ ++ len = strlen (location); ++ ++ if (len <= 0) { ++ return FALSE; ++ } ++ ++ do { ++ gtk_tree_model_get (model, iter, GWEATHER_XML_COL_LOC, &aux_loc, -1); ++ ++ if (g_ascii_strncasecmp (aux_loc, location, len) == 0) { ++ g_free (aux_loc); ++ return TRUE; ++ } ++ ++ if (gtk_tree_model_iter_has_child (model, iter)) { ++ gtk_tree_model_iter_nth_child (model, &iter_child, iter, 0); ++ if (find_location (model, &iter_child, location, FALSE)) { ++ /* Manual copying of the iter */ ++ iter->stamp = iter_child.stamp; ++ iter->user_data = iter_child.user_data; ++ iter->user_data2 = iter_child.user_data2; ++ iter->user_data3 = iter_child.user_data3; ++ ++ g_free (aux_loc); ++ ++ return TRUE; ++ } ++ } ++ ++ g_free (aux_loc); ++ ++ valid = gtk_tree_model_iter_next (model, iter); ++ } while (valid); ++ ++ if (go_parent) { ++ iter_parent = *iter; ++ while (gtk_tree_model_iter_parent (model, iter, &iter_parent)) { ++ if (gtk_tree_model_iter_next (model, iter)) ++ return find_location (model, iter, location, TRUE); ++ iter_parent = *iter; ++ } ++ } ++ ++ return FALSE; ++} ++ ++static void ++find_next_location (GtkButton *button, IntlClockUI *this) ++{ ++ IntlClockUIPrivate *priv = PRIVATE (this); ++ GtkTreeView *tree; ++ GtkTreeModel *model; ++ GtkEntry *entry; ++ GtkTreeSelection *selection; ++ GtkTreeIter iter; ++ GtkTreeIter iter_parent; ++ GtkTreePath *path; ++ const gchar *location; ++ ++ tree = GTK_TREE_VIEW (priv->location_tree); ++ model = gtk_tree_view_get_model (tree); ++ entry = GTK_ENTRY (priv->find_location_entry); ++ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree)); ++ ++ if (gtk_tree_selection_count_selected_rows (selection) >= 1) { ++ gtk_tree_selection_get_selected (selection, &model, &iter); ++ /* Select next or select parent */ ++ if (!gtk_tree_model_iter_next (model, &iter)) { ++ iter_parent = iter; ++ if (!gtk_tree_model_iter_parent (model, &iter, &iter_parent) || ++ !gtk_tree_model_iter_next (model, &iter)) ++ gtk_tree_model_get_iter_first (model, &iter); ++ } ++ } ++ else { ++ gtk_tree_model_get_iter_first (model, &iter); ++ } ++ location = gtk_entry_get_text (entry); ++ ++ if (find_location (model, &iter, location, TRUE)) { ++ gtk_widget_set_sensitive (priv->find_next_location_button, TRUE); ++ path = gtk_tree_model_get_path (model, &iter); ++ gtk_tree_view_expand_to_path (tree, path); ++ gtk_tree_selection_select_path (selection, path); ++ gtk_tree_view_scroll_to_cell (tree, path, NULL, TRUE, 0.5, 0); ++ ++ gtk_tree_path_free (path); ++ } ++ else { ++ gtk_widget_set_sensitive (priv->find_next_location_button, FALSE); ++ } ++} ++ ++static void ++find_entry_changed (GtkEditable *entry, IntlClockUI *this) ++{ ++ IntlClockUIPrivate *priv = PRIVATE (this); ++ GtkTreeView *tree; ++ GtkTreeModel *model; ++ GtkTreeSelection *selection; ++ GtkTreeIter iter; ++ GtkTreePath *path; ++ const gchar *location; ++ ++ tree = GTK_TREE_VIEW (priv->location_tree); ++ model = gtk_tree_view_get_model (tree); ++ ++ selection = gtk_tree_view_get_selection (tree); ++ gtk_tree_model_get_iter_first (model, &iter); ++ ++ location = gtk_entry_get_text (GTK_ENTRY (entry)); ++ if (find_location (model, &iter, location, TRUE)) { ++ gtk_widget_set_sensitive (priv->find_next_location_button, TRUE); ++ path = gtk_tree_model_get_path (model, &iter); ++ gtk_tree_view_expand_to_path (tree, path); ++ gtk_tree_selection_select_iter (selection, &iter); ++ gtk_tree_view_scroll_to_cell (tree, path, NULL, TRUE, 0.5, 0); ++ gtk_tree_path_free (path); ++ } ++ else { ++ gtk_widget_set_sensitive (priv->find_next_location_button, FALSE); ++ } ++} ++ ++static void ++cancel_time_settings (GtkWidget *button, IntlClockUI *this) ++{ ++ IntlClockUIPrivate *priv = PRIVATE (this); ++ ++ gtk_widget_hide (priv->set_time_window); ++ ++ intlclock_ui_reset_timeout (this); ++} ++ ++static void ++run_time_settings (GtkWidget *button, IntlClockUI *this) ++{ ++ IntlClockUIPrivate *priv = PRIVATE (this); ++ GtkWidget *cancel_set_time_button; ++ ++ if (!priv->set_time_button) { ++ priv->set_time_button = glade_xml_get_widget (priv->glade_xml, "set-time-button"); ++ g_signal_connect (priv->set_time_button, "clicked", G_CALLBACK (set_time), this); ++ ++ cancel_set_time_button = glade_xml_get_widget (priv->glade_xml, "cancel-set-time-button"); ++ g_signal_connect (cancel_set_time_button, "clicked", G_CALLBACK (cancel_time_settings), this); ++ ++ priv->current_time_label = glade_xml_get_widget (priv->glade_xml, "current_time_label"); ++ } ++ ++ priv->set_time_window = glade_xml_get_widget (priv->glade_xml, "set-time-window"); ++ gtk_window_present (GTK_WINDOW (priv->set_time_window)); ++ ++ intlclock_ui_reset_timeout (this); ++} ++ ++static void ++display_prefs_window (IntlClockUI *this, gboolean locations) + { + IntlClockUIPrivate *priv = PRIVATE (this); + GtkWidget *edit_window; +- GtkWidget *prefs_window; + GtkWidget *prefs_close_button; +- GtkWidget *prefs_settings_button; + GtkWidget *edit_cancel_button; + GtkWidget *edit_ok_button; + GtkWidget *zone_combo; +- +- GtkWidget *tmp_button; ++ GtkWidget *find_window; ++ GtkWidget *find_location_button; ++ GtkWidget *find_location_cancel_button; ++ GtkTreeSelection *selection; + + if (!priv->prefs_window) { + priv->prefs_window = +@@ -1170,43 +1355,42 @@ display_prefs_window (IntlClockUI *this) + + prefs_close_button = + glade_xml_get_widget (priv->glade_xml, "prefs-close-button"); +- +- prefs_settings_button = +- glade_xml_get_widget (priv->glade_xml, "prefs-time-settings-button"); +- + priv->prefs_locations = + GTK_TREE_VIEW (glade_xml_get_widget (priv->glade_xml, "cities_list")); + ++ selection = gtk_tree_view_get_selection (priv->prefs_locations); ++ g_signal_connect (G_OBJECT (selection), "changed", ++ G_CALLBACK (intlclock_prefs_locations_changed), this); ++ + g_signal_connect (G_OBJECT (priv->prefs_window), "delete_event", + G_CALLBACK (intlclock_prefs_hide_event), this); + + g_signal_connect (G_OBJECT (prefs_close_button), "clicked", + G_CALLBACK (intlclock_prefs_hide), this); + +- g_signal_connect (G_OBJECT (prefs_settings_button), "clicked", +- G_CALLBACK (run_time_configuration_cb), this); +- +- tmp_button = ++ priv->prefs_location_remove_button = + glade_xml_get_widget (priv->glade_xml, "prefs-locations-remove-button"); + +- g_signal_connect (G_OBJECT (tmp_button), "clicked", ++ g_signal_connect (G_OBJECT (priv->prefs_location_remove_button), "clicked", + G_CALLBACK (run_prefs_locations_remove), this); +- +- tmp_button = ++ ++ priv->prefs_location_add_button = + glade_xml_get_widget (priv->glade_xml, "prefs-locations-add-button"); + +- g_signal_connect (G_OBJECT (tmp_button), "clicked", ++ g_signal_connect (G_OBJECT (priv->prefs_location_add_button), "clicked", + G_CALLBACK (run_prefs_locations_add), this); + +- tmp_button = ++ priv->prefs_location_edit_button = + glade_xml_get_widget (priv->glade_xml, "prefs-locations-edit-button"); + +- g_signal_connect (G_OBJECT (tmp_button), "clicked", ++ g_signal_connect (G_OBJECT (priv->prefs_location_edit_button), "clicked", + G_CALLBACK (run_prefs_locations_edit), this); + + edit_window = glade_xml_get_widget (priv->glade_xml, + "edit-location-window"); + ++ gtk_window_set_transient_for (GTK_WINDOW (edit_window), ++ GTK_WINDOW (priv->prefs_window)); + + g_signal_connect (G_OBJECT (edit_window), "delete_event", + G_CALLBACK (intlclock_edit_hide_event), this); +@@ -1219,7 +1403,35 @@ display_prefs_window (IntlClockUI *this) + + zone_combo = glade_xml_get_widget (priv->glade_xml, "edit-location-timezone-combo"); + +- g_signal_connect (G_OBJECT (zone_combo), "changed", ++ find_window = glade_xml_get_widget (priv->glade_xml, ++ "find-location-window"); ++ ++ gtk_window_set_transient_for (GTK_WINDOW (find_window), ++ GTK_WINDOW (edit_window)); ++ ++ g_signal_connect (G_OBJECT (find_window), "delete_event", ++ G_CALLBACK (intlclock_find_hide_event), this); ++ ++ find_location_button = ++ glade_xml_get_widget (priv->glade_xml, "find-location-button"); ++ ++ priv->find_location_ok_button = ++ glade_xml_get_widget (priv->glade_xml, "find-location-ok-button"); ++ ++ find_location_cancel_button = ++ glade_xml_get_widget (priv->glade_xml, "find-location-cancel-button"); ++ ++ priv->find_next_location_button = ++ glade_xml_get_widget (priv->glade_xml, "find-next-location-button"); ++ ++ priv->find_location_entry = ++ glade_xml_get_widget (priv->glade_xml, "find-location-entry"); ++ ++ priv->location_tree = ++ glade_xml_get_widget (priv->glade_xml, "find-location-tree"); ++ ++ priv->zone_combo_changed = ++ g_signal_connect (G_OBJECT (zone_combo), "changed", + G_CALLBACK (zone_combo_changed), this); + + +@@ -1229,16 +1441,72 @@ display_prefs_window (IntlClockUI *this) + g_signal_connect (G_OBJECT (edit_ok_button), "clicked", + G_CALLBACK (run_prefs_edit_save), this); + ++ g_signal_connect (find_location_button, "clicked", ++ G_CALLBACK (run_find_location), this); ++ ++ g_signal_connect (G_OBJECT (find_location_cancel_button), "clicked", ++ G_CALLBACK (intlclock_find_hide), this); ++ ++ g_signal_connect (G_OBJECT (priv->find_location_ok_button), "clicked", ++ G_CALLBACK (run_find_location_save), this); ++ ++ g_signal_connect (G_OBJECT (priv->find_next_location_button), "clicked", ++ G_CALLBACK (find_next_location), this); ++ ++ g_signal_connect (G_OBJECT (priv->find_location_entry), "changed", ++ G_CALLBACK (find_entry_changed), this); ++ ++ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->location_tree)); ++ g_signal_connect (selection, "changed", ++ G_CALLBACK (location_tree_selection_changed), this); ++ + /* We have to put an item in the combo box in the glade file + to get the simpler, string-only store. Here we remove that + item. */ + gtk_combo_box_remove_text (GTK_COMBO_BOX (zone_combo), 0); + ++ /* Set up the time setting section */ ++ ++ priv->time_settings_button = glade_xml_get_widget (priv->glade_xml, "time-settings-button"); ++ g_signal_connect (priv->time_settings_button, "clicked", G_CALLBACK (run_time_settings), this); ++ ++ priv->calendar = glade_xml_get_widget (priv->glade_xml, "calendar"); ++ priv->hours_spin = glade_xml_get_widget (priv->glade_xml, "hours_spin"); ++ priv->minutes_spin = glade_xml_get_widget (priv->glade_xml, "minutes_spin"); ++ priv->seconds_spin = glade_xml_get_widget (priv->glade_xml, "seconds_spin"); ++ ++ gtk_entry_set_width_chars (GTK_ENTRY (priv->hours_spin), 2); ++ gtk_entry_set_width_chars (GTK_ENTRY (priv->minutes_spin), 2); ++ gtk_entry_set_width_chars (GTK_ENTRY (priv->seconds_spin), 2); ++ ++ gtk_entry_set_alignment (GTK_ENTRY (priv->hours_spin), 1.0); ++ gtk_entry_set_alignment (GTK_ENTRY (priv->minutes_spin), 1.0); ++ gtk_entry_set_alignment (GTK_ENTRY (priv->seconds_spin), 1.0); ++ g_signal_connect (priv->seconds_spin, "wrapped", G_CALLBACK (wrap_cb), this); ++ g_signal_connect (priv->minutes_spin, "wrapped", G_CALLBACK (wrap_cb), this); ++ g_signal_connect (priv->hours_spin, "wrapped", G_CALLBACK (wrap_cb), this); ++ + /* fill it with the current preferences */ + fill_prefs_window (this); + } + ++ if (locations) { ++ GtkWidget *notebook = ++ glade_xml_get_widget (priv->glade_xml, "notebook"); ++ gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), 1); ++ } ++ ++ update_set_time_button (this); ++ + gtk_window_present (GTK_WINDOW (priv->prefs_window)); ++ ++ intlclock_ui_reset_timeout (this); ++} ++ ++void ++intlclock_ui_edit_locations (IntlClockUI *ui) ++{ ++ display_prefs_window (ui, TRUE); + } + + static void +@@ -1306,16 +1574,11 @@ intlclock_ui_new (IntlClock *clock, PanelApplet *applet) + create_events_window (this); + create_clock_window (this); + create_cities_store (this); +- +- if (priv->show_locations) { +- create_cities_section (this); +- } +- +- if (priv->show_map) { +- create_map_section (this); +- intlclock_map_refresh (INTLCLOCK_MAP (priv->map_widget)); +- } +- ++ create_cities_section (this); ++ create_map_section (this); ++ ++ intlclock_map_refresh (INTLCLOCK_MAP (priv->map_widget)); ++ + intlclock_ui_reset_timeout (this); + + return this; +@@ -1365,9 +1628,6 @@ intlclock_ui_init (IntlClockUI *this) + + priv->cities_store = NULL; + +- priv->show_locations = TRUE; +- priv->show_map = TRUE; +- + priv->prefs_window = NULL; + + priv->format_12hr = TRUE; +@@ -1547,56 +1807,6 @@ gconf_show_week_changed (GConfClient *client, + } + + static void +-gconf_show_locations_changed (GConfClient *client, +- guint cnxn_id, +- GConfEntry *entry, +- IntlClockUI *this) +-{ +- IntlClockUIPrivate *priv = PRIVATE (this); +- GtkWidget *widget; +- +- gboolean value; +- +- if (!entry->value || entry->value->type != GCONF_VALUE_BOOL) +- return; +- +- value = gconf_value_get_bool (entry->value); +- +- priv->show_locations = (value != 0); +- +- widget = glade_xml_get_widget (priv->glade_xml, "show_locations_check"); +- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), +- priv->show_locations); +- +- create_cities_section (this); +-} +- +-static void +-gconf_show_map_changed (GConfClient *client, +- guint cnxn_id, +- GConfEntry *entry, +- IntlClockUI *this) +-{ +- IntlClockUIPrivate *priv = PRIVATE (this); +- GtkWidget *widget; +- +- gboolean value; +- +- if (!entry->value || entry->value->type != GCONF_VALUE_BOOL) +- return; +- +- value = gconf_value_get_bool (entry->value); +- +- priv->show_map = (value != 0); +- +- widget = glade_xml_get_widget (priv->glade_xml, "show_map_check"); +- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), +- priv->show_map); +- +- create_map_section (this); +-} +- +-static void + location_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, +@@ -1614,6 +1824,7 @@ location_start_element (GMarkupParseContext *context, + gchar *timezone = NULL; + gfloat latitude = 0.0; + gfloat longitude = 0.0; ++ gchar *code = NULL; + + int index = 0; + +@@ -1633,6 +1844,8 @@ location_start_element (GMarkupParseContext *context, + sscanf (attribute_values[index], "%f", &latitude); + } else if (strcmp (att_name, "longitude") == 0) { + sscanf (attribute_values[index], "%f", &longitude); ++ } else if (strcmp (att_name, "code") == 0) { ++ code = (gchar *)attribute_values[index]; + } + } + +@@ -1642,7 +1855,7 @@ location_start_element (GMarkupParseContext *context, + return; + } + +- loc = intlclock_location_new (name, timezone, latitude, longitude); ++ loc = intlclock_location_new (name, timezone, latitude, longitude, code); + + *(GList **)user_data = g_list_append (ret, loc); + } +@@ -1713,10 +1926,11 @@ gconf_loc_to_string (IntlClockLocation *loc) + prev_locale = setlocale (LC_NUMERIC, "POSIX"); + + ret = g_markup_printf_escaped +- ("", ++ ("", + intlclock_location_get_name (loc), + intlclock_location_get_timezone (loc), +- latitude, longitude); ++ latitude, longitude, ++ intlclock_location_get_weather_code (loc)); + + setlocale (LC_NUMERIC, ""); + +@@ -1727,7 +1941,7 @@ static void + intlclock_ui_save_cities_store (IntlClockUI *this) + { + IntlClockUIPrivate *priv = PRIVATE (this); +- IntlClockLocation *loc, *cur; ++ IntlClockLocation *loc; + GList *node = intlclock_get_locations (priv->clock); + + GSList *root = NULL; +@@ -1803,24 +2017,6 @@ setup_gconf (IntlClockUI *this) + g_free (key); + + key = panel_applet_gconf_get_full_key +- (PANEL_APPLET (priv->panel_applet), KEY_SHOW_LOCATIONS); +- priv->listeners [index++] = +- gconf_client_notify_add ( +- client, key, +- (GConfClientNotifyFunc) gconf_show_locations_changed, +- this, NULL, NULL); +- g_free (key); +- +- key = panel_applet_gconf_get_full_key +- (PANEL_APPLET (priv->panel_applet), KEY_SHOW_MAP); +- priv->listeners [index++] = +- gconf_client_notify_add ( +- client, key, +- (GConfClientNotifyFunc) gconf_show_map_changed, +- this, NULL, NULL); +- g_free (key); +- +- key = panel_applet_gconf_get_full_key + (PANEL_APPLET (priv->panel_applet), KEY_CITIES); + priv->listeners [index++] = + gconf_client_notify_add ( +@@ -1868,14 +2064,6 @@ load_gconf_settings (IntlClockUI *this) + panel_applet_gconf_get_bool (priv->panel_applet, + KEY_SHOW_WEEK, NULL); + +- priv->show_locations = +- panel_applet_gconf_get_bool (priv->panel_applet, +- KEY_SHOW_LOCATIONS, NULL); +- +- priv->show_map = +- panel_applet_gconf_get_bool (priv->panel_applet, +- KEY_SHOW_MAP, NULL); +- + values = panel_applet_gconf_get_list (priv->panel_applet, KEY_CITIES, + GCONF_VALUE_STRING, NULL); + +@@ -1892,92 +2080,72 @@ static void bonobo_display_properties_dialog (BonoboUIComponent *uic, + IntlClockUI *this, + const gchar *verbname) + { +- IntlClockUIPrivate *priv = PRIVATE (this); +- +- display_prefs_window (this); ++ display_prefs_window (this, FALSE); + } + +-static void bonobo_run_time_configuration (BonoboUIComponent *uic, +- IntlClockUI *this, +- const gchar *verbname) +-{ +- IntlClockUIPrivate *priv = PRIVATE (this); +- +- run_time_configuration (this); +-} +- +-static void display_prefs_window_cb (GtkButton *button, gpointer this) ++static void ++copy_time (BonoboUIComponent *uic, ++ IntlClockUI *this, ++ const gchar *verbname) + { + IntlClockUIPrivate *priv = PRIVATE (this); ++ gchar *utf8; ++ time_t now_t; ++ struct tm now; + +- display_prefs_window (this); +-} ++ tzset (); ++ time (&now_t); ++ localtime_r (&now_t, &now); + +-static void +-run_time_configuration_cb (GtkButton *button, gpointer this) +-{ +- IntlClockUIPrivate *priv = PRIVATE (this); ++ utf8 = intlclock_format_time (priv->clock, &now, FALSE, ++ priv->format_12hr, ++ priv->format_show_seconds, ++ FALSE, FALSE, NULL, FALSE); ++ ++ gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY), ++ utf8, -1); ++ gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD), ++ utf8, -1); + +- run_time_configuration (this); ++ g_free (utf8); + } + + static void +-run_time_configuration (IntlClockUI *this) ++copy_date (BonoboUIComponent *uic, ++ IntlClockUI *this, ++ const gchar *verbname) + { +- IntlClockUIPrivate *priv = PRIVATE (this); +- +- GtkWidget *dialog; +- GError *err; +- char **argv; +- char *path; ++ char string[256]; ++ char *utf8, *loc; + +- GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (priv->events_window)); +- gchar *tool = "/opt/gnome/bin/gnomesu /sbin/yast2 timezone"; +- +- if (!tool || tool[0] == '\0') +- return; +- +- if (!g_shell_parse_argv (tool, NULL, &argv, NULL)) +- return; +- +- if (!(path = g_find_program_in_path (argv [0]))) { +- g_strfreev (argv); +- return; +- } +- +- g_free (path); +- +- err = NULL; +- if (gdk_spawn_on_screen (screen, +- NULL, +- argv, +- NULL, +- G_SPAWN_SEARCH_PATH, +- NULL, +- NULL, +- NULL, +- &err)) { +- g_strfreev (argv); +- return; +- } +- +- g_strfreev (argv); ++ time_t now_t; ++ struct tm now; + +- dialog = gtk_message_dialog_new (NULL, +- GTK_DIALOG_DESTROY_WITH_PARENT, +- GTK_MESSAGE_ERROR, +- GTK_BUTTONS_OK, +- _("Failed to launch time configuration tool: %s"), +- err->message); +- g_error_free (err); ++ tzset (); ++ time (&now_t); ++ localtime_r (&now_t, &now); + +- g_signal_connect (dialog, "response", +- G_CALLBACK (gtk_widget_destroy), NULL); ++ loc = g_locale_from_utf8 (_("%A, %B %d %Y"), -1, NULL, NULL, NULL); ++ if (!loc) ++ strcpy (string, "???"); ++ else if (strftime (string, sizeof (string), loc, &now) <= 0) ++ strcpy (string, "???"); ++ g_free (loc); + +- gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); +- gtk_window_set_screen (GTK_WINDOW (dialog), screen); ++ utf8 = g_locale_to_utf8 (string, -1, NULL, NULL, NULL); ++ gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY), ++ utf8, -1); ++ gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD), ++ utf8, -1); ++ g_free (utf8); ++} + +- gtk_widget_show_all (dialog); ++static void ++config_date (BonoboUIComponent *uic, ++ IntlClockUI *this, ++ const char *verbname) ++{ ++ run_time_settings (NULL, this); + } + + static void +@@ -2000,6 +2168,46 @@ remove_tree_row (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, + } + + static void ++update_coords (IntlClockUI *this, gboolean valid, gfloat lat, gfloat lon) ++{ ++ IntlClockUIPrivate *priv = PRIVATE (INTLCLOCK_UI (this)); ++ GtkWidget *lat_entry = glade_xml_get_widget (priv->glade_xml, "edit-location-latitude-entry"); ++ GtkWidget *lon_entry = glade_xml_get_widget (priv->glade_xml, "edit-location-longitude-entry"); ++ GtkWidget *lat_combo = glade_xml_get_widget (priv->glade_xml, "edit-location-latitude-combo"); ++ GtkWidget *lon_combo = glade_xml_get_widget (priv->glade_xml, "edit-location-longitude-combo"); ++ gchar *tmp; ++ ++ if (!valid) { ++ gtk_entry_set_text (GTK_ENTRY (lat_entry), ""); ++ gtk_entry_set_text (GTK_ENTRY (lon_entry), ""); ++ gtk_combo_box_set_active (GTK_COMBO_BOX (lat_combo), -1); ++ gtk_combo_box_set_active (GTK_COMBO_BOX (lon_combo), -1); ++ ++ return; ++ } ++ ++ tmp = g_strdup_printf ("%f", fabsf(lat)); ++ gtk_entry_set_text (GTK_ENTRY (lat_entry), tmp); ++ g_free (tmp); ++ ++ if (lat > 0) { ++ gtk_combo_box_set_active (GTK_COMBO_BOX (lat_combo), 0); ++ } else { ++ gtk_combo_box_set_active (GTK_COMBO_BOX (lat_combo), 1); ++ } ++ ++ tmp = g_strdup_printf ("%f", fabsf(lon)); ++ gtk_entry_set_text (GTK_ENTRY (lon_entry), tmp); ++ g_free (tmp); ++ ++ if (lon > 0) { ++ gtk_combo_box_set_active (GTK_COMBO_BOX (lon_combo), 0); ++ } else { ++ gtk_combo_box_set_active (GTK_COMBO_BOX (lon_combo), 1); ++ } ++} ++ ++static void + zone_combo_changed (GtkComboBox *widget, gpointer this) + { + IntlClockUIPrivate *priv = PRIVATE (INTLCLOCK_UI (this)); +@@ -2009,17 +2217,12 @@ zone_combo_changed (GtkComboBox *widget, gpointer this) + gchar *city = NULL; + gfloat lat = 0; + gfloat lon = 0; +- gchar *tmp; ++ GtkTreeModel *model; ++ gchar *name; + + GtkWidget *name_entry = glade_xml_get_widget (priv->glade_xml, "edit-location-name-entry"); +- +- GtkWidget *lat_entry = glade_xml_get_widget (priv->glade_xml, "edit-location-latitude-entry"); +- +- GtkWidget *lon_entry = glade_xml_get_widget (priv->glade_xml, "edit-location-longitude-entry"); +- +- GtkWidget *lat_combo = glade_xml_get_widget (priv->glade_xml, "edit-location-latitude-combo"); +- +- GtkWidget *lon_combo = glade_xml_get_widget (priv->glade_xml, "edit-location-longitude-combo"); ++ GtkWidget *edit_window = glade_xml_get_widget (priv->glade_xml, "edit-location-window"); ++ gchar *weather_code; + + IntlClockCountry *country; + IntlClockZoneInfo *info; +@@ -2028,6 +2231,14 @@ zone_combo_changed (GtkComboBox *widget, gpointer this) + return; + } + ++ /* only fill in other field if name is not set yet, to ++ * allow correcting a guessed timezone ++ */ ++ name = gtk_entry_get_text (name_entry); ++ if (name && name[0]) { ++ return; ++ } ++ + info = intlclock_zonetable_get_l10n_zone (zones, timezone); + g_free (timezone); + +@@ -2049,26 +2260,13 @@ zone_combo_changed (GtkComboBox *widget, gpointer this) + g_free (city); + + intlclock_zoneinfo_get_coords (info, &lat, &lon); ++ update_coords (this, TRUE, lat, lon); + +- tmp = g_strdup_printf ("%f", fabsf(lat)); +- gtk_entry_set_text (GTK_ENTRY (lat_entry), tmp); +- g_free (tmp); +- +- if (lat > 0) { +- gtk_combo_box_set_active (GTK_COMBO_BOX (lat_combo), 0); +- } else { +- gtk_combo_box_set_active (GTK_COMBO_BOX (lat_combo), 1); +- } +- +- tmp = g_strdup_printf ("%f", fabsf(lon)); +- gtk_entry_set_text (GTK_ENTRY (lon_entry), tmp); +- g_free (tmp); +- +- if (lon > 0) { +- gtk_combo_box_set_active (GTK_COMBO_BOX (lon_combo), 0); +- } else { +- gtk_combo_box_set_active (GTK_COMBO_BOX (lon_combo), 1); +- } ++ fill_location_tree (this); ++ model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->location_tree)); ++ weather_code = find_weather_code (model, city, lat * M_PI/180.0, lon * M_PI/180.0); ++ g_object_set_data_full (G_OBJECT (edit_window), "weather-code", ++ weather_code, g_free); + } + + static void +@@ -2144,12 +2342,8 @@ edit_tree_row (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, + IntlClockUIPrivate *priv = PRIVATE (INTLCLOCK_UI (this)); + + IntlClockLocation *loc; +- int i; +- int timezone_idx = -1; + gchar *tmp; + gfloat lat, lon; +- GList *list; +- GList *cur; + + /* fill the dialog with this location's data, show it */ + GtkWidget *edit_window = glade_xml_get_widget (priv->glade_xml, "edit-location-window"); +@@ -2205,13 +2399,165 @@ static void + run_prefs_locations_remove (GtkButton *button, gpointer this) + { + IntlClockUIPrivate *priv = PRIVATE (INTLCLOCK_UI (this)); +- GtkTreeModel *model = gtk_tree_view_get_model (priv->prefs_locations);; + GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->prefs_locations)); + + gtk_tree_selection_selected_foreach (sel, remove_tree_row, this); + } + + static void ++run_find_location (GtkButton *button, gpointer this) ++{ ++ IntlClockUIPrivate *priv = PRIVATE (INTLCLOCK_UI (this)); ++ ++ GtkWidget *window = glade_xml_get_widget (priv->glade_xml, "find-location-window"); ++ ++ gtk_tree_view_collapse_all (priv->location_tree); ++ gtk_widget_grab_focus (priv->find_location_entry); ++ gtk_window_present (GTK_WINDOW (window)); ++} ++ ++static gdouble ++distance (gdouble lat1, gdouble lon1, ++ gdouble lat2, gdouble lon2) ++{ ++ gdouble radius = 6372.795; ++ ++ return acos (cos (lat1) * cos (lat2) * cos (lon1 - lon2) + sin (lat1) * sin (lat2)) * radius; ++} ++ ++static gchar * ++find_timezone (IntlClockUI *this, ++ const char *name, ++ gfloat lat, ++ gfloat lon) ++{ ++ IntlClockUIPrivate *priv = PRIVATE (INTLCLOCK_UI (this)); ++ IntlClockZoneTable *zonetab = intlclock_get_zonetable (priv->clock); ++ GList *zones, *l; ++ double dist, d; ++ gfloat zlat, zlon; ++ IntlClockZoneInfo *best; ++ ++ g_print ("find zone for %s (%f %f)\n", name, lat, lon); ++ dist = 1e6; ++ best = NULL; ++ zones = intlclock_zonetable_get_zones (zonetab); ++ for (l = zones; l; l = l->next) { ++ IntlClockZoneInfo *info = l->data; ++ intlclock_zoneinfo_get_coords (info, &zlat, &zlon); ++ ++ d = distance (lat, lon, zlat*M_PI/180.0, zlon*M_PI/180.0); ++ ++ if (d < dist) { ++ best = info; ++ dist = d; ++ } ++ } ++ ++ intlclock_zoneinfo_get_coords (best, &zlat, &zlon); ++ g_print ("best: %s (%f, %f), distance: %f\n", ++ intlclock_zoneinfo_get_name (best), zlat, zlon, dist); ++ ++ return g_strdup (intlclock_zoneinfo_get_name (best)); ++} ++ ++static void ++update_timezone (IntlClockUI *this, ++ const char *name, ++ gfloat lat, ++ gfloat lon) ++{ ++ IntlClockUIPrivate *priv = PRIVATE (INTLCLOCK_UI (this)); ++ GtkWidget *zone_combo = glade_xml_get_widget (priv->glade_xml, "edit-location-timezone-combo"); ++ GtkTreeModel *model; ++ gchar *timezone; ++ IntlClockLocation *loc; ++ ++ timezone = find_timezone (this, name, lat, lon); ++ loc = intlclock_location_new (name, timezone, lat*180.0/M_PI, lon*180.0/M_PI, NULL); ++ ++ g_signal_handler_block (zone_combo, priv->zone_combo_changed); ++ ++ fill_timezone_combo_from_location (this, zone_combo, loc); ++ ++ g_signal_handler_unblock (zone_combo, priv->zone_combo_changed); ++ ++ g_object_unref (loc); ++ g_free (timezone); ++} ++ ++static void ++run_find_location_save (GtkButton *button, gpointer this) ++{ ++ IntlClockUIPrivate *priv = PRIVATE (INTLCLOCK_UI (this)); ++ GtkTreeSelection *selection; ++ GtkTreeModel *model; ++ GtkTreeIter iter; ++ WeatherLocation *loc = NULL; ++ GtkWidget *name_entry; ++ GtkWidget *edit_window; ++ ++ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->location_tree)); ++ if (!gtk_tree_selection_get_selected (selection, &model, &iter)) ++ return; ++ ++ gtk_tree_model_get (model, &iter, GWEATHER_XML_COL_POINTER, &loc, -1); ++ ++ if (!loc) ++ return; ++ ++ edit_window = glade_xml_get_widget (priv->glade_xml, "edit-location-window"); ++ name_entry = glade_xml_get_widget (priv->glade_xml, "edit-location-name-entry"); ++ gtk_entry_set_text (GTK_ENTRY (name_entry), loc->name); ++ g_object_set_data_full (G_OBJECT (edit_window), "weather-code", ++ g_strdup (loc->code), g_free); ++ ++ update_coords (this, loc->latlon_valid, loc->latitude*180.0/M_PI, loc->longitude*180.0/M_PI); ++ ++ update_timezone (this, loc->name, loc->latitude, loc->longitude); ++ ++ intlclock_find_hide (button, this); ++} ++ ++static void ++location_row_activated (GtkTreeView *tree_view, ++ GtkTreePath *path, ++ GtkTreeViewColumn *column, ++ IntlClockUI *this) ++{ ++ run_find_location_save (tree_view, this); ++} ++ ++static void ++fill_location_tree (IntlClockUI *this) ++{ ++ IntlClockUIPrivate *priv = PRIVATE (INTLCLOCK_UI (this)); ++ GtkTreeView *tree; ++ GtkTreeModel *model; ++ GtkTreeViewColumn *column; ++ GtkCellRenderer *cell; ++ ++ tree = (GtkTreeView*)priv->location_tree; ++ ++ if (gtk_tree_view_get_model (tree) != NULL) ++ return; ++ ++ model = (GtkTreeModel*)gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_POINTER); ++ gtk_tree_view_set_model (tree, model); ++ ++ cell = gtk_cell_renderer_text_new (); ++ column = gtk_tree_view_column_new_with_attributes ("not used", cell, ++ "text", GWEATHER_XML_COL_LOC, NULL); ++ gtk_tree_view_append_column (tree, column); ++ gtk_tree_view_set_expander_column (tree, column); ++ ++ g_signal_connect (tree, "row-activated", ++ G_CALLBACK (location_row_activated), this); ++ ++ gweather_xml_load_locations (tree, NULL); ++} ++ ++static void + run_prefs_locations_add (GtkButton *button, gpointer this) + { + IntlClockUIPrivate *priv = PRIVATE (INTLCLOCK_UI (this)); +@@ -2220,7 +2566,8 @@ run_prefs_locations_add (GtkButton *button, gpointer this) + GtkWidget *zone_combo = glade_xml_get_widget (priv->glade_xml, "edit-location-timezone-combo"); + + fill_timezone_combo_from_location (this, zone_combo, NULL); +- ++ fill_location_tree (this); ++ + g_object_set_data (G_OBJECT (edit_window), "intlclock-location", NULL); + gtk_window_present (GTK_WINDOW (edit_window)); + } +@@ -2230,7 +2577,6 @@ run_prefs_locations_edit (GtkButton *button, gpointer this) + { + IntlClockUIPrivate *priv = PRIVATE (INTLCLOCK_UI (this)); + +- GtkTreeModel *model = gtk_tree_view_get_model (priv->prefs_locations);; + GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->prefs_locations)); + + gtk_tree_selection_selected_foreach (sel, edit_tree_row, this); +@@ -2245,7 +2591,6 @@ run_prefs_edit_save (GtkButton *button, gpointer this) + IntlClockZoneTable *zones = intlclock_get_zonetable (priv->clock); + + IntlClockLocation *loc = g_object_get_data (G_OBJECT (edit_window), "intlclock-location"); +- + GtkWidget *zone_combo = glade_xml_get_widget (priv->glade_xml, "edit-location-timezone-combo"); + GtkWidget *name_entry = glade_xml_get_widget (priv->glade_xml, "edit-location-name-entry"); + +@@ -2255,6 +2600,8 @@ run_prefs_edit_save (GtkButton *button, gpointer this) + GtkWidget *lon_combo = glade_xml_get_widget (priv->glade_xml, "edit-location-longitude-combo"); + + gchar *timezone_l10n = gtk_combo_box_get_active_text (GTK_COMBO_BOX (zone_combo)); ++ gchar *weather_code = g_object_get_data (G_OBJECT (edit_window), "weather-code"); ++ + IntlClockZoneInfo *info = intlclock_zonetable_get_l10n_zone (zones, timezone_l10n); + + if (!info) { +@@ -2281,9 +2628,11 @@ run_prefs_edit_save (GtkButton *button, gpointer this) + intlclock_location_set_timezone (loc, intlclock_zoneinfo_get_name (info)); + intlclock_location_set_name (loc, name); + intlclock_location_set_coords (loc, lat, lon); ++ intlclock_location_set_weather_code (loc, weather_code); + } else { + GList *locs; +- loc = intlclock_location_new (name, intlclock_zoneinfo_get_name (info), lat, lon); ++ ++ loc = intlclock_location_new (name, intlclock_zoneinfo_get_name (info), lat, lon, weather_code); + + locs = g_list_copy (intlclock_get_locations (priv->clock)); + locs = g_list_append (locs, loc); +@@ -2294,3 +2643,11 @@ run_prefs_edit_save (GtkButton *button, gpointer this) + + intlclock_edit_hide (edit_window, this); + } ++ ++void ++intlclock_ui_update_weather_icon (IntlClockUI *ui, GdkPixbuf *pixbuf) ++{ ++ IntlClockUIPrivate *priv = PRIVATE (INTLCLOCK_UI (ui)); ++ ++ gtk_image_set_from_pixbuf (GTK_IMAGE (priv->panel_weather_icon), pixbuf); ++} +diff --git a/src/intlclock-ui.h b/src/intlclock-ui.h +index ecbee03..a10099a 100644 +--- a/src/intlclock-ui.h ++++ b/src/intlclock-ui.h +@@ -29,6 +29,8 @@ GType intlclock_ui_get_type (void); + + IntlClockUI *intlclock_ui_new (IntlClock *clock, PanelApplet *applet); + gboolean intlclock_ui_is_12hr (IntlClockUI *ui); ++void intlclock_ui_edit_locations (IntlClockUI *ui); ++void intlclock_ui_update_weather_icon (IntlClockUI *ui, GdkPixbuf *pixbuf); + + G_END_DECLS + #endif /* __INTLCLOCK_UI_H__ */ +diff --git a/src/intlclock-zonetable.c b/src/intlclock-zonetable.c +index a2a0b6f..e6e27cb 100644 +--- a/src/intlclock-zonetable.c ++++ b/src/intlclock-zonetable.c +@@ -27,8 +27,6 @@ typedef struct { + GHashTable *country_table; + } IntlClockZoneTablePrivate; + +-#define USE_CRIPPLED_ZONELIST 1 +- + /* Seeded with the list from Nat's Blackberry */ + char *available_zones[] = { + /* Eniwetok (-12) */ +diff --git a/src/intlclock.c b/src/intlclock.c +index 02f5b0a..552c45c 100644 +--- a/src/intlclock.c ++++ b/src/intlclock.c +@@ -32,6 +32,8 @@ typedef struct { + enum { + TICK, + LOCATIONS_CHANGED, ++ BLINK_LOCATION, ++ CURRENT_TIMEZONE_CHANGED, + LAST_SIGNAL + }; + +@@ -87,7 +89,25 @@ intlclock_class_init (IntlClockClass *this_class) + _intlclock_marshal_VOID__VOID, + G_TYPE_NONE, 0); + +- g_type_class_add_private (this_class, sizeof (IntlClockPrivate)); ++ intlclock_signals[BLINK_LOCATION] = g_signal_new ++ ("blink-location", ++ G_OBJECT_CLASS_TYPE (obj_class), ++ G_SIGNAL_RUN_FIRST, ++ G_STRUCT_OFFSET (IntlClockClass, blink_location), ++ NULL, NULL, ++ _intlclock_marshal_VOID__OBJECT, ++ G_TYPE_NONE, 1, INTLCLOCK_LOCATION_TYPE); ++ ++ intlclock_signals[CURRENT_TIMEZONE_CHANGED] = g_signal_new ++ ("current-timezone-changed", ++ G_OBJECT_CLASS_TYPE (obj_class), ++ G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, ++ G_STRUCT_OFFSET (IntlClockClass, current_timezone_changed), ++ NULL, NULL, ++ _intlclock_marshal_VOID__VOID, ++ G_TYPE_NONE, 0); ++ ++ g_type_class_add_private (this_class, sizeof (IntlClockPrivate)); + } + + static void +@@ -133,7 +153,7 @@ intlclock_set_locations (IntlClock *this, GList *locations) + + priv->locations = locations; + +- g_signal_emit_by_name (this, "locations-changed"); ++ g_signal_emit (this, intlclock_signals[LOCATIONS_CHANGED], 0); + } + + GList * +@@ -202,7 +222,7 @@ intlclock_emit_tick (gpointer data) + IntlClock *this = INTLCLOCK (data); + IntlClockPrivate *priv = PRIVATE (this); + +- g_signal_emit_by_name (this, "tick"); ++ g_signal_emit (this, intlclock_signals[TICK], 0); + + if (priv->in_partial_timeout) { + intlclock_reset_timeout (this); +@@ -353,3 +373,9 @@ intlclock_free_locations (IntlClock *this) + g_list_free (priv->locations); + priv->locations = NULL; + } ++ ++void ++intlclock_blink_location (IntlClock *this, IntlClockLocation *loc) ++{ ++ g_signal_emit (this, intlclock_signals[BLINK_LOCATION], 0, loc); ++} +diff --git a/src/intlclock.h b/src/intlclock.h +index 3b0012c..20c681d 100644 +--- a/src/intlclock.h ++++ b/src/intlclock.h +@@ -27,6 +27,9 @@ typedef struct + + void (* tick) (IntlClock *clock); + void (* locations_changed) (IntlClock *clock); ++ void (* blink_location) (IntlClock *clock, IntlClockLocation *loc); ++ void (* current_timezone_changed) (IntlClock *clock); ++ + } IntlClockClass; + + GType intlclock_get_type (void); +@@ -35,6 +38,7 @@ IntlClock *intlclock_new (void); + + void intlclock_set_locations (IntlClock *this, GList *list); + GList *intlclock_get_locations (IntlClock *this); ++void intlclock_blink_location (IntlClock *this, IntlClockLocation *loc); + + IntlClockZoneTable *intlclock_get_zonetable (IntlClock *this); + gchar *intlclock_format_time (IntlClock *this, struct tm *now, +diff --git a/src/set-timezone.c b/src/set-timezone.c +new file mode 100644 +index 0000000..5c96149 +--- /dev/null ++++ b/src/set-timezone.c +@@ -0,0 +1,426 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- ++ * ++ * Copyright (C) 2007 David Zeuthen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include "config.h" ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++ ++static DBusGConnection * ++get_session_bus (void) ++{ ++ GError *error; ++ static DBusGConnection *bus = NULL; ++ ++ if (bus == NULL) { ++ error = NULL; ++ bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error); ++ if (bus == NULL) { ++ g_warning ("Couldn't connect to session bus: %s", ++ error->message); ++ g_error_free (error); ++ } ++ } ++ ++ return bus; ++} ++ ++static DBusGConnection * ++get_system_bus (void) ++{ ++ GError *error; ++ static DBusGConnection *bus = NULL; ++ ++ if (bus == NULL) { ++ error = NULL; ++ bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); ++ if (bus == NULL) { ++ g_warning ("Couldn't connect to system bus: %s", ++ error->message); ++ g_error_free (error); ++ } ++ } ++ ++ return bus; ++} ++ ++static PolKitContext * ++get_pk_context (void) ++{ ++ static PolKitContext *pk_context = NULL; ++ ++ if (pk_context == NULL) { ++ pk_context = polkit_context_new (); ++ if (!polkit_context_init (pk_context, NULL)) { ++ polkit_context_unref (pk_context); ++ pk_context = NULL; ++ } ++ } ++ ++ return pk_context; ++} ++ ++gboolean ++set_system_timezone (const char *filename, GError **err) ++{ ++ DBusGConnection *session_bus; ++ DBusGConnection *system_bus; ++ DBusGProxy *mechanism_proxy; ++ DBusGProxy *polkit_gnome_proxy; ++ gboolean ret = FALSE; ++ ++ session_bus = get_session_bus (); ++ if (session_bus == NULL) ++ goto out; ++ ++ system_bus = get_system_bus (); ++ if (system_bus == NULL) ++ goto out; ++ ++ mechanism_proxy = dbus_g_proxy_new_for_name (system_bus, ++ "org.gnome.ClockApplet.Mechanism", ++ "/", ++ "org.gnome.ClockApplet.Mechanism"); ++ ++ polkit_gnome_proxy = dbus_g_proxy_new_for_name (session_bus, ++ "org.gnome.PolicyKit", ++ "/org/gnome/PolicyKit/Manager", ++ "org.gnome.PolicyKit.Manager"); ++ ++ if (filename != NULL) { ++ GError *error; ++ ++ g_debug ("Trying to set timezone '%s'", filename); ++ try_again: ++ error = NULL; ++ /* first, try to call into the mechanism */ ++ if (!dbus_g_proxy_call_with_timeout (mechanism_proxy, ++ "SetTimezone", ++ INT_MAX, ++ &error, ++ /* parameters: */ ++ G_TYPE_STRING, filename, ++ G_TYPE_INVALID, ++ /* return values: */ ++ G_TYPE_INVALID)) { ++ if (dbus_g_error_has_name (error, "org.gnome.ClockApplet.Mechanism.NotPrivileged")) { ++ char **tokens; ++ char *polkit_result_textual; ++ char *polkit_action; ++ gboolean gained_privilege; ++ ++ tokens = g_strsplit (error->message, " ", 2); ++ g_error_free (error); ++ if (g_strv_length (tokens) != 2) { ++ g_warning ("helper return string malformed"); ++ g_strfreev (tokens); ++ goto out; ++ } ++ polkit_action = tokens[0]; ++ polkit_result_textual = tokens[1]; ++ ++ g_debug ("helper refused; returned polkit_result='%s' and polkit_action='%s'", ++ polkit_result_textual, polkit_action); ++ ++ /* Now ask the user for auth... */ ++ if (!dbus_g_proxy_call_with_timeout (polkit_gnome_proxy, ++ "ShowDialog", ++ INT_MAX, ++ &error, ++ /* parameters: */ ++ G_TYPE_STRING, polkit_action, ++ G_TYPE_UINT, 0, /* X11 window ID; none */ ++ G_TYPE_INVALID, ++ /* return values: */ ++ G_TYPE_BOOLEAN, &gained_privilege, ++ G_TYPE_INVALID)) { ++ g_propagate_error (err, error); ++ g_strfreev (tokens); ++ goto out; ++ } ++ g_strfreev (tokens); ++ ++ if (gained_privilege) { ++ g_debug ("Gained privilege; trying to set timezone again"); ++ goto try_again; ++ } ++ ++ } else { ++ g_propagate_error (err, error); ++ } ++ goto out; ++ } ++ ++ g_debug ("Successfully set time zone to '%s'", filename); ++ } ++ ++ ret = TRUE; ++out: ++ g_object_unref (mechanism_proxy); ++ g_object_unref (polkit_gnome_proxy); ++ ++ return ret; ++} ++ ++static gint ++can_do (const gchar *pk_action_id) ++{ ++ DBusConnection *system_bus; ++ PolKitCaller *pk_caller; ++ PolKitAction *pk_action; ++ PolKitResult pk_result; ++ PolKitContext *pk_context; ++ DBusError dbus_error; ++ gint res = 0; ++ ++ system_bus = dbus_g_connection_get_connection (get_system_bus ()); ++ if (system_bus == NULL) ++ goto out; ++ ++ pk_context = get_pk_context (); ++ if (pk_context == NULL) ++ goto out; ++ ++ pk_caller = NULL; ++ pk_action = NULL; ++ ++ pk_action = polkit_action_new (); ++ polkit_action_set_action_id (pk_action, pk_action_id); ++ ++ dbus_error_init (&dbus_error); ++ pk_caller = polkit_caller_new_from_pid (system_bus, getpid (), &dbus_error); ++ if (pk_caller == NULL) { ++ fprintf (stderr, "cannot get caller from dbus name\n"); ++ goto out; ++ } ++ ++ pk_result = polkit_context_can_caller_do_action (pk_context, pk_action, pk_caller); ++ ++ switch (pk_result) { ++ default: ++ case POLKIT_RESULT_UNKNOWN: ++ case POLKIT_RESULT_NO: ++ res = 0; ++ break; ++ case POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH: ++ case POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_KEEP_SESSION: ++ case POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_KEEP_ALWAYS: ++ case POLKIT_RESULT_ONLY_VIA_SELF_AUTH: ++ case POLKIT_RESULT_ONLY_VIA_SELF_AUTH_KEEP_SESSION: ++ case POLKIT_RESULT_ONLY_VIA_SELF_AUTH_KEEP_ALWAYS: ++ res = 1; ++ break; ++ case POLKIT_RESULT_YES: ++ res = 2; ++ break; ++ } ++ ++out: ++ if (pk_action != NULL) ++ polkit_action_unref (pk_action); ++ if (pk_caller != NULL) ++ polkit_caller_unref (pk_caller); ++ ++ return res; ++} ++ ++gint ++can_set_system_timezone (void) ++{ ++ return can_do ("org.gnome.clockapplet.mechanism.settimezone"); ++} ++ ++gint ++can_set_system_time (void) ++{ ++ return can_do ("org.gnome.clockapplet.mechanism.settime"); ++} ++ ++typedef struct { ++ gint ref_count; ++ gint64 time; ++ GFunc callback; ++ gpointer data; ++ GDestroyNotify notify; ++} SetTimeCallbackData; ++ ++static void ++free_data (gpointer d) ++{ ++ SetTimeCallbackData *data = d; ++ ++ data->ref_count--; ++ if (data->ref_count == 0) { ++ if (data->notify) ++ data->notify (data->data); ++ g_free (data); ++ } ++} ++ ++static void set_time_async (SetTimeCallbackData *data); ++ ++static void ++auth_notify (DBusGProxy *proxy, ++ DBusGProxyCall *call, ++ void *user_data) ++{ ++ SetTimeCallbackData *data = user_data; ++ GError *error = NULL; ++ gboolean gained_privilege; ++ ++ if (dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_BOOLEAN, &gained_privilege, G_TYPE_INVALID)) { ++ if (gained_privilege) ++ set_time_async (data); ++ } ++ else { ++ if (data->callback) ++ data->callback (data->data, error); ++ else ++ g_error_free (error); ++ } ++} ++ ++static void ++do_auth_async (const gchar *action, ++ const gchar *result, ++ SetTimeCallbackData *data) ++{ ++ DBusGConnection *bus; ++ DBusGProxy *proxy; ++ ++ g_debug ("helper refused; returned polkit_result='%s' and polkit_action='%s'", ++ result, action); ++ ++ /* Now ask the user for auth... */ ++ bus = get_session_bus (); ++ if (bus == NULL) ++ return; ++ ++ proxy = dbus_g_proxy_new_for_name (bus, ++ "org.gnome.PolicyKit", ++ "/org/gnome/PolicyKit/Manager", ++ "org.gnome.PolicyKit.Manager"); ++ ++ data->ref_count++; ++ dbus_g_proxy_begin_call_with_timeout (proxy, ++ "ShowDialog", ++ auth_notify, ++ data, free_data, ++ INT_MAX, ++ G_TYPE_STRING, action, ++ G_TYPE_UINT, 0, ++ G_TYPE_INVALID); ++} ++ ++static void ++set_time_notify (DBusGProxy *proxy, ++ DBusGProxyCall *call, ++ void *user_data) ++{ ++ SetTimeCallbackData *data = user_data; ++ GError *error = NULL; ++ ++ if (dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID)) { ++ if (data->callback) ++ data->callback (data->data, NULL); ++ } ++ else { ++ if (dbus_g_error_has_name (error, "org.gnome.ClockApplet.Mechanism.NotPrivileged")) { ++ gchar **tokens; ++ ++ tokens = g_strsplit (error->message, " ", 2); ++ g_error_free (error); ++ if (g_strv_length (tokens) == 2) ++ do_auth_async (tokens[0], tokens[1], data); ++ else ++ g_warning ("helper return string malformed"); ++ g_strfreev (tokens); ++ } ++ else { ++ if (data->callback) ++ data->callback (data->data, error); ++ else ++ g_error_free (error); ++ } ++ } ++} ++ ++static void ++set_time_async (SetTimeCallbackData *data) ++{ ++ DBusGConnection *bus; ++ DBusGProxy *proxy; ++ DBusGProxyCall *call; ++ ++ bus = get_system_bus (); ++ if (bus == NULL) ++ return; ++ ++ proxy = dbus_g_proxy_new_for_name (bus, ++ "org.gnome.ClockApplet.Mechanism", ++ "/", ++ "org.gnome.ClockApplet.Mechanism"); ++ ++ data->ref_count++; ++ dbus_g_proxy_begin_call_with_timeout (proxy, ++ "SetTime", ++ set_time_notify, ++ data, free_data, ++ INT_MAX, ++ /* parameters: */ ++ G_TYPE_INT64, data->time, ++ G_TYPE_INVALID, ++ /* return values: */ ++ G_TYPE_INVALID); ++} ++ ++void ++set_system_time_async (gint64 time, ++ GFunc callback, ++ gpointer d, ++ GDestroyNotify notify) ++{ ++ SetTimeCallbackData *data; ++ ++ if (time == -1) ++ return; ++ ++ data = g_new (SetTimeCallbackData, 1); ++ data->ref_count = 1; ++ data->time = time; ++ data->callback = callback; ++ data->data = d; ++ data->notify = notify; ++ ++ set_time_async (data); ++ free_data (data); ++} +diff --git a/src/set-timezone.h b/src/set-timezone.h +new file mode 100644 +index 0000000..c71622c +--- /dev/null ++++ b/src/set-timezone.h +@@ -0,0 +1,36 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- ++ * ++ * Copyright (C) 2007 David Zeuthen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ */ ++ ++#ifndef __SET_SYSTEM_TIMEZONE_H__ ++ ++#include ++ ++gboolean set_system_timezone (const char *filename, ++ GError **err); ++gint can_set_system_timezone (void); ++ ++gint can_set_system_time (void); ++ ++void set_system_time_async (gint64 time, ++ GFunc callback, ++ gpointer data, ++ GDestroyNotify notify); ++ ++#endif