From 667a11d69be4e0ed89e6c9f276e5c1ef890dcfec Mon Sep 17 00:00:00 2001 From: andrewlalis Date: Tue, 28 Aug 2018 21:28:49 +0200 Subject: [PATCH] Added icon image, and dialog class. --- src/main/java/nl/andrewlalis/Main.java | 30 ++---------------- .../ui/control/OutputTextHandler.java | 17 +++++++--- .../ui/control/command/CommandExecutor.java | 6 ++-- .../listeners/ReadStudentsFileListener.java | 17 ++++++++++ .../ui/view/DefineTaTeamsDialog.java | 13 ++++++++ .../andrewlalis/ui/view/InitializerApp.java | 13 +++++--- .../andrewlalis/ui/view/OutputTextPane.java | 16 +++++++--- .../java/nl/andrewlalis/util/Logging.java | 25 +++++++-------- src/main/resources/image/icon.png | Bin 0 -> 8714 bytes 9 files changed, 80 insertions(+), 57 deletions(-) create mode 100644 src/main/java/nl/andrewlalis/ui/view/DefineTaTeamsDialog.java create mode 100644 src/main/resources/image/icon.png diff --git a/src/main/java/nl/andrewlalis/Main.java b/src/main/java/nl/andrewlalis/Main.java index b6e7202..7241c30 100644 --- a/src/main/java/nl/andrewlalis/Main.java +++ b/src/main/java/nl/andrewlalis/Main.java @@ -34,12 +34,7 @@ public class Main { Map userOptions = CommandLine.parseArgs(args); // Initialize logger. - try { - Logging.setup(true); // TODO: Replace true with command line arg. - - } catch (IOException e) { - logger.severe("Unable to save log to file."); - } + Logging.setup(); // Command executor which will be used by all actions the user can do. CommandExecutor executor = new CommandExecutor(); @@ -47,6 +42,7 @@ public class Main { // Initialize User Interface. InitializerApp app = new InitializerApp(executor); app.begin(); + app.setAccessToken(userOptions.get("token")); Database db = new Database("database/initializer.sqlite"); db.initialize(); @@ -56,28 +52,6 @@ public class Main { executor.registerCommand("generateassignments", new GenerateAssignmentsRepo()); logger.info("GithubManager for Github Repositories in Educational Organizations. Program initialized."); - - - - // Get studentTeams from CSV file. -// List studentTeams = getStudentTeamsFromCSV(userOptions.get("input"), Integer.parseInt(userOptions.get("teamsize"))); -// -// GithubManager githubManager = new GithubManager( -// userOptions.get("organization"), -// userOptions.get("token"), -// "assignments_2018", -// "teaching-assistants", -// "advoop_2018" -// ); - - try { - //githubManager.initializeGithubRepos(studentTeams); - //githubManager.archiveAllRepositories("team"); - } catch (Exception e) { - e.printStackTrace(); - } } - - } diff --git a/src/main/java/nl/andrewlalis/ui/control/OutputTextHandler.java b/src/main/java/nl/andrewlalis/ui/control/OutputTextHandler.java index 875c148..b964e24 100644 --- a/src/main/java/nl/andrewlalis/ui/control/OutputTextHandler.java +++ b/src/main/java/nl/andrewlalis/ui/control/OutputTextHandler.java @@ -6,6 +6,7 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.logging.Handler; +import java.util.logging.Level; import java.util.logging.LogRecord; /** @@ -26,11 +27,19 @@ public class OutputTextHandler extends Handler { public void publish(LogRecord logRecord) { DateFormat df = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"); String dateString = df.format(new Date(logRecord.getMillis())); - String sourceLocationString = logRecord.getSourceClassName() + "::" + logRecord.getSourceMethodName(); this.outputPane.printStyled(dateString + ' ', "gray_italics"); - this.outputPane.printStyled(logRecord.getLevel().getName() + ": ", "bold"); - this.outputPane.printStyled(sourceLocationString + "\n\t", "bold"); - this.outputPane.printStyled(logRecord.getMessage() + '\n', "smaller"); + String style = "default"; + Level level = logRecord.getLevel(); + if (level == Level.SEVERE) { + style = "error_red"; + } else if (level == Level.FINE + || level == Level.FINER + || level == Level.FINEST) { + style = "smaller"; + } else if (level == Level.WARNING) { + style = "warning_orange"; + } + this.outputPane.printStyled(logRecord.getMessage() + '\n', style); } @Override diff --git a/src/main/java/nl/andrewlalis/ui/control/command/CommandExecutor.java b/src/main/java/nl/andrewlalis/ui/control/command/CommandExecutor.java index dd5d84a..d66bb01 100644 --- a/src/main/java/nl/andrewlalis/ui/control/command/CommandExecutor.java +++ b/src/main/java/nl/andrewlalis/ui/control/command/CommandExecutor.java @@ -63,8 +63,10 @@ public class CommandExecutor { */ public void executeCommand(String commandName, String[] args) { if (this.commands.containsKey(commandName)) { - logger.info(commandName + ' ' + Arrays.toString(args)); - this.commands.get(commandName).execute(args); + logger.info("Command executed: " + commandName + ' ' + Arrays.toString(args)); + if (!this.commands.get(commandName).execute(args)) { + logger.warning("Command did not execute successfully."); + } } else { logger.warning(commandName + " is not a valid command."); } diff --git a/src/main/java/nl/andrewlalis/ui/control/listeners/ReadStudentsFileListener.java b/src/main/java/nl/andrewlalis/ui/control/listeners/ReadStudentsFileListener.java index 788c431..4a270c8 100644 --- a/src/main/java/nl/andrewlalis/ui/control/listeners/ReadStudentsFileListener.java +++ b/src/main/java/nl/andrewlalis/ui/control/listeners/ReadStudentsFileListener.java @@ -4,7 +4,9 @@ import nl.andrewlalis.ui.control.command.CommandExecutor; import nl.andrewlalis.ui.view.InitializerApp; import javax.swing.*; +import javax.swing.filechooser.FileFilter; import java.awt.event.ActionEvent; +import java.io.File; /** * Listens for when the user performs an action to read all students from a file, and output the contents to a database. @@ -18,6 +20,21 @@ public class ReadStudentsFileListener extends ExecutableListener { @Override public void actionPerformed(ActionEvent actionEvent) { JFileChooser chooser = new JFileChooser(); + chooser.setAcceptAllFileFilterUsed(false); + chooser.addChoosableFileFilter(new FileFilter() { + @Override + public boolean accept(File file) { + if (file.isDirectory()) { + return true; + } + return file.getName().toLowerCase().endsWith(".csv"); + } + + @Override + public String getDescription() { + return "CSV Files (*.csv)"; + } + }); int fileResponse = chooser.showOpenDialog(this.app); if (fileResponse == JFileChooser.APPROVE_OPTION) { diff --git a/src/main/java/nl/andrewlalis/ui/view/DefineTaTeamsDialog.java b/src/main/java/nl/andrewlalis/ui/view/DefineTaTeamsDialog.java new file mode 100644 index 0000000..3a3e641 --- /dev/null +++ b/src/main/java/nl/andrewlalis/ui/view/DefineTaTeamsDialog.java @@ -0,0 +1,13 @@ +package nl.andrewlalis.ui.view; + +import nl.andrewlalis.ui.control.command.executables.DefineTaTeams; + +import javax.swing.*; + +public class DefineTaTeamsDialog extends JDialog { + + public DefineTaTeamsDialog(InitializerApp parentApp) { + super(parentApp); + } + +} diff --git a/src/main/java/nl/andrewlalis/ui/view/InitializerApp.java b/src/main/java/nl/andrewlalis/ui/view/InitializerApp.java index 7db93eb..526840f 100644 --- a/src/main/java/nl/andrewlalis/ui/view/InitializerApp.java +++ b/src/main/java/nl/andrewlalis/ui/view/InitializerApp.java @@ -2,11 +2,7 @@ package nl.andrewlalis.ui.view; import nl.andrewlalis.ui.control.OutputTextHandler; import nl.andrewlalis.ui.control.command.CommandExecutor; -import nl.andrewlalis.ui.control.command.executables.ArchiveRepos; -import nl.andrewlalis.ui.control.listeners.ArchiveAllListener; -import nl.andrewlalis.ui.control.listeners.CommandFieldKeyListener; -import nl.andrewlalis.ui.control.listeners.GenerateAssignmentsRepoListener; -import nl.andrewlalis.ui.control.listeners.ReadStudentsFileListener; +import nl.andrewlalis.ui.control.listeners.*; import javax.swing.*; import java.awt.*; @@ -45,6 +41,8 @@ public class InitializerApp extends JFrame { this.executor = executor; // UI initialization. + ImageIcon icon = new ImageIcon(getClass().getResource("/image/icon.png")); + this.setIconImage(icon.getImage()); this.initFrame(); } @@ -120,6 +118,7 @@ public class InitializerApp extends JFrame { commonActionsPanel.add(generateAssignmentsRepoButton); JButton defineTaTeamsButton = new JButton("Define TA Teams"); + defineTaTeamsButton.addActionListener(new DefineTaTeamsListener(this.executor, this)); commonActionsPanel.add(defineTaTeamsButton); githubManagerPanel.add(commonActionsPanel, BorderLayout.CENTER); @@ -194,4 +193,8 @@ public class InitializerApp extends JFrame { return this.accessTokenField.getText().trim(); } + public void setAccessToken(String accessToken) { + this.accessTokenField.setText(accessToken); + } + } diff --git a/src/main/java/nl/andrewlalis/ui/view/OutputTextPane.java b/src/main/java/nl/andrewlalis/ui/view/OutputTextPane.java index 64569cd..62ab0c7 100644 --- a/src/main/java/nl/andrewlalis/ui/view/OutputTextPane.java +++ b/src/main/java/nl/andrewlalis/ui/view/OutputTextPane.java @@ -1,10 +1,7 @@ package nl.andrewlalis.ui.view; import javax.swing.*; -import javax.swing.text.BadLocationException; -import javax.swing.text.Style; -import javax.swing.text.StyleConstants; -import javax.swing.text.StyledDocument; +import javax.swing.text.*; import java.awt.*; import java.util.HashMap; import java.util.Map; @@ -26,6 +23,9 @@ public class OutputTextPane extends JTextPane { this.initStyles(); this.setEditable(false); this.setAutoscrolls(true); + + DefaultCaret caret = (DefaultCaret) this.getCaret(); + caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); } private void initStyles() { @@ -47,6 +47,14 @@ public class OutputTextPane extends JTextPane { Style smaller = this.addStyle("smaller", defaultStyle); smaller.addAttribute(StyleConstants.FontSize, 11); this.styles.put("smaller", smaller); + + Style errorRed = this.addStyle("error_red", bold); + errorRed.addAttribute(StyleConstants.Foreground, new Color(255, 0, 0)); + this.styles.put("error_red", errorRed); + + Style warningOrange = this.addStyle("warning_orange", defaultStyle); + warningOrange.addAttribute(StyleConstants.Foreground, new Color(255, 127, 0)); + this.styles.put("warning_orange", warningOrange); } /** diff --git a/src/main/java/nl/andrewlalis/util/Logging.java b/src/main/java/nl/andrewlalis/util/Logging.java index 9161693..f0014ca 100644 --- a/src/main/java/nl/andrewlalis/util/Logging.java +++ b/src/main/java/nl/andrewlalis/util/Logging.java @@ -11,24 +11,21 @@ public class Logging { private static FileHandler outputFile; private static SimpleFormatter formatter; - public static void setup(boolean verbose) throws IOException { + public static void setup() { Logger logger = Logger.getGlobal(); - outputFile = new FileHandler("log/latest.log"); - formatter = new SimpleFormatter(); - - outputFile.setFormatter(formatter); - outputFile.setLevel(Level.FINEST); - - if (verbose) { - Handler systemOut = new ConsoleHandler(); - systemOut.setLevel(Level.ALL); - - logger.addHandler(systemOut); + try { + outputFile = new FileHandler("log/latest.log"); + formatter = new SimpleFormatter(); + outputFile.setFormatter(formatter); + outputFile.setLevel(Level.FINEST); + logger.addHandler(outputFile); + } catch (IOException e) { + logger.warning("Unable to save log to output file."); + e.printStackTrace(); } - logger.addHandler(outputFile); - logger.setLevel(Level.ALL); + logger.setLevel(Level.ALL); Logger.getLogger("").setLevel(Level.OFF); } diff --git a/src/main/resources/image/icon.png b/src/main/resources/image/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b3ecc63b86432f49641767cc99dc944c91689beb GIT binary patch literal 8714 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