001package apps.gui3; 002 003import jmri.util.gui.GuiLafPreferencesManager; 004 005import java.awt.BorderLayout; 006import java.awt.Component; 007import java.awt.Cursor; 008import java.awt.Dimension; 009import java.awt.FlowLayout; 010import java.awt.Image; 011import java.awt.Toolkit; 012import java.awt.event.ActionEvent; 013import java.util.ArrayList; 014import java.util.HashMap; 015import java.util.Locale; 016 017import javax.swing.BoxLayout; 018import javax.swing.ImageIcon; 019import javax.swing.JButton; 020import javax.swing.JComboBox; 021import javax.swing.JComponent; 022import javax.swing.JLabel; 023import javax.swing.JPanel; 024import javax.swing.JTextArea; 025import javax.swing.JTextField; 026import javax.swing.border.BevelBorder; 027 028import jmri.Application; 029import jmri.ConfigureManager; 030import jmri.InstanceManager; 031import jmri.jmrit.logix.WarrantPreferences; 032import jmri.jmrit.roster.RosterConfigManager; 033import jmri.jmrix.AbstractConnectionConfig; 034import jmri.jmrix.ConnectionConfig; 035import jmri.jmrix.JmrixConfigPane; 036import jmri.jmrix.PortAdapter; 037import jmri.profile.Profile; 038import jmri.profile.ProfileManager; 039import jmri.util.FileUtil; 040import jmri.util.prefs.InitializationException; 041import jmri.util.swing.JmriJOptionPane; 042 043public class FirstTimeStartUpWizard implements Thread.UncaughtExceptionHandler { 044 045 Image splashIm; 046 047 jmri.util.JmriJFrame parent; 048 private final JmrixConfigPane connectionConfigPane = JmrixConfigPane.createNewPanel(); 049 050 public FirstTimeStartUpWizard(jmri.util.JmriJFrame parent, apps.gui3.Apps3 app) { 051 this.parent = parent; 052 this.app = app; 053 mainWizardPanel.setLayout(new BorderLayout()); 054 055 localeNames = new String[Locale.getAvailableLocales().length]; 056 057 mainWizardPanel.add(createTopBanner(), BorderLayout.NORTH); 058 059 mainWizardPanel.add(createHelpPanel(), BorderLayout.WEST); 060 061 mainWizardPanel.add(createEntryPanel(), BorderLayout.CENTER); 062 063 mainWizardPanel.add(createButtonPanel(), BorderLayout.SOUTH); 064 } 065 066 JLabel header = new JLabel(); 067 068 JPanel createTopBanner() { 069 JPanel top = new JPanel(); 070 071 header.setText("Welcome to JMRI StartUp Wizard"); 072 top.add(header); 073 074 return top; 075 } 076 077 JPanel createHelpPanel() { 078 splashIm = Toolkit.getDefaultToolkit().getImage(FileUtil.findURL("resources/logo.gif", FileUtil.Location.INSTALLED)); 079 ImageIcon img = new ImageIcon(splashIm, "JMRI splash screen"); 080 int imageWidth = img.getIconWidth(); 081 minHelpFieldDim = new Dimension(imageWidth, 20); 082 maxHelpFieldDim = new Dimension((imageWidth + 20), 350); 083 helpPanel.setPreferredSize(maxHelpFieldDim); 084 helpPanel.setMaximumSize(maxHelpFieldDim); 085 helpPanel.setLayout( 086 new BoxLayout(helpPanel, BoxLayout.Y_AXIS)); 087 088 JLabel l = new JLabel(img); 089 l.setAlignmentX(Component.CENTER_ALIGNMENT); 090 l.setOpaque(false); 091 helpPanel.add(l); 092 return helpPanel; 093 } 094 095 ArrayList<WizardPage> wizPage = new ArrayList<>(); 096 097 void createScreens() { 098 firstWelcome(); 099 setDefaultOwner(); 100 setConnection(); 101 finishAndConnect(); 102 } 103 104 public void dispose() { 105 Cursor normalCursor = new Cursor(Cursor.DEFAULT_CURSOR); 106 parent.setCursor(normalCursor); 107 app.createAndDisplayFrame(); 108 parent.setVisible(false); 109 parent.dispose(); 110 } 111 112 apps.gui3.Apps3 app; 113 114 JPanel entryPanel = new JPanel(); 115 JPanel helpPanel = new JPanel(); 116 117 JComponent createEntryPanel() { 118 createScreens(); 119 for (int i = 0; i < wizPage.size(); i++) { 120 entryPanel.add(wizPage.get(i).getPanel()); 121 helpPanel.add(wizPage.get(i).getHelpDetails()); 122 } 123 wizPage.get(0).getPanel().setVisible(true); 124 wizPage.get(0).getHelpDetails().setVisible(true); 125 header.setFont(header.getFont().deriveFont(14f)); 126 return entryPanel; 127 } 128 129 void setDefaultOwner() { 130 JPanel p = new JPanel(); 131 132 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); 133 p.add(formatText("Select your language<br>")); 134 initalLocale = Locale.getDefault(); 135 p.add(doLocale()); 136 137 p.add(formatText("<br>Enter in the default owner for all your loco roster entries<p>If you are part of group or club, where different people will be accessing DecoderPro, then you can leave this blank</p>")); 138 JPanel p2 = new JPanel(); 139 p2.setLayout(new FlowLayout()); 140 p2.add(new JLabel(/*rb.getString("LabelDefaultOwner")*/"Default Owner")); 141 142 owner.setText(InstanceManager.getDefault(RosterConfigManager.class).getDefaultOwner()); 143 if (owner.getText().isEmpty()) { 144 owner.setText(System.getProperty("user.name")); 145 } 146 p2.add(owner); 147 p.add(p2); 148 149 wizPage.add(new WizardPage(p, new JPanel(), "Set the Default Language and Owner")); 150 } 151 152 void setConnection() { 153 154 JPanel h = new JPanel(); 155 h.setLayout(new BoxLayout(h, BoxLayout.Y_AXIS)); 156 h.setMaximumSize(maxHelpFieldDim); 157 158 JTextArea text = new JTextArea("First select the manufacturer of your DCC system\n\nFollowed by the type of connection being used.\n\nFinally select the serial port or enter in the IP address of the device"); 159 text.setEditable(false); 160 text.setLineWrap(true); 161 text.setWrapStyleWord(true); 162 text.setOpaque(false); 163 text.setMinimumSize(minHelpFieldDim); 164 text.setMaximumSize(maxHelpFieldDim); 165 h.add(text); 166 167 wizPage.add(new WizardPage(this.connectionConfigPane, h, "Select your DCC Connection")); 168 } 169 170 void firstWelcome() { 171 JPanel p = new JPanel(); 172 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); 173 p.add(formatText("Welcome to JMRI's " + Application.getApplicationName() + "<p><br>This little wizard will help to guide you through setting up " + Application.getApplicationName() + " for the first time")); 174 175 wizPage.add(new WizardPage(p, new JPanel(), "Welcome to JMRI StartUp Wizard")); 176 } 177 178 Dimension minHelpFieldDim = new Dimension(160, 20); 179 Dimension maxHelpFieldDim = new Dimension(160, 300); 180 181 void finishAndConnect() { 182 JPanel p = new JPanel(); 183 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); 184 p.add(formatText("Configuration is now all complete, press finish below to connect to your system and start using " + Application.getApplicationName() + "\n\nIf at any time you need to change your settings, you can find the preference setting under the Edit Menu")); 185 wizPage.add(new WizardPage(p, new JPanel(), "Finish and Connect")); 186 } 187 188 JTextField owner = new JTextField(20); 189 190 int currentScreen = 0; 191 192 JPanel createButtonPanel() { 193 JPanel buttonPanel = new JPanel(); 194 buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT)); 195 buttonPanel.setBorder(new BevelBorder(BevelBorder.LOWERED)); 196 final JButton previous = new JButton("< Back"); 197 final JButton next = new JButton("Next >"); 198 final JButton finish = new JButton("Finish"); 199 finish.setVisible(false); 200 JButton cancel = new JButton("Cancel"); 201 cancel.addActionListener((java.awt.event.ActionEvent e) -> { 202 Locale.setDefault(initalLocale); 203 dispose(); 204 }); 205 206 previous.addActionListener((java.awt.event.ActionEvent e) -> { 207 if (currentScreen < wizPage.size()) { 208 wizPage.get(currentScreen).getPanel().setVisible(false); 209 wizPage.get(currentScreen).getHelpDetails().setVisible(false); 210 } 211 finish.setVisible(false); 212 213 currentScreen = currentScreen - 1; 214 if (currentScreen != -1) { 215 wizPage.get(currentScreen).getPanel().setVisible(true); 216 wizPage.get(currentScreen).getHelpDetails().setVisible(true); 217 header.setText(wizPage.get(currentScreen).getHeaderText()); 218 header.setFont(header.getFont().deriveFont(14f)); 219 220 if (currentScreen == 0) { 221 previous.setEnabled(false); 222 } 223 next.setEnabled(true); 224 next.setVisible(true); 225 } else { 226 currentScreen = 0; 227 previous.setEnabled(false); 228 } 229 }); 230 next.addActionListener((java.awt.event.ActionEvent e) -> { 231 wizPage.get(currentScreen).getPanel().setVisible(false); 232 wizPage.get(currentScreen).getHelpDetails().setVisible(false); 233 currentScreen++; 234 if (currentScreen < wizPage.size()) { 235 wizPage.get(currentScreen).getPanel().setVisible(true); 236 wizPage.get(currentScreen).getHelpDetails().setVisible(true); 237 header.setText(wizPage.get(currentScreen).getHeaderText()); 238 header.setFont(header.getFont().deriveFont(14f)); 239 previous.setEnabled(true); 240 if (currentScreen == (wizPage.size() - 1)) { 241 next.setEnabled(false); 242 next.setVisible(false); 243 finish.setVisible(true); 244 } 245 } 246 }); 247 248 finish.addActionListener((java.awt.event.ActionEvent e) -> { 249 Runnable r = new Connect(); 250 Thread connectThread = new Thread(r); 251 connectThread.start(); 252 connectThread.setName("Start-Up Wizard Connect"); 253 connectThread.setUncaughtExceptionHandler(this); 254 }); 255 256 buttonPanel.add(previous); 257 buttonPanel.add(next); 258 buttonPanel.add(new JLabel(" ")); // filler 259 buttonPanel.add(finish); 260 buttonPanel.add(cancel); 261 previous.setEnabled(false); 262 263 return buttonPanel; 264 } 265 266 @Override 267 public void uncaughtException(Thread t, Throwable e) { 268 showDialogue(e); 269 } 270 271 private void showDialogue(Throwable ex) { 272 log.error("Exception: ", ex); 273 Cursor normalCursor = new Cursor(Cursor.DEFAULT_CURSOR); 274 parent.setCursor(normalCursor); 275 var conn = connectionConfigPane.getCurrentObject(); 276 String connName = ( conn == null ? "No Connection " : conn.getConnectionName() ); 277 jmri.util.ThreadingUtil.runOnGUI(() -> { 278 JmriJOptionPane.showMessageDialog(parent, 279 "<html>An error occurred while trying to connect to " + connName 280 + ", <br>press the back button and check the connection details.<br>" 281 + ex.getLocalizedMessage() + "</html>", 282 "Error Opening Connection", 283 JmriJOptionPane.ERROR_MESSAGE); 284 }); 285 } 286 287 //The connection process is placed into its own thread so that it doens't hog the swingthread while waiting for the connections to open. 288 protected class Connect implements Runnable { 289 290 @Override 291 public void run() { 292 Cursor hourglassCursor = new Cursor(Cursor.WAIT_CURSOR); 293 parent.setCursor(hourglassCursor); 294 ConnectionConfig connect = connectionConfigPane.getCurrentObject(); 295 ConfigureManager cm = InstanceManager.getNullableDefault(jmri.ConfigureManager.class); 296 if (cm != null) { 297 cm.registerPref(connect); 298 } 299 if (connect instanceof jmri.jmrix.AbstractConnectionConfig) { 300 ((AbstractConnectionConfig) connect).updateAdapter(); 301 PortAdapter adp = connect.getAdapter(); 302 try { 303 adp.connect(); 304 adp.configure(); 305 } catch (Exception ex) { 306 showDialogue(ex); 307 return; 308 } 309 } 310 Profile project = ProfileManager.getDefault().getActiveProfile(); 311 InstanceManager.getDefault(RosterConfigManager.class).setDefaultOwner(project, owner.getText()); 312 InstanceManager.getDefault(GuiLafPreferencesManager.class).setLocale(Locale.getDefault()); 313 InstanceManager.getDefault(GuiLafPreferencesManager.class).setDefaultFontSize(); 314 InstanceManager.getDefault(GuiLafPreferencesManager.class).setFontSize( 315 InstanceManager.getDefault(GuiLafPreferencesManager.class).getDefaultFontSize()); 316 InstanceManager.getDefault(RosterConfigManager.class).savePreferences(project); 317 InstanceManager.getDefault(GuiLafPreferencesManager.class).savePreferences(project); 318 connectionConfigPane.savePreferences(); 319 try { 320 InstanceManager.getDefault(WarrantPreferences.class).initialize(project); 321 } catch ( InitializationException ex ){ 322 log.error("Exception Initialising warrant preferences: ", ex); 323 } 324 InstanceManager.getDefault(ConfigureManager.class).storePrefs(); 325 326 dispose(); 327 } 328 } 329 330 public JPanel doLocale() { 331 JPanel panel = new JPanel(); 332 // add JComboBoxen for language and country 333 panel.setLayout(new FlowLayout()); 334 localeBox = new JComboBox<>(new String[]{ 335 Locale.getDefault().getDisplayName(), 336 "(Please Wait)"}); 337 panel.add(localeBox); 338 339 // create object to find locales in new Thread 340 Runnable r = () -> { 341 Locale[] locales = Locale.getAvailableLocales(); 342 locale = new HashMap<>(); 343 for (int i = 0; i < locales.length; i++) { 344 locale.put(locales[i].getDisplayName(), locales[i]); 345 localeNames[i] = locales[i].getDisplayName(); 346 } 347 java.util.Arrays.sort(localeNames); 348 Runnable update = () -> { 349 localeBox.setModel(new javax.swing.DefaultComboBoxModel<>(localeNames)); 350 localeBox.setSelectedItem(Locale.getDefault().getDisplayName()); 351 }; 352 javax.swing.SwingUtilities.invokeLater(update); 353 }; 354 new Thread(r).start(); 355 356 localeBox.addActionListener((ActionEvent a) -> { 357 if (localeBox == null || locale == null) { 358 return; 359 } 360 String desired = (String) localeBox.getSelectedItem(); 361 Locale.setDefault(locale.get(desired)); 362 }); 363 364 return panel; 365 366 } 367 368 Locale initalLocale; 369 370 JLabel formatText(String text) { 371 JLabel label = new JLabel(); 372 label.setText("<html><body width='450'>" + text + "</html>"); 373 return label; 374 } 375 376 JComboBox<String> localeBox; 377 HashMap<String, Locale> locale; 378 final String[] localeNames; 379 380 JPanel mainWizardPanel = new JPanel(); 381 382 public JPanel getPanel() { 383 return mainWizardPanel; 384 } 385 386 static class WizardPage { 387 388 static Dimension defaultInfoSize = new Dimension(500, 300); 389 JComponent panel; 390 JPanel helpDetails = new JPanel(); 391 String headerText = ""; 392 393 WizardPage(JComponent mainPanel, JPanel helpDetails, String headerText) { 394 this.panel = mainPanel; 395 396 if (helpDetails != null) { 397 this.helpDetails = helpDetails; 398 this.helpDetails.setLayout( 399 new BoxLayout(this.helpDetails, BoxLayout.Y_AXIS)); 400 } 401 if (this.panel != null) { 402 this.panel.setPreferredSize(defaultInfoSize); 403 this.panel.setVisible(false); 404 } 405 this.helpDetails.setVisible(false); 406 this.headerText = headerText; 407 } 408 409 JComponent getPanel() { 410 return panel; 411 } 412 413 JPanel getHelpDetails() { 414 return helpDetails; 415 } 416 417 String getHeaderText() { 418 return headerText; 419 } 420 421 } 422 423 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(FirstTimeStartUpWizard.class); 424 425}