/**************************************************************************
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 and Maxym Mykhalchuk
2007 Didier Briel
2009-2010 Wildrich Fourie
2010 Alex Buloichik
2012 Jean-Christophe Helary
2013 Aaron Madlon-Kay, Alex Buloichik
2015 Yu Tang, Aaron Madlon-Kay
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.glossary;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import java.io.File;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.List;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.text.AttributeSet;
import javax.swing.text.JTextComponent;
import javax.swing.text.StyledDocument;
import org.omegat.core.Core;
import org.omegat.core.data.ProjectProperties;
import org.omegat.core.data.SourceTextEntry;
import org.omegat.core.data.StringEntry;
import org.omegat.gui.common.EntryInfoThreadPane;
import org.omegat.gui.dialogs.CreateGlossaryEntry;
import org.omegat.gui.editor.EditorUtils;
import org.omegat.gui.main.DockableScrollPane;
import org.omegat.gui.main.IMainWindow;
import org.omegat.util.Log;
import org.omegat.util.OConsts;
import org.omegat.util.OStrings;
import org.omegat.util.Preferences;
import org.omegat.util.StringUtil;
import org.omegat.util.gui.DragTargetOverlay;
import org.omegat.util.gui.DragTargetOverlay.FileDropInfo;
import org.omegat.util.gui.IPaneMenu;
import org.omegat.util.gui.JTextPaneLinkifier;
import org.omegat.util.gui.StaticUIUtils;
import org.omegat.util.gui.Styles;
import org.omegat.util.gui.UIThreadsUtil;
/**
* This is a Glossary pane that displays glossary entries.
*
* @author Keith Godfrey
* @author Maxym Mykhalchuk
* @author Didier Briel
* @author Wildrich Fourie
* @author Alex Buloichik (alex73mail@gmail.com)
* @author Jean-Christophe Helary
* @author Aaron Madlon-Kay
*/
@SuppressWarnings("serial")
public class GlossaryTextArea extends EntryInfoThreadPane<List<GlossaryEntry>>
implements IGlossaries, IPaneMenu {
private static final String EXPLANATION = OStrings.getString("GUI_GLOSSARYWINDOW_explanation");
private static final AttributeSet NO_ATTRIBUTES = Styles.createAttributeSet(null, null, false, null);
private static final AttributeSet PRIORITY_ATTRIBUTES = Styles.createAttributeSet(null, null, true, null);
/**
* Currently processed entry. Used to detect if user moved into new entry. In this case, new find should
* be started.
*/
protected StringEntry processedEntry;
/**
* Holds the current GlossaryEntries for the TransTips
*/
protected static List<GlossaryEntry> nowEntries;
private CreateGlossaryEntry createGlossaryEntryDialog;
private final DockableScrollPane scrollPane;
/** Creates new form MatchGlossaryPane */
public GlossaryTextArea(IMainWindow mw) {
super(true);
String title = OStrings.getString("GUI_MATCHWINDOW_SUBWINDOWTITLE_Glossary");
scrollPane = new DockableScrollPane("GLOSSARY", title, this, true);
mw.addDockable(scrollPane);
setEditable(false);
StaticUIUtils.makeCaretAlwaysVisible(this);
setText(EXPLANATION);
setMinimumSize(new Dimension(100, 50));
addMouseListener(mouseListener);
Core.getEditor().registerPopupMenuConstructors(300, new TransTipsPopup());
if (!GraphicsEnvironment.isHeadless()) {
DragTargetOverlay.apply(this, new FileDropInfo(false) {
@Override
public boolean canAcceptDrop() {
return Core.getProject().isProjectLoaded();
}
@Override
public String getOverlayMessage() {
return OStrings.getString("DND_ADD_GLOSSARY_FILE");
}
@Override
public String getImportDestination() {
return Core.getProject().getProjectProperties().getGlossaryRoot();
}
@Override
public boolean acceptFile(File pathname) {
String name = pathname.getName().toLowerCase();
return name.endsWith(OConsts.EXT_CSV_UTF8) || name.endsWith(OConsts.EXT_TBX)
|| name.endsWith(OConsts.EXT_TSV_DEF) || name.endsWith(OConsts.EXT_TSV_TXT)
|| name.endsWith(OConsts.EXT_TSV_UTF8);
}
@Override
public Component getComponentToOverlay() {
return scrollPane;
}
});
}
JTextPaneLinkifier.linkify(this);
}
@Override
protected void onProjectOpen() {
clear();
Core.getGlossaryManager().start();
}
@Override
protected void onProjectClose() {
clear();
setText(EXPLANATION);
Core.getGlossaryManager().stop();
}
@Override
protected void startSearchThread(SourceTextEntry newEntry) {
new FindGlossaryThread(GlossaryTextArea.this, newEntry, Core.getGlossaryManager()).start();
}
/**
* Refresh content on glossary file changed.
*/
public void refresh() {
SourceTextEntry ste = Core.getEditor().getCurrentEntry();
if (ste != null) {
startSearchThread(ste);
}
}
@Override
public void onEntryActivated(SourceTextEntry newEntry) {
scrollPane.stopNotifying();
super.onEntryActivated(newEntry);
}
/**
* Sets the list of glossary entries to show in the pane. Each element of the list should be an instance
* of {@link GlossaryEntry}.
*/
@Override
protected void setFoundResult(SourceTextEntry en, List<GlossaryEntry> entries) {
UIThreadsUtil.mustBeSwingThread();
clear();
if (entries == null) {
return;
}
if (!entries.isEmpty() && Preferences.isPreference(Preferences.NOTIFY_GLOSSARY_HITS)) {
scrollPane.notify(true);
}
nowEntries = entries;
// If the TransTips is enabled then underline all the matched glossary
// entries
if (Preferences.isPreference(Preferences.TRANSTIPS)) {
Core.getEditor().remarkOneMarker(TransTipsMarker.class.getName());
}
GlossaryEntry.StyledString buf = new GlossaryEntry.StyledString();
for (GlossaryEntry entry : entries) {
GlossaryEntry.StyledString str = entry.toStyledString();
buf.append(str);
buf.append("\n\n");
}
setText(buf.text.toString());
StyledDocument doc = getStyledDocument();
doc.setCharacterAttributes(0, doc.getLength(), NO_ATTRIBUTES, true); // remove old bold settings first
for (int i = 0; i < buf.boldStarts.size(); i++) {
doc.setCharacterAttributes(buf.boldStarts.get(i), buf.boldLengths.get(i), PRIORITY_ATTRIBUTES,
true);
}
}
/** Clears up the pane. */
@Override
public void clear() {
super.clear();
nowEntries = Collections.emptyList();
}
@Override
public List<GlossaryEntry> getDisplayedEntries() {
return nowEntries;
}
/**
* MouseListener for the GlossaryTextArea.
*/
protected final transient MouseListener mouseListener = new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
if (e.isPopupTrigger()) {
doPopup(e.getPoint());
}
}
@Override
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) {
doPopup(e.getPoint());
}
}
private void doPopup(Point p) {
JPopupMenu popup = new JPopupMenu();
populateContextMenu(popup);
popup.show(GlossaryTextArea.this, p.x, p.y);
}
};
private void populateContextMenu(JPopupMenu popup) {
boolean projectLoaded = Core.getProject().isProjectLoaded();
final String selection = getSelectedText();
JMenuItem item = popup.add(OStrings.getString("GUI_GLOSSARYWINDOW_insertselection"));
item.setEnabled(projectLoaded && !StringUtil.isEmpty(selection));
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Core.getEditor().insertText(selection);
}
});
item = popup.add(OStrings.getString("GUI_GLOSSARYWINDOW_addentry"));
item.setEnabled(projectLoaded);
item.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
showCreateGlossaryEntryDialog(Core.getMainWindow().getApplicationFrame());
}
});
}
@Override
public void showCreateGlossaryEntryDialog(final Frame parent) {
CreateGlossaryEntry d = createGlossaryEntryDialog;
if (d != null) {
d.requestFocus();
return;
}
ProjectProperties props = Core.getProject().getProjectProperties();
final File out = new File(props.getWriteableGlossary());
final CreateGlossaryEntry dialog = new CreateGlossaryEntry(parent);
String txt = dialog.getGlossaryFileText().getText();
txt = MessageFormat.format(txt, out.getAbsolutePath());
dialog.getGlossaryFileText().setText(txt);
dialog.getSourceText().requestFocus();
dialog.addWindowFocusListener(new WindowFocusListener() {
@Override
public void windowLostFocus(WindowEvent e) {
}
@Override
public void windowGainedFocus(WindowEvent e) {
Window w = e.getOppositeWindow();
if (w != null) {
String sel = getSelectedText(w.getMostRecentFocusOwner());
if (!StringUtil.isEmpty(sel)) {
if (StringUtil.isEmpty(dialog.getSourceText().getText())) {
setText(dialog.getSourceText(), sel);
} else if (StringUtil.isEmpty(dialog.getTargetText().getText())) {
setText(dialog.getTargetText(), sel);
} else if (StringUtil.isEmpty(dialog.getCommentText().getText())) {
setText(dialog.getCommentText(), sel);
}
}
}
}
private String getSelectedText(Component comp) {
String result = null;
if (comp instanceof JTextComponent) {
result = ((JTextComponent) comp).getSelectedText();
if (!StringUtil.isEmpty(result)) {
result = EditorUtils.removeDirectionChars(result);
}
}
return result;
}
private void setText(JTextComponent comp, String text) {
comp.setText(text);
comp.requestFocus();
comp.selectAll();
}
});
dialog.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
createGlossaryEntryDialog = null;
if (dialog.getReturnStatus() == CreateGlossaryEntry.RET_OK) {
String src = StringUtil.normalizeUnicode(dialog.getSourceText().getText());
String loc = StringUtil.normalizeUnicode(dialog.getTargetText().getText());
String com = StringUtil.normalizeUnicode(dialog.getCommentText().getText());
if (!StringUtil.isEmpty(src) && !StringUtil.isEmpty(loc)) {
try {
GlossaryReaderTSV.append(out, new GlossaryEntry(src, loc, com, true));
} catch (Exception ex) {
Log.log(ex);
}
}
}
}
});
StaticUIUtils.persistGeometry(dialog, Preferences.CREATE_GLOSSARY_GEOMETRY_PREFIX);
dialog.setVisible(true);
createGlossaryEntryDialog = dialog;
}
@Override
public void populatePaneMenu(JPopupMenu menu) {
populateContextMenu(menu);
menu.addSeparator();
final JMenuItem openFile = new JMenuItem(OStrings.getString("GUI_GLOSSARYWINDOW_SETTINGS_OPEN_FILE"));
openFile.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Core.getMainWindow().getMainMenu().invokeAction("projectAccessWriteableGlossaryMenuItem", e.getModifiers());
}
});
openFile.setEnabled(false);
if (Core.getProject().isProjectLoaded()) {
String glossaryPath = Core.getProject().getProjectProperties().getWriteableGlossary();
openFile.setEnabled(!StringUtil.isEmpty(glossaryPath) && new File(glossaryPath).isFile());
}
menu.add(openFile);
menu.addSeparator();
final JMenuItem notify = new JCheckBoxMenuItem(OStrings.getString("GUI_GLOSSARYWINDOW_SETTINGS_NOTIFICATIONS"));
notify.setSelected(Preferences.isPreference(Preferences.NOTIFY_GLOSSARY_HITS));
notify.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Preferences.setPreference(Preferences.NOTIFY_GLOSSARY_HITS, notify.isSelected());
}
});
menu.add(notify);
}
}