]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/function/SelectTimezoneFunction.java
Version 19, May 2018
[GpsPrune.git] / tim / prune / function / SelectTimezoneFunction.java
1 package tim.prune.function;
2
3 import java.awt.BorderLayout;
4 import java.awt.Component;
5 import java.awt.Dimension;
6 import java.awt.FlowLayout;
7 import java.awt.GridLayout;
8 import java.awt.event.ActionEvent;
9 import java.awt.event.ActionListener;
10 import java.awt.event.FocusAdapter;
11 import java.awt.event.FocusEvent;
12 import java.awt.event.FocusListener;
13 import java.awt.event.KeyAdapter;
14 import java.awt.event.KeyEvent;
15 import java.util.ArrayList;
16 import java.util.TimeZone;
17 import java.util.TreeSet;
18
19 import javax.swing.BorderFactory;
20 import javax.swing.BoxLayout;
21 import javax.swing.ButtonGroup;
22 import javax.swing.JButton;
23 import javax.swing.JDialog;
24 import javax.swing.JLabel;
25 import javax.swing.JPanel;
26 import javax.swing.JRadioButton;
27 import javax.swing.JScrollPane;
28 import javax.swing.event.ListSelectionEvent;
29 import javax.swing.event.ListSelectionListener;
30
31 import tim.prune.App;
32 import tim.prune.DataSubscriber;
33 import tim.prune.GenericFunction;
34 import tim.prune.I18nManager;
35 import tim.prune.UpdateMessageBroker;
36 import tim.prune.config.Config;
37 import tim.prune.gui.CombinedListAndModel;
38 import tim.prune.gui.GuiGridLayout;
39
40 /**
41  * Class to provide the gui for selecting an alternative timezone
42  */
43 public class SelectTimezoneFunction extends GenericFunction
44 {
45         /** Arraylist of timezone infos */
46         private ArrayList<TimezoneDetails> _zoneInfo;
47         /** Dialog */
48         private JDialog _dialog = null;
49         /** Radio button to select system timezone instead of using listboxes */
50         private JRadioButton _systemRadio = null;
51         /** Radio button to select timezone using listboxes */
52         private JRadioButton _customRadio = null;
53         /** Array of list boxes */
54         private CombinedListAndModel[] _listBoxes = null;
55         /** Label for selected zone */
56         private JLabel _selectedZoneLabel = null;
57         /** Label for offset of selected zone */
58         private JLabel _selectedOffsetLabel = null;
59         /** OK button for finishing */
60         private JButton _okButton = null;
61
62         private static final int LIST_REGIONS = 0;
63         private static final int LIST_OFFSETS = 1;
64         private static final int LIST_GROUPS  = 2;
65         private static final int LIST_NAMES   = 3;
66
67         /**
68          * Inner class for listening to list clicks
69          */
70         class ListListener implements ListSelectionListener
71         {
72                 private int _key = 0;
73                 /** Constructor */
74                 ListListener(int inKey) {_key = inKey;}
75                 /** Listen for selection changes */
76                 public void valueChanged(ListSelectionEvent inEvent) {
77                         if (!inEvent.getValueIsAdjusting()) {
78                                 processListClick(_key);
79                         }
80                 }
81         }
82
83         /** Inner class to hold categorisation info for a timezone */
84         class TimezoneDetails
85         {
86                 public String _id;
87                 public String _region;
88                 public int    _offset;
89                 public String _group;
90                 public String _name;
91         }
92
93         /**
94          * Constructor
95          * @param inApp App object
96          */
97         public SelectTimezoneFunction(App inApp)
98         {
99                 super(inApp);
100         }
101
102         /** Get the name key */
103         public String getNameKey() {
104                 return "function.selecttimezone";
105         }
106
107         /**
108          * Begin the function
109          */
110         public void begin()
111         {
112                 if (_dialog == null)
113                 {
114                         _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
115                         _dialog.setLocationRelativeTo(_parentFrame);
116                         _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
117                         _dialog.getContentPane().add(makeDialogComponents());
118                         _dialog.pack();
119                 }
120                 collectTimezoneInfo();
121                 _systemRadio.setText(I18nManager.getText("dialog.settimezone.system") + " ("
122                         + TimeZone.getDefault().getID() + ")");
123                 // Set up dialog according to current config
124                 String selectedTimezone = Config.getConfigString(Config.KEY_TIMEZONE_ID);
125                 if (selectedTimezone == null || selectedTimezone.equals(""))
126                 {
127                         _systemRadio.setSelected(true);
128                 }
129                 else
130                 {
131                         _customRadio.setSelected(true);
132                 }
133                 _dialog.setVisible(true);
134         }
135
136         /**
137          * Create dialog components
138          * @return Panel containing all gui elements in dialog
139          */
140         private Component makeDialogComponents()
141         {
142                 JPanel dialogPanel = new JPanel();
143                 dialogPanel.setLayout(new BorderLayout(5, 5));
144                 // Listener for radio buttons
145                 ActionListener radioListener = new ActionListener() {
146                         public void actionPerformed(ActionEvent inEvent) {
147                                 radioSelected(_systemRadio.isSelected());
148                         }
149                 };
150                 FocusListener radioFocusListener = new FocusAdapter() {
151                         public void focusGained(FocusEvent inEvent) {
152                                 radioSelected(_systemRadio.isSelected());
153                         }
154                 };
155
156                 // Panel at top
157                 JPanel topPanel = new JPanel();
158                 topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.Y_AXIS));
159                 JLabel topLabel = new JLabel(I18nManager.getText("dialog.settimezone.intro"));
160                 topLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
161                 topPanel.add(topLabel);
162                 _systemRadio = new JRadioButton(I18nManager.getText("dialog.settimezone.system"));
163                 _systemRadio.addActionListener(radioListener);
164                 _systemRadio.addFocusListener(radioFocusListener);
165                 topPanel.add(_systemRadio);
166                 _customRadio = new JRadioButton(I18nManager.getText("dialog.settimezone.custom"));
167                 _customRadio.addActionListener(radioListener);
168                 _customRadio.addFocusListener(radioFocusListener);
169                 topPanel.add(_customRadio);
170                 ButtonGroup radioGroup = new ButtonGroup();
171                 radioGroup.add(_systemRadio); radioGroup.add(_customRadio);
172                 dialogPanel.add(topPanel, BorderLayout.NORTH);
173
174                 // Main panel with box layout, list Panel with four lists in a grid
175                 JPanel mainPanel = new JPanel();
176                 mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
177
178                 JPanel listsPanel = new JPanel();
179                 listsPanel.setLayout(new GridLayout(1, 4));
180                 _listBoxes = new CombinedListAndModel[4];
181                 // First list for regions
182                 _listBoxes[LIST_REGIONS] = new CombinedListAndModel(0);
183                 // Add listener for list selection changes
184                 _listBoxes[LIST_REGIONS].addListSelectionListener(new ListListener(LIST_REGIONS));
185                 JScrollPane scrollPane = new JScrollPane(_listBoxes[LIST_REGIONS]);
186                 scrollPane.setPreferredSize(new Dimension(100, 200));
187                 scrollPane.setMinimumSize(new Dimension(100, 200));
188                 scrollPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
189                 listsPanel.add(scrollPane);
190
191                 // second list for offsets
192                 _listBoxes[LIST_OFFSETS] = new CombinedListAndModel(1);
193                 _listBoxes[LIST_OFFSETS].setMaxNumEntries(24);
194                 _listBoxes[LIST_OFFSETS].addListSelectionListener(new ListListener(LIST_OFFSETS));
195                 scrollPane = new JScrollPane(_listBoxes[LIST_OFFSETS]);
196                 scrollPane.setPreferredSize(new Dimension(100, 200));
197                 scrollPane.setMinimumSize(new Dimension(100, 200));
198                 scrollPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
199                 listsPanel.add(scrollPane);
200
201                 // third list for groups
202                 _listBoxes[LIST_GROUPS] = new CombinedListAndModel(2);
203                 _listBoxes[LIST_GROUPS].setMaxNumEntries(20);
204                 _listBoxes[LIST_GROUPS].addListSelectionListener(new ListListener(LIST_GROUPS));
205                 scrollPane = new JScrollPane(_listBoxes[LIST_GROUPS]);
206                 scrollPane.setPreferredSize(new Dimension(100, 200));
207                 scrollPane.setMinimumSize(new Dimension(100, 200));
208                 scrollPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
209                 listsPanel.add(scrollPane);
210
211                 // fourth list for names
212                 _listBoxes[LIST_NAMES] = new CombinedListAndModel(3);
213                 _listBoxes[LIST_NAMES].setMaxNumEntries(20);
214                 _listBoxes[LIST_NAMES].addListSelectionListener(new ListListener(LIST_NAMES));
215                 scrollPane = new JScrollPane(_listBoxes[LIST_NAMES]);
216                 scrollPane.setPreferredSize(new Dimension(100, 200));
217                 scrollPane.setMinimumSize(new Dimension(100, 200));
218                 scrollPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
219                 listsPanel.add(scrollPane);
220                 mainPanel.add(listsPanel);
221
222                 // Details labels underneath lists - description and offset
223                 JPanel detailsPanel = new JPanel();
224                 GuiGridLayout grid = new GuiGridLayout(detailsPanel);
225                 grid.add(new JLabel(I18nManager.getText("dialog.settimezone.selectedzone") + " :"));
226                 _selectedZoneLabel = new JLabel("");
227                 grid.add(_selectedZoneLabel);
228                 grid.add(new JLabel(I18nManager.getText("dialog.settimezone.offsetfromutc") + " :"));
229                 _selectedOffsetLabel = new JLabel("");
230                 grid.add(_selectedOffsetLabel);
231                 mainPanel.add(detailsPanel);
232                 dialogPanel.add(mainPanel, BorderLayout.CENTER);
233
234                 // close window if escape pressed
235                 KeyAdapter escListener = new KeyAdapter() {
236                         public void keyReleased(KeyEvent inE) {
237                                 if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) {
238                                         _dialog.dispose();
239                                 }
240                         }
241                 };
242                 _listBoxes[LIST_REGIONS].addKeyListener(escListener);
243                 _listBoxes[LIST_OFFSETS].addKeyListener(escListener);
244
245                 // button panel at bottom
246                 JPanel buttonPanel = new JPanel();
247                 buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
248                 // OK button
249                 _okButton = new JButton(I18nManager.getText("button.ok"));
250                 _okButton.addActionListener(new ActionListener() {
251                         public void actionPerformed(ActionEvent e) {
252                                 finishSelectTimezone();
253                         }
254                 });
255                 buttonPanel.add(_okButton);
256                 _okButton.addKeyListener(escListener);
257                 // Cancel button
258                 JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
259                 cancelButton.addActionListener(new ActionListener() {
260                         public void actionPerformed(ActionEvent e) {
261                                 _dialog.dispose();
262                         }
263                 });
264                 cancelButton.addKeyListener(new KeyAdapter() {
265                         public void keyPressed(KeyEvent inE) {
266                                 if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) {_dialog.dispose();}
267                         }
268                 });
269                 buttonPanel.add(cancelButton);
270                 dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
271                 return dialogPanel;
272         }
273
274         /**
275          * React to changes in the radio buttons
276          * @param inUseSystem true for system, false for custom
277          */
278         private void radioSelected(boolean inUseSystem)
279         {
280                 for (int i=0; i<_listBoxes.length; i++)
281                 {
282                         if (inUseSystem)
283                         {
284                                 _listBoxes[i].clear();
285                         }
286                         _listBoxes[i].setEnabled(!inUseSystem);
287                 }
288                 if (!inUseSystem)
289                 {
290                         populateTimezoneRegions();
291                         populateTimezoneOffsets(null);
292                         preselectTimezone(Config.getConfigString(Config.KEY_TIMEZONE_ID));
293                 }
294                 showTimezoneDetails();
295         }
296
297         /**
298          * React to a selection change on one of our lists
299          * @param inKey key of list which was clicked
300          */
301         private void processListClick(int inKey)
302         {
303                 final boolean offsetSelected = _listBoxes[LIST_OFFSETS].getSelectedItem() != null;
304                 final boolean groupSelected = _listBoxes[LIST_GROUPS].getSelectedItem() != null;
305                 // Update offsets?
306                 if (inKey == LIST_REGIONS)
307                 {
308                         populateTimezoneOffsets(_listBoxes[LIST_REGIONS].getSelectedItem());
309                 }
310                 // Update groups?
311                 if (inKey == LIST_OFFSETS
312                         || (inKey == LIST_REGIONS && !offsetSelected))
313                 {
314                         populateTimezoneGroups(_listBoxes[LIST_REGIONS].getSelectedItem(), _listBoxes[LIST_OFFSETS].getSelectedItem());
315                 }
316                 // Update names?
317                 if (inKey == LIST_GROUPS
318                         || (inKey <= LIST_OFFSETS && !groupSelected))
319                 {
320                         populateTimezoneNames(_listBoxes[LIST_REGIONS].getSelectedItem(), _listBoxes[LIST_OFFSETS].getSelectedItem(),
321                                 _listBoxes[LIST_GROUPS].getSelectedItem());
322                 }
323                 // Show the details of the selected timezone
324                 showTimezoneDetails();
325         }
326
327         /**
328          * Use the system information to populate the list of available timezones
329          */
330         private void collectTimezoneInfo()
331         {
332                 _zoneInfo = new ArrayList<TimezoneDetails>();
333                 for (String id : TimeZone.getAvailableIDs())
334                 {
335                         String region = getRegion(id);
336                         if (region != null)
337                         {
338                                 TimeZone tz = TimeZone.getTimeZone(id);
339                                 TimezoneDetails details = new TimezoneDetails();
340                                 details._id = id;
341                                 details._region = region;
342                                 details._offset = tz.getOffset(System.currentTimeMillis()) / 1000 / 60;
343                                 details._group = tz.getDisplayName();
344                                 details._name = getNameWithoutRegion(id);
345                                 _zoneInfo.add(details);
346                         }
347                 }
348         }
349
350         /**
351          * Populate the timezone regions into the region list
352          */
353         private void populateTimezoneRegions()
354         {
355                 _listBoxes[LIST_REGIONS].clear();
356                 TreeSet<String> regions = new TreeSet<String>();
357                 for (TimezoneDetails currZone : _zoneInfo)
358                 {
359                         regions.add(currZone._region);
360                 }
361                 for (String region : regions)
362                 {
363                         _listBoxes[LIST_REGIONS].addItem(region);
364                 }
365         }
366
367         /**
368          * Extract the timezone region from the id
369          */
370         private static String getRegion(String inId)
371         {
372                 final int slashPos = (inId == null ? -1 : inId.indexOf('/'));
373                 if (slashPos > 0)
374                 {
375                         return inId.substring(0, slashPos);
376                 }
377                 return null;
378         }
379
380         /**
381          * Populate the second listbox with the offsets for the given region
382          * @param inRegion selected region, or null if none selected
383          */
384         private void populateTimezoneOffsets(String inRegion)
385         {
386                 _listBoxes[LIST_OFFSETS].clear();
387                 TreeSet<Integer> offsetsinMinutes = new TreeSet<Integer>();
388                 for (TimezoneDetails currZone : _zoneInfo)
389                 {
390                         String region = currZone._region;
391                         if (inRegion == null || region.equals(inRegion))
392                         {
393                                 offsetsinMinutes.add(currZone._offset);
394                         }
395                 }
396                 for (Integer offset : offsetsinMinutes)
397                 {
398                         _listBoxes[LIST_OFFSETS].addItem(makeOffsetString(offset));
399                 }
400         }
401
402         /**
403          * @return String containing offset for display
404          */
405         private static String makeOffsetString(int inOffsetInMinutes)
406         {
407                 if (inOffsetInMinutes == 0) return "0";
408                 final boolean isWholeHours = (inOffsetInMinutes % 60) == 0;
409                 if (isWholeHours)
410                 {
411                         return (inOffsetInMinutes > 0 ? "+" : "") + (inOffsetInMinutes / 60);
412                 }
413                 final double numHours = inOffsetInMinutes / 60.0;
414                 return (inOffsetInMinutes > 0 ? "+" : "") + numHours;
415         }
416
417         /**
418          * Populate the group list using the specified region and offset
419          * @param inRegion selected region (if any) from the first list
420          * @param inOffset selected offset (if any) from the second list
421          */
422         private void populateTimezoneGroups(String inRegion, String inOffset)
423         {
424                 _listBoxes[LIST_GROUPS].clear();
425                 // Convert given offset string (in hours) into numeric offset (in minutes)
426                 final int offsetMins = convertToMinutes(inOffset);
427
428                 TreeSet<String> zoneGroups = new TreeSet<String>();
429                 for (TimezoneDetails currZone : _zoneInfo)
430                 {
431                         if (inRegion == null || currZone._region.equals(inRegion))
432                         {
433                                 if (offsetMins == -1 || offsetMins == currZone._offset)
434                                 {
435                                         zoneGroups.add(currZone._group);
436                                 }
437                         }
438                 }
439                 // If the region and offset were given, then list is unlimited
440                 _listBoxes[LIST_GROUPS].setUnlimited(inRegion != null && inOffset != null);
441                 // Add all the found names to the listbox
442                 for (String group : zoneGroups)
443                 {
444                         _listBoxes[LIST_GROUPS].addItem(group);
445                 }
446         }
447
448         /**
449          * Populate the group list using the specified region, offset and group
450          * @param inRegion selected region (if any) from the first list
451          * @param inOffset selected offset (if any) from the second list
452          * @param inGroup selected group (if any) from the third list
453          */
454         private void populateTimezoneNames(String inRegion, String inOffset, String inGroup)
455         {
456                 CombinedListAndModel nameList = _listBoxes[LIST_NAMES];
457                 nameList.clear();
458                 // Convert given offset string (in hours) into numeric offset (in minutes)
459                 final int offsetMins = convertToMinutes(inOffset);
460
461                 TreeSet<String> zoneNames = new TreeSet<String>();
462                 for (TimezoneDetails currZone : _zoneInfo)
463                 {
464                         if ((inRegion == null || currZone._region.equals(inRegion))
465                                 && (offsetMins == -1 || currZone._offset == offsetMins)
466                                 && (inGroup == null || currZone._group.equals(inGroup)))
467                         {
468                                 zoneNames.add(currZone._name);
469                         }
470                 }
471                 // If the region and offset were given, then list is unlimited
472                 nameList.setUnlimited(inRegion != null && inOffset != null && inRegion != null);
473                 // Add all the found names to the listbox
474                 for (String name : zoneNames)
475                 {
476                         nameList.addItem(name);
477                 }
478         }
479
480         /**
481          * Convert the given String from hours to minutes
482          * @param inOffsetInHours String from listbox in +/- hours
483          * @return offset in minutes, or -1
484          */
485         private static int convertToMinutes(String inOffsetInHours)
486         {
487                 int offsetMins = -1;
488                 try {
489                         offsetMins = (int) (60 * Double.parseDouble(inOffsetInHours));
490                 }
491                 catch (NumberFormatException nfe) {} // offset stays -1
492                 catch (NullPointerException npe) {} // offset stays -1
493                 return offsetMins;
494         }
495
496         /**
497          * Remove the timezone region from the id to just leave the name after the slash
498          */
499         private static String getNameWithoutRegion(String inId)
500         {
501                 final int slashPos = (inId == null ? -1 : inId.indexOf('/'));
502                 if (slashPos > 0)
503                 {
504                         return inId.substring(slashPos + 1);
505                 }
506                 return null;
507         }
508
509         /**
510          * Get the selected timezone, or null if none selected
511          */
512         private TimeZone getSelectedTimezone()
513         {
514                 if (_systemRadio.isSelected())
515                 {
516                         return TimeZone.getDefault();
517                 }
518
519                 String chosenRegion = _listBoxes[LIST_REGIONS].getSelectedItem();
520                 // Convert given offset string (in hours) into numeric offset (in minutes)
521                 final int offsetMins = convertToMinutes(_listBoxes[LIST_OFFSETS].getSelectedItem());
522                 String chosenGroup = _listBoxes[LIST_GROUPS].getSelectedItem();
523                 String chosenName = _listBoxes[LIST_NAMES].getSelectedItem();
524
525                 TreeSet<String> zoneIds = new TreeSet<String>();
526                 for (TimezoneDetails currZone : _zoneInfo)
527                 {
528                         if ((chosenRegion == null || currZone._region.equals(chosenRegion))
529                                 && (offsetMins == -1 || currZone._offset == offsetMins)
530                                 && (chosenGroup == null || currZone._group.equals(chosenGroup))
531                                 && (chosenName == null || currZone._name.equals(chosenName)))
532                         {
533                                 zoneIds.add(currZone._id);
534                                 if (zoneIds.size() > 1) {
535                                         break;  // exit loop now, we've got too many
536                                 }
537                         }
538                 }
539                 // Should have exactly one result now
540                 if (zoneIds.size() == 1)
541                 {
542                         return TimeZone.getTimeZone(zoneIds.first());
543                 }
544
545                 // none selected (yet)
546                 return null;
547         }
548
549         /**
550          * Show the details of the selected timezone
551          */
552         private void showTimezoneDetails()
553         {
554                 TimeZone selectedTimezone = getSelectedTimezone();
555                 if (selectedTimezone == null)
556                 {
557                         // Clear details labels
558                         _selectedZoneLabel.setText("");
559                         _selectedOffsetLabel.setText("");
560                 }
561                 else
562                 {
563                         // Fill results in labels
564                         String desc = selectedTimezone.getID() + " - " + selectedTimezone.getDisplayName();
565                         _selectedZoneLabel.setText(desc);
566                         String offsets = getOffsetDescription(selectedTimezone);
567                         _selectedOffsetLabel.setText(offsets);
568                 }
569                 _okButton.setEnabled(selectedTimezone != null);
570         }
571
572         /**
573          * @param inTimezone selected timezone
574          * @return String describing the time offset(s) of this zone including winter/summer time
575          */
576         private static String getOffsetDescription(TimeZone inTimezone)
577         {
578                 if (inTimezone == null)
579                 {
580                         return "";
581                 }
582                 TreeSet<Integer> offsetsinMinutes = new TreeSet<Integer>();
583                 long testTimeMillis = System.currentTimeMillis();
584                 final long testPeriodInMillis = 1000L * 60 * 60 * 24 * 30 * 2;
585                 for (int i=0; i<5; i++)
586                 {
587                         offsetsinMinutes.add(inTimezone.getOffset(testTimeMillis) / 1000 / 60);
588                         testTimeMillis += testPeriodInMillis;
589                 }
590                 // Make String describing the sorted set
591                 StringBuffer buff = new StringBuffer();
592                 for (Integer offset : offsetsinMinutes)
593                 {
594                         if (buff.length() > 0)
595                         {
596                                 buff.append(" / ");
597                         }
598                         buff.append(makeOffsetString(offset));
599                 }
600                 return buff.toString();
601         }
602
603         /**
604          * On entry to the dialog, select the items in each listbox
605          * according to the given preselected timezone id
606          * @param zoneId id of zone to select
607          */
608         private void preselectTimezone(String zoneId)
609         {
610                 TimeZone tz = (zoneId == null ? TimeZone.getDefault() : TimeZone.getTimeZone(zoneId));
611                 if (tz != null)
612                 {
613                         _listBoxes[LIST_REGIONS].selectItem(getRegion(zoneId));
614                         _listBoxes[LIST_OFFSETS].selectItem(makeOffsetString(tz.getOffset(System.currentTimeMillis()) / 1000 / 60));
615                         _listBoxes[LIST_GROUPS].selectItem(tz.getDisplayName());
616                         _listBoxes[LIST_NAMES].selectItem(getNameWithoutRegion(zoneId));
617                 }
618         }
619
620         /**
621          * Finish the dialog by setting the config according to the selected zone
622          */
623         private void finishSelectTimezone()
624         {
625                 TimeZone selectedTimezone = getSelectedTimezone();
626                 if (_systemRadio.isSelected() || selectedTimezone == null)
627                 {
628                         // Clear config, use default system timezone instead
629                         Config.setConfigString(Config.KEY_TIMEZONE_ID, null);
630                 }
631                 else
632                 {
633                         // Get selected timezone, set in config
634                         Config.setConfigString(Config.KEY_TIMEZONE_ID, selectedTimezone.getID());
635                 }
636                 _dialog.dispose();
637                 // Make sure listeners know to update themselves
638                 UpdateMessageBroker.informSubscribers(DataSubscriber.UNITS_CHANGED);
639         }
640 }