]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/function/AddMapSourceDialog.java
Version 13.4, May 2012
[GpsPrune.git] / tim / prune / function / AddMapSourceDialog.java
1 package tim.prune.function;
2
3 import java.awt.BorderLayout;
4 import java.awt.CardLayout;
5 import java.awt.Component;
6 import java.awt.FlowLayout;
7 import java.awt.GridBagConstraints;
8 import java.awt.GridBagLayout;
9 import java.awt.GridLayout;
10 import java.awt.Insets;
11 import java.awt.event.ActionEvent;
12 import java.awt.event.ActionListener;
13 import java.awt.event.KeyAdapter;
14 import java.awt.event.KeyEvent;
15
16 import javax.swing.BorderFactory;
17 import javax.swing.ButtonGroup;
18 import javax.swing.JButton;
19 import javax.swing.JComboBox;
20 import javax.swing.JDialog;
21 import javax.swing.JLabel;
22 import javax.swing.JPanel;
23 import javax.swing.JRadioButton;
24 import javax.swing.JTextField;
25
26 import tim.prune.I18nManager;
27 import tim.prune.gui.map.CloudmadeMapSource;
28 import tim.prune.gui.map.MapSource;
29 import tim.prune.gui.map.MapSourceLibrary;
30 import tim.prune.gui.map.OsmMapSource;
31
32 /**
33  * Class to handle the adding of a new map source
34  */
35 public class AddMapSourceDialog
36 {
37         private SetMapBgFunction _parent = null;
38         private JDialog _addDialog = null;
39         private JRadioButton[] _sourceTypeRadios = null;
40         private JPanel _cards = null;
41         private MapSource _originalSource = null;
42         // controls for osm panel
43         private JTextField _oNameField = null;
44         private JTextField _baseUrlField = null, _topUrlField = null;
45         private JRadioButton[] _baseTypeRadios = null, _topTypeRadios = null;
46         private JComboBox _oZoomCombo = null;
47         // controls for cloudmade panel
48         private JTextField _cNameField = null;
49         private JTextField _cStyleField = null;
50         private JComboBox _cZoomCombo = null;
51         private JButton _okButton = null;
52
53         /** array of file types */
54         private static final String[] FILE_TYPES = {"png", "jpg", "gif"};
55
56
57         /**
58          * Constructor
59          * @param inParent parent dialog
60          */
61         public AddMapSourceDialog(JDialog inParentDialog, SetMapBgFunction inParentFunction)
62         {
63                 _parent = inParentFunction;
64                 _addDialog = new JDialog(inParentDialog, I18nManager.getText("dialog.addmapsource.title"), true);
65                 _addDialog.add(makeDialogComponents());
66                 _addDialog.setLocationRelativeTo(inParentDialog);
67                 _addDialog.pack();
68         }
69
70
71         /**
72          * Create dialog components
73          * @return Panel containing all gui elements in dialog
74          */
75         private Component makeDialogComponents()
76         {
77                 JPanel dialogPanel = new JPanel();
78                 dialogPanel.setLayout(new BorderLayout());
79                 // Top panel with two radio buttons to select source type
80                 JPanel radioPanel = new JPanel();
81                 ButtonGroup radioGroup = new ButtonGroup();
82                 radioPanel.setLayout(new GridLayout(1, 0));
83                 _sourceTypeRadios = new JRadioButton[2];
84                 _sourceTypeRadios[0] = new JRadioButton("Openstreetmap");
85                 radioGroup.add(_sourceTypeRadios[0]);
86                 radioPanel.add(_sourceTypeRadios[0]);
87                 _sourceTypeRadios[1] = new JRadioButton("Cloudmade");
88                 radioGroup.add(_sourceTypeRadios[1]);
89                 radioPanel.add(_sourceTypeRadios[1]);
90                 _sourceTypeRadios[0].setSelected(true);
91                 // listener for clicks on type radios
92                 ActionListener typeListener = new ActionListener() {
93                         public void actionPerformed(ActionEvent arg0) {
94                                 onRadioClicked();
95                         }
96                 };
97                 _sourceTypeRadios[0].addActionListener(typeListener);
98                 _sourceTypeRadios[1].addActionListener(typeListener);
99                 radioPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
100                 dialogPanel.add(radioPanel, BorderLayout.NORTH);
101
102                 _cards = new JPanel();
103                 _cards.setLayout(new CardLayout());
104                 // listener
105                 KeyAdapter keyListener = new KeyAdapter() {
106                         public void keyReleased(KeyEvent e) {
107                                 super.keyReleased(e);
108                                 enableOK();
109                         }
110                 };
111                 // openstreetmap panel
112                 JPanel osmPanel = new JPanel();
113                 osmPanel.setLayout(new BorderLayout());
114                 osmPanel.setBorder(BorderFactory.createEmptyBorder(6, 3, 4, 3));
115                 JPanel gbPanel = new JPanel();
116                 GridBagLayout gridbag = new GridBagLayout();
117                 GridBagConstraints c = new GridBagConstraints();
118                 gbPanel.setLayout(gridbag);
119                 c.gridx = 0; c.gridy = 0;
120                 c.gridheight = 1; c.gridwidth = 1;
121                 c.weightx = 0.0; c.weighty = 0.0;
122                 c.ipadx = 3; c.ipady = 5;
123                 c.insets = new Insets(0, 0, 5, 0);
124                 c.anchor = GridBagConstraints.FIRST_LINE_START;
125                 gbPanel.add(new JLabel(I18nManager.getText("dialog.addmapsource.sourcename")), c);
126                 _oNameField = new JTextField(18);
127                 _oNameField.addKeyListener(keyListener);
128                 c.gridx = 1; c.weightx = 1.0;
129                 gbPanel.add(_oNameField, c);
130                 // Base layer
131                 c.gridx = 0; c.gridy = 1;
132                 c.weightx = 0.0;
133                 c.insets = new Insets(0, 0, 0, 0);
134                 gbPanel.add(new JLabel(I18nManager.getText("dialog.addmapsource.layer1url")), c);
135                 _baseUrlField = new JTextField(18);
136                 _baseUrlField.addKeyListener(keyListener);
137                 c.gridx = 1; c.weightx = 1.0;
138                 gbPanel.add(_baseUrlField, c);
139                 _baseTypeRadios = new JRadioButton[3];
140                 radioGroup = new ButtonGroup();
141                 for (int i=0; i<3; i++)
142                 {
143                         _baseTypeRadios[i] = new JRadioButton(FILE_TYPES[i]);
144                         radioGroup.add(_baseTypeRadios[i]);
145                         c.gridx = 2+i; c.weightx = 0.0;
146                         gbPanel.add(_baseTypeRadios[i], c);
147                 }
148
149                 // Top layer
150                 c.gridx = 0; c.gridy = 2;
151                 gbPanel.add(new JLabel(I18nManager.getText("dialog.addmapsource.layer2url")), c);
152                 _topUrlField = new JTextField(18);
153                 _topUrlField.addKeyListener(keyListener);
154                 c.gridx = 1; c.weightx = 1.0;
155                 gbPanel.add(_topUrlField, c);
156                 _topTypeRadios = new JRadioButton[3];
157                 radioGroup = new ButtonGroup();
158                 for (int i=0; i<3; i++)
159                 {
160                         _topTypeRadios[i] = new JRadioButton(FILE_TYPES[i]);
161                         radioGroup.add(_topTypeRadios[i]);
162                         c.gridx = 2+i; c.weightx = 0.0;
163                         gbPanel.add(_topTypeRadios[i], c);
164                 }
165                 // Max zoom
166                 c.gridx = 0; c.gridy = 3;
167                 gbPanel.add(new JLabel(I18nManager.getText("dialog.addmapsource.maxzoom")), c);
168                 _oZoomCombo = new JComboBox();
169                 for (int i=10; i<=20; i++) {
170                         _oZoomCombo.addItem("" + i);
171                 }
172                 c.gridx = 1;
173                 gbPanel.add(_oZoomCombo, c);
174                 osmPanel.add(gbPanel, BorderLayout.NORTH);
175                 _cards.add(osmPanel, "card1");
176
177                 // Panel for cloudmade source
178                 JPanel cloudPanel = new JPanel();
179                 cloudPanel.setBorder(BorderFactory.createEmptyBorder(6, 3, 4, 3));
180                 // Use a gridlayout inside a borderlayout to avoid stretching
181                 cloudPanel.setLayout(new BorderLayout());
182                 JPanel cloudGridPanel = new JPanel();
183                 cloudGridPanel.setLayout(new GridLayout(0, 2, 5, 5));
184                 cloudGridPanel.add(new JLabel(I18nManager.getText("dialog.addmapsource.sourcename")));
185                 _cNameField = new JTextField(18);
186                 _cNameField.addKeyListener(keyListener);
187                 cloudGridPanel.add(_cNameField);
188                 cloudGridPanel.add(new JLabel(I18nManager.getText("dialog.addmapsource.cloudstyle")));
189                 _cStyleField = new JTextField(18);
190                 _cStyleField.addKeyListener(keyListener);
191                 cloudGridPanel.add(_cStyleField);
192                 cloudGridPanel.add(new JLabel(I18nManager.getText("dialog.addmapsource.maxzoom")));
193                 _cZoomCombo = new JComboBox();
194                 for (int i=10; i<=20; i++) {
195                         _cZoomCombo.addItem("" + i);
196                 }
197                 cloudGridPanel.add(_cZoomCombo);
198                 cloudPanel.add(cloudGridPanel, BorderLayout.NORTH);
199                 _cards.add(cloudPanel, "card2");
200                 // cards
201                 JPanel holderPanel = new JPanel();
202                 holderPanel.setLayout(new BorderLayout());
203                 holderPanel.add(_cards, BorderLayout.NORTH);
204                 dialogPanel.add(holderPanel, BorderLayout.CENTER);
205
206                 // button panel at bottom
207                 JPanel buttonPanel = new JPanel();
208                 buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
209                 _okButton = new JButton(I18nManager.getText("button.ok"));
210                 ActionListener okListener = new ActionListener() {
211                         public void actionPerformed(ActionEvent e)
212                         {
213                                 finish();
214                         }
215                 };
216                 _okButton.addActionListener(okListener);
217                 buttonPanel.add(_okButton);
218                 JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
219                 cancelButton.addActionListener(new ActionListener() {
220                         public void actionPerformed(ActionEvent e)
221                         {
222                                 _addDialog.dispose();
223                         }
224                 });
225                 buttonPanel.add(cancelButton);
226                 dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
227                 return dialogPanel;
228         }
229
230
231         /**
232          * Init and show the dialog
233          * @param inSource source object before edit, or null to add
234          */
235         public void showDialog(MapSource inSource)
236         {
237                 _originalSource = inSource;
238                 populateFields();
239         }
240
241         /**
242          * Clear all the dialog fields to prepare for an add
243          */
244         private void clearAllFields()
245         {
246                 _oNameField.setText("");
247                 _baseUrlField.setText("");
248                 _baseTypeRadios[0].setSelected(true);
249                 _topUrlField.setText("");
250                 _topTypeRadios[0].setSelected(true);
251                 _oZoomCombo.setSelectedIndex(8);
252                 _cNameField.setText("");
253                 _cStyleField.setText("");
254                 _cZoomCombo.setSelectedIndex(8);
255                 _okButton.setEnabled(false);
256                 for (int i=0; i<2; i++) {
257                         _sourceTypeRadios[i].setEnabled(true);
258                 }
259                 _addDialog.setVisible(true);
260         }
261
262         /**
263          * Init the dialog fields from the given source object
264          */
265         private void populateFields()
266         {
267                 if (_originalSource == null)
268                 {
269                         clearAllFields();
270                         return;
271                 }
272                 boolean sourceFound = false;
273                 // See if it's a cloudmade source
274                 try
275                 {
276                         CloudmadeMapSource cloudSource = (CloudmadeMapSource) _originalSource;
277                         sourceFound = true;
278                         _cNameField.setText(cloudSource.getName());
279                         _cStyleField.setText(cloudSource.getStyle());
280                         _cZoomCombo.setSelectedIndex(getZoomIndex(cloudSource.getMaxZoomLevel()));
281                         _sourceTypeRadios[1].setSelected(true);
282                 }
283                 catch (ClassCastException cce) {} // ignore, sourceFound flag stays false
284
285                 // See if it's an osm source
286                 if (!sourceFound)
287                 {
288                         try
289                         {
290                                 OsmMapSource osmSource = (OsmMapSource) _originalSource;
291                                 sourceFound = true;
292                                 _oNameField.setText(osmSource.getName());
293                                 _baseUrlField.setText(osmSource.getBaseUrl(0));
294                                 int baseType = getBaseType(osmSource.getFileExtension(0));
295                                 _baseTypeRadios[baseType].setSelected(true);
296                                 _topUrlField.setText(osmSource.getNumLayers()==0?"":osmSource.getBaseUrl(1));
297                                 int topType = getBaseType(osmSource.getFileExtension(1));
298                                 _topTypeRadios[topType].setSelected(true);
299                                 _oZoomCombo.setSelectedIndex(getZoomIndex(osmSource.getMaxZoomLevel()));
300                                 _sourceTypeRadios[0].setSelected(true);
301                         }
302                         catch (ClassCastException cce) {} // ignore, sourceFound flag stays false
303                 }
304                 for (int i=0; i<2; i++) {
305                         _sourceTypeRadios[i].setEnabled(false);
306                 }
307                 onRadioClicked();
308                 _okButton.setEnabled(false);
309                 _addDialog.setVisible(true);
310         }
311
312
313         /**
314          * React to one of the type radio buttons being clicked
315          */
316         private void onRadioClicked()
317         {
318                 CardLayout cl = (CardLayout) _cards.getLayout();
319                 if (_sourceTypeRadios[0].isSelected()) {cl.first(_cards);}
320                 else {cl.last(_cards);}
321                 enableOK();
322         }
323
324         /**
325          * Check the currently entered details and enable the OK button if it looks OK
326          */
327         private void enableOK()
328         {
329                 boolean ok = false;
330                 if (_sourceTypeRadios[0].isSelected()) {ok = isOsmPanelOk();}
331                 if (_sourceTypeRadios[1].isSelected()) {ok = isCloudPanelOk();}
332                 _okButton.setEnabled(ok);
333         }
334
335         /**
336          * Check the openstreetmap panel if all details are complete
337          * @return true if details look ok
338          */
339         private boolean isOsmPanelOk()
340         {
341                 boolean ok = _oNameField.getText().trim().length() > 1;
342                 String baseUrl = null, topUrl = null;
343                 // Try to parse base url if given
344                 String baseText = _baseUrlField.getText().trim();
345                 baseUrl = MapSource.fixBaseUrl(baseText);
346                 if (baseText.length() > 0 && baseUrl == null) {ok = false;}
347                 // Same again for top url if given
348                 String topText = _topUrlField.getText().trim();
349                 topUrl = MapSource.fixBaseUrl(topText);
350                 if (topText.length() > 0 && topUrl == null) {ok = false;}
351                 // looks ok if at least one url given
352                 return (ok && (baseUrl != null || topUrl != null));
353         }
354
355         /**
356          * Check the cloudmade panel if all details are complete
357          * @return true if details look ok
358          */
359         private boolean isCloudPanelOk()
360         {
361                 boolean ok = _cNameField.getText().trim().length() > 1;
362                 int styleNum = 0;
363                 try {
364                         styleNum = Integer.parseInt(_cStyleField.getText());
365                 }
366                 catch (NumberFormatException nfe) {}
367                 return (ok && styleNum > 0);
368         }
369
370         /**
371          * Finish by adding the requested source and refreshing the parent
372          */
373         private void finish()
374         {
375                 MapSource newSource = null;
376                 String origName = (_originalSource == null ? null : _originalSource.getName());
377                 if (_sourceTypeRadios[0].isSelected())
378                 {
379                         // Openstreetmap source
380                         String sourceName = getValidSourcename(_oNameField.getText(), origName);
381                         String url1 = _baseUrlField.getText().trim();
382                         String ext1 = getFileExtension(_baseTypeRadios);
383                         String url2 = _topUrlField.getText().trim();
384                         String ext2 = getFileExtension(_topTypeRadios);
385                         newSource = new OsmMapSource(sourceName, url1, ext1, url2, ext2, _oZoomCombo.getSelectedIndex()+10);
386                 }
387                 else if (_sourceTypeRadios[1].isSelected())
388                 {
389                         String sourceName = getValidSourcename(_cNameField.getText(), origName);
390                         newSource = new CloudmadeMapSource(sourceName, _cStyleField.getText(),
391                                 _cZoomCombo.getSelectedIndex()+10);
392                 }
393                 // Add new source if ok
394                 if (newSource != null)
395                 {
396                         if (_originalSource == null) {
397                                 MapSourceLibrary.addSource(newSource);
398                         }
399                         else {
400                                 MapSourceLibrary.editSource(_originalSource, newSource);
401                         }
402                         // inform setmapbg dialog
403                         _parent.updateList();
404                         _addDialog.setVisible(false);
405                 }
406         }
407
408         /**
409          * Check the given source name is valid and whether it exists in library already
410          * @param inName name to check
411          * @param inOriginalName name of source before edit (or null for new source)
412          * @return valid name for the new source
413          */
414         private static String getValidSourcename(String inName, String inOriginalName)
415         {
416                 String name = inName;
417                 if (name == null) {name = "";}
418                 else {name = name.trim();}
419                 if (name.equals("")) {
420                         name = I18nManager.getText("dialog.addmapsource.noname");
421                 }
422                 // Check there isn't already a map source with this name
423                 if (inOriginalName == null || !inOriginalName.equals(name))
424                 {
425                         if (MapSourceLibrary.hasSourceName(name))
426                         {
427                                 int suffix = 1;
428                                 while (MapSourceLibrary.hasSourceName(name + suffix)) {
429                                         suffix++;
430                                 }
431                                 name += suffix;
432                         }
433                 }
434                 return name;
435         }
436
437         /**
438          * Get the selected file extension
439          * @param inRadios array of radio buttons for selection
440          * @return selected file extension
441          */
442         private String getFileExtension(JRadioButton[] inRadios)
443         {
444                 if (inRadios != null)
445                 {
446                         for (int i=0; i<inRadios.length; i++) {
447                                 if (inRadios[i] != null && inRadios[i].isSelected()) {
448                                         return FILE_TYPES[i];
449                                 }
450                         }
451                 }
452                 return FILE_TYPES[0];
453         }
454
455         /**
456          * Get the index of the given image extension
457          * @param inExt file extension, such as "png"
458          * @return index from 0 to 2
459          */
460         private static int getBaseType(String inExt)
461         {
462                 for (int i=0; i<FILE_TYPES.length; i++) {
463                         if (FILE_TYPES[i].equals(inExt)) {
464                                 return i;
465                         }
466                 }
467                 // Not found so default to png
468                 return 0;
469         }
470
471         /**
472          * Get the dropdown index of the given zoom level
473          * @param inZoomLevel zoom level, eg 18
474          * @return index of dropdown to select
475          */
476         private static int getZoomIndex(int inZoomLevel)
477         {
478                 return Math.max(0, inZoomLevel - 10);
479         }
480 }