-
Notifications
You must be signed in to change notification settings - Fork 0
/
gworldclock.c.no_time_t
1722 lines (1409 loc) · 52.7 KB
/
gworldclock.c.no_time_t
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* gworldclock
This program is designed to keep track of the time and date in various
timezones around the world.
The zones are kept in a configuration file (default ${XDG_CONFIG_HOME}/gworldclock/tzlist.list), one zone
per line. Each line has one or two entries: the first is the TZ value
corresponding, the second is an optional description string enclosed in
inverted commas ('"').
The config file is compatible for use with tzwatch, a shell script
writing the time in the given zones to stdout.
Note, time_t is limited. It apparently resolves to a 32bit integer (on x86 at
least), which only lets the clocks go back to 6:46am, 14 Dec 1901
(2147483648 seconds before 1 Jan 1970).
On the other side, the limit is 19 January 2038, 3:13am.
It might therefore be an idea to only use struct tm, never time_t.
However time_t is a useful concept...
To do:
- update list of days in month to be correct for the given month
- check autoconf stuff (why aren't .h's recognised by make?)
- have someone compile it for Win32 so I can give it to Kostya & Andrei
*/
/* Copyright (C) 2000-2001 Drew F. Parsons
*
* 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; ( version 2 of the License at time of
* writing).
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <errno.h>
#include <unistd.h> /* getopt */
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include "gworldclock.h"
#include "dialog.h"
/* so many global variables....how naughty!
collect them in a structure to be passed around? */
gint changed=0;
gint OKtoQuit=1;
gint selectedRow=-1;
gchar *defaultConfigFilename="/.tzlist";
GString *configfile;
gchar *ZONE_TABLE="/usr/share/zoneinfo/zone.tab";
gchar *COUNTRY_TABLE="/usr/share/zoneinfo/iso3166.tab";
/* id of second timer.
Set to -1 during synchronisation.
*/
gint timer;
GtkBox *syncBox;
void GetOptions( int argc, char **argv )
{
extern GString *configfile;
int c;
while ((c = getopt (argc, argv, "f:")) != -1)
switch (c)
{
case 'f':
configfile = g_string_new(g_strdup((gchar *)optarg));
break;
default:
break;
}
}
void get_main_menu( GtkWidget *window,
GtkWidget **menubar,
GtkWidget *clocklist)
/* adapted from menu example in GTK+ tutorial */
{
GtkItemFactory *item_factory;
GtkAccelGroup *accel_group;
gint nmenu_items = sizeof (mainmenu_items) / sizeof (mainmenu_items[0]);
accel_group = gtk_accel_group_new ();
item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>",
accel_group);
gtk_item_factory_create_items (item_factory, nmenu_items, mainmenu_items, (gpointer)clocklist);
gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
if (menubar)
/* Finally, return the actual menu bar created by the item factory. */
*menubar = gtk_item_factory_get_widget (item_factory, "<main>");
}
void start_clocks(GtkWidget *clocklist)
{
FILE *cf;
extern gint changed;
extern GString *configfile;
extern gchar *defaultConfigFilename;
/* format of list:
name of timezone (TZ variable), description, time in zone
*/
gchar *rowdata[2], *localstr="Local";
/* is there a way of reading the information without hardwiring '200' here?
eg can flag 'a' in sscanf work with %[ as well as %s ? */
gchar description[200], *timezone, line[200];
int row;
GString *title, *msg;
gchar *button[]={"OK"};
/* ??? associate configfile with list as widget data ?? */
rowdata[1]=NULL; /* necessary to set to NULL, or segfault related
to List Reordering results */
/* if configfile not already defined, set to default file in home directory */
if (!configfile) {
configfile = g_string_new(g_strdup((gchar *) getenv("XDG_CONFIG_HOME")));
g_string_append(configfile,defaultConfigFilename);
}
cf=fopen(configfile->str,"r");
if (cf) {
while(fgets(line,200,cf)) {
/* configfile is assumed to have two entries per line,
the TZ value followed by a description enclosed in quotation marks.
If the description is missing, the TZ string is used as a description instead */
/* the second entry looks complicated, but just copies the whole set of
characters between two quotation marks
[finds the first '"', accounting for any spaces or tabs,
then grabs everything up to the second '"'] */
if ( sscanf(line,"%as %*[ \t\"]%[^\"]",&timezone,description) < 2 )
strncpy(description,timezone,200);
rowdata[0]=g_strdup(description);
row=gtk_clist_append( (GtkCList *) clocklist,rowdata);
gtk_clist_set_row_data( (GtkCList *) clocklist, row, g_strdup(timezone));
g_free(rowdata[0]);
}
fclose(cf);
}
else {
title = g_string_new("Read Zone File");
msg = g_string_new(NULL);
g_string_sprintf(msg," Error reading zone file \"%s\": \n %s \n",
configfile->str, g_strerror(errno) );
/* note: the do_dialog window cannot yet be made non-modal
We'd have to do it by hand to do that */
do_dialog(title->str, msg->str, 1, button, 1, NULL, NULL, TRUE);
g_string_free(msg,TRUE);
g_string_free(title,TRUE);
rowdata[0]=g_strdup(localstr);
row=gtk_clist_append( (GtkCList *) clocklist,rowdata);
gtk_clist_set_row_data( (GtkCList *) clocklist, row, g_strdup(rowdata[0]));
g_free(rowdata[0]);
changed=1;
}
SetTime((gpointer)clocklist);
}
gint SetTime(gpointer clocklist)
{
time_t currenttime;
struct tm timeStruct, *tp;
gchar *TZdefault;
TZdefault = (gchar *)getenv("TZ");
/* current time must be passed as UTC */
setenv("TZ", "UTC", 1);
time(¤ttime);
tzset();
tp = localtime_r( ¤ttime, &timeStruct );
if (TZdefault)
setenv("TZ",TZdefault,1);
else
unsetenv("TZ");
printf("set current time to %s\n", asctime( localtime_r( ¤ttime, &timeStruct ) ) );
SetToGivenTime( clocklist, tp );
return 1;
}
/* UTCtimeToSet must be in universal time, as the name suggests */
gint SetToGivenTime(gpointer clocklist, struct tm *UTCtimeToSet)
{
gchar *timezone, *TZdefault;
gchar *text, text2[200];
gint N,i;
time_t t;
struct tm timeStruct, *tp;
printf("setting time to %s\n", asctime( UTCtimeToSet ) );
TZdefault = (gchar *)getenv("TZ");
setenv("TZ", "UTC", 1);
t = mktime( UTCtimeToSet );
tp = localtime_r( &t, &timeStruct );
printf("time via time_t = %s (%s)\n", ctime(&t), asctime( tp ));
N = ((GtkCList *)clocklist)->rows;
gtk_clist_freeze( (GtkCList *) clocklist );
for(i=0;i<N;i++) {
timezone=gtk_clist_get_row_data(( GtkCList *)clocklist, i);
if(strcasecmp(timezone,"Local"))
setenv("TZ",timezone,1);
else {
/* local time is set by the ordinary value of TZ */
if (TZdefault)
setenv("TZ",TZdefault,1);
else
unsetenv("TZ");
}
tzset();
ctime(&t);
printf("time in zone = %s\n", asctime( UTCtimeToSet ));
/* tp *only* gets updated for zone when ctime is used in this same line.
doing it above isn't good enough. That's sick. */
tzset();
ctime(&t);
printf("time via time_t = (%s)\n", asctime( tp ));
printf("time via time_t = %s (%s)\n", ctime(&t), asctime( tp ));
printf("time via time_t = (%s) %s\n", asctime( tp ), ctime(&t));
printf("time via time_t = (%s)\n", asctime( tp ));
/************************************************
THIS IS THE CRUCIAL LINE
why I can't avoid time_t. asctime will not adjust for a different
timezone, unless ctime is call in the same line, see examples above.
Pathetic really. */
/* gtk_clist_set_text( (GtkCList *)clocklist,i,1,asctime( UTCtimeToSet ));*/
/************************************************/
/* unless.... */
/* asprintf( &text, "%s %s", ctime(&t), asctime( UTCtimeToSet ) );*/
strftime( text2, sizeof(text2), "%x %X", UTCtimeToSet );
gtk_clist_set_text( (GtkCList *)clocklist,i,1, strdup(text2));
/* free(text);*/
/** or better.. UTCtimeToSet->tm_gmtoff = 0 for UTC.
Change it to the appropriate value for the time zone, and you're done :)
But daylight savings makes it tricky.
*/
}
gtk_clist_thaw( (GtkCList *) clocklist );
if (TZdefault)
setenv("TZ",TZdefault,1);
else
unsetenv("TZ");
return 1;
}
gint start_timer( gpointer clocklist )
{
return gtk_timeout_add( 1000, SetTime, clocklist);
}
void stop_timer()
{
gtk_timeout_remove( timer );
/* set timer to -1 to show that there is no timer, and therefore
synchronisation is taking place */
timer = -1;
}
void select_row_callback(GtkWidget *widget,
gint row,
gint column,
GdkEventButton *event,
gpointer data)
{
extern gint selectedRow;
selectedRow = row;
}
void AboutDialog( gpointer clocklist )
{
GString *title, *about;
gchar *button[]={"OK"};
/* at some point in the future strings like these will have to
be reorganised to allow for internationalisation */
title = g_string_new("About gworldclock");
about = g_string_new("gworldclock\n\n");
g_string_append(about, "v ");
g_string_append(about, VERSION);
g_string_append(about, "\n(C) Drew Parsons <dparsons@emerall.com> ");
g_string_append(about, RELEASE_DATE);
g_string_append(about, "\nReleased under the General Public License (GPL) v2.0\n");
g_string_append(about, "\ngworldclock allows you to keep track of the current time\n");
g_string_append(about, "in time zones all round the world.\n");
g_string_append(about, "You may also synchronise your time zones to a specified time and date.");
do_dialog(title->str, about->str, 1, button, 1, NULL, NULL, TRUE);
g_string_free(about,TRUE);
g_string_free(title,TRUE);
}
void DeleteZone( gpointer clocklist )
{
extern gint changed;
extern gint selectedRow;
GString *title, *msg;
gchar *button[]={"OK"};
if((selectedRow >= 0) && (selectedRow < ((GtkCList *)clocklist)->rows) ) {
gtk_clist_remove( (GtkCList *)clocklist, selectedRow);
changed = 1;
}
else {
title = g_string_new("Delete Zone");
msg = g_string_new("No zone chosen for deleting.");
/* note: the do_dialog window cannot yet be made non-modal
We'd have to do it by hand to do that */
do_dialog(title->str, msg->str, 1, button, 1, NULL, NULL, TRUE);
g_string_free(msg,TRUE);
g_string_free(title,TRUE);
}
}
/* Save list of timezones to configfile */
gint SaveZones(gpointer clocklist)
{
FILE *cf;
extern GString *configfile;
extern gint changed;
gint N,i;
gchar *description;
if ( !(cf=fopen(configfile->str,"w")) ) {
return 0;
}
N = ((GtkCList *)clocklist)->rows;
gtk_clist_freeze( (GtkCList *) clocklist );
for(i=0;i<N;i++) {
gtk_clist_get_text( (GtkCList *) clocklist, i, 0, &description);
/* only write description if there is one! */
if(strlen(description)==0)
fprintf(cf,"%s\n",
(gchar *)gtk_clist_get_row_data(( GtkCList *)clocklist, i) );
else
fprintf(cf,"%s \"%s\"\n",
(gchar *)gtk_clist_get_row_data(( GtkCList *)clocklist, i),
description );
}
changed=0;
gtk_clist_thaw( (GtkCList *) clocklist );
return 1;
}
/* returns name of month (as defined by locale)
corresponding to given index (between 1 and 12) */
gchar* getNameOfMonth( gint index )
{
gchar *number, monthName[50];
struct tm tmonth;
if ( (index < 1) || (index > 12) ) {
return NULL;
}
asprintf(&number, "%i", index );
strptime(number, "%m", &tmonth);
strftime(monthName, sizeof(monthName), "%B", &tmonth);
free(number);
return strdup( monthName );
}
/* returns the index (between 1 and 12 ) of the given month,
identified by name */
gint getMonthIndex( gchar *monthName )
{
struct tm tmonth;
strptime(monthName, "%B", &tmonth);
/* don't forget to add 1, since struct tm counts months 0 to 11 */
return ( tmonth.tm_mon + 1 );
}
/* set the display values for date and time in the SyncBox to the current time */
void setDefaultSyncValues(gchar *description, gchar *timezone)
{
GtkWidget *label;
GtkWidget *hour;
GtkWidget *minute;
GtkWidget *day;
GtkWidget *month;
GtkWidget *year;
struct tm *timeSet, timeSetStruct;
time_t t; /* for grabbing current time only */
gchar *TZdefault;
gchar *s, *oldtimezone;
timeSet = &timeSetStruct;
label = gtk_object_get_data( (GtkObject *) syncBox, "label" );
gtk_label_set_text( (GtkLabel *) label, description);
/* initialise time from sync time if syncing has started */
if ( timer == -1) {
oldtimezone = gtk_object_get_data( (GtkObject *) syncBox, "timezone" );
extractSyncTime(timeSet);
}
gtk_object_set_data( (GtkObject *) syncBox, "timezone", (gpointer) timezone );
day = gtk_object_get_data( (GtkObject *) syncBox, "day" );
month = gtk_object_get_data( (GtkObject *) syncBox, "month" );
year = gtk_object_get_data( (GtkObject *) syncBox, "year" );
hour = gtk_object_get_data( (GtkObject *) syncBox, "hour" );
minute = gtk_object_get_data( (GtkObject *) syncBox, "minute" );
TZdefault = (gchar *)getenv("TZ");
/* assign timezone in which time is to be given, if not "Local". */
if(strcasecmp(timezone,"Local"))
setenv("TZ",timezone,1);
/* initialise time from current time if syncing hasn't started yet */
if ( timer != -1 ) {
time(&t);
timeSet = localtime_r( &t, timeSet );
}
/* must read in timezone here, if we don't want to use localtime(&t) */
tzset();
/* initialise timeSet, setting timezone info */
/* timeSet = localtime_r( &t, timeSet ); */
asprintf( &s, "%i", timeSet->tm_mday);
gtk_entry_set_text( GTK_ENTRY(GTK_COMBO(day)->entry), strdup(s) );
free( s );
s = getNameOfMonth( (timeSet->tm_mon) + 1 );
gtk_entry_set_text( GTK_ENTRY(GTK_COMBO(month)->entry), strdup(s) );
free( s );
asprintf( &s, "%i", (1900 + timeSet->tm_year) );
gtk_spin_button_set_value( (GtkSpinButton *)year, atoi(s) );
/* gtk_entry_set_text( GTK_ENTRY(GTK_COMBO(year)->entry), strdup(s) );*/
free( s );
asprintf( &s, "%.2i", timeSet->tm_hour);
gtk_entry_set_text( GTK_ENTRY(hour), strdup(s) );
free( s );
asprintf( &s, "%.2i", timeSet->tm_min);
gtk_entry_set_text( GTK_ENTRY(minute), strdup(s) );
free( s );
/* ensure user's default timezone is not lost */
if (TZdefault)
setenv("TZ",TZdefault,1);
else
unsetenv("TZ");
}
void Synchronise(gpointer clocklist)
{
extern gint changed;
extern gint selectedRow;
GString *title, *msg;
gchar *button[]={"OK"};
GdkWindow *window;
gint width, height;
gchar *description, *timezone;
if((selectedRow >= 0) && (selectedRow < ((GtkCList *)clocklist)->rows) ) {
gtk_clist_get_text( (GtkCList *) clocklist, selectedRow, 0, &description);
timezone=gtk_clist_get_row_data(( GtkCList *)clocklist, selectedRow);
/* set sync values to current time */
setDefaultSyncValues(description, timezone);
if ( timer != -1 ) {
/* freeze timer */
stop_timer();
/* adjust size of window to fit syncbox */
window = gtk_widget_get_parent_window( (GtkWidget *) syncBox );
gdk_window_get_size(window, &width, &height);
gdk_window_resize( window, width, height + SYNCBOX_HEIGHT );
}
/* and change display to correspond to given time */
gtk_widget_show( (GtkWidget *) syncBox );
}
else {
title = g_string_new("Delete Zone");
msg = g_string_new("No zone chosen for synchronising.");
do_dialog(title->str, msg->str, 1, button, 1, NULL, NULL, TRUE);
g_string_free(msg,TRUE);
g_string_free(title,TRUE);
}
}
/* reads the values set in the syncBox fields and turns them into
a struct tm time. */
struct tm *extractSyncTime( struct tm *tp)
{
int newDay, newMonth, newYear, newHour, newMinute;
GtkWidget *hour;
GtkWidget *minute;
GtkWidget *day;
GtkWidget *month;
GtkWidget *year;
gchar *timezone;
time_t t; /* for initialising timezone via time() only! */
struct tm *timeSet, timeSetStruct;
gchar *TZdefault;
gchar *s;
timeSet = tp; /*&timeSetStruct;*/
day = gtk_object_get_data( (GtkObject *) syncBox, "day" );
month = gtk_object_get_data( (GtkObject *) syncBox, "month" );
year = gtk_object_get_data( (GtkObject *) syncBox, "year" );
hour = gtk_object_get_data( (GtkObject *) syncBox, "hour" );
minute = gtk_object_get_data( (GtkObject *) syncBox, "minute" );
newDay = atoi( gtk_entry_get_text( GTK_ENTRY(GTK_COMBO(day)->entry) ) );
/* don't forget to knock 1 off, since struct tm counts months 0 to 11 */
newMonth = getMonthIndex( gtk_entry_get_text( GTK_ENTRY(GTK_COMBO(month)->entry) ) ) - 1;
/* newYear = atoi( gtk_entry_get_text( GTK_ENTRY(GTK_COMBO(year)->entry) ) ) - 1900; */
newYear = gtk_spin_button_get_value_as_int( (GtkSpinButton *) year ) - 1900;
printf("change year to %i (%i)\n",gtk_spin_button_get_value_as_int( (GtkSpinButton *) year) , newYear );
newHour = atoi( gtk_entry_get_text( GTK_ENTRY(hour) ) );
newMinute = atoi( gtk_entry_get_text( GTK_ENTRY(minute) ) );
TZdefault = (gchar *)getenv("TZ");
/* assign timezone in which time is to be given, if not "Local". */
timezone = gtk_object_get_data( (GtkObject *) syncBox, "timezone" );
if(strcasecmp(timezone,"Local"))
setenv("TZ",timezone,1);
/* initialise timeSet, setting timezone info */
time(&t);
tzset();
timeSet = localtime_r( &t, timeSet );
/* set new day to get proper daylight savings calculations */
timeSet->tm_mday = newDay;
timeSet->tm_mon = newMonth;
timeSet->tm_year = newYear;
timeSet->tm_hour = newHour;
timeSet->tm_min = newMinute;
printf("in initial year %i, time is %s and t=%li\n", timeSet->tm_year, asctime(timeSet), t );
/* reset timeSet to time on new day, to make sure daylight savings is set properly */
/* t = mktime( timeSet );
timeSet = localtime_r( &t, timeSet );
printf("in middle year %i, time is %s (%s) and t=%li\n", timeSet->tm_year, asctime(timeSet), ctime(&t), t );
*/
/* and set new time (set everything just to be sure) */
/* timeSet->tm_mday = newDay;
timeSet->tm_mon = newMonth;
timeSet->tm_year = newYear;
timeSet->tm_hour = newHour;
timeSet->tm_min = newMinute;
/* set seconds to zero for neatness */
/* timeSet->tm_sec = 0;
t = mktime( timeSet );
printf("in final year %i, time is %s (%s) and t=%li\n", timeSet->tm_year, asctime(timeSet), ctime(&t), t );
*/
/* ensure user's default timezone is not lost */
if (TZdefault)
setenv("TZ",TZdefault,1);
else
unsetenv("TZ");
/* note: t crashes (mktime returns -1) at 6:45am, 14 Dec 1901
At 6:46am, t=-2147483640 (2^31=2147483648) - 32bit int limitation :( useless! */
return timeSet;
}
/* callback function to be run if any of the sync fields are changed.
Updates the times in each zone to the new sync time.
The changed field is referenced as field, but is not used as such.
*/
void synchroniseTimes( GtkWidget *field, gpointer clocklist)
{
struct tm timeStruct, *tp;
tp = &timeStruct;
/* grab sync time */
tp = extractSyncTime(tp);
printf("syncing to %s\n", asctime(tp) );
/* and display it */
SetToGivenTime( clocklist, tp );
}
/* after done synchronising zones, return back to current time */
void unsynchronise( GtkWidget *button, gpointer clocklist)
{
GdkWindow *window;
gint width, height;
SetTime( clocklist );
gtk_widget_hide( (GtkWidget *) syncBox );
/* adjust window to normal size */
window = gtk_widget_get_parent_window( (GtkWidget *) syncBox );
gdk_window_get_size(window, &width, &height);
gdk_window_resize( window, width, height - SYNCBOX_HEIGHT );
timer = start_timer( clocklist );
}
/* ensure we go through the same save routine when quitting from the menu (C-Q)
or by pressing the window's 'close' button */
static void send_clock_quit( gpointer clocklist )
{
GdkEvent event;
gint return_val;
extern gint OKtoQuit;
gtk_signal_emit_by_name((GtkObject *) gtk_widget_get_toplevel((GtkWidget *)clocklist),
"delete_event", &event, &return_val);
/* why didn't the above send "delete", which then sends "destroy"??
Have to "destroy" by hand... */
if(OKtoQuit)
gtk_main_quit();
else
OKtoQuit=1;
}
/* save the config data when quitting, if necessary */
gint worldclock_quit(GtkWidget *widget,
GdkEvent *event,
gpointer clocklist )
{
extern gint changed;
extern gint OKtoQuit;
extern GString *configfile;
gint choice;
gchar *title="Save Zones";
GString *msg;
gchar *buttons3[] = { "Yes", "No", "Cancel" };
gchar *buttons2[] = { "Yes", "No" };
if(changed) {
msg = g_string_new(NULL);
g_string_sprintf(msg," Do you want to save your modified zone list? ");
choice = do_dialog(title, msg->str, 3, buttons3, 1, NULL, NULL, TRUE);
g_string_free(msg,TRUE);
if(choice==1) { /* yes, save */
if (!SaveZones(clocklist)) {
msg = g_string_new(NULL);
g_string_sprintf(msg," Error saving zone file \"%s\": \n %s \n\n Continue quitting? ",
configfile->str, g_strerror(errno));
choice = do_dialog(title, msg->str, 2, buttons2, 1, NULL, NULL, TRUE);
g_string_free(msg,TRUE);
if(choice==2) { /* no, don't quit if there was an error */
OKtoQuit=0;
return TRUE;
}
}
OKtoQuit=1;
return FALSE;
}
else if (choice==2) { /* no, don't save */
OKtoQuit=1;
return FALSE;
}
else {
OKtoQuit=0;
return TRUE;
}
}
return FALSE;
}
gint CodeInList(gchar *code, GSList *List)
{
/* can't use g_slist_find, unfortunately, since the data in the list is both the
country code and (unknown) country name
*/
GSList *item;
item = List;
while (item) {
if ( ! strcmp(code,((NameCodeType *)item->data)->code) )
return TRUE; /* found match */
item = item->next;
}
return FALSE;
}
GSList* AddNameCodeEntry(gchar *code, gchar *name, GSList *List)
{
NameCodeType *entry;
entry = g_malloc0(sizeof(NameCodeType));
if(!entry)
g_print("Could not create list: %s",g_strerror(errno));
entry->name = g_strdup(name);
entry->code = g_strdup(code);
List = g_slist_append(List, (gpointer)entry);
/* we don't free entry here do we? It's on record and is only to be freed when
the item is released from the list */
return List;
}
/* the documentation is not too clear about allocating and free lists */
/* does g_slist_free deallocate all the links in the list, or just the first? */
/* I will assume it does the entire list */
void ClearNameCodeList(GSList **List)
{
GSList *item;
if(*List) {
item = *List;
while (item) {
g_free( ((NameCodeType *)item->data)->name );
g_free( ((NameCodeType *)item->data)->code );
item = item->next;
}
g_slist_free(*List);
}
*List=NULL;
}
/* for given continent, find corresponding countries as identified in ZONE_TABLE
and prepare list of country name using COUNTRY_TABLE
*/
GSList* FetchCountries(gchar *continent)
{
FILE *fpc, *fpz;
GString *title, *msg;
gchar *button[]={"OK"};
gchar line[500];
gchar *codec, *codez, *name;
GSList *Countries;
if (strlen(continent)==0 )
return NULL;
if ( !(fpz=fopen(ZONE_TABLE,"r")) ) {
title = g_string_new("Read Zone Table");
msg = g_string_new(NULL);
g_string_sprintf(msg," Error reading zone table \"%s\": \n %s \nHow very sad.\n",
ZONE_TABLE , g_strerror(errno) );
/* note: the do_dialog window cannot yet be made non-modal
We'd have to do it by hand to do that */
do_dialog(title->str, msg->str, 1, button, 1, NULL, NULL, TRUE);
g_string_free(msg,TRUE);
g_string_free(title,TRUE);
}
if ( !(fpc=fopen(COUNTRY_TABLE,"r")) ) {
title = g_string_new("Read Zone Table");
msg = g_string_new(NULL);
g_string_sprintf(msg," Error reading country table \"%s\": \n %s \nHow very sad.\n",
COUNTRY_TABLE , g_strerror(errno) );
/* note: the do_dialog window cannot yet be made non-modal
We'd have to do it by hand to do that */
do_dialog(title->str, msg->str, 1, button, 1, NULL, NULL, TRUE);
g_string_free(msg,TRUE);
g_string_free(title,TRUE);
}
Countries=NULL;
while(fgets(line,500,fpz)) {
if (line[0] != '#') {
/* check for continent in TZ value (third item on the line in ZONE_TABLE)
Also read country code at beginning of zone table entry.
Strictly this is only 2 characters, but I will allow for a whole string
(in my opinion 3 character would be more meaningful. The standard sux */
sscanf(line,"%as %*s %as",&codez,&name);
if(strstr(name,continent)) {
if(!CodeInList(codez,Countries)) {
g_free(name);
rewind(fpc);
while(fgets(line,500,fpc)) {
if (line[0] != '#') {
/* first, identify country */
if(sscanf(line,"%as",&codec)==1) {
if (!strcmp(codez,codec)) {
/* then extract name as entire string to \0 after tab */
/* (first make sure \n in line is reset to \0) */
name = (gchar *) strchr(line,'\n');
*name = '\0';
name = (gchar *) strchr(line,'\t');
name++;
Countries = AddNameCodeEntry(codec,name,Countries);
}
g_free(codec);
}
}
}
}
}
else
g_free(name);
g_free(codez);
}
}
fclose(fpc);
fclose(fpz);
return Countries;
}
/* from given country code ("*country"), find list of regions in ZONE_TABLE */
/* input: country is the two-letter country code from ISO3166 */
GSList* FetchRegions(gchar *country)
{
FILE *fp;
GString *title, *msg;
gchar *button[]={"OK"};
gchar line[500];
gchar *code, *TZvalue, *region, *ptr;
GSList *Regions;
if (strlen(country)==0 )
return NULL;
if ( !(fp=fopen(ZONE_TABLE,"r")) ) {
title = g_string_new("Read Zone Table");
msg = g_string_new(NULL);
g_string_sprintf(msg," Error reading zone table \"%s\": \n %s \nHow very sad.\n",
ZONE_TABLE , g_strerror(errno) );
/* note: the do_dialog window cannot yet be made non-modal
We'd have to do it by hand to do that */
do_dialog(title->str, msg->str, 1, button, 1, NULL, NULL, TRUE);
g_string_free(msg,TRUE);
g_string_free(title,TRUE);
}
Regions=NULL;
while(fgets(line,500,fp)) {
if (line[0] != '#') {
/* check for entries corresponding to country code value
(first item on the line in ZONE_TABLE)
Get name of region from third item on the line. */
/* alternatively we may want to get the description from the optional
fourth item, where available. Worry about that some other time */
sscanf(line,"%as %*s %as",&code,&TZvalue);
if(!strcmp(code,country)) {
/* region name is in format: continent/region
Extract the region part from the continent */
ptr = (gchar *) strchr(TZvalue,'/');
if(ptr)
region = g_strdup((gchar*)(ptr+1));
else
region = g_strdup(TZvalue);
/* Some regions have an underscore '_' in place of space */
/* convert these to a real space */
while( (ptr=(gchar*)strchr(region,'_')) )
*ptr = ' ';
if(!CodeInList(TZvalue,Regions))
Regions = AddNameCodeEntry(TZvalue,region,Regions);
g_free(region);
}
g_free(TZvalue);
g_free(code);
}
}
fclose(fp);
return Regions;
}
typedef struct AddZoneStruct {
GtkWidget *clocklist;
GtkWidget *countryCList;
GtkWidget *regionCList;
GtkWidget *DescriptionEntry;
GtkWidget *TZEntry;
GString *continent;
GString *country;
gint row;
} AddZoneStruct;
void UpdateCountries(GtkWidget *ContinentCList,
gint row,
gint column,
GdkEventButton *event,
gpointer ZoneData)
{
gchar *continent, *ptr;
GSList *Countries, *item;
gchar *rowdata[1];
gint newRow;
GtkCList *clist;
gtk_clist_get_text( (GtkCList *) ContinentCList, row, 0, &ptr);
/* check value of continent here */
continent = g_strdup(ptr);
/* change "Americas" to "America" by wiping out the 's' */
if(!strcmp(continent,"Americas"))
continent[7]='\0';
/* similarly remove " Ocean" in "Pacific Ocean", etc
by making the ' ' a null character */
ptr = (gchar *) strchr(continent,' ');
if(ptr)
*ptr='\0';
clist = (GtkCList *) ((AddZoneStruct*)ZoneData)->countryCList;
Countries=FetchCountries(continent);
gtk_clist_freeze( clist );
gtk_clist_clear( clist );
item = Countries;
while(item) {
rowdata[0]=g_strdup( ((NameCodeType *)item->data)->name);
newRow=gtk_clist_append(clist, rowdata);
gtk_clist_set_row_data( clist, newRow,
g_strdup(((NameCodeType *)item->data)->code));
g_free(rowdata[0]);
item = item->next;
}
ClearNameCodeList(&Countries);
g_free(continent);