|
/* :name=Acivate source text :description=Acivate source text on the Editor with keyboard shortcut |
|
* |
|
* The workaround by script for RFE #821: |
|
* Showing cursor on the original segment |
|
* http://sourceforge.net/p/omegat/feature-requests/821/ |
|
* |
|
* @author Yu Tang |
|
* @date 2015-08-31 |
|
* @version 0.2 |
|
*/ |
|
|
|
// ******************* |
|
// BEGIN USER SETTINGS |
|
//******************** |
|
|
|
FONT_BOLD = true // or false |
|
|
|
// ***************************************************** |
|
// END USER SETTINGS, NOTHING FURTHER NEEDS TO BE EDITED |
|
// ***************************************************** |
|
|
|
import groovy.transform.Field |
|
|
|
import java.awt.ComponentOrientation |
|
import java.awt.Dimension |
|
import java.awt.event.ActionEvent |
|
import java.awt.event.KeyAdapter |
|
import java.awt.event.KeyEvent |
|
import java.awt.event.KeyListener |
|
import java.awt.event.WindowFocusListener |
|
import java.awt.Font |
|
import java.awt.Point |
|
import java.awt.Rectangle |
|
import java.awt.Toolkit |
|
import java.awt.event.InputEvent |
|
|
|
import javax.swing.border.EmptyBorder |
|
import javax.swing.JDialog |
|
import javax.swing.JEditorPane |
|
import javax.swing.JRootPane |
|
import javax.swing.JViewport |
|
import javax.swing.SwingUtilities |
|
|
|
import org.omegat.core.Core |
|
import org.omegat.core.CoreEvents |
|
import org.omegat.core.events.IProjectEventListener |
|
import org.omegat.core.events.IProjectEventListener.PROJECT_CHANGE_TYPE |
|
import org.omegat.core.search.SearchMode |
|
import org.omegat.gui.editor.EditorUtils |
|
import org.omegat.gui.editor.SegmentBuilder |
|
import org.omegat.gui.search.SearchWindowController |
|
import org.omegat.util.gui.StaticUIUtils |
|
import org.omegat.util.gui.Styles |
|
import org.omegat.util.gui.UIThreadsUtil |
|
import org.omegat.util.StaticUtils |
|
|
|
@Field final String SCRIPT_NAME = 'activate_source_text' |
|
|
|
KeyListener createEditorKeyListener() { |
|
[ |
|
keyPressed : { |
|
if (StaticUtils.isKey(it, KeyEvent.VK_F2, 0)) { |
|
showDialog() |
|
} |
|
} |
|
] as KeyAdapter |
|
} |
|
|
|
KeyListener createDialogEditorKeyListener() { |
|
[ |
|
keyPressed : { KeyEvent e -> |
|
String selection = e.source.selectedText |
|
switch(true) { |
|
case StaticUtils.isKey(e, KeyEvent.VK_ENTER, 0): |
|
case StaticUtils.isKey(e, KeyEvent.VK_F2, 0): |
|
// insert selected text to the editor |
|
if (selection) { |
|
editor.insertText selection |
|
} |
|
|
|
// close the dialog |
|
JDialog dialog = getDialogAncestor(e.source) |
|
def closeAction = dialog.rootPane.actionMap.get("ESCAPE") |
|
def action = new ActionEvent(e.source, ActionEvent.ACTION_PERFORMED, "ESCAPE") |
|
closeAction.actionPerformed action |
|
break |
|
|
|
case StaticUtils.isKey(e, KeyEvent.VK_F, Toolkit.defaultToolkit.menuShortcutKeyMask): |
|
// open search window |
|
SearchWindowController search = new SearchWindowController(mainWindow, selection, SearchMode.SEARCH) |
|
mainWindow.addSearchWindow search |
|
break |
|
|
|
case StaticUtils.isKey(e, KeyEvent.VK_G, Toolkit.defaultToolkit.menuShortcutKeyMask | InputEvent.SHIFT_MASK): |
|
// open add glossary term dialog |
|
Core.glossary.showCreateGlossaryEntryDialog() |
|
if (selection) { |
|
Core.glossary.createGlossaryEntryDialog.sourceText.text = selection |
|
//Core.glossary.createGlossaryEntryDialog.targetText.text = selection |
|
//Core.glossary.createGlossaryEntryDialog.commentText.text = selection |
|
} |
|
break |
|
} |
|
} |
|
] as KeyAdapter |
|
} |
|
|
|
JDialog getDialogAncestor(comp) { |
|
def win = SwingUtilities.getWindowAncestor(comp) |
|
while (win == null || !win instanceof JDialog) { |
|
win = SwingUtilities.getWindowAncestor(win) |
|
} |
|
win |
|
} |
|
|
|
void showDialog() { |
|
try { |
|
def ste = editor.currentEntry |
|
if (ste == null) { |
|
return |
|
} |
|
|
|
JDialog dialog = createDialog(ste.srcText) |
|
StaticUIUtils.setEscapeClosable dialog |
|
setLostFocusClosable dialog |
|
UIThreadsUtil.executeInSwingThread { dialog.setVisible true } as Runnable |
|
} catch(ex) { |
|
console.println "$SCRIPT_NAME >> $ex" |
|
} |
|
} |
|
|
|
void setLostFocusClosable(JDialog dialog) { |
|
def closeAction = dialog.rootPane.actionMap.get("ESCAPE") |
|
def focusListener = [ |
|
windowGainedFocus : {}, |
|
windowLostFocus : { |
|
def action = new ActionEvent(it.source, ActionEvent.ACTION_PERFORMED, "ESCAPE") |
|
closeAction.actionPerformed action |
|
} |
|
] as WindowFocusListener |
|
dialog.addWindowFocusListener focusListener |
|
} |
|
|
|
JDialog createDialog(String text) { |
|
JDialog dialog = new JDialog(mainWindow) |
|
dialog.setUndecorated true |
|
dialog.rootPane.setWindowDecorationStyle JRootPane.PLAIN_DIALOG |
|
JEditorPane pane = createEditorPane(text) |
|
pane.addKeyListener createDialogEditorKeyListener() |
|
dialog.add pane |
|
dialog.pack() |
|
dialog.setBounds sourceSegmentRect |
|
dialog |
|
} |
|
|
|
JEditorPane createEditorPane(String text) { |
|
JEditorPane pane = new JEditorPane('text/plain', text) |
|
pane.with { |
|
setDragEnabled true |
|
setComponentOrientation sourceOrientation |
|
setFont FONT_BOLD ? editor.font.deriveFont(Font.BOLD) : editor.font |
|
setForeground Styles.EditorColor.COLOR_ACTIVE_SOURCE_FG.color |
|
setBackground Styles.EditorColor.COLOR_ACTIVE_SOURCE.color |
|
setCaretPosition 0 |
|
def b = editor.editor.border |
|
def border = new EmptyBorder(0, b.left, 0, b.right) // top and bottom = 0 |
|
setBorder border |
|
} |
|
pane |
|
} |
|
|
|
ComponentOrientation getSourceOrientation() { |
|
editor.sourceLangIsRTL \ |
|
? ComponentOrientation.RIGHT_TO_LEFT |
|
: ComponentOrientation.LEFT_TO_RIGHT |
|
} |
|
|
|
Rectangle getSourceSegmentRect() { |
|
int activeSegment = editor.displayedEntryIndex |
|
JViewport viewport = editor.scrollPane.viewport |
|
Rectangle viewRect = viewport.viewRect |
|
|
|
SegmentBuilder sb = editor.m_docSegList[activeSegment] |
|
int startSourcePosition = sb.startSourcePosition |
|
int startTranslationPosition = sb.startTranslationPosition |
|
if (startTranslationPosition == -1) { |
|
startTranslationPosition = startSourcePosition + sb.sourceText.size() + 1 // + 1 for line break |
|
} |
|
Point sourceLocation = editor.editor.modelToView(startSourcePosition).location |
|
Point transLocation = editor.editor.modelToView(startTranslationPosition).location |
|
|
|
if (!viewRect.contains(sourceLocation)) { // location is NOT viewable |
|
throw new RuntimeException("Source segment must be viewable"); |
|
} |
|
|
|
// create new Rectangle for source segment |
|
int x = viewRect.x |
|
int y = sourceLocation.y |
|
int width = viewRect.width |
|
int height = transLocation.y - y |
|
Point point = new Point(x, y) |
|
SwingUtilities.convertPointToScreen point, viewport.view |
|
Rectangle rect = new Rectangle(point.@x, point.@y, width, height) |
|
} |
|
|
|
CoreEvents.registerProjectChangeListener new F2Controller(createEditorKeyListener()) |
|
"${SCRIPT_NAME}.groovy is available in the current session." |
|
|
|
// controller class |
|
class F2Controller implements IProjectEventListener { |
|
|
|
KeyListener _listener |
|
|
|
F2Controller(KeyListener listener) { |
|
_listener = listener |
|
if (Core.project.isProjectLoaded()) { |
|
installKeyListener() |
|
} |
|
} |
|
|
|
void onProjectChanged(PROJECT_CHANGE_TYPE eventType) { |
|
switch(eventType) { |
|
case PROJECT_CHANGE_TYPE.CREATE: |
|
case PROJECT_CHANGE_TYPE.LOAD: |
|
// すぐリスナー登録するとエディターがドキュメントを開く前で |
|
// タイミングが合わないので、遅延登録する |
|
Runnable doRun = { installKeyListener() } as Runnable |
|
SwingUtilities.invokeLater doRun |
|
break |
|
case PROJECT_CHANGE_TYPE.CLOSE: |
|
uninstallKeyListener() |
|
break |
|
} |
|
} |
|
|
|
void installKeyListener() { |
|
Core.editor.editor.addKeyListener _listener |
|
} |
|
|
|
void uninstallKeyListener() { |
|
Core.editor.editor.removeKeyListener _listener |
|
} |
|
} |
There's a tiny problem with the Metal LAF, at least on Linux. It has to do with spawned source pane's placement. When it spawns, the last line isn't visible, so if the segment is only one line long, I see only the border. The pane is resizable, but I would much rather just have it guess the segment length properly. If the segment is longer than just a few lines, then the pane covers some of the target. My rude fix for that was editing line 203 to move the pane 40 pixels higher and make it 40 pixels taller ( Rectangle rect = new Rectangle(point.@x, point.@Y-40, width, height+40)), but it's rather an ugly workaround, as it would fail for others who use different fonts.
After comparing with other LAF's and trying it on Windows, I see that the pane is decorated when Metal LAF is used, and the decoration steals from the overall geometry.