/* * Copyright 2011 Luke Usherwood. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.bettyluke.tracinstant; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.IOException; import java.net.Authenticator; import java.net.PasswordAuthentication; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.UIManager.LookAndFeelInfo; import net.bettyluke.tracinstant.data.CachedTicketLoadTask; import net.bettyluke.tracinstant.data.SiteData; import net.bettyluke.tracinstant.data.Ticket; import net.bettyluke.tracinstant.data.TicketLoadTask; import net.bettyluke.tracinstant.plugins.AnnotationPanel; import net.bettyluke.tracinstant.plugins.FindInTextPanel; import net.bettyluke.tracinstant.plugins.HistogramPane; import net.bettyluke.tracinstant.prefs.SiteSettings; import net.bettyluke.tracinstant.prefs.TracInstantProperties; import net.bettyluke.tracinstant.ui.SlurpAction; import net.bettyluke.tracinstant.ui.TracInstantFrame; public final class TracInstantApp { public static void main(String[] args) { SwingUtilities.invokeLater(() -> new TracInstantApp().startOnEDT()); } public TracInstantApp() { TracInstantProperties.initialise("bettyluke.net", "TracInstant"); } public void startOnEDT() { setLaF(); final SiteData site = new SiteData(); // In truth this is a HACK that wipes out Trac 1.0 data for in-house users who have // updated to Trac 1.2 around the same time that I'm adding password support. Users at // other companies (which currently amount to a total around zero) will have to click the // "Connect to" button themselves if they end up having duplicated columns, etc. due to the // field renaming that occurred. (I don't currently have a mechanism to detect Trac versions // that would be robust in the face of customised ticket fields.) if (!TracInstantProperties.hasPasswordSupport()) { site.reset(); } final TracInstantFrame frame = new TracInstantFrame(site); frame.installToolPanel(AnnotationPanel.createPlugin()); frame.installToolPanel(FindInTextPanel.createPlugin()); frame.installToolPanel(HistogramPane.createPlugin()); frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { frame.getSlurpAction().cancel(); frame.dispose(); site.saveState(); saveApplicationState(); } }); // HACK: Cache-loading should be pretty darn fast, nevertheless it will not be // cancelled by a user selecting a different Trac server and starting a new // "slurp". So: we currently just disable the action. // (TODO: Create new 'TicketLoader' to more-cleanly manage loading from the 4 // data sources, and the cancellation of these tasks.) frame.getSlurpAction().setEnabled(false); frame.setVisible(true); if (site.isOkToUseCachedTickets()) { TicketLoadTask loadTask = new CachedTicketLoadTask(site); frame.monitorTask(loadTask); loadTask.executeWithNotification(() -> loadServerTickets(frame, site)); } else { // Proceed with next step immediately. loadServerTickets(frame, site); } } private void loadServerTickets(TracInstantFrame frame, final SiteData site) { // HACK SlurpAction slurper = frame.getSlurpAction(); slurper.setEnabled(true); Authenticator.setDefault(SITE_AUTHENTICATOR); if (shouldPromptOnStartup(site)) { if (slurper.promptForTracSettings()) { slurper.slurpAll(); } else { frame.dispose(); } } else { site.loadUserData(); String error = slurper.slurpIncremental(); if (error != null) { // Allow a one-time full slurp all to cover the possibility that we don't know the // date format (after which incremental slurps will be disabled). slurper.slurpAll(); } } } public boolean shouldPromptOnStartup(SiteData site) { if (!TracInstantProperties.hasPasswordSupport() || !TracInstantProperties.getUsername().isEmpty() && !TracInstantProperties.getRememberPassword()) { return true; } Ticket[] tickets = site.getTableModel().getTickets(); return tickets.length == 0; } private static final Authenticator SITE_AUTHENTICATOR = new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { SiteSettings ss = SiteSettings.getInstance(); return new PasswordAuthentication(ss.getUsername(), ss.getPassword().toCharArray()); } }; private void saveApplicationState() { try { TracInstantProperties.get().saveProperties(); } catch (IOException e) { e.printStackTrace(); } } private static void setLaF() { try { for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) { if ("Nimbus".equals(info.getName())) { UIManager.setLookAndFeel(info.getClassName()); break; } } } catch (RuntimeException ex) { throw ex; } catch (Exception ex) { // If the unexpected happens, just roll with the defaults. } } }