package jp.ac.kyutech.mns.ist;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
import java.util.TooManyListenersException;
import java.util.TreeMap;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import jp.ac.kyutech.mns.ist.filter.RegionSSFilter;
import jp.ac.kyutech.mns.ist.plugin.DefaultPlugin;
import jp.ac.kyutech.mns.ist.plugin.WindowManagerPlugin;
import jp.ac.kyutech.mns.ist.sheetmap.SeatmapController;
import jp.ac.kyutech.mns.ist.util.FileReadWriter;
import jp.ac.kyutech.mns.ist.util.MemoryMonitor;
import jp.ac.kyutech.mns.ist.util.MultiDisplay;
import jp.ac.kyutech.mns.ist.util.MyAction;
import jp.ac.kyutech.mns.ist.util.MyPImage;
import jp.ac.kyutech.mns.ist.util.NetUtil;
import jp.ac.kyutech.mns.ist.util.NullFileFilter;
import jp.ac.kyutech.mns.ist.util.PrintPrintable;
import jp.ac.kyutech.mns.ist.util.SingleTableSelector;
import jp.ac.kyutech.mns.ist.util.WheelRotationHandler;
import edu.umd.cs.piccolo.PCamera;
import edu.umd.cs.piccolo.PCanvas;
import edu.umd.cs.piccolo.PLayer;
import edu.umd.cs.piccolo.PNode;
import edu.umd.cs.piccolo.event.PBasicInputEventHandler;
import edu.umd.cs.piccolo.event.PInputEvent;
import edu.umd.cs.piccolo.event.PInputEventFilter;
import edu.umd.cs.piccolo.event.PPanEventHandler;
import edu.umd.cs.piccolo.nodes.PText;
import edu.umd.cs.piccolo.util.PBounds;
import edu.umd.cs.piccolo.util.PObjectOutputStream;
import edu.umd.cs.piccolo.util.PPaintContext;
// TODO
// FocusFrameをぴったりのサイズにする.とくに集約表示.先に推奨比率を調べる
// レイアウトのリファクタリング
// Macでもうごくように,なるべく同一のコードをつかう
/**
* SimpleなAirTransNote.筆記を表示するだけ.
* 右ドラッグ範囲にズーム,マウスホイールでズーム,
* @author miuramo
*/
public class SimpleATN extends FullScreenableJFrame implements Runnable, PathDropActionListener, LayoutTarget {
private static final long serialVersionUID = -8183529835329375441L;
public static SimpleATN theapp;
public static int zoommsec = 1000;
public SimpleATN_MenuToolbarAction menutoolbar;
public PLayer layer;
public ATN_Mysql_DB mysqldb;
public ATN_Mysql_DB mysqldb_root;
boolean isNoDB = false;
public DB_Lecture openingLecture;
public String openingLectureid;
ZoomRegionHandler zrh;//Event_onSheetが参照するため
PPanEventHandler panEventHandler;
DrawPenToolbar drawToolbar;
public String dbuser="atnuser", dbpass="atnuser", dbname="atnrepos";
int layoutcols = 0;
PBounds tempAfterLayoutBounds = null;
int filedialogopennum = 0;
DropTarget dropTarget = null;
public static boolean isMACOSX = false;
static {
String lcOSName = System.getProperty("os.name").toLowerCase();
isMACOSX = lcOSName.startsWith("mac os x");
}
public TreeMap<Integer,Note> notes;
public String pimagePath;//画像があるフォルダのパス.カレントディレクトリから探索し,複数あったら選択する.
// public ConfigPanel_PImageFinder pimagePathFinder;
public Hashtable<Integer,MyPImage> pimages;
// public Hashtable<Integer,MyPImage> oldpimages;
int current_cursornum = Cursor.DEFAULT_CURSOR;
int sheetVisibleMode[] = new int[7];//{2,2,2,2,2,2,2,2}; //0:all hide, 1: only data, 2: all show
SimpleATN_UDPNoticeReceiver updator;
InsideToolbar_SheetVisible sheetVisibleToolbuttons;
PText toolTipPText = null;
ArrayList<DefaultPlugin> plugins;
InspectorPlugin inspector;
Event_onSATN event_onsatn;
LayoutAbstract layoutabst;
LayoutBySeat layoutseat;
public String initArgFile;
public String openingFile;
public SimpleATN() {
this(null,null);
}
public SimpleATN(String arg, int count){
this(null,arg);
}
public void changeTitle(String file){
openingFile = file;
setTitle("SimpleATN ver "+SimpleATN.class.getPackage().getImplementationVersion() + " "+file);
}
public SimpleATN(final PCanvas aCanvas, final String file) {
super("SimpleATN ver "+SimpleATN.class.getPackage().getImplementationVersion() + " "+file);//TODO:satnファイル読み込み時に修正する
theapp = this;
initArgFile = file;
setBackground(null);
setBounds(getDefaultFrameBounds());
for(int i=1;i<7;i++) sheetVisibleMode[i] = 1;
sheetVisibleMode[0]=2;
try {
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
}
catch (final SecurityException e) {
System.out.println("Ignoring security exception. Assuming Applet Context.");
}
addWindowListener(new WindowListener(){
public void windowActivated(WindowEvent arg0) { }
public void windowClosed(WindowEvent arg0) {
}
public void windowClosing(WindowEvent arg0) {
ConfigWindow.configWin.filter.saveBeforeExit();
System.exit(0);
}
public void windowDeactivated(WindowEvent arg0) {
}
public void windowDeiconified(WindowEvent arg0) {
}
public void windowIconified(WindowEvent arg0) {
}
public void windowOpened(WindowEvent arg0) {
}
});
if (canvas == null) {
this.canvas = new PCanvas();
//マウスが動いたら,カーソルをもとに戻す
this.canvas.addInputEventListener(new PBasicInputEventHandler() {
public void mouseMoved(final PInputEvent aEvent) {
popCursor();
aEvent.setHandled(true);
}
});
}
canvas.setDefaultRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING);
canvas.setAnimatingRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING);
canvas.setInteractingRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING);
// canvas.removeInputEventListener(canvas.getPanEventHandler());
notes = new TreeMap<Integer, Note>();
pimages = new Hashtable<Integer, MyPImage>();
// oldpimages = new Hashtable<Integer, MyPImage>();
menutoolbar = new SimpleATN_MenuToolbarAction(this);
menutoolbar.foldToolbarText(true);
ConfigWindow.setVisible(this, false);
checkSecondaryDisplay();//もし,セカンダリ画面があれば,アイコン選択状態にする.あとで解除可能.
getContentPane().add(this.canvas, BorderLayout.CENTER);
validate();
setFullScreenMode(false);
// pimagePathFinder = new ConfigPanel_PImageFinder(this, new File(System.getProperty("user.dir")), SimpleATN_ConfigWindow.configWin);
dropTarget = new DropTarget();
try{
dropTarget.addDropTargetListener(new PathDropTargetListener(this));
getCanvas().setDropTarget(dropTarget);
}catch(TooManyListenersException ex){
ex.printStackTrace(System.out);
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
SimpleATN.this.initialize(file);
repaint();
}
});
sheetVisibleToolbuttons = new InsideToolbar_SheetVisible(this);
getCanvas().getCamera().addChild(sheetVisibleToolbuttons);
InsideToolbar_ZoomHandle zh = new InsideToolbar_ZoomHandle(getCanvas().getCamera());
getCanvas().getCamera().addChild(zh);
InsideToolbar_PanHandle ph = new InsideToolbar_PanHandle(getCanvas().getCamera());
getCanvas().getCamera().addChild(ph);
updator = new SimpleATN_UDPNoticeReceiver(this);
setVisible(true);
}
/**
* 画像フォルダの設定と,保存済み筆跡マップの読み込み
* @param f
*/
public void setBGImagePath(File f){
// File f = new File("atntemplates");
if (f != null && f.exists()){
pimagePath = f.getAbsolutePath();
System.out.println(pimagePath);
pimages.clear();//一旦削除
if (isMACOSX){
for(int i=1;i<7;i++){
byte[] b = FileReadWriter.readBytesFromFile(pimagePath+File.separator+"スライド"+i+".png");
MyPImage pimg = new MyPImage(b, false);
pimages.put(i,pimg);
}
} else {
for(int i=1;i<7;i++){
byte[] b = FileReadWriter.readBytesFromFile(pimagePath+File.separator+"スライド"+i+".PNG");
MyPImage pimg = new MyPImage(b, false);
pimages.put(i,pimg);
}
}
//設定ファイルを読み込む
ArrayList<String> setup = FileReadWriter.getLinesList(pimagePath+File.separator+"setup.txt", true);
ArrayList<String> copy = new ArrayList<String>();
for(String ts: setup){
copy.add(ts);
}
boolean hasModification = false;
for(String ts: copy){
String s = ts.trim();
if (s.startsWith("#")) continue;
else if (s.length()<1) continue;
else if (s.startsWith("seatmap")){
int conf = JOptionPane.showConfirmDialog(this, "筆席マップ "+s+" を読み込みますか?\n\n「いいえ」をおすと,次回も尋ねます.\n「取消」をおすと,設定を削除し,今後は尋ねません.");
if (conf == JOptionPane.YES_OPTION){
new SeatmapController(s, SimpleATN.theapp);
} else if (conf == JOptionPane.CANCEL_OPTION){
setup.remove(ts);
hasModification = true;
}
}
}
if (hasModification){
FileReadWriter.putLines(this.pimagePath+File.separator+"setup.txt", setup);
}
} else {
JOptionPane.showMessageDialog(this, "背景画像フォルダがみつかりませんでした");
}
}
public void pushCursor(int cursornum){
if (cursornum != current_cursornum){
current_cursornum = cursornum;
canvas.pushCursor(new Cursor(current_cursornum));
}
}
public void popCursor(){
canvas.popCursor();
current_cursornum = Cursor.DEFAULT_CURSOR;
}
public void initialize(String file) {
//remove default zoom event hander
getCanvas().removeInputEventListener(getCanvas().getZoomEventHandler());
getCanvas().removeInputEventListener(getCanvas().getPanEventHandler());
// Button1 - Pan
panEventHandler = new PPanEventHandler();
panEventHandler.setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_MASK));
getCanvas().addInputEventListener(panEventHandler);
// Button2 Wheel Rotation - Zoom
WheelRotationHandler wrHandler = new WheelRotationHandler(getCanvas(), getCanvas().getCamera());
wrHandler.setEventFilter(new PInputEventFilter(InputEvent.BUTTON2_MASK));
getCanvas().addInputEventListener(wrHandler);
// Button3 Drag - Zoom Region
zrh = new ZoomRegionHandler(this.getCanvas());
getCanvas().addInputEventListener(zrh);
zrh.setEventFilter(new PInputEventFilter(InputEvent.BUTTON3_MASK));
plugins = new ArrayList<DefaultPlugin>();
plugins.add(new WindowManagerPlugin(getCanvas(), this));
//プラグインとして,インスペクタを追加
inspector = new InspectorPlugin(getCanvas(), this);
plugins.add(inspector);
event_onsatn = new Event_onSATN(this);
getCanvas().getCamera().addInputEventListener(event_onsatn);
getCanvas().getPanEventHandler().setEventFilter(new PInputEventFilter(InputEvent.BUTTON3_DOWN_MASK));
//キー入力(ESCキーで画面全体表示)
getCanvas().addKeyListener(new KeyListener() {
@Override
public void keyTyped(KeyEvent e) { }
@Override
public void keyReleased(KeyEvent e) { }
@Override
public void keyPressed(KeyEvent e) {
// System.out.println("keyCode=" + e.getKeyCode() + " keyChar ["+ e.getKeyChar() + "]");
if (e.getKeyCode() == 27) {// ESC
upview();
}
if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == KeyEvent.VK_F || e.getKeyCode() == KeyEvent.VK_R || e.getKeyCode() == KeyEvent.VK_M){
if (zrh.selection != null){
if (zrh.pressN != null && zrh.pressN instanceof Sheet){
Sheet s = (Sheet)zrh.pressN;
s.collect(zrh.selection, e.getKeyCode());
}
if (zrh.pressN != null && zrh.pressN instanceof SSGroup){ //SSGRoup だったら,シートに変換
SSGroup ssg = (SSGroup)zrh.pressN;
ssg.sheet.collect(zrh.selection, e.getKeyCode());
}
}
zrh.dismissRegionLater();
}
if (e.getKeyCode() == KeyEvent.VK_Z){
System.out.println("SimpleATN zoomLevel = "+getZoomLevel());
}
if (e.getKeyCode() == KeyEvent.VK_A || e.getKeyCode() == KeyEvent.VK_0){
// addNote();
for(int i=0;i<7;i++){
sheetVisibleMode[i]=2;
}
sheetVisibleToolbuttons.updateStatus();
setSheetVisible(-1, 2);
}
if (e.getKeyCode() == KeyEvent.VK_F11){
menutoolbar.getButton("Full Screen").doClick();
}
//範囲選択中なら...
if (zrh.isDragging() && zrh.selection != null){
if (KeyEvent.VK_1 <= e.getKeyCode() && e.getKeyCode() <= KeyEvent.VK_5){
//領域フィルタに追加
if (zrh.pressN != null && zrh.pressN instanceof Sheet){
Sheet s = (Sheet)zrh.pressN;
ConfigWindow.configWin.filter.addRegionFilter(s, zrh.selection, e.getKeyCode());
}
if (zrh.pressN != null && zrh.pressN instanceof SSGroup){ //SSGRoup だったら,シートに変換
SSGroup ssg = (SSGroup)zrh.pressN;
// ssg.sheet.collect(zrh.selection, e.getKeyCode());
ConfigWindow.configWin.filter.addRegionFilter(ssg.sheet, zrh.selection, e.getKeyCode());
}
zrh.dismissRegionLater();
}
} else {
if (KeyEvent.VK_1 <= e.getKeyCode() && e.getKeyCode() <= KeyEvent.VK_6){
//以前:1〜6の数字を入力したら,そのシートの表示モードの切り替え
// int sn = e.getKeyCode() - KeyEvent.VK_0;
// sheetVisibleMode[sn] = (sheetVisibleMode[sn]+1)%3;
// setSheetVisible(sn, sheetVisibleMode[sn]);
// sheetVisibleToolbuttons.updateStatus();
//現在:そのシートのみを表示
for(int i=0;i<7;i++){
sheetVisibleMode[i]=0;
}
sheetVisibleToolbuttons.updateStatus();
int sn = e.getKeyCode() - KeyEvent.VK_0;
for(Note n: notes.values()) n.applySheetVisible();
setSheetVisible(sn, 2);
}
}
PBounds pb = getCanvas().getCamera().getViewBounds();
int keyCode = e.getKeyCode();
if (keyCode == 38) {// 上
pb.moveBy(0, -pb.getHeight());
zoomToBounds(pb,1000,"KeyPan");
}
if (keyCode == 37) {// 左
pb.moveBy(-pb.getWidth(),0);
zoomToBounds(pb,1000,"KeyPan");
}
if (keyCode == 39) {// 右
pb.moveBy(pb.getWidth(),0);
zoomToBounds(pb,1000,"KeyPan");
}
if (keyCode == 40) {// 下
pb.moveBy(0,pb.getHeight());
zoomToBounds(pb,1000,"KeyPan");
}
}
});
getCanvas().requestFocus();
layer = getCanvas().getLayer();
//最初にデータベースを読みに行く
if (file==null){
openConnection();
} else {
// loadFromFile(file);
}
}
/**
* シートの表示・非表示.
* sn=sheet number, 0はペン番号, 1~6はシート, -1なら全て(ペン番号含む)
* mode=0は非表示,1は筆記があれば,2は常に
* @param sn
* @param mode
*/
public void setSheetVisible(int sn, int mode){
if (sn > -1) sheetVisibleMode[sn] = mode;
// for(int i=0;i<7;i++) System.out.print(sheetVisibleMode[i]+" ");
// System.out.println("");
setSheetVisible(sn, mode, true, true);
}
public void setSheetVisible(int sn, int mode, boolean doLayout, boolean showMessage){
if (sn == -1){ //Apply to All sheets
for(int i=0;i<7;i++){
for(Note n: notes.values()){
n.toggleVisible(i, mode);
}
}
if (doLayout){
relayout(1000, "SATN.setSheetVisible");
zoomToBounds(tempAfterLayoutBounds, 1000,"SATN.setSheetVisible");
}
if (showMessage){
String m[] = {"を隠す","に書き込みがあれば表示","を表示"};
showFadingMessage("すべてのシート "+m[mode], messageColor, Color.black, 2, 0.9f);
}
} else {
for(Note n: notes.values()){
n.toggleVisible(sn, mode);
}
if (doLayout){
relayout(1000, "SATN.setSheetVisible");
zoomToBounds(tempAfterLayoutBounds, 1000,"SATN.setSheetVisible");
}
if (showMessage){
String num = "シート "+String.valueOf(sn);
if (sn==0) num="ペン番号";
String m[] = {"を隠す","に書き込みがあれば表示","を表示"};
if (sn==0) m[1] = "は表示すべきシートがあるときのみ表示";
showFadingMessage(num+" "+m[mode], messageColor, Color.black, 2, 0.9f);
}
}
}
/**
* ズームアウト,レイアウト済みなら-1
* 表示領域の高さがノートの高さより,すこしでも大きければ0
* 表示領域の高さがノートの高さと同じなら1
* 表示領域の高さがノートの高さの70%以下なら,ズームイン=2
* @return
*/
public int getZoomLevel(){
double h = getCanvas().getCamera().getViewBounds().getHeight();
double w = getCanvas().getCamera().getViewBounds().getWidth();
// PBounds pb = notes.get(0).getGlobalFullBounds();
PBounds gfb = getCanvas().getLayer().getGlobalFullBounds();
if (Math.abs(gfb.getWidth()-w)<w*0.01 || Math.abs(gfb.getHeight()-h)<h*0.01) return -1; //もうレイアウト済み
if (h > Note.height * 1.02){
return 0;
} else if (h < Note.height * 0.70){
return 2;
}
return 1;
}
public void upview(){
upview(getZoomLevel());
}
public void upview(int zoomLevel){
if (zoomLevel == 2){
Point2D centerP = getCanvas().getCamera().getViewBounds().getCenter2D();
centerP = getCanvas().getCamera().localToGlobal(centerP);
//中心点がふくまれる,Sheetを検索する.複数あったら最初のものを採用.
Sheet sheet = findSheetAtGlobalPoint(centerP);
if (sheet != null){
Rectangle2D globalb = sheet.getGlobalBounds();
zoomToBounds(globalb, 1000,"SATN.upview(int)");
} else {
zoomToBounds(tempAfterLayoutBounds, 1000,"SATN.upview(int)");
}
} else if (zoomLevel == -1){
relayout(1000, "SATN.upview(int)");
zoomToBounds(tempAfterLayoutBounds, 1000,"SATN.upview(int)");
} else {
zoomToBounds(tempAfterLayoutBounds, 1000,"SATN.upview(int)");
}
}
public Sheet findSheetAtGlobalPoint(Point2D p){
@SuppressWarnings("unchecked")
Collection<PNode> c = getCanvas().getLayer().getAllNodes();
for(PNode n: c){
if (n instanceof Sheet){
Sheet s = (Sheet)n;
if (s.getGlobalBounds().contains(p)){
return s;
}
}
}
return null;
}
// //全体画面を表示
// public void zoomHome(int msec){
// getCanvas().getCamera().animateViewToCenterBounds(layer.getGlobalFullBounds(),true, msec);//msecミリ秒かけてアニメーション
// }
//called from DB import (initial)
public Note getNote(int penid){
Note n = notes.get(penid);
if (n==null){
n = addNote(penid);
}
return n;
}
//あいている番号を手当たり次第追加する
public Note addNote(){
int n = 1;
while(true){ if (notes.get(n)==null) {return addNote(n);} n++; }
}
//ボタンで,あとから追加する(ここでは,表示シートの設定やレイアウトはしない)
public Note addNote(int penid){
if (notes.get(penid)!=null) return null;
Note n = new Note(penid, this);
notes.put(penid, n);
getCanvas().getLayer().addChild(n);
for(FocusFrame ff: FocusFrame.frames){
ff.applyToNote(n,true);
}
return n;
}
public static void main(final String[] args) {
if (args.length >= 2){
StringBuffer sb = new StringBuffer();
for(int i=1;i<args.length;i++){
sb.append(args[i]+" ");
}
new SimpleATN(sb.toString().trim(), args.length);
} else {
new SimpleATN();
}
}
public void openConnection() {
if (mysqldb == null) mysqldb = new ATN_Mysql_DB(this, dbuser, dbpass, dbname);
if (mysqldb == null) return;
if (mysqldb_root == null) mysqldb_root = new ATN_Mysql_DB(this, "root", "root", dbname);
if (mysqldb_root == null) return;
ArrayList<DB_Lecture> dblec = mysqldb.get_db_lectures(true);
if (dblec == null) return;
if (dblec.size()==0) {
JOptionPane.showMessageDialog(this, "筆記がありませんでした");
return;
}
DB_Lecture selectedDBLec = null;
if (dblec.size()>1){
// System.out.println(dblec.size());
// SingleSelector<DB_Lecture> sel = new SingleSelector<DB_Lecture>(dblec, this, "読み込む講義を選択してください");
SingleTableSelector<DB_Lecture> sel = new SingleTableSelector<DB_Lecture>(dblec, this, "読み込む講義を選択してください",
new String[]{"id","name","size","Start datetime"," "},
new String[]{"id","name","dataamount","tstamp",""},
new String[]{"start"},
new int[]{30,300,60,200,100});
if (sel.selectedObj == null) return;
selectedDBLec = sel.selectedObj;
} else {
//TODO: もうここは使わない?
selectedDBLec = dblec.get(0);
if (selectedDBLec.dataamount==0){
JOptionPane.showMessageDialog(this, "筆記がありませんでした");
return;
}
}
//もし新規読み取りで、継続データがあれば、名前をつけて保存する。
if (selectedDBLec.dataamount == 0 && selectedDBLec == dblec.get(0)){
if (dblec.get(1).id == 0 && dblec.get(1).dataamount > 0){
//以前の筆記にテンポラリーな名前をつけて保存
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mmに保存");
String name = JOptionPane.showInputDialog(this, "新規取り込みを開始するには、既存の筆記に名前をつけて保存してください\nキャンセルすると新規取り込みを中止します", sdf.format(System.currentTimeMillis()));
if (name == null){
return;
}
mysqldb.fixAsNamedLecture(name);
}
showFadingMessage("新規取り込みを開始しました", SimpleATN.messageColor, Color.black, 2, 0.9f);
}
//もし以前に開いているノートがあれば,消す
if (openingLecture != null) {
openingLecture.stop();
this.removeAllNote(true);
//シートマップの登録も解除
for(SeatmapController smap: ConfigWindow.configWin.seatmap.smaps){
smap.clean();
}
}
System.out.println("Loading "+selectedDBLec.toString());
selectedDBLec.loadPenData(this, mysqldb);
// TODO: grouping
// for(Note n: notes.values()){
// n.grouping(100);
// }
openingLecture = selectedDBLec;
openingLectureid = String.valueOf(selectedDBLec.id);
menutoolbar.getAction("Reload").setEnabled(true);
// menutoolbar.getAction("Reload").setSelected(true);//UDP Notificationができたので自動で定期チェックはしない
//ここで,フィルタがあれば読み込む TODO: 一旦削除
/// RegionSSFilter.loadAfterLectureLoad();
// relayout(0);//最初のレイアウト
// zoomToBounds(tempAfterLayoutBounds,1000);
Timer t = new Timer(1000, new SimpleATN_LayoutAction(this));
t.setRepeats(false);
t.start();
}
//OpenConnectionに失敗したとき
public void findDBName(){
//まずroot/rootで試みて,データベースを探す
JOptionPane.showMessageDialog(this, "データベース探索機能,パスワード再設定機能は未実装です");
}
public void removeAllNote(boolean deleteff) {
int p;
if (!deleteff){
p = JOptionPane.showConfirmDialog(this, "集約表示枠も削除する?\n(表示リセットをキャンセルするときは「取消」)","表示リセット",JOptionPane.YES_NO_CANCEL_OPTION);
if (p == JOptionPane.CANCEL_OPTION) return;
} else {
p = JOptionPane.YES_OPTION;
}
if (p == JOptionPane.YES_OPTION) {
for(FocusFrame ff: FocusFrame.frames){
ff.dispose();
}
FocusFrame.frames.clear();
}
getCanvas().getLayer().removeAllChildren();
notes.clear();
//筆記モードなら,もとに戻す
if (menutoolbar.getAction("Draw").isSelected()){
menutoolbar.getAction("Draw").doClick();
showFadingMessage("筆記モードを解除しました.\nDraw Mode has been reverted.", SimpleATN.messageColor, Color.black, 2, 0.9f);
}
}
// //テスト用.筆記をそれぞれのNoteのSheetに追加する
// public void addPNodeToEachNote(int sheetnum, Rectangle2D pb){
// for(Note n: notes.values()){
// Sheet s = n.sheets.get(sheetnum);
// s.addChild(new PPath(pb));
// }
// }
@Override
public void run() {
try {Thread.sleep(1200);} catch (InterruptedException e) { e.printStackTrace();}
for(FocusFrame ff: FocusFrame.frames) ff.updateCameraView();
}
public ArrayList<LayoutContent> getLayoutContents(){
ArrayList<LayoutContent> cnt = new ArrayList<LayoutContent>();
cnt.addAll(notes.values());
return cnt;
}
public void relayout(int msec, String caller){
boolean finished = false;
//筆跡マップが有効か?
MyAction seatmap;
if (menutoolbar != null){
seatmap = menutoolbar.getAction("SheetMap");
if (seatmap != null && seatmap.isSelected()){
if (ConfigWindow.configWin.seatmap.active_smap != null){
layoutseat = new LayoutBySeat(getCanvas().getCamera(), ConfigWindow.configWin.seatmap.active_smap);
layoutseat.layout(0, this);
tempAfterLayoutBounds = layoutseat.contentregion(this);
finished = true;
}
}
}
if (!finished){
layoutabst = new LayoutAbstract(getCanvas().getCamera());
layoutabst.layout(0, this);
tempAfterLayoutBounds = layoutabst.contentregion(this);
//最適な配置を考える
// System.out.println("relayout by "+caller);
// tempAfterLayoutBounds = new PBounds();
// int gap = 0;
// ArrayList<Note> rects = new ArrayList<Note>();
// rects.addAll(notes.values());
// int cols = getBestFitLayout(rects, getCanvas().getCamera().getViewBounds(), gap, gap);
// ArrayList<PActivity> palist = layout(rects, cols, gap, gap, msec, tempAfterLayoutBounds);
// layoutcols = cols;
// long mtime = System.currentTimeMillis();
// for (PActivity pact : palist) if (pact != null) pact.setStartTime(mtime);
}
// update FocusFrame view
Thread t = new Thread(this);
t.start();
//もしペン筆記到来からの再レイアウト要求でなければ
if (!caller.equals("DB_Lec.loadPenData")){
for(Note n: notes.values()){
for(Sheet s: n.sheets.values()){
s.showFadingSheetNum(2000);
}
}
}
}
/**
* シートマップでレイアウト
* @param stu2pos
*/
public void layoutBySeatmap() {
if (layoutseat == null){
if (ConfigWindow.configWin.seatmap.active_smap != null){
layoutseat = new LayoutBySeat(getCanvas().getCamera(), ConfigWindow.configWin.seatmap.active_smap);
}
}
layoutseat.layout(0, this);
tempAfterLayoutBounds = layoutseat.contentregion(this);
zoomToBounds(tempAfterLayoutBounds,1000,"satn.layoutBySeatmap");
}
public void zoomToBounds(Rectangle2D pb, int msec, String caller){
zoomToBounds(pb, msec, caller, null);
}
public void zoomToBounds(Rectangle2D pb, int msec, String caller, PInputEvent aEvent) {
if (pb != null) {
// System.out.println("zoom to "+pb.toString()+" by "+caller);
new SimpleATN_animateCameraView(getCanvas().getCamera(), pb, msec, aEvent);
}
}
public static void openMemoryMonitor() {
final MemoryMonitor demo = new MemoryMonitor();
final JFrame f = new JFrame("MemoryMonitor");
WindowListener l = new WindowAdapter() {
public void windowClosing(WindowEvent e) {
demo.surf.stop();
f.dispose();
}
public void windowDeiconified(WindowEvent e) {
demo.surf.start();
}
public void windowIconified(WindowEvent e) {
demo.surf.stop();
}
};
f.addWindowListener(l);
f.getContentPane().add(demo, BorderLayout.CENTER);
f.pack();
f.setSize(new Dimension(200, 200));
f.setVisible(true);
f.setLocation(965, 1);
demo.surf.start();
}
public void setDrawMode(boolean b) {
if (drawToolbar == null) drawToolbar = new DrawPenToolbar();
if (b){
panEventHandler.setEventFilter(new PInputEventFilter(InputEvent.BUTTON2_MASK));//本当は効果を消したい
PCamera cam = getCanvas().getCamera();
cam.addChild(drawToolbar);
drawToolbar.setOffset((cam.getWidth()-drawToolbar.getWidth())/2,0);
if (SimpleATN.theapp.isNoDB){
SimpleATN.theapp.showFadingMessage("DB未接続です.筆記を保存するにはファイルに保存してください", SimpleATN.messageColor, Color.black, 2, 0.9f);
}
} else {
panEventHandler.setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_MASK));
drawToolbar.removeFromParent();
}
for(Note n: notes.values()){
for(Sheet s: n.sheets.values()){
s.setDrawMode(b);
}
}
}
public String selectfile(boolean issave, String ext){
// ファイルダイアログを開く
String cdir = System.getProperty("user.dir");
JFileChooser fd = new JFileChooser(cdir);
NullFileFilter nff = new NullFileFilter(ext);
fd.setFileFilter(nff);
int returnval;
if (issave) returnval = fd.showSaveDialog(this);
else returnval = fd.showOpenDialog(this);
if (returnval == JFileChooser.APPROVE_OPTION){
// 拡張子がついていないとき,むりやりつける
StringBuffer path = new StringBuffer(fd.getSelectedFile().getAbsolutePath());
if (!path.toString().endsWith(ext)){
path.append(ext);
}
return path.toString();
}else{
return null;
}
}
public void saveToFile(String fn) {
if (fn == null) fn = selectfile(true,".satn");
if (fn == null) return;//もしファイル名を選択しなかったら
FileReadWriter.writeBytesToFile(fn, byteSerializeExport(true));
File f = new File(fn);
showFadingMessage(f.getName()+"\nに保存しました", messageColor, Color.black, 2, 0.9f);
}
public void saveFrameToFile(String fn) {
if (fn == null) fn = selectfile(true,".waku");
if (fn == null) return;//もしファイル名を選択しなかったら
FileReadWriter.writeBytesToFile(fn, byteSerializeExport(false));
File f = new File(fn);
showFadingMessage("領域データを "+f.getName()+"\nに保存しました", messageColor, Color.black, 2, 0.9f);
}
public void loadFromFile(String fn,int num){
if (filedialogopennum == 2 && num==2) return;
System.out.println(num); filedialogopennum = num;
if (fn == null) fn = selectfile(false,".satn");
if (fn == null) return;//もしファイル名を選択しなかったら
removeAllNote(true);
byte[] ba = FileReadWriter.readBytesFromFile(fn);
byteSerializeImport(ba,true);
File f = new File(fn);
showFadingMessage(f.getName()+"\nから読み込みました", messageColor, Color.black, 2, 0.9f);
changeTitle(f.getAbsolutePath());
openingLectureid = f.getName();
//ここで,フィルタがあれば読み込む
RegionSSFilter.loadAfterLectureLoad();
relayout(1000, "SimpleATN.loadFromFile");
zoomToBounds(tempAfterLayoutBounds, 1000, "DB_Lec.loadPenData");
}
public void loadFrameFromFile(String fn){
if (fn == null) fn = selectfile(false,".waku");
if (fn == null) return;//もしファイル名を選択しなかったら
byte[] ba = FileReadWriter.readBytesFromFile(fn);
byteSerializeImport(ba,false);
File f = new File(fn);
showFadingMessage("領域データを "+f.getName()+"\nから読み込みました", messageColor, Color.black, 2, 0.9f);
}
public transient static Color messageColor = new Color(204,238,255);
public byte[] byteSerializeExport(boolean waku_all) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
OutputStream gzipos = null;
PObjectOutputStream pobjos = null; // PNodeを保存するときはPObjectOutputStream
try {
gzipos = new GZIPOutputStream(baos);
pobjos = new PObjectOutputStream(gzipos);
if (waku_all){ // collect target nodes
pobjos.writeInt(notes.size());
System.out.println("Note size "+notes.size());
for(Note n: notes.values()){
pobjos.writeInt(n.getAll().size());
System.out.println("Note "+n.penid+" SS size "+n.getAll().size());
for(ShortStroke ss: n.getAll()){
ss.penid = n.penid;
pobjos.writeObjectTree(ss);
}
}
pobjos.writeInt(pimages.size());
System.out.println("PImages size "+pimages.size());
for(int i=1;i<=pimages.size();i++){
pobjos.writeObjectTree(pimages.get(i));
}
}
pobjos.close();
gzipos.close();
} catch (IOException iex) {
iex.printStackTrace(System.out);
}
System.out.println("save size: " + baos.size() + " bytes.");
return baos.toByteArray();
}
public void byteSerializeImport(byte[] ba, boolean waku_all){
ByteArrayInputStream bais = new ByteArrayInputStream(ba);
InputStream gzipis = null;
ObjectInputStream ois = null;
PNode pn = null;
try {
gzipis = new GZIPInputStream(bais);
ois = new ObjectInputStream(gzipis);
if (waku_all){
int num = ois.readInt();
for(int i=0;i<num;i++){
int ssnum = ois.readInt();
for(int j=0;j<ssnum;j++){
pn = (PNode) ois.readObject();
if (pn instanceof ShortStroke){
ShortStroke ss = (ShortStroke)pn;
ss.addEventOnSS();
this.getNote(ss.penid).addStroke(ss);
}
}
}
int pinum = ois.readInt();
if (pinum > 0) pimages.clear();
for(int i=1;i<=pinum;i++){
pn = (PNode) ois.readObject();
if (pn instanceof MyPImage){
MyPImage ss = (MyPImage)pn;
pimages.put(i, ss);
}
}
}
ois.close();
gzipis.close();
bais.close();
} catch (EOFException eofex) {
eofex.printStackTrace();
} catch (IOException excep) {
excep.printStackTrace(System.err);
} catch (ClassNotFoundException excnf) {
System.err.println("ClassNotFound Error");
} finally {
}
}
public void printToPDF() {
// setSize(592,876);
String sht = JOptionPane.showInputDialog(this,"(1/2) 印刷するシート番号(1-6)を入力してください。");
int shtnum = 0;
if (sht == null) return;
try{
shtnum = Integer.parseInt(sht);
} catch(NumberFormatException nfex){
return;
}
if (shtnum == 0) return;
if (menutoolbar.getAction("Landscape Mode").isSelected()) {
setSize(876-90,572+90);
} else {
setSize(572,876);
}
//ハンドルの消去
for(Object o: getCanvas().getCamera().getAllNodes()){
if (o instanceof PNode){
PNode pn = (PNode)o;
if (pn instanceof InsideToolbar_ZoomHandle){
pn.setVisible(false);
} else if (pn instanceof InsideToolbar_PanHandle){
pn.setVisible(false);
}
}
}
validate();
//非表示ノートチェック
boolean noshowsheet_exist = false;
for(Note n: notes.values()){
Sheet s = n.getSheet(shtnum);
if (!s.getVisible()) noshowsheet_exist = true;
}
if (noshowsheet_exist) {
int res = JOptionPane.showConfirmDialog(this, "非表示設定のシートがあります.それらは印刷スキップされませんが,続けてよろしいですか?","非表示シート",JOptionPane.YES_NO_OPTION);
if (res == JOptionPane.NO_OPTION) return;
}
String ret = JOptionPane.showInputDialog(this,"(2/2) 出力するPDFファイル名を入力してください。");
if (ret == null) return;
if (!ret.endsWith(".pdf")){
ret = ret + ".pdf";
}
PrintPrintable pdfprint = new PrintPrintable();
if (menutoolbar.getAction("Landscape Mode").isSelected()) {
pdfprint.prePrintByInstance(getCanvas(), "A4L", ret);
} else {
pdfprint.prePrintByInstance(getCanvas(), "A4P", ret);
}
ArrayList<Sheet> targetSheet = new ArrayList<Sheet>();
for(Note n: notes.values()){
Sheet s = n.getSheet(shtnum);
if (s.getVisible()) targetSheet.add(s);
s.prepare_for_print(true);//true=before
n.prepare_for_print(true);
}
for(Sheet s: targetSheet){
if (menutoolbar.getAction("Landscape Mode").isSelected()) {
//調整なし
PBounds b = s.getGlobalBounds();
getCanvas().getCamera().setViewBounds(b);
pdfprint.printPageByInstance();
} else {
PBounds b = s.getGlobalBounds();
b.setOrigin(b.getX()-20, b.getY()-20);
getCanvas().getCamera().setViewBounds(b);
pdfprint.printPageByInstance();
}
}
for(Sheet s: targetSheet){
s.prepare_for_print(false);//false = after
}
for(Note n: notes.values()){
n.prepare_for_print(false);
}
pdfprint.postPrintByInstance();
//ハンドルの再表示
for(Object o: getCanvas().getCamera().getAllNodes()){
if (o instanceof PNode){
PNode pn = (PNode)o;
if (pn instanceof InsideToolbar_ZoomHandle){
pn.setVisible(true);
} else if (pn instanceof InsideToolbar_PanHandle){
pn.setVisible(true);
}
}
}
showFadingMessage("PDF出力が完了しました", messageColor, Color.black, 2, 0.9f);
NetUtil.openPDF(ret);
zoomToBounds(tempAfterLayoutBounds, 1000, "SATN.printToPDF");
NetUtil.openpath(".");
}
//ファイルのドラッグ&ドロップ
@Override
public void dropPath(String path, DropTargetDropEvent dtde) {
System.out.println(path);
if (path.endsWith(".satn")){
loadFromFile(path,99);
}
if (path.endsWith(".waku")){
loadFrameFromFile(path);
}
File f = new File(path);
if (f.isDirectory()){
// System.out.println("Load Image");
for(int i=1;i<7;i++){
byte[] b = FileReadWriter.readBytesFromFile(path+File.separator+"スライド"+i+".PNG");
MyPImage pimg = new MyPImage(b, false);
pimages.put(i,pimg);
}
showFadingMessage("画像を読み込みました", messageColor, Color.black, 2, 0.9f);
}
}
//画面(Camera)の左上に,ペン番号とシート番号を表示(b==trueのとき)
public void showSheetInfoOnCamera(Sheet sheet, boolean b) {
// if (sheetVisibleMode[0]==0){
// System.out.println("Tooltip "+sheetVisibleMode[0]);
// getCanvas().requestFocus();
if (toolTipPText == null) {
toolTipPText = new PText();
toolTipPText.setScale(2f);
getCanvas().getCamera().addChild(toolTipPText);
}
toolTipPText.setText(" "+sheet.getNote().penid+" - "+sheet.num+" ");
toolTipPText.setTextPaint(Sheet.numPTColorAry[sheet.num]);
toolTipPText.setVisible(b);
// }
}
public void checkSecondaryDisplay(){
ArrayList<Rectangle> dispList = MultiDisplay.getMultiDisplayRectList();
if (dispList.size()>1){
menutoolbar.getAction("Secondary Display").setSelected(true);
}
}
public void showFrameOnSecondaryDisplay(JFrame f) {
//thisとは別のディスプレイ(true)に,全画面で表示する(true)
MultiDisplay.moveWindow(this, f, true, true);
}
//追加機能分のメニュー
public void addPluginMenuTo(JPopupMenu menu) {
for(DefaultPlugin dp: plugins){
dp.addMenu(menu);
}
}
public void setLandscapeMode(boolean b, boolean showMessage, boolean doLayout) {
for(Note n: notes.values()){
n.setLandscapeMode(b);
}
if (showMessage){
if (b) {
showFadingMessage("横長モード", messageColor, Color.black, 2, 0.9f);
} else {
showFadingMessage("縦長モード", messageColor, Color.black, 2, 0.9f);
}
}
if (doLayout){
relayout(1000, "SATN.setSheetVisible");
zoomToBounds(tempAfterLayoutBounds, 1000,"SATN.setSheetVisible");
}
}
public void applyLandscapeMode(){
setLandscapeMode(menutoolbar.getAction("Landscape Mode").isSelected(), false, false);
}
public void applyDrawingMode() {
for(Note n: notes.values()) n.applyDrawingMode();
}
}