/**************************************************************************
OmegaT - Computer Assisted Translation (CAT) tool
with fuzzy matching, translation memory, keyword search,
glossaries, and translation leveraging into updated projects.
Copyright (C) 2009 Alex Buloichik
2013 Aaron Madlon-Kay, Zoltan Bartko
2015 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.editor;
import java.awt.Font;
import javax.swing.event.DocumentEvent;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.Element;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.Position;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import org.omegat.util.gui.Styles;
/**
* We need to redefine some standard document behavior.
*
* @author Alex Buloichik (alex73mail@gmail.com)
* @author Aaron Madlon-Kay
* @author Zoltan Bartko
*/
@SuppressWarnings("serial")
public class Document3 extends DefaultStyledDocument {
public enum ORIENTATION {
/** All text is left-to-right oriented. */
ALL_LTR,
/** All text is right-to-left oriented. */
ALL_RTL,
/** different texts/segments have different orientation, depending on language/locale. */
DIFFER
};
protected final EditorController controller;
/** Position of active translation in text. */
Position activeTranslationBeginM1, activeTranslationEndP1;
/**
* Flag for check internal changes of content, which should be always acceptable.
* <p>
* Note that there is a concurrency bug with the AquaCaret class (part of the OS X native LAF) whereby
* <i>insertion</i> into the Document while the doc is visible can cause the caret to try to update itself while the
* doc internals are inconsistent, leading to exceptions whenever any visual update of the Editor is performed (the
* Editor becomes unusable).
* <p>
* This bug is very old (reported as early as February 2006) and appears to not have been addressed even in December
* 2015 in Java 1.8.0_66), though it is very hard to reproduce (it appears to be a concurrency issue that only
* manifests itself under certain circumstances).
* <p>
* There is a chance that the "real" bug is in the way we are manipulating the JEditorPane and/or the underlying
* document, but it is unclear what the correct solution would be.
* <p>
* As a workaround, when setting this flag to true, if the changes are to include insertions or deletions of text in
* the document, you must also disable the editor's caret updates temporarily (e.g. with
* {@code StaticUIUtils#setCaretUpdateEnabled()}). After the document changes are complete and you have set this
* flag back to false, caret update can be re-enabled.
*
* @see <a href="https://sourceforge.net/p/omegat/bugs/162/">Initial ticket</a>
* @see <a href="https://sourceforge.net/p/omegat/bugs/529/">Later, more specific ticket</a>
*/
protected boolean trustedChangesInProgress = false;
/**
* Flag to indicate that text is currently being composed (should
* not be considered to have been input yet) by an IME.
*/
protected boolean textBeingComposed = false;
public Document3(final EditorController controller) {
this.controller = controller;
Style defaultStyle = getDefaultStyle();
StyleConstants.setForeground(defaultStyle, Styles.EditorColor.COLOR_FOREGROUND.getColor());
StyleConstants.setBackground(defaultStyle, Styles.EditorColor.COLOR_BACKGROUND.getColor());
setFont(controller.font);
}
private Style getDefaultStyle() {
StyleContext styleContext = (StyleContext) getAttributeContext();
return styleContext.getStyle(StyleContext.DEFAULT_STYLE);
}
void setFont(Font font) {
Style defaultStyle = getDefaultStyle();
StyleConstants.setFontFamily(defaultStyle, font.getFamily());
StyleConstants.setFontSize(defaultStyle, font.getSize());
StyleConstants.setBold(defaultStyle, font.isBold());
StyleConstants.setItalic(defaultStyle, font.isItalic());
}
/**
* Calculate the position of the start of the current translation
*/
public int getTranslationStart() {
return activeTranslationBeginM1.getOffset() + 1;
}
/**
* Calculate the position of the end of the current translation
*/
protected int getTranslationEnd() {
return activeTranslationEndP1.getOffset() - 1;
}
/**
* Check if document is in edit mode, i.e. one of segment activated for
* edit.
*/
boolean isEditMode() {
return activeTranslationBeginM1 != null && activeTranslationEndP1 != null;
}
/**
* Stop edit mode, remove info about active translation position.
*/
void stopEditMode() {
activeTranslationBeginM1 = null;
activeTranslationEndP1 = null;
}
/**
* Extract active translation.
*
* @return active translation text
*/
String extractTranslation() {
if (!isEditMode()) {
return null;
}
int start = getTranslationStart();
int end = getTranslationEnd();
try {
return getText(start, end - start);
} catch (BadLocationException ex) {
throw new RuntimeException(ex);
}
}
/**
* Set alignment for specified part of text.
*
* @param beginOffset
* begin offset
* @param endOffset
* end offset
* @param isRightAlignment
* false - left alignment, true - right alignment
*/
protected void setAlignment(int beginOffset, int endOffset, boolean isRightAlignment) {
try {
writeLock();
DefaultDocumentEvent changes = new DefaultDocumentEvent(beginOffset, endOffset - beginOffset,
DocumentEvent.EventType.CHANGE);
Element root = getDefaultRootElement();
int parBeg = root.getElementIndex(beginOffset);
int parEnd = root.getElementIndex(endOffset - 1);
for (int par = parBeg; par <= parEnd; par++) {
Element el = root.getElement(par);
MutableAttributeSet attr = (MutableAttributeSet) el.getAttributes();
attr.addAttribute(StyleConstants.Alignment, isRightAlignment ? StyleConstants.ALIGN_RIGHT
: StyleConstants.ALIGN_LEFT);
}
changes.end();
fireChangedUpdate(changes);
} finally {
writeUnlock();
}
}
}