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