3
This program is designed to keep track of the time and date in various
4
time zones around the world.
6
The zones are kept in a configuration file (default ~/.tzlist), one zone
7
per line. Each line has one or two entries: the first is the TZ value
8
corresponding, the second is an optional description string enclosed in
11
The config file is compatible for use with tzwatch, a shell script
12
writing the time in the given zones to stdout.
14
Note, time_t is evil. It apparently resolves to a 32bit integer (on x86 at
15
least), which only lets the clocks go back to 8:48pm, 13 Dec 1901 (GMT)
16
(2147483648 seconds before 1 Jan 1970).
17
On the other side, the limit is 19 January 2038, 3:13am.
18
There appears to be no simple way around this limitation. If we use struct tm
19
throughout, then on printing to display for each time zone, asctime does not
20
update the display for the zone in the way that ctime does.
21
(curiously, asctime *is* updated if preceded by ctime in the same line, e.g.
22
asprintf( &text, "%.0s%s", ctime(&t), asctime( tp ) );
23
but in this case we still suffer the limitation of the 32bit counter).
24
So it's all very silly really. You'll just have to put up with not syncing
25
earlier than 1902. Too bad for your time machine.
26
The alternative is to do some dreadful dreadful frigging around with the
27
daylight savings and time difference fields in struct tm, which I'm
28
certainly not going to do right now.
32
- change CLists (add zone) to TreeViews
33
- rewrite "About" text using neater GTK text formatting capabilities.
34
- update list of days in month to be correct for the given month
35
- convert time_t references to struct tm and tweak time difference
36
by hand to allow years beyond the 2^32 sec limit (?)
37
- have someone compile it for Win32 so I can give it to Kostya & Andrei
41
/* Copyright (C) 2000-2005 Drew F. Parsons
43
* This program is free software; you can redistribute it and/or modify
44
* it under the terms of the GNU General Public License as published by
45
* the Free Software Foundation; ( version 2 of the License at time of
48
* This program is distributed in the hope that it will be useful,
49
* but WITHOUT ANY WARRANTY; without even the implied warranty of
50
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
51
* GNU General Public License for more details.
53
* You should have received a copy of the GNU General Public License
54
* along with this program; if not, write to the Free Software
55
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
64
#define _(A) gettext(A)
71
/* so many global variables....how naughty!
72
collect them in a structure to be passed around? */
78
/* id of second timer.
79
Set to -1 during synchronisation.
85
const gchar* translate_func (const gchar * s, gpointer p)
88
translation function used for itemfactory
89
added by dancerj, Junichi Uekawa, 26 Jul 2003.
95
void AboutDialog( GtkWidget *w, gpointer clocklist )
99
GString *copyright, *comments, *licence;
102
copyright = g_string_new( "Drew Parsons <dparsons@emerall.com> " );
104
g_string_append(copyright, RELEASE_DATE);
107
licence = g_string_new( _("Released under the General Public License (GPL) v2.0") );
109
comments = g_string_new( _( "gworldclock allows you to keep track of the current time"
110
" in time zones all round the world. You may also \"rendezvous\""
111
" your time zones to a specified time and date.") );
114
/* build the about dialog */
116
dialog = gtk_about_dialog_new();
118
#ifdef PACKAGE_VERSION
119
gtk_about_dialog_set_version( GTK_ABOUT_DIALOG(dialog), PACKAGE_VERSION );
121
gtk_about_dialog_set_copyright( GTK_ABOUT_DIALOG(dialog), copyright->str );
122
gtk_about_dialog_set_comments( GTK_ABOUT_DIALOG(dialog), comments->str );
123
gtk_about_dialog_set_license( GTK_ABOUT_DIALOG(dialog) , licence->str );
124
gtk_about_dialog_set_translator_credits( GTK_ABOUT_DIALOG(dialog), _("translator-credits") );
126
gtk_show_about_dialog( NULL,
127
"copyright", gtk_about_dialog_get_copyright( GTK_ABOUT_DIALOG(dialog) ),
128
"license", gtk_about_dialog_get_license( GTK_ABOUT_DIALOG(dialog) ),
129
"comments", gtk_about_dialog_get_comments( GTK_ABOUT_DIALOG(dialog) ),
132
g_string_free(copyright,TRUE);
133
g_string_free(comments,TRUE);
134
g_string_free(licence,TRUE);
139
/* ensure we go through the same save routine when quitting from the menu (C-Q)
140
or by pressing the window's 'close' button */
141
void send_clock_quit( GtkWidget *w, gpointer clocklist )
145
extern gint OKtoQuit;
147
g_signal_emit_by_name((GObject *) gtk_widget_get_toplevel(
148
(GtkWidget *)g_object_get_data(clocklist, MAIN_WINDOW)),
149
"delete_event", &event, &return_val);
151
/* why didn't the above send "delete", which then sends "destroy"??
152
Have to "destroy" by hand... */
159
/* save the config data when quitting, if necessary */
160
gint worldclock_quit(GtkWidget *widget,
165
extern gint OKtoQuit;
166
extern GString *configfile;
170
gchar *title="Save Zones";
172
gchar *buttons3[] = { "Yes", "No", "Cancel" };
173
gchar *buttons2[] = { "Yes", "No" };
179
msg = g_string_new(NULL);
180
g_string_sprintf(msg," Do you want to save your modified zone list? ");
181
dialog = gtk_message_dialog_new( NULL,
183
GTK_MESSAGE_QUESTION,
186
gtk_dialog_add_buttons( GTK_DIALOG(dialog),
187
"Cancel", GTK_RESPONSE_CANCEL,
189
choice = gtk_dialog_run( GTK_DIALOG(dialog) );
190
gtk_widget_destroy (dialog);
191
g_string_free(msg,TRUE);
192
if(choice==GTK_RESPONSE_YES) /* yes, save */
194
if (!SaveZones(widget, clocklist))
196
msg = g_string_new(NULL);
197
g_string_sprintf(msg," Error saving zone file \"%s\": \n %s \n\n Continue quitting? ",
198
configfile->str, g_strerror(errno));
199
dialog = gtk_message_dialog_new( NULL,
201
GTK_MESSAGE_QUESTION,
205
choice = gtk_dialog_run (GTK_DIALOG (dialog));
206
gtk_widget_destroy (dialog);
208
g_string_free(msg,TRUE);
209
if(choice==GTK_RESPONSE_NO) /* no, don't quit if there was an error */
218
else if (choice==GTK_RESPONSE_NO) /* no, don't save */
223
else /* cancel from quitting */
234
/* Process mouse click when a zone in the list is selected.
235
Double-click with left mouse button (button 1) pops up the
236
"Change Description" dialog box.
237
Single-click with right button (button 3) pops up the menu of actions.
239
static gboolean ButtonPressedInList (GtkWidget *clocklist,
240
GdkEventButton *event,
243
GtkTreeModel *clocklistModel;
245
static GtkWidget *popup;
247
gboolean returnVal = FALSE;
249
if( (event->button==1) && ( event->type==GDK_2BUTTON_PRESS))
251
ChangeZoneDescription(clocklist, (gpointer)clocklist);
254
/* attempting to identify when reorder has occurred
255
Will probably have to wipe this code
256
else if ( (event->type==GDK_BUTTON_RELEASE) && (event->button==1))
258
g_print("released button 1 for row %d\n", selectedRow);
261
else if (event->button==1)
263
g_print("pressed button 1 for row %d\n", selectedRow);
267
else if (event->button==3)
269
if(!popup) { /* create only once! */
270
popup = gtk_ui_manager_get_widget (g_object_get_data(G_OBJECT(clocklist), UI_MANAGER), "/PopupMenu");
272
gtk_menu_popup (GTK_MENU(popup), NULL, NULL, NULL, NULL,
273
event->button, event->time);
281
int main( int argc, char *argv[] )
283
GtkWidget *window, *scrolled_window;
284
GtkWidget *main_vbox;
286
GtkListStore *clocklistModel;
287
GtkWidget *clocklist;
288
gchar *titles[2] = { N_("Time Zone"), N_("Time&Date") };
289
GtkCellRenderer *renderer;
290
GtkTreeViewColumn *column;
291
GtkTreeSelection *select;
295
GtkActionGroup *action_group;
296
GtkUIManager *ui_manager;
297
GtkAccelGroup *accel_group;
300
gint xoff, yoff, h, w;
303
gtk_init (&argc, &argv);
304
GetOptions(argc,argv);
305
bindtextdomain(PACKAGE_NAME, LOCALEDIR);
306
bind_textdomain_codeset (PACKAGE_NAME, "UTF-8");
307
textdomain(PACKAGE_NAME);
309
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
310
g_signal_connect (G_OBJECT (window), "destroy",
311
G_CALLBACK (gtk_main_quit),
313
gtk_window_set_title (GTK_WINDOW(window), "gworldclock");
315
main_vbox = gtk_vbox_new (FALSE, 5);
316
gtk_container_border_width (GTK_CONTAINER (main_vbox), 1);
317
gtk_container_add (GTK_CONTAINER (window), main_vbox);
318
gtk_widget_show (main_vbox);
320
/* Create a scrolled window to pack the list widget into */
321
scrolled_window = gtk_scrolled_window_new (NULL, NULL);
322
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
323
GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
325
gtk_box_pack_end(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
326
gtk_widget_show (scrolled_window);
329
/* the main clock object is created here */
330
clocklistModel = gtk_list_store_new (LIST_COLUMNS,
331
G_TYPE_STRING, /* TZ name */
332
G_TYPE_STRING, /* TZ description */
333
G_TYPE_STRING /* time/date */
336
clocklist = gtk_tree_view_new_with_model (GTK_TREE_MODEL (clocklistModel));
337
gtk_tree_view_set_reorderable( GTK_TREE_VIEW(clocklist), TRUE );
338
g_signal_connect(G_OBJECT(clocklistModel), "rows_reordered",
339
G_CALLBACK(registerReorderedRows), NULL);
341
renderer = gtk_cell_renderer_text_new ();
342
column = gtk_tree_view_column_new_with_attributes (gettext(titles[0]),
344
"text", TZ_DESCRIPTION,
346
gtk_tree_view_column_set_sizing( column, GTK_TREE_VIEW_COLUMN_AUTOSIZE );
348
g_signal_connect(G_OBJECT(column), "notify::width",
349
G_CALLBACK(updateColumnWidth), clocklist);
350
gtk_tree_view_append_column (GTK_TREE_VIEW (clocklist), column);
352
renderer = gtk_cell_renderer_text_new ();
353
column = gtk_tree_view_column_new_with_attributes (gettext(titles[1]),
357
gtk_tree_view_column_set_sizing( column, GTK_TREE_VIEW_COLUMN_AUTOSIZE );
358
gtk_tree_view_append_column (GTK_TREE_VIEW (clocklist), column);
360
select = gtk_tree_view_get_selection (GTK_TREE_VIEW (clocklist));
361
gtk_tree_selection_set_mode (select, GTK_SELECTION_SINGLE);
363
g_signal_connect(G_OBJECT(clocklist),
364
"button_press_event",
365
G_CALLBACK(ButtonPressedInList),
368
gtk_container_add(GTK_CONTAINER(scrolled_window), clocklist);
369
start_clocks((gpointer)clocklist);
370
gtk_widget_show (clocklist);
372
g_object_set_data(G_OBJECT(clocklist), MAIN_WINDOW, window);
373
g_signal_connect (G_OBJECT (window), "delete_event",
374
G_CALLBACK (worldclock_quit), (gpointer)clocklist);
377
action_group = gtk_action_group_new ("UIActions");
378
gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), clocklist);
379
gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), clocklist);
380
g_object_set_data(G_OBJECT(clocklist), ACTIONS, action_group);
383
ui_manager = gtk_ui_manager_new ();
384
gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
386
accel_group = gtk_ui_manager_get_accel_group (ui_manager);
387
gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
390
if (!gtk_ui_manager_add_ui_from_string (ui_manager, ui_description, -1, &error))
392
g_message ("building menus failed: %s", error->message);
393
g_error_free (error);
397
menubar = gtk_ui_manager_get_widget (ui_manager, "/MainMenu");
398
gtk_box_pack_start (GTK_BOX (main_vbox), menubar, FALSE, FALSE, 0);
400
g_object_set_data(G_OBJECT(clocklist), UI_MANAGER, ui_manager);
403
/* stick the synchronise control panel at the top and hide it */
404
syncBox = constructSyncBox((gpointer)clocklist);
405
gtk_box_pack_start(GTK_BOX(main_vbox), (GtkWidget *)syncBox, FALSE, FALSE, 0);
406
gtk_widget_hide((GtkWidget *) syncBox );
408
/* calculate the time each second */
409
timer = start_timer( (gpointer)clocklist );
411
gtk_widget_show (window);
412
resizeWindow( window, GTK_TREE_VIEW(clocklist) );