package open.dolphin.client;
import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.io.*;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.text.Position;
import open.dolphin.infomodel.IInfoModel;
import open.dolphin.infomodel.ModuleModel;
import open.dolphin.order.StampEditorDialog;
import open.dolphin.project.Project;
import open.dolphin.ui.MyBorderFactory;
import open.dolphin.util.PreferencesUtils;
import open.dolphin.util.StringTool;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
/**
* KartePane に Component として挿入されるスタンプを保持するクラス。
*
* @author Kazushi Minagawa, Digital Globe, Inc. modified by pns
*/
public final class StampHolder extends AbstractComponentHolder implements ComponentHolder {
private static final long serialVersionUID = 5853431116398862958L;
public static final String STAMP_MODIFIED = "stampModified";
private static final Color FOREGROUND = new Color(20, 20, 140);
private static final Color BACKGROUND = new Color(0,0,0,0);
private static final Color COMMENT_COLOR = new Color(120, 20, 140);
private ModuleModel stamp;
private StampRenderingHints hints;
private KartePane kartePane;
private Position start;
private Position end;
private boolean selected;
private static final Border MY_SELECTED_BORDER = MyBorderFactory.createSelectedBorder();
private static final Border MY_CLEAR_BORDER = MyBorderFactory.createClearBorder();
// 検索語にマークする
private String searchText = null;
private String startTag = null;
private String endTag = null;
public StampHolder(final KartePane kartePane, final ModuleModel stamp) {
super();
setForeground(FOREGROUND);
setBackground(BACKGROUND);
setBorder(MY_CLEAR_BORDER);
this.kartePane = kartePane;
this.hints = new StampRenderingHints();
hints.setCommentColor(COMMENT_COLOR);
// スタンプの初期幅は ChartImpl の幅から決定する
Rectangle bounds = PreferencesUtils.getRectangle(Project.getPreferences(), ChartImpl.PN_FRAME, new Rectangle(0,0,0,0));
int w = (bounds.width + 1)/2 - 168; // 実験から連立方程式で求めた
hints.setWidth((w<320)? 320 : w);
setStamp(stamp);
}
/**
* TextPane の幅に応じてリアルタイムにスタンプ幅を変える
* @param g
*/
//@Override
//public void paintComponent(Graphics g) {
// if (stamp != null) {
// int w = getStampWidth();
// if (w != hints.getWidth()) {
// hints.setWidth(w);
// setMyText();
// }
// }
// super.paintComponent(g);
//}
/**
* TextPane の幅に応じてリアルタイムにスタンプ幅を計算
* @return
*/
//private int getStampWidth() {
// JTextPane textPane = kartePane.getTextPane();
// // width は 0 か,320以上の数になる
// int width = (textPane == null)? 0 : textPane.getWidth();
// if (width < 320) width = 320;
// // これを 20 とかにすると textPane がどんどん広がってしまう
// width -= 30;
// return width;
//}
/**
* Focusされた場合のメニュー制御とボーダーを表示する。
*/
@Override
public void enter(ActionMap map) {
map.get(GUIConst.ACTION_COPY).setEnabled(true);
if (kartePane.getTextPane().isEditable()) {
map.get(GUIConst.ACTION_CUT).setEnabled(true);
} else {
map.get(GUIConst.ACTION_CUT).setEnabled(false);
}
map.get(GUIConst.ACTION_PASTE).setEnabled(false);
setSelected(true);
// 隠しコマンドセット
addHiddenCommand();
}
/**
* Focusがはずれた場合のメニュー制御とボーダーの非表示を行う。
*/
@Override
public void exit(ActionMap map) {
setSelected(false);
// 隠しコマンド除去
removeHiddenCommand();
}
/**
* Popupメニューを表示する。
*/
@Override
public void maybeShowPopup(MouseEvent e) {
StampHolderPopupMenu popup = new StampHolderPopupMenu(this);
popup.addPropertyChangeListener(this);
// 内服薬の場合は処方日数,外用剤の場合は処方量を選択するポップアップを作成
if (kartePane.getTextPane().isEditable() &&
IInfoModel.ENTITY_MED_ORDER.equals(stamp.getModuleInfo().getEntity())) {
popup.addStampChangeMenu();
popup.addSeparator();
}
ChartMediator mediator = kartePane.getMediator();
popup.add(mediator.getAction(GUIConst.ACTION_CUT));
popup.add(mediator.getAction(GUIConst.ACTION_COPY));
popup.add(mediator.getAction(GUIConst.ACTION_PASTE));
popup.show(e.getComponent(), e.getX(), e.getY());
}
@Override
public Component getComponent() {
return this;
}
/**
* このスタンプホルダのKartePaneを返す。
*/
@Override
public KartePane getKartePane() {
return kartePane;
}
/**
* スタンプホルダのコンテントタイプを返す。
*/
@Override
public int getContentType() {
return ComponentHolder.TT_STAMP;
}
/**
* このホルダのモデルを返す。
* @return
*/
public ModuleModel getStamp() {
return stamp;
}
/**
* このホルダのモデルを設定する。
* @param stamp
*/
public void setStamp(ModuleModel stamp) {
this.stamp = stamp;
setMyText();
}
public StampRenderingHints getHints() {
return hints;
}
public void setHints(StampRenderingHints hints) {
this.hints = hints;
}
/**
* 選択されているかどうかを返す。
* @return 選択されている時 true
*/
@Override
public boolean isSelected() {
return selected;
}
/**
* 選択属性を設定する。
* @param selected 選択の時 true
*/
@Override
public void setSelected(boolean selected) {
boolean old = this.selected;
this.selected = selected;
if (old != this.selected) {
if (this.selected) {
this.setBorder(MY_SELECTED_BORDER);
} else {
this.setBorder(MY_CLEAR_BORDER);
}
}
}
/**
* KartePane でこのスタンプがダブルクリックされた時コールされる。
* StampEditor を開いてこのスタンプを編集する。
*/
@Override
public void edit() {
if (kartePane.getTextPane().isEditable() && this.isEditable()) {
String category = stamp.getModuleInfo().getEntity();
StampEditorDialog stampEditor = new StampEditorDialog(category, stamp);
stampEditor.addPropertyChangeListener(StampEditorDialog.VALUE_PROP, this);
stampEditor.start();
// 二重起動の禁止 - エディタから戻ったら propertyChange で解除する
//kartePane.getTextPane().setEditable(false); // こうすると,なぜか focus が次の component にうつってしまう
this.setEditable(false);
} else {
Toolkit.getDefaultToolkit().beep();
}
}
/**
* エディタで編集した値を受け取り内容を表示する。
*/
@Override
public void propertyChange(PropertyChangeEvent e) {
String prop = e.getPropertyName();
// StampEditor から値がセットされた場合 or StampHolderPopupMenu からセットされた場合
if (StampEditorDialog.VALUE_PROP.equals(prop)
|| StampHolder.STAMP_MODIFIED.equals(prop)) {
// 二重起動禁止の解除
//kartePane.getTextPane().setEditable(true);
this.setEditable(true);
ModuleModel newStamp = (ModuleModel) e.getNewValue();
if (newStamp != null) {
// スタンプを置き換える
importStamp(newStamp);
}
}
}
/**
* スタンプの内容を置き換える。
* @param newStamp
*/
public void importStamp(ModuleModel newStamp) {
// 「月 日」の自動挿入:replace の場合はここに入る
// replace でない場合は,KartePane でセット
StampModifier.modify(newStamp);
setStamp(newStamp);
kartePane.setDirty(true);
kartePane.getTextPane().validate();
kartePane.getTextPane().repaint();
}
/**
* TextPane内での開始と終了ポジションを保存する。
*/
@Override
public void setEntry(Position start, Position end) {
this.start = start;
this.end = end;
}
/**
* 開始ポジションを返す。
*/
@Override
public int getStartPos() {
return start.getOffset();
}
/**
* 終了ポジションを返す。
*/
@Override
public int getEndPos() {
return end.getOffset();
}
/**
* Velocity を利用してスタンプの内容を表示する。
*/
private void setMyText() {
try {
IInfoModel model = getStamp().getModel();
VelocityContext context = ClientContext.getVelocityContext();
context.put("model", model);
context.put("hints", getHints());
context.put("stampName", getStamp().getModuleInfo().getStampName());
String templateFile = getStamp().getModel().getClass().getName() + ".vm";
// このスタンプのテンプレートファイルを得る
if (getStamp().getModuleInfo().getEntity().equals(IInfoModel.ENTITY_LABO_TEST)) {
if (Project.getPreferences().getBoolean("laboFold", true)) {
templateFile = "labo.vm";
}
}
// Merge する
StringWriter sw = new StringWriter();
BufferedWriter bw = new BufferedWriter(sw);
InputStream instream = ClientContext.getTemplateAsStream(templateFile);
BufferedReader reader = new BufferedReader(new InputStreamReader(instream, "UTF-8"));
Velocity.evaluate(context, bw, "stmpHolder", reader);
bw.flush();
bw.close();
reader.close();
// 全角数字とスペースを直す
String text = sw.toString();
text = StringTool.toHankakuNumber(text);
text = StringTool.toHankakuUpperLower(text);
text = text.replaceAll(" ", " ");
// 検索語の attribute をセットする
if (searchText != null) {
String taggedText = startTag + searchText + endTag;
int pos = text.indexOf(searchText);
while (pos != -1) {
text = text.substring(0,pos) + taggedText + text.substring(pos + searchText.length());
pos = text.indexOf(searchText, pos + taggedText.length());
}
}
this.setText(text);
// カルテペインへ展開された時広がるのを防ぐ
this.setMaximumSize(this.getPreferredSize());
} catch (IOException ex) {
System.out.println("StampHolder.java: " + ex);
} catch (ParseErrorException ex) {
System.out.println("StampHolder.java: " + ex);
} catch (MethodInvocationException ex) {
System.out.println("StampHolder.java: " + ex);
} catch (ResourceNotFoundException ex) {
System.out.println("StampHolder.java: " + ex);
}
}
public void setAttr(String searchText, String startTag, String endTag) {
this.searchText = searchText;
this.startTag = startTag;
this.endTag = endTag;
setMyText();
}
public void removeAttr() {
this.searchText = null;
this.startTag = null;
this.endTag = null;
setMyText();
}
/**
* Shift-commnad-C ショートカットでクリップボードにスタンプをコピーする
*/
private void addHiddenCommand() {
InputMap im = this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.META_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK), "copyAsText");
this.getActionMap().put("copyAsText", new AbstractAction(){
private static final long serialVersionUID = 1L;
@Override
public void actionPerformed(ActionEvent e) {
if (getStamp().getModel().getClass().getName().equals("open.dolphin.infomodel.BundleMed")) {
try {
IInfoModel model = getStamp().getModel();
VelocityContext context = ClientContext.getVelocityContext();
context.put("model", model);
String templateFile = "open.dolphin.infomodel.BundleMed-text.vm";
StringWriter sw = new StringWriter();
BufferedWriter bw = new BufferedWriter(sw);
InputStream instream = ClientContext.getTemplateAsStream(templateFile);
BufferedReader reader = new BufferedReader(new InputStreamReader(instream, "UTF-8"));
Velocity.evaluate(context, bw, "stmpHolder", reader);
bw.flush();
bw.close();
reader.close();
// 全角数字とスペースを直す
String text = sw.toString();
text = StringTool.toHankakuNumber(text);
text = StringTool.toHankakuUpperLower(text);
text = text.replaceAll(" ", " ");
text = text.replace("\n", " ");
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents(new StringSelection(text), null);
} catch (ParseErrorException ex) {
System.out.println("StampHolder.java: " + ex);
} catch (MethodInvocationException ex) {
System.out.println("StampHolder.java: " + ex);
} catch (ResourceNotFoundException ex) {
System.out.println("StampHolder.java: " + ex);
} catch (UnsupportedEncodingException ex) {
System.out.println("StampHolder.java: " + ex);
} catch (IOException ex) {
System.out.println("StampHolder.java: " + ex);
}
}
}
});
}
/**
* 登録した Shift-command-C ショートカットを削除する
*/
private void removeHiddenCommand() {
// Shift+command C
InputMap im = this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
im.remove(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.META_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK));
}
}