/* LanguageTool, a natural language style checker * Copyright (C) 2005 Daniel Naber (http://www.danielnaber.de) * * This library 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 library 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 library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 * USA */ package de.danielnaber.languagetool.gui; import java.awt.AWTException; import java.awt.Container; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Image; import java.awt.Insets; import java.awt.MenuItem; import java.awt.PopupMenu; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.Reader; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.MissingResourceException; import java.util.ResourceBundle; import java.util.Set; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTextArea; import javax.swing.JTextPane; import javax.swing.UIManager; import javax.swing.WindowConstants; import javax.swing.filechooser.FileFilter; import javax.xml.parsers.ParserConfigurationException; import org.jdesktop.jdic.tray.SystemTray; import org.jdesktop.jdic.tray.TrayIcon; import org.xml.sax.SAXException; import de.danielnaber.languagetool.JLanguageTool; import de.danielnaber.languagetool.Language; import de.danielnaber.languagetool.language.RuleFilenameException; import de.danielnaber.languagetool.rules.Rule; import de.danielnaber.languagetool.rules.RuleMatch; import de.danielnaber.languagetool.server.HTTPServer; import de.danielnaber.languagetool.server.PortBindingException; import de.danielnaber.languagetool.tools.StringTools; /** * A simple GUI to check texts with. * * @author Daniel Naber */ public final class Main implements ActionListener { private final ResourceBundle messages; private static final String HTML_FONT_START = ""; private static final String HTML_FONT_END = ""; private final static String SYSTEM_TRAY_ICON_NAME = "/TrayIcon.png"; private static final String SYSTEM_TRAY_TOOLTIP = "LanguageTool"; private static final String CONFIG_FILE = ".languagetool.cfg"; private final Configuration config; private JFrame frame; private JTextArea textArea; private JTextPane resultArea; private JComboBox langBox; private HTTPServer httpServer; private final Map configDialogs = new HashMap(); // whether clicking on the window close button hides to system tray: private boolean trayMode; private boolean isInTray; private Main() throws IOException { config = new Configuration(new File(System.getProperty("user.home")), CONFIG_FILE); messages = JLanguageTool.getMessageBundle(); maybeStartServer(); } private void createGUI() { frame = new JFrame("LanguageTool " + JLanguageTool.VERSION); try { for (UIManager.LookAndFeelInfo info : UIManager .getInstalledLookAndFeels()) { if ("Nimbus".equals(info.getName())) { UIManager.setLookAndFeel(info.getClassName()); break; } } } catch (Exception ex) { // Well, what can we do... } frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); frame.addWindowListener(new CloseListener()); frame.setIconImage(new ImageIcon(JLanguageTool.getDataBroker().getFromResourceDirAsUrl( Main.SYSTEM_TRAY_ICON_NAME)).getImage()); frame.setJMenuBar(new MainMenuBar(this, messages)); textArea = new JTextArea(messages.getString("guiDemoText")); // TODO: wrong line number is displayed for lines that are wrapped // automatically: textArea.setLineWrap(true); textArea.setWrapStyleWord(true); resultArea = new JTextPane(); resultArea.setContentType("text/html"); resultArea.setText(HTML_FONT_START + messages.getString("resultAreaText") + HTML_FONT_END); resultArea.setEditable(false); final JLabel label = new JLabel(messages.getString("enterText")); final JButton button = new JButton(StringTools.getLabel(messages .getString("checkText"))); button .setMnemonic(StringTools.getMnemonic(messages.getString("checkText"))); button.addActionListener(this); final JPanel panel = new JPanel(); panel.setLayout(new GridBagLayout()); final GridBagConstraints buttonCons = new GridBagConstraints(); buttonCons.gridx = 0; buttonCons.gridy = 0; panel.add(button, buttonCons); buttonCons.gridx = 1; buttonCons.gridy = 0; panel.add(new JLabel(" " + messages.getString("textLanguage") + " "), buttonCons); buttonCons.gridx = 2; buttonCons.gridy = 0; langBox = new JComboBox(); populateLanguageBox(); // use the system default language to preselect the language from the combo // box: try { final Locale defaultLocale = Locale.getDefault(); langBox.setSelectedItem(messages.getString(defaultLocale.getLanguage())); } catch (final MissingResourceException e) { // language not supported, so don't select a default } panel.add(langBox, buttonCons); final Container contentPane = frame.getContentPane(); final GridBagLayout gridLayout = new GridBagLayout(); contentPane.setLayout(gridLayout); final GridBagConstraints cons = new GridBagConstraints(); cons.insets = new Insets(5, 5, 5, 5); cons.fill = GridBagConstraints.BOTH; cons.weightx = 10.0f; cons.weighty = 10.0f; cons.gridx = 0; cons.gridy = 1; cons.weighty = 5.0f; final JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, new JScrollPane(textArea), new JScrollPane(resultArea)); splitPane.setDividerLocation(200); contentPane.add(splitPane, cons); cons.fill = GridBagConstraints.NONE; cons.gridx = 0; cons.gridy = 2; cons.weighty = 0.0f; cons.insets = new Insets(3, 3, 3, 3); // cons.fill = GridBagConstraints.NONE; contentPane.add(label, cons); cons.gridy = 3; contentPane.add(panel, cons); frame.pack(); frame.setSize(600, 550); } private void populateLanguageBox() { final List toSort = new ArrayList(); langBox.removeAllItems(); for (final Language lang : Language.LANGUAGES) { if (lang != Language.DEMO) { try { toSort.add(messages.getString(lang.getShortName())); } catch (final MissingResourceException e) { // can happen with external rules: toSort.add(lang.getName()); } } } Collections.sort(toSort); for (final String lng : toSort) { langBox.addItem(lng); } } private void showGUI() { frame.setVisible(true); } public void actionPerformed(final ActionEvent e) { try { if (e.getActionCommand().equals( StringTools.getLabel(messages.getString("checkText")))) { final JLanguageTool langTool = getCurrentLanguageTool(); checkTextAndDisplayResults(langTool, getCurrentLanguage()); } else { throw new IllegalArgumentException("Unknown action " + e); } } catch (final Exception exc) { Tools.showError(exc); } } void loadFile() { final File file = Tools.openFileDialog(frame, new PlainTextFilter()); if (file == null) { return; } try { final String fileContents = StringTools.readFile(new FileInputStream(file .getAbsolutePath())); textArea.setText(fileContents); final JLanguageTool langTool = getCurrentLanguageTool(); checkTextAndDisplayResults(langTool, getCurrentLanguage()); } catch (final IOException e) { Tools.showError(e); } } void hideToTray() { final String version = System.getProperty("java.version"); if (!isInTray && version.startsWith("1.5")) { // we don't run under <= 1.4, // so we don't check for that TrayIcon trayIcon = null; try { final Icon sysTrayIcon = new ImageIcon(JLanguageTool.getDataBroker().getFromResourceDirAsUrl(Main.SYSTEM_TRAY_ICON_NAME)); trayIcon = new TrayIcon(sysTrayIcon); } catch (final NoClassDefFoundError e) { throw new MissingJdicException(e); } final SystemTray tray = SystemTray.getDefaultSystemTray(); trayIcon.addActionListener(new TrayActionListener()); trayIcon.setToolTip(SYSTEM_TRAY_TOOLTIP); tray.addTrayIcon(trayIcon); } else if (!isInTray) { // Java 1.6 or later final java.awt.SystemTray tray = java.awt.SystemTray.getSystemTray(); final Image img = Toolkit.getDefaultToolkit().getImage( JLanguageTool.getDataBroker().getFromResourceDirAsUrl(Main.SYSTEM_TRAY_ICON_NAME)); final PopupMenu popup = makePopupMenu(); try { final java.awt.TrayIcon trayIcon = new java.awt.TrayIcon(img, "tooltip", popup); trayIcon.addMouseListener(new TrayActionListener()); trayIcon.setToolTip(SYSTEM_TRAY_TOOLTIP); tray.add(trayIcon); } catch (final AWTException e1) { // thrown if there's no system tray Tools.showError(e1); } } isInTray = true; frame.setVisible(false); } private PopupMenu makePopupMenu() { final PopupMenu popup = new PopupMenu(); final ActionListener rmbListener = new TrayActionRMBListener(); // Check clipboard text: final MenuItem checkClipboardItem = new MenuItem(StringTools .getLabel(messages.getString("guiMenuCheckClipboard"))); checkClipboardItem.addActionListener(rmbListener); popup.add(checkClipboardItem); // Open main window: final MenuItem restoreItem = new MenuItem(StringTools.getLabel(messages .getString("guiMenuShowMainWindow"))); restoreItem.addActionListener(rmbListener); popup.add(restoreItem); // Exit: final MenuItem exitItem = new MenuItem(StringTools.getLabel(messages .getString("guiMenuQuit"))); exitItem.addActionListener(rmbListener); popup.add(exitItem); return popup; } void addLanguage() { final LanguageManagerDialog lmd = new LanguageManagerDialog(frame, Language .getExternalLanguages()); lmd.show(); try { Language.reInit(lmd.getLanguages()); } catch (final RuleFilenameException e) { Tools.showErrorMessage(e); } populateLanguageBox(); } void showOptions() { final JLanguageTool langTool = getCurrentLanguageTool(); final List rules = langTool.getAllRules(); final ConfigurationDialog configDialog = getCurrentConfigDialog(); configDialog.show(rules); // this blocks until OK/Cancel is clicked in the // dialog config.setDisabledRuleIds(configDialog.getDisabledRuleIds()); config.setEnabledRuleIds(configDialog.getEnabledRuleIds()); config.setDisabledCategoryNames(configDialog.getDisabledCategoryNames()); config.setMotherTongue(configDialog.getMotherTongue()); config.setRunServer(configDialog.getRunServer()); config.setServerPort(configDialog.getServerPort()); // Stop server, start new server if requested: stopServer(); maybeStartServer(); } private void restoreFromTray() { frame.setVisible(true); } // show GUI and check the text from clipboard/selection: private void restoreFromTrayAndCheck() { final String s = getClipboardText(); restoreFromTray(); textArea.setText(s); final JLanguageTool langTool = getCurrentLanguageTool(); checkTextAndDisplayResults(langTool, getCurrentLanguage()); } void checkClipboardText() { final String s = getClipboardText(); textArea.setText(s); final JLanguageTool langTool = getCurrentLanguageTool(); checkTextAndDisplayResults(langTool, getCurrentLanguage()); } private String getClipboardText() { // get text from clipboard or selection: Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemSelection(); if (clipboard == null) { // on Windows clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); } String s = null; final Transferable data = clipboard.getContents(this); try { if (data != null && data.isDataFlavorSupported(DataFlavor.getTextPlainUnicodeFlavor())) { final DataFlavor df = DataFlavor.getTextPlainUnicodeFlavor(); final Reader sr = df.getReaderForText(data); s = StringTools.readerToString(sr); } else { s = ""; } } catch (final Exception ex) { ex.printStackTrace(); if (data != null) { s = data.toString(); } else { s = ""; } } return s; } void quitOrHide() { if (trayMode) { hideToTray(); } else { quit(); } } void quit() { stopServer(); try { config.saveConfiguration(); } catch (final IOException e) { Tools.showError(e); } frame.setVisible(false); System.exit(0); } private void maybeStartServer() { if (config.getRunServer()) { httpServer = new HTTPServer(config.getServerPort()); try { httpServer.run(); } catch (final PortBindingException e) { JOptionPane.showMessageDialog(null, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); } } } private void stopServer() { if (httpServer != null) { httpServer.stop(); httpServer = null; } } private Language getCurrentLanguage() { final String langName = langBox.getSelectedItem().toString(); String lang = langName; for (final Enumeration e = messages.getKeys(); e.hasMoreElements();) { final String elem = e.nextElement(); if (messages.getString(elem).equals(langName)) { lang = elem; break; } } // external rules: if (lang.length() > 2) { return Language.getLanguageForName(lang); } return Language.getLanguageForShortName(lang); } private ConfigurationDialog getCurrentConfigDialog() { final Language language = getCurrentLanguage(); ConfigurationDialog configDialog = null; if (configDialogs.containsKey(language)) { configDialog = configDialogs.get(language); } else { configDialog = new ConfigurationDialog(frame, false); configDialog.setMotherTongue(config.getMotherTongue()); configDialog.setDisabledRules(config.getDisabledRuleIds()); configDialog.setEnabledRules(config.getEnabledRuleIds()); configDialog.setDisabledCategories(config.getDisabledCategoryNames()); configDialog.setRunServer(config.getRunServer()); configDialog.setServerPort(config.getServerPort()); configDialogs.put(language, configDialog); } return configDialog; } private JLanguageTool getCurrentLanguageTool() { final JLanguageTool langTool; try { final ConfigurationDialog configDialog = getCurrentConfigDialog(); langTool = new JLanguageTool(getCurrentLanguage(), configDialog .getMotherTongue()); langTool.activateDefaultPatternRules(); langTool.activateDefaultFalseFriendRules(); final Set disabledRules = configDialog.getDisabledRuleIds(); if (disabledRules != null) { for (final String ruleId : disabledRules) { langTool.disableRule(ruleId); } } final Set disabledCategories = configDialog .getDisabledCategoryNames(); if (disabledCategories != null) { for (final String categoryName : disabledCategories) { langTool.disableCategory(categoryName); } } final Set enabledRules = configDialog.getEnabledRuleIds(); if (enabledRules != null) { for (String ruleName : enabledRules) { langTool.enableDefaultOffRule(ruleName); langTool.enableRule(ruleName); } } } catch (final IOException ioe) { throw new RuntimeException(ioe); } catch (final ParserConfigurationException ex) { throw new RuntimeException(ex); } catch (final SAXException ex) { throw new RuntimeException(ex); } return langTool; } private void checkTextAndDisplayResults(final JLanguageTool langTool, final Language lang) { if (StringTools.isEmpty(textArea.getText().trim())) { textArea.setText(messages.getString("enterText2")); } else { final StringBuilder sb = new StringBuilder(); final String startCheckText = Tools.makeTexti18n(messages, "startChecking", new Object[] { lang.getTranslatedName(messages) }); resultArea.setText(HTML_FONT_START + startCheckText + "
\n" + HTML_FONT_END); resultArea.repaint(); // FIXME: why doesn't this work? // TODO: resultArea.setCursor(new Cursor(Cursor.WAIT_CURSOR)); sb.append(startCheckText); sb.append("...
\n"); int matches = 0; try { matches = checkText(langTool, textArea.getText(), sb); } catch (final Exception ex) { sb.append("

" + ex.toString() + "
"); final StackTraceElement[] elements = ex.getStackTrace(); for (final StackTraceElement element : elements) { sb.append(element); sb.append("
"); } sb.append("

"); ex.printStackTrace(); } final String checkDone = Tools.makeTexti18n(messages, "checkDone", new Object[] {matches}); sb.append(checkDone); sb.append("
\n"); resultArea.setText(HTML_FONT_START + sb.toString() + HTML_FONT_END); resultArea.setCaretPosition(0); } } private int checkText(final JLanguageTool langTool, final String text, final StringBuilder sb) throws IOException { final long startTime = System.currentTimeMillis(); final List ruleMatches = langTool.check(text); final long startTimeMatching = System.currentTimeMillis(); int i = 0; for (final RuleMatch match : ruleMatches) { final String output = Tools.makeTexti18n(messages, "result1", new Object[] {i + 1, match.getLine() + 1, match.getColumn()}); sb.append(output); String msg = match.getMessage(); msg = msg.replaceAll("", ""); msg = msg.replaceAll("", ""); msg = msg.replaceAll("", ""); msg = msg.replaceAll("", ""); sb.append("" + messages.getString("errorMessage") + " " + msg + "
\n"); if (match.getSuggestedReplacements().size() > 0) { final String repl = StringTools.listToString(match .getSuggestedReplacements(), "; "); sb.append("" + messages.getString("correctionMessage") + " " + repl + "
\n"); } final String context = Tools.getContext(match.getFromPos(), match .getToPos(), text); sb.append("" + messages.getString("errorContext") + " " + context); sb.append("
\n"); i++; } final long endTime = System.currentTimeMillis(); sb.append(Tools.makeTexti18n(messages, "resultTime", new Object[] { endTime - startTime, endTime - startTimeMatching})); return ruleMatches.size(); } private void setTrayMode(boolean trayMode) { this.trayMode = trayMode; } public static void main(final String[] args) { try { final Main prg = new Main(); if (args.length == 1 && (args[0].equals("-t") || args[0].equals("--tray"))) { // dock to systray on startup javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { try { prg.createGUI(); prg.setTrayMode(true); prg.hideToTray(); } catch (final MissingJdicException e) { JOptionPane.showMessageDialog(null, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); System.exit(1); } catch (final Exception e) { Tools.showError(e); System.exit(1); } } }); } else if (args.length >= 1) { System.out .println("Usage: java de.danielnaber.languagetool.gui.Main [-t|--tray]"); System.out .println(" -t, --tray: dock LanguageTool to system tray on startup"); } else { javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { try { prg.createGUI(); prg.showGUI(); } catch (final Exception e) { Tools.showError(e); } } }); } } catch (final Exception e) { Tools.showError(e); } } // // The System Tray stuff // class TrayActionRMBListener implements ActionListener { public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equalsIgnoreCase( StringTools.getLabel(messages.getString("guiMenuCheckClipboard")))) { restoreFromTrayAndCheck(); } else if (e.getActionCommand().equalsIgnoreCase( StringTools.getLabel(messages.getString("guiMenuShowMainWindow")))) { restoreFromTray(); } else if (e.getActionCommand().equalsIgnoreCase( StringTools.getLabel(messages.getString("guiMenuQuit")))) { quit(); } else { JOptionPane.showMessageDialog(null, "Unknown action: " + e.getActionCommand(), "Error", JOptionPane.ERROR_MESSAGE); } } } class TrayActionListener implements ActionListener, MouseListener { // for Java 1.5 / Jdic: public void actionPerformed(@SuppressWarnings("unused")ActionEvent e) { handleClick(); } // Java 1.6: public void mouseClicked(@SuppressWarnings("unused")MouseEvent e) { handleClick(); } private void handleClick() { if (frame.isVisible() && frame.isActive()) { frame.setVisible(false); } else if (frame.isVisible() && !frame.isActive()) { frame.toFront(); restoreFromTrayAndCheck(); } else { restoreFromTrayAndCheck(); } } public void mouseEntered(@SuppressWarnings("unused") MouseEvent e) { } public void mouseExited(@SuppressWarnings("unused")MouseEvent e) { } public void mousePressed(@SuppressWarnings("unused")MouseEvent e) { } public void mouseReleased(@SuppressWarnings("unused")MouseEvent e) { } } class CloseListener implements WindowListener { public void windowClosing(@SuppressWarnings("unused")WindowEvent e) { quitOrHide(); } public void windowActivated(@SuppressWarnings("unused")WindowEvent e) { } public void windowClosed(@SuppressWarnings("unused")WindowEvent e) { } public void windowDeactivated(@SuppressWarnings("unused")WindowEvent e) { } public void windowDeiconified(@SuppressWarnings("unused")WindowEvent e) { } public void windowIconified(@SuppressWarnings("unused")WindowEvent e) { } public void windowOpened(@SuppressWarnings("unused")WindowEvent e) { } } static class PlainTextFilter extends FileFilter { @Override public boolean accept(final File f) { if (f.getName().toLowerCase().endsWith(".txt")) { return true; } return false; } @Override public String getDescription() { return "*.txt"; } } }