package vg.userInterface;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import javax.swing.*;
import vg.core.AMenuItem;
import vg.core.AModel;
import vg.core.AUserInterface;
import vg.core.AUserInterfaceElement;
import vg.core.VisualGraph;
import vg.core.event.AUIEvent;
import vg.core.exception.UserInterfaceException;
import vg.core.request.AUIRequest;
import vg.core.request.EUIRequestType;
import vg.core.request.IUIRequestOwner;
import vg.userInterface.core.IGraphLayoutManager;
import vg.userInterface.instruments.Instruments;
import vg.userInterface.swingComponents.SimpleStatusBar;
/**
* This class realizes user interface and use swing for it.
* @author tzolotuhin
*/
public class SwingUserInterface extends AUserInterface {
private static final long serialVersionUID = 1000L;
// Components
private JMenu fileMenu, editMenu, windowMenu, otherMenu, aboutMenu;
private JMenuItem quitMenuItem, aboutMenuItem;
private JSplitPane splitFirst, splitSecond, splitThird;
// Main Components
private final JFrame frame;
private final JMenuBar menuBar;
private final Instruments instruments;
private final SimpleStatusBar statusBar;
private final SimpleUserInterfacePanel navigatorPanel, desktopPanel, attributePanel, minimapPanel;
private final Observer eventListener; // it need for UIEventChangeUIstyle and other
//-------------------------------------------------------------------------
private final static String DEF_WINDOWS_SIZE_X = "WindowSizeX";
private final static String DEF_WINDOWS_SIZE_Y = "WindowSizeY";
private final static String DEF_WINDOWS_POS_X = "WindowPosX";
private final static String DEF_WINDOWS_POS_Y = "WindowPosY";
private Integer windowSizeX, windowSizeY;
private Integer windowPosX, windowPosY;
//-------------------------------------------------------------------------
private WorkThread workThread;
private JProgressBar progressBar;
private final Timer progressTimer;
//-------------------------------------------------------------------------
public SwingUserInterface(final AModel theModel, final String title, IGraphLayoutManager layoutManager) throws UserInterfaceException {
super(theModel, layoutManager);
//this.frameTitle = title;
this.frame = new JFrame(title);
// Creating of window
this.loadWindowOptions();
this.frame.setSize(this.windowSizeX, this.windowSizeY);
this.frame.setLocation(this.windowPosX, this.windowPosY);
this.frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
this.frame.setLayout(new BorderLayout());
this.frame.addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
super.componentResized(e);
Component c = (Component)e.getSource();
Integer sizeX = c.getSize().width;
Integer sizeY = c.getSize().height;
VisualGraph.config.setProperty(DEF_WINDOWS_SIZE_X, sizeX.toString());
VisualGraph.config.setProperty(DEF_WINDOWS_SIZE_Y, sizeY.toString());
}
public void componentMoved(ComponentEvent e) {
super.componentMoved(e);
Component c = (Component)e.getSource();
Integer posX = c.getLocation().x;
Integer posY = c.getLocation().y;
VisualGraph.config.setProperty(DEF_WINDOWS_POS_X, posX.toString());
VisualGraph.config.setProperty(DEF_WINDOWS_POS_Y, posY.toString());
}
});
this.frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
quit();
}
});
// Creating of menu
this.menuBar = new JMenuBar();
this.frame.setJMenuBar(menuBar);
this.fileMenu = new JMenu("File");
this.editMenu = new JMenu("Edit");
this.windowMenu = new JMenu("Window");
this.otherMenu = new JMenu("Other");
this.aboutMenu = new JMenu("Help");
this.quitMenuItem = new JMenuItem("Quit");
this.aboutMenuItem = new JMenuItem("About");
this.quitMenuItem.setIcon(new ImageIcon("./data/resources/textures/quit.png"));
// Creating components
this.navigatorPanel = new SimpleUserInterfacePanel("Navigator", null);
this.desktopPanel = new SimpleUserInterfacePanel("Desktop", null);
this.attributePanel = new SimpleUserInterfacePanel("Attribute panel", null);
this.minimapPanel = new SimpleUserInterfacePanel("Mini map", null);
this.splitFirst = new JSplitPane(JSplitPane.VERTICAL_SPLIT, this.navigatorPanel.getView(), this.minimapPanel.getView());
this.splitSecond = new JSplitPane(JSplitPane.VERTICAL_SPLIT, this.desktopPanel.getView(), this.attributePanel.getView());
this.splitThird = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, this.splitFirst, this.splitSecond);
this.splitFirst.setResizeWeight(0.7);
this.splitFirst.setDividerSize(8);
this.splitFirst.setOneTouchExpandable(true);
this.splitSecond.setResizeWeight(0.7);
this.splitSecond.setDividerSize(8);
this.splitSecond.setOneTouchExpandable(true);
this.splitThird.setResizeWeight(0.2);
this.splitThird.setDividerSize(8);
this.splitThird.setOneTouchExpandable(true);
this.instruments = new Instruments();
this.statusBar = new SimpleStatusBar();
this.progressBar = new JProgressBar(0,100);
this.progressBar.setMaximumSize(new Dimension(100,this.statusBar.getPreferredSize().height-2));
this.statusBar.add(progressBar);
this.progressTimer = new Timer(500, new ActionListener() {
public void actionPerformed(ActionEvent e) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
progressBar.setValue((int) VisualGraph.progressManager.updateProgress());
progressBar.setVisible(VisualGraph.progressManager.getTaskCount() > 0);
}
});
}
});
progressTimer.start();
this.workThread = new WorkThread();
Thread t = new Thread(this.workThread);
t.setDaemon(true);
t.start();
// Package UI
this.frame.add(this.instruments.getView(),BorderLayout.NORTH);
this.frame.add(this.splitThird,BorderLayout.CENTER);
this.frame.add(this.statusBar,BorderLayout.SOUTH);
// Adding of menu bar
fileMenu.add(quitMenuItem);
aboutMenu.add(aboutMenuItem);
menuBar.add(fileMenu);
menuBar.add(editMenu);
menuBar.add(windowMenu);
menuBar.add(aboutMenu);
// Adding mnemonic for menus
fileMenu.setMnemonic('f');
editMenu.setMnemonic('e');
aboutMenu.setMnemonic('h');
quitMenuItem.setMnemonic('q');
quitMenuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
quit();
}
});
quitMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4, KeyEvent.ALT_DOWN_MASK));
// Add event and request listener for this window
this.eventListener = new Observer() {
public synchronized void update(Observable o, Object arg) {
if(arg instanceof AUIEvent) {
final AUIEvent event = (AUIEvent)arg;
switch (event.getType()) {
case DEF_CLOSE_PROGRAM: {
quit();
break;
}
case DEF_CHANGE_UI_STYLE: {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
SwingUtilities.updateComponentTreeUI(SwingUserInterface.this.frame.getContentPane());
SwingUserInterface.this.instruments.updateUIStyle();
}
});
break;
}
}
}
}
};
this.addListener(this.eventListener);
// Run
this.frame.setVisible(true);
}
public void addMenuItem(AMenuItem item, String place) {
if(item==null || item.getMenuItem()==null) {
VisualGraph.log.printDebug("[" + this.getClass().getName() + ".addMenuItem] [FAIL] adding of menu item. Item = null or menuItem = null, place = " + place);
return;
}
VisualGraph.log.printDebug("[" + this.getClass().getName() + ".addMenuItem] [PROCESS] adding of menu item. Item = " + item.getMenuItem().getText() + ", place = " + place);
if(place == null) {
this.otherMenu.add(item.getMenuItem());
VisualGraph.log.printDebug("[" + this.getClass().getName() + ".addMenuItem] [BAD] adding of menu item. Item = " + item.getMenuItem().getText() + ", place = " + place + "(This place is not found.)");
} else if(place.equalsIgnoreCase("File")) {
this.fileMenu.add(item.getMenuItem());
VisualGraph.log.printDebug("[" + this.getClass().getName() + ".addMenuItem] [OK] adding of menu item. Item = " + item.getMenuItem().getText() + ", place = " + place);
} else if(place.equalsIgnoreCase("Edit")) {
this.editMenu.add(item.getMenuItem());
VisualGraph.log.printDebug("[" + this.getClass().getName() + ".addMenuItem] [OK] adding of menu item. Item = " + item.getMenuItem().getText() + ", place = " + place);
} else if(place.equalsIgnoreCase("Window")) {
this.windowMenu.add(item.getMenuItem());
VisualGraph.log.printDebug("[" + this.getClass().getName() + ".addMenuItem] [OK] adding of menu item. Item = " + item.getMenuItem().getText() + ", place = " + place);
} else {
this.otherMenu.add(item.getMenuItem());
VisualGraph.log.printDebug("[" + this.getClass().getName() + ".addMenuItem] [BAD] adding of menu item. Item = " + item.getMenuItem().getText() + ", place = " + place + "(This place is not found.)");
}
this.workThread.addElement(item);
}
public void addElement(AUserInterfaceElement element, String place) {
if(element==null) {
VisualGraph.log.printDebug("[" + this.getClass().getName() + ".addElement] [FAIL] adding of interface's element. element = null, place = " + place);
return;
}
if(place==null) {
this.navigatorPanel.addTab(element);
VisualGraph.log.printDebug("[" + this.getClass().getName() + ".addElement] [BAD] adding of interface's element. place = " + place + ", name = " + element.getTitle() + "(This place is not found)");
} else if(place.equalsIgnoreCase("MiniMap")) {
this.minimapPanel.setMainTab(element);
VisualGraph.log.printDebug("[" + this.getClass().getName() + ".addElement] [OK] adding of interface's element. place = " + place + ", name = " + element.getTitle());
} else if(place.equalsIgnoreCase("Navigator")) {
this.navigatorPanel.addTab(element);
VisualGraph.log.printDebug("[" + this.getClass().getName() + ".addElement] [OK] adding of interface's element. place = " + place + ", name = " + element.getTitle());
} else if(place.equalsIgnoreCase("Desktop")) {
this.desktopPanel.setMainTab(element);
VisualGraph.log.printDebug("[" + this.getClass().getName() + ".addElement] [OK] adding of interface's element. place = " + place + ", name = " + element.getTitle());
} else if(place.equalsIgnoreCase("AttributePanel")) {
this.attributePanel.addTab(element);
VisualGraph.log.printDebug("[" + this.getClass().getName() + ".addElement] [OK] adding of interface's element. place = " + place + ", name = " + element.getTitle());
} else {
this.navigatorPanel.addTab(element);
VisualGraph.log.printDebug("[" + this.getClass().getName() + ".addElement] [BAD] adding of interface's element. place = " + place + ", name = " + element.getTitle());
}
this.workThread.addElement(element);
}
public void addListener(Observer obs) {
this.workThread.addElement(obs);
}
public void addInstrument(AUserInterfaceElement element) {
this.instruments.addInstrument(element.getView());
this.workThread.addElement(element);
}
public void addRequest(AUIRequest request) {
this.workThread.addRequest(request);
}
public void addEvent(AUIEvent event) {
this.workThread.addEvent(event);
}
public Component getView() {
return(this.frame);
}
public void quit() {
this.model.quit();
this.frame.dispose();
this.progressTimer.stop();
this.workThread.close();
for(Window w : Window.getWindows()) {
w.dispose();
}
}
/**
* Load window options.
*/
private void loadWindowOptions() {
String wSizeStrX = VisualGraph.config.getProperty(DEF_WINDOWS_SIZE_X);
String wSizeStrY = VisualGraph.config.getProperty(DEF_WINDOWS_SIZE_Y);
String wPosStrX = VisualGraph.config.getProperty(DEF_WINDOWS_POS_X);
String wPosStrY = VisualGraph.config.getProperty(DEF_WINDOWS_POS_Y);
//set size X(width of window)
if(wSizeStrX == null) {
this.windowSizeX = 800;
VisualGraph.config.setProperty(DEF_WINDOWS_SIZE_X, this.windowSizeX.toString());
} else {
try {
this.windowSizeX = new Integer(wSizeStrX);
} catch(NumberFormatException ex) {
this.windowSizeX = 800;
VisualGraph.config.setProperty(DEF_WINDOWS_SIZE_X, this.windowSizeX.toString());
}
}
//set size Y(height of window)
if(wSizeStrY == null) {
this.windowSizeY = 600;
VisualGraph.config.setProperty(DEF_WINDOWS_SIZE_Y, this.windowSizeY.toString());
} else {
try {
this.windowSizeY = new Integer(wSizeStrY);
} catch(NumberFormatException ex) {
this.windowSizeY = 600;
VisualGraph.config.setProperty(DEF_WINDOWS_SIZE_Y, this.windowSizeY.toString());
}
}
//set position X
if(wPosStrX == null) {
this.windowPosX = 200;
VisualGraph.config.setProperty(DEF_WINDOWS_POS_X, this.windowPosX.toString());
} else {
try {
this.windowPosX = new Integer(wPosStrX);
} catch(NumberFormatException ex) {
this.windowPosX = 800;
VisualGraph.config.setProperty(DEF_WINDOWS_POS_X, this.windowPosX.toString());
}
}
//set position Y
if(wPosStrY == null) {
this.windowPosY = 200;
VisualGraph.config.setProperty(DEF_WINDOWS_POS_Y, this.windowPosY.toString());
} else {
try {
this.windowPosY = new Integer(wPosStrY);
} catch(NumberFormatException ex) {
this.windowPosY = 800;
VisualGraph.config.setProperty(DEF_WINDOWS_POS_Y, this.windowPosY.toString());
}
}
}
}
/**
* This class is main class for user interface. This class provides
* communications for all modules in the program.
* @author tzolotuhin
*/
class WorkThread implements Runnable {
private List<Observer> arrayOfListeners;//array of listeners for different modules(or plugins) in system.
private List<AUIEvent> arrayOfEvent;//array of events by modules(or plugins).
private List<AUIRequest> arrayOfRequest;//array of requests by modules(or plugins).
private boolean close = false;
//-------------------------------------------------------------------------
public WorkThread() {
this.arrayOfListeners = new ArrayList<Observer>();
this.arrayOfEvent = new ArrayList<AUIEvent>();
this.arrayOfRequest = new ArrayList<AUIRequest>();
}
/**
* This method adds new module.
*/
public void addElement(Observer e) {
synchronized (this.arrayOfListeners) {
this.arrayOfListeners.add(e);
}
}
/**
* This methods adds new event.
* This event will be sent to all system(or user) modules.
*/
public void addEvent(AUIEvent event) {
synchronized (this.arrayOfEvent) {
this.arrayOfEvent.add(event);
}
Thread.interrupted();
}
/**
* This methods adds new request.
* This request will be sent to all system(or user) modules.
*/
public void addRequest(AUIRequest request) {
synchronized (this.arrayOfRequest) {
this.arrayOfRequest.add(request);
}
Thread.interrupted();
}
/**
* It's an endless cycle.
*/
public void run() {
//condition of exit
while(!this.close) {
try {
Thread.sleep(100);
} catch (InterruptedException ex) {}
int countRequest = 0; // [DEBUG PERFORMANCE]
int countEvent = 0; // [DEBUG PERFORMANCE]
List<AUIEvent>bufEvents = null;
List<AUIRequest>bufRequest = null;
synchronized (this.arrayOfEvent) {
bufEvents = new ArrayList<AUIEvent>(this.arrayOfEvent);
countEvent = this.arrayOfEvent.size(); // [DEBUG PERFORMANCE]
this.arrayOfEvent.clear();
}
synchronized (this.arrayOfRequest) {
bufRequest = new ArrayList<AUIRequest>(this.arrayOfRequest);
countRequest = this.arrayOfRequest.size(); // [DEBUG PERFORMANCE]
this.arrayOfRequest.clear();
}
long startRun = new Date().getTime(); // [DEBUG PERFORMANCE]
// circle for events
for(final AUIEvent bufInner : bufEvents) {
long startEventTime = new Date().getTime(); // [DEBUG PERFORMANCE]
synchronized (this.arrayOfListeners) {
for(final Observer buf : this.arrayOfListeners) {
try {
long start = new Date().getTime();
buf.update(null, bufInner);
long finish = new Date().getTime();
if(finish - start > 1) {
VisualGraph.log.printDebug("[EVENT] [" + bufInner.getType().toString() + "] [" + buf.getClass().getName() + "] "
+ " work time: " + (finish - start) / 1000.0 + " sec");
}
} catch (Throwable ex) {
if(VisualGraph.log != null) {
VisualGraph.log.printException(ex);
}
VisualGraph.windowMessage.errorMessage("Fail.\nException : " + ex.getMessage(), "Thread error");
}
}
}
// [DEBUG PERFORMANCE]
long finishEventTime = new Date().getTime();
if(finishEventTime - startEventTime > 1) {
VisualGraph.log.printDebug("[EVENTS FOR ALL PLUGINS] [" + bufInner.getType() + "] work time: " + (finishEventTime - startEventTime) / 1000.0 + " sec");
}
}
// circle for requests
for(final AUIRequest bufInner : bufRequest) {
long startRequestTime = new Date().getTime(); // [DEBUG PERFORMANCE]
synchronized (this.arrayOfListeners) {
for(final Observer buf : this.arrayOfListeners) {
try {
long start = new Date().getTime();
buf.update(null, bufInner);
long finish = new Date().getTime();
if(finish - start > 1) {
VisualGraph.log.printDebug("[REQUEST] [" + bufInner.getType().toString() + "] [" + buf.getClass().getName() + "] "
+ " work time: " + (finish - start) / 1000.0 + " sec");
}
} catch (Throwable ex) {
if(VisualGraph.log != null) {
VisualGraph.log.printException(ex);
}
VisualGraph.windowMessage.errorMessage("Fail.\nException : " + ex.getMessage(), "Thread error");
}
}
try {
IUIRequestOwner owner = bufInner.getOwner();
if(owner != null) {
owner.callRequestOwner(new AUIRequest(EUIRequestType.PASS, null){});
}
} catch(Throwable ex) {
VisualGraph.log.printException(ex);
}
}
// [DEBUG PERFORMANCE]
long finishRequestTime = new Date().getTime();
if(finishRequestTime - startRequestTime > 1) {
VisualGraph.log.printDebug("[REQUESTS FOR ALL PLUGINS] [" + bufInner.getType() + "] work time: " + (finishRequestTime - startRequestTime) / 1000.0 + " sec");
}
}
// [DEBUG PERFORMANCE]
long finishRun = new Date().getTime();
if(finishRun - startRun > 10) {
VisualGraph.log.printDebug("[WORKTHREAD] Event's count = " + countEvent + " Request's count = " + countRequest + " work time: " + (finishRun - startRun) / 1000.0 + " sec");
}
}
}
public void close() {
this.close = true;
Thread.interrupted();
}
}