/**************************************************************************
OmegaT - Computer Assisted Translation (CAT) tool
with fuzzy matching, translation memory, keyword search,
glossaries, and translation leveraging into updated projects.
Copyright (C) 2000-2006 Keith Godfrey, Maxym Mykhalchuk, Henry Pijffers,
Benjamin Siband, and Kim Bruning
2007 Zoltan Bartko
2008 Andrzej Sawula, Alex Buloichik, Didier Briel
2013 Yu Tang, Aaron Madlon-Kay
2014 Piotr Kulik
2015 Yu Tang, Aaron Madlon-Kay
2016 Didier Briel
Home page: http://www.omegat.org/
Support center: http://groups.yahoo.com/group/OmegaT/
This file is part of OmegaT.
OmegaT is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OmegaT 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
**************************************************************************/
package org.omegat.gui.main;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Font;
import java.awt.Frame;
import java.awt.HeadlessException;
import java.awt.Window;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.WindowConstants;
import javax.swing.text.JTextComponent;
import org.omegat.core.Core;
import org.omegat.core.CoreEvents;
import org.omegat.core.events.IApplicationEventListener;
import org.omegat.core.events.IProjectEventListener;
import org.omegat.core.matching.NearString;
import org.omegat.gui.filelist.ProjectFilesListController;
import org.omegat.gui.matches.IMatcher;
import org.omegat.gui.search.SearchWindowController;
import org.omegat.util.FileUtil;
import org.omegat.util.OStrings;
import org.omegat.util.Preferences;
import org.omegat.util.StringUtil;
import org.omegat.util.gui.DockingUI;
import org.omegat.util.gui.StaticUIUtils;
import org.omegat.util.gui.UIThreadsUtil;
import com.vlsolutions.swing.docking.Dockable;
import com.vlsolutions.swing.docking.DockableState;
import com.vlsolutions.swing.docking.DockingDesktop;
import com.vlsolutions.swing.docking.FloatingDialog;
/**
* The main window of OmegaT application (unless the application is started in
* consoleMode).
*
* @author Keith Godfrey
* @author Benjamin Siband
* @author Maxym Mykhalchuk
* @author Kim Bruning
* @author Henry Pijffers (henry.pijffers@saxnot.com)
* @author Zoltan Bartko - bartkozoltan@bartkozoltan.com
* @author Andrzej Sawula
* @author Alex Buloichik (alex73mail@gmail.com)
* @author Yu Tang
* @author Aaron Madlon-Kay
* @author Piotr Kulik
* @author Didier Briel
*/
@SuppressWarnings("serial")
public class MainWindow extends JFrame implements IMainWindow {
public final MainWindowMenu menu;
protected ProjectFilesListController m_projWin;
/**
* The font for main window (source and target text) and for match and
* glossary windows
*/
private Font m_font;
/** Set of all open search windows. */
private final List<SearchWindowController> m_searches = new ArrayList<SearchWindowController>();
protected JLabel lengthLabel;
protected JLabel progressLabel;
protected JLabel statusLabel;
protected DockingDesktop desktop;
/** Creates new form MainWindow */
public MainWindow() {
menu = new MainWindowMenu(this, new MainWindowMenuHandler(this));
setJMenuBar(menu.initComponents());
setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
menu.mainWindowMenuHandler.projectExitMenuItemActionPerformed();
}
@Override
public void windowDeactivated(WindowEvent we) {
Core.getEditor().windowDeactivated();
super.windowDeactivated(we);
}
});
// load default font from preferences
String fontName = Preferences.getPreferenceDefault(Preferences.TF_SRC_FONT_NAME, Preferences.TF_FONT_DEFAULT);
int fontSize = Preferences.getPreferenceDefault(Preferences.TF_SRC_FONT_SIZE,
Preferences.TF_FONT_SIZE_DEFAULT);
m_font = new Font(fontName, Font.PLAIN, fontSize);
MainWindowUI.createMainComponents(this, m_font);
getContentPane().add(MainWindowUI.initDocking(this), BorderLayout.CENTER);
pack();
getContentPane().add(MainWindowUI.createStatusBar(this), BorderLayout.SOUTH);
StaticUIUtils.setWindowIcon(this);
CoreEvents.registerProjectChangeListener(new IProjectEventListener() {
public void onProjectChanged(PROJECT_CHANGE_TYPE eventType) {
updateTitle();
if (eventType == PROJECT_CHANGE_TYPE.CLOSE) {
closeSearchWindows();
}
}
});
CoreEvents.registerApplicationEventListener(new IApplicationEventListener() {
public void onApplicationStartup() {
MainWindowUI.initializeScreenLayout(MainWindow.this);
DockingUI.removeUnusedMenuSeparators(menu.getOptionsMenu().getPopupMenu());
}
public void onApplicationShutdown() {
}
});
CoreEvents.registerFontChangedEventListener(newFont -> m_font = newFont);
MainWindowUI.handlePerProjectLayouts(this);
updateTitle();
// Set up prompt to reload if segmentation or filters settings change
Preferences.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (Core.getProject().isProjectLoaded()) {
String prop = evt.getPropertyName();
if (prop.equals(Preferences.PROPERTY_SRX)
&& Core.getProject().getProjectProperties().getProjectSRX() == null) {
SwingUtilities.invokeLater(ProjectUICommands::promptReload);
} else if (prop.equals(Preferences.PROPERTY_FILTERS)
&& Core.getProject().getProjectProperties().getProjectFilters() == null) {
SwingUtilities.invokeLater(ProjectUICommands::promptReload);
}
}
}
});
}
/**
* {@inheritDoc}
*/
public JFrame getApplicationFrame() {
return this;
}
/**
* {@inheritDoc}
*/
public Font getApplicationFont() {
return m_font;
}
/**
* {@inheritDoc}
*/
public IMainMenu getMainMenu() {
return menu;
}
/**
* {@inheritDoc}
*/
public void addDockable(Dockable pane) {
desktop.addDockable(pane);
}
/**
* Sets the title of the main window appropriately
*/
private void updateTitle() {
String s = OStrings.getDisplayNameAndVersion();
if (Core.getProject().isProjectLoaded()) {
s += " :: " + Core.getProject().getProjectProperties().getProjectName();
}
setTitle(s);
}
/** insert current fuzzy match or selection at cursor position */
public void doInsertTrans() {
if (!Core.getProject().isProjectLoaded()) {
return;
}
String text = getSelectedTextInMatcher();
boolean fromMT = false;
if (StringUtil.isEmpty(text)) {
NearString near = Core.getMatcher().getActiveMatch();
if (near != null) {
text = near.translation;
if (near.comesFrom == NearString.MATCH_SOURCE.TM
&& FileUtil.isInPath(new File(Core.getProject().getProjectProperties().getTMRoot(), "mt"),
new File(near.projs[0]))) {
fromMT = true;
}
}
}
if (!StringUtil.isEmpty(text)) {
if (fromMT) {
Core.getEditor().insertTextAndMark(text);
} else {
Core.getEditor().insertText(text);
}
Core.getEditor().requestFocus();
}
}
/** replace entire edit area with active fuzzy match or selection */
public void doRecycleTrans() {
if (!Core.getProject().isProjectLoaded()) {
return;
}
String selection = getSelectedTextInMatcher();
if (!StringUtil.isEmpty(selection)) {
Core.getEditor().replaceEditText(selection);
Core.getEditor().requestFocus();
return;
}
NearString near = Core.getMatcher().getActiveMatch();
if (near != null) {
String translation = near.translation;
if (Preferences.isPreference(Preferences.CONVERT_NUMBERS)) {
translation = Core.getMatcher().substituteNumbers(Core.getEditor().getCurrentEntry().getSrcText(),
near.source, near.translation);
}
if (near.comesFrom == NearString.MATCH_SOURCE.TM
&& FileUtil.isInPath(new File(Core.getProject().getProjectProperties().getTMRoot(), "mt"),
new File(near.projs[0]))) {
Core.getEditor().replaceEditTextAndMark(translation);
} else {
Core.getEditor().replaceEditText(translation);
}
Core.getEditor().requestFocus();
}
}
private String getSelectedTextInMatcher() {
IMatcher matcher = Core.getMatcher();
return matcher instanceof JTextComponent
? ((JTextComponent) matcher).getSelectedText()
: null;
}
protected void addSearchWindow(final SearchWindowController newSearchWindow) {
newSearchWindow.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
removeSearchWindow(newSearchWindow);
}
});
synchronized (m_searches) {
m_searches.add(newSearchWindow);
}
}
private void removeSearchWindow(SearchWindowController searchWindow) {
synchronized (m_searches) {
m_searches.remove(searchWindow);
}
}
private void closeSearchWindows() {
synchronized (m_searches) {
// dispose other windows
for (SearchWindowController sw : m_searches) {
sw.dispose();
}
m_searches.clear();
}
}
protected List<SearchWindowController> getSearchWindows() {
return Collections.unmodifiableList(m_searches);
}
/**
* {@inheritDoc}
*/
public void showStatusMessageRB(final String messageKey, final Object... params) {
final String msg = getLocalizedString(messageKey, params);
UIThreadsUtil.executeInSwingThread(new Runnable() {
@Override
public void run() {
statusLabel.setText(msg);
}
});
}
private String getLocalizedString(String messageKey, Object... params) {
if (messageKey == null) {
return " ";
} else if (params == null) {
return OStrings.getString(messageKey);
} else {
return StringUtil.format(OStrings.getString(messageKey), params);
}
}
/**
* {@inheritDoc}
*/
@Override
public void showTimedStatusMessageRB(String messageKey, Object... params) {
showStatusMessageRB(messageKey, params);
if (messageKey == null) {
return;
}
// clear the message after 10 seconds
String localizedString = getLocalizedString(messageKey, params);
Timer timer = new Timer(10_000, evt -> {
String text = statusLabel.getText();
if (localizedString.equals(text)) {
statusLabel.setText(null);
}
});
timer.setRepeats(false); // one-time only
timer.start();
}
/**
* Show message in progress bar.
*
* @param messageText
* message text
*/
public void showProgressMessage(String messageText) {
progressLabel.setText(messageText);
}
/* Set progress bar tooltip text.
*
* @param tooltipText
* tooltip text
*/
public void setProgressToolTipText(String toolTipText) {
progressLabel.setToolTipText(toolTipText);
}
/**
* Show message in length label.
*
* @param messageText
* message text
*/
public void showLengthMessage(String messageText) {
lengthLabel.setText(messageText);
}
// /////////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////
// display oriented code
private JLabel lastDialogText;
private String lastDialogKey;
/**
* {@inheritDoc}
*/
public void displayWarningRB(String warningKey, Object... params) {
displayWarningRB(warningKey, null, params);
};
/**
* {@inheritDoc}
*/
public void displayWarningRB(final String warningKey, final String supercedesKey, final Object... params) {
UIThreadsUtil.executeInSwingThread(new Runnable() {
public void run() {
String msg;
if (params != null) {
msg = StringUtil.format(OStrings.getString(warningKey), params);
} else {
msg = OStrings.getString(warningKey);
}
if (supercedesKey != null && lastDialogText != null && supercedesKey.equals(lastDialogKey)) {
Window w = SwingUtilities.getWindowAncestor(lastDialogText);
if (w != null) {
w.dispose();
}
}
lastDialogText = new JLabel(msg);
lastDialogKey = warningKey;
statusLabel.setText(msg);
JOptionPane.showMessageDialog(MainWindow.this, lastDialogText, OStrings.getString("TF_WARNING"),
JOptionPane.WARNING_MESSAGE);
}
});
}
/**
* {@inheritDoc}
*/
public void displayErrorRB(final Throwable ex, final String errorKey, final Object... params) {
UIThreadsUtil.executeInSwingThread(new Runnable() {
public void run() {
String msg;
if (params != null) {
msg = StringUtil.format(OStrings.getString(errorKey), params);
} else {
msg = OStrings.getString(errorKey);
}
statusLabel.setText(msg);
String fulltext = msg;
if (ex != null)
fulltext += "\n" + ex.toString();
JOptionPane.showMessageDialog(MainWindow.this, fulltext, OStrings.getString("TF_ERROR"),
JOptionPane.ERROR_MESSAGE);
}
});
}
/**
* {@inheritDoc}
*/
public void lockUI() {
UIThreadsUtil.mustBeSwingThread();
// lock application frame
setEnabled(false);
for (Frame f : Frame.getFrames()) {
f.setEnabled(false);
}
// lock undocked dockables
for (DockableState dock : desktop.getDockables()) {
if (!dock.isDocked()) {
dock.getDockable().getComponent().setEnabled(false);
for (Container parent = dock.getDockable().getComponent().getParent(); parent != null; parent = parent
.getParent()) {
if (parent instanceof FloatingDialog) {
parent.setEnabled(false);
break;
}
}
}
}
}
/**
* {@inheritDoc}
*/
public void unlockUI() {
UIThreadsUtil.mustBeSwingThread();
// unlock undocked dockables
for (DockableState dock : desktop.getDockables()) {
if (!dock.isDocked()) {
for (Container parent = dock.getDockable().getComponent().getParent(); parent != null; parent = parent
.getParent()) {
if (parent instanceof FloatingDialog) {
parent.setEnabled(true);
break;
}
}
dock.getDockable().getComponent().setEnabled(true);
}
}
for (Frame f : Frame.getFrames()) {
f.setEnabled(true);
}
// unlock application frame
setEnabled(true);
}
/**
* {@inheritDoc}
*/
public void showErrorDialogRB(String title, String message, Object... args) {
JOptionPane.showMessageDialog(this.getApplicationFrame(),
StringUtil.format(OStrings.getString(message), args), OStrings.getString(title),
JOptionPane.ERROR_MESSAGE);
}
/**
* {@inheritDoc}
*
* @see JOptionPane#showConfirmDialog(java.awt.Component, Object, String,
* int, int)
*/
public int showConfirmDialog(Object message, String title, int optionType,
int messageType) throws HeadlessException {
return JOptionPane.showConfirmDialog(this, message, title, optionType, messageType);
}
public void showMessageDialog(String message) {
JOptionPane.showMessageDialog(this, message);
}
}