/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package cli_fmw.utils.intelliwriter;
import cli_fmw.delegate.directory.complex.DirectoryIntelliWriter;
import cli_fmw.delegate.directory.complex.DirectoryLocator;
import cli_fmw.delegate.directory.simple.DirectorySimpleItem;
import cli_fmw.delegate.directory.simple.iwtype.DirectoryIWTypeItemAbstract;
import cli_fmw.main.ClipsException;
import cli_fmw.utils.MessageBox;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import java.awt.event.WindowStateListener;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.KeyStroke;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
/**
* Объект, управляющий текстовым контролом, списком контекстного меню и окном
* контекстного меню.
* Посылает в контекстное меню список строк меню,
* команды для его отображения и скрытия меню,
* клавиши нажатые в контроле (стрелки, энтер, эскейп и тд)
* @author axe
*/
public class IntelliWriter implements TextController {
private static final HashSet<Character> SEPARATORS;
static {
SEPARATORS = new HashSet<Character>();
SEPARATORS.add('.');
SEPARATORS.add('\n');
SEPARATORS.add(';');
}
private Set<String> rows;
private WordMenuWindow contextHelper;
private JTextComponent component;
private Window owner;
private DocListener dl;
/**
* конструктор
* @param type тип справочника вспомогательный текстов
* @param owner окно, являющееся родительским по отношению к всплывающему списку
* @param textComponent компонент, ввод которго контролирует intelliwriter
* @throws ClipsException
*/
public IntelliWriter(DirectoryIWTypeItemAbstract type, JFrame owner, JTextComponent textComponent)
throws ClipsException {
this(DirectoryLocator.getDirectory(DirectoryIntelliWriter.class, false).getItems(type),
owner, textComponent);
}
/**
* конструктор
* @param strings набор строк
* @param owner окно, являющееся родительским по отношению к всплывающему списку
* @param textComponent компонент, ввод которго контролирует intelliwriter
*/
public IntelliWriter(Set<String> strings, JFrame owner, JTextComponent textComponent) {
this.owner = owner;
component = textComponent;
contextHelper = new WordMenuWindow(owner, this);
this.rows = strings;
dl= new DocListener();
setEventManagement();
}
/**
*
*/
private void setEventManagement() {
/*
component.addFocusListener(new FocusAdapter() {
@Override
public void focusLost(FocusEvent e) {
component.requestFocus();
}
});*/
component.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, InputEvent.CTRL_MASK), "home");
component.getActionMap().put("home", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
if(contextHelper.isVisible()) {
contextHelper.moveStart();
}
}
});
component.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_END, InputEvent.CTRL_MASK), "end");
component.getActionMap().put("end", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
if(contextHelper.isVisible()) {
contextHelper.moveEnd();
}
}
});
component.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.CTRL_MASK), "controlEspace");
component.getActionMap().put("controlEspace", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
showMenu(component.getDocument(), component.getCaretPosition());
}
});
component.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.isConsumed()) {
return;
}
if (contextHelper.isVisible()) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
contextHelper.forceSelect();
contextHelper.clearRollbackHistory();
System.err.println("VK_ENTER");
e.consume();
} else if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
contextHelper.setVisible(false);
contextHelper.clearRollbackHistory();
System.err.println("VK_ESCAPE");
e.consume();
} else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
contextHelper.moveDown();
System.err.println("VK_DOWN");
e.consume();
} else if (e.getKeyCode() == KeyEvent.VK_UP) {
contextHelper.moveUp();
System.err.println("VK_UP");
e.consume();
} else if (e.getKeyCode() == KeyEvent.VK_PAGE_DOWN) {
contextHelper.movePageDown();
System.err.println("VK_PAGE_DOWN");
e.consume();
} else if (e.getKeyCode() == KeyEvent.VK_PAGE_UP) {
contextHelper.movePageUp();
System.err.println("VK_PAGE_UP");
e.consume();
} else if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
contextHelper.wordCommit();
e.consume();
} else if(e.getKeyCode() == KeyEvent.VK_LEFT) {
contextHelper.wordRollback();
e.consume();
}
}
}
});
component.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
hideMenu();
}
});
component.getDocument().addDocumentListener(dl);
if(owner != null) {
owner.addComponentListener(new ComponentAdapter() {
@Override
public void componentHidden(ComponentEvent e) {
hideMenu();
}
@Override
public void componentMoved(ComponentEvent e) {
if (contextHelper.isVisible()) {
Point point = getMenuPosition();
if(point != null) {
contextHelper.onMove(point);
} else {
hideMenu();
}
}
}
});
owner.addWindowFocusListener(new WindowFocusListener() {
@Override
public void windowGainedFocus(WindowEvent e) {
//do nothing
}
@Override
public void windowLostFocus(WindowEvent e) {
hideMenu();
}
});
}
component.addFocusListener(new FocusListener() {
public void focusGained(FocusEvent e) {
//do nothing
}
public void focusLost(FocusEvent e) {
hideMenu();
}
});
}
class DocListener implements DocumentListener {
@Override
public void insertUpdate(DocumentEvent e) {
showMenu(e.getDocument(), e.getOffset() + e.getLength());
}
@Override
public void removeUpdate(DocumentEvent e) {
showMenu(e.getDocument(), e.getOffset());
}
@Override
public void changedUpdate(DocumentEvent e) {
}
}
/**
*
* @param mask
* @return
*/
private List<String> getMatchSentences(String mask) {
//sentence = sentence.toLowerCase();
ArrayList<String> returnSet = new ArrayList<String>();
Iterator<String> iterator = rows.iterator();
while(iterator.hasNext()) {
String string = iterator.next();
if (string.length() != mask.length()
&& string.startsWith(mask)
) {
returnSet.add(string.substring(mask.length()));
}
}
return returnSet;
}
private static boolean isSentenceSeparator(char aChar) {
return SEPARATORS.contains(aChar);
}
public void hideMenu() {
if(contextHelper.isVisible()) {
contextHelper.setVisible(false);
contextHelper.clearRollbackHistory();
}
}
/**
* Displays menu
* @param currentSentence
*/
private void showMenu(Document doc, int pos) {
try {
String txt = doc.getText(0, pos);
String currentSentence = getCurrentSentence(txt);
if (contextHelper.isVisible()) {
hideMenu();
}
List<String> words = getMatchSentences(currentSentence);
if (words.size() > 0) {
contextHelper.setWords(words);
Point point = getMenuPosition();
if (point == null) {
return;
}
contextHelper.display(point);
component.requestFocus();
} else {
hideMenu();
}
} catch (Exception ex) {
hideMenu();
ex.printStackTrace();
}
}
/**
*
* @return preferred position for drop-down menu
* (much close as possible to caret)
*/
private Point getMenuPosition() {
int index = component.getCaretPosition();
Rectangle rect = null;
try {
rect = component.getUI().modelToView(component, index);
} catch (BadLocationException e) {
}
if (rect == null) {
return null;
}
Point origin = component.getLocationOnScreen();
origin.x += rect.x;
origin.y += rect.y + rect.height;
Dimension ss = Toolkit.getDefaultToolkit().getScreenSize();
if(origin.x + contextHelper.getWidth() > ss.getWidth()) {
origin.x = (int) (ss.getWidth() - contextHelper.getWidth());
}
if(origin.y + contextHelper.getHeight() > ss.getHeight()) {
origin.y = (int) (ss.getHeight() - contextHelper.getHeight());
}
if(origin.x < 0) {
origin.x = 0;
}
if(origin.y < 0) {
origin.y = 0;
}
return origin;
}
private String getCurrentSentence(String txt) {
int index = txt.length()-1;
if(index < 0) {
return new String();
}
boolean found = false;
while ((index > 0) && (!found)) {
char current = txt.charAt(index);
if (isSentenceSeparator(current)) {
found = true;
index++;
} else {
index--;
}
}
String sen = txt.substring(index);
sen = sen.replaceAll(" +", " ");
return sen;
}
public void insertString(String str) {
component.getDocument().removeDocumentListener(dl);
try {
hideMenu();
int pos = component.getCaretPosition();
Document doc = component.getDocument();
doc.insertString(pos, str, null);
showMenu(doc, pos + str.length());
} catch (BadLocationException ex) {
ex.printStackTrace();
}
component.getDocument().addDocumentListener(dl);
}
public void removeCharacters(int count) {
component.getDocument().removeDocumentListener(dl);
try {
hideMenu();
int pos = component.getCaretPosition() - count;
Document doc = component.getDocument();
doc.remove(pos, count);
showMenu(doc, pos);
} catch (BadLocationException ex) {
MessageBox.showException(ex);
}
component.getDocument().addDocumentListener(dl);
}
}