package vg.modules.notepad.components;
import java.awt.BorderLayout;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
import vg.core.event.UIEventCloseAIF;
import vg.core.event.UIEventOpenNewAIF;
import vg.core.plugin.PluginParameter;
import vg.modules.notepad.components.textComponent.ITextComponent;
import vg.userInterface.swingComponents.SimpleTabWithCloseButton;
/**
* This class realizes desktop for notepad.
* All methods of this class by need called from EDT only!!!
* @author tzolotuhin
*/
public class Desktop {
private static AtomicInteger counterId = new AtomicInteger(0);
// Panels
private final JPanel view;
// Components
private final JTabbedPane tabs;
// Main data
private final PluginParameter parameter;
private final Map<Integer, String> comTabIdAndFullFileName; // composition full file names and tabs
private final Map<Integer, String> comTabIdAndShortFileName; // composition short file names and tabs
private final Map<Integer, ITextComponent> comTabIdAndTextComponent; // composition full file names and tabs
private Map<Integer, Integer> comTabIdAndTab;
// Additional data
private final Map<String, ArrayList<Integer>> tabTitleStatistic; // statistic of titles
// Mutex
private final Object theMutexObject;
/**
* Constructor.
* You need to create this component in EDT only!!!
*/
public Desktop(final PluginParameter param) {
// init mutex
this.theMutexObject = new Object();
// init main data
this.parameter = param;
this.comTabIdAndShortFileName = new HashMap<Integer, String>();
this.comTabIdAndFullFileName = new HashMap<Integer, String>();
this.comTabIdAndTab = new HashMap<Integer, Integer>();
this.comTabIdAndTextComponent = new HashMap<Integer, ITextComponent>();
// init additional data
this.tabTitleStatistic = new HashMap<String, ArrayList<Integer>>();
// create components and panels
this.view = new JPanel(new BorderLayout());
this.tabs = new JTabbedPane(JTabbedPane.TOP, JTabbedPane.SCROLL_TAB_LAYOUT);
this.view.add(this.tabs, BorderLayout.CENTER);
// ctrl-tab and ctrl-shift-tab
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new KeyEventDispatcher() {
public boolean dispatchKeyEvent(KeyEvent e) {
if (e.getID() == KeyEvent.KEY_PRESSED && e.getKeyCode() == KeyEvent.VK_TAB && e.isControlDown()) {
int tabcount = Desktop.this.tabs.getTabCount();
if (tabcount > 0) {
int sel = Desktop.this.tabs.getSelectedIndex();
if (e.isShiftDown()) {
sel = sel - 1;
if (sel < 0)
sel = tabcount - 1;
} else {
sel = sel + 1;
if (sel >= tabcount)
sel = 0;
}
Desktop.this.tabs.setSelectedIndex(sel);
}
return(true);
}
return(false);
}
});
}
public JComponent getView() {
return(this.view);
}
/**
* This method adds new tab on the desktop.
*/
public void addTab(final String shortFileName, final String fullFileName, final ITextComponent textComponent) {
if(SwingUtilities.isEventDispatchThread()) {
synchronized (theMutexObject) {
localAddTab(getNextId(), shortFileName, fullFileName, textComponent);
}
} else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
synchronized (theMutexObject) {
localAddTab(getNextId(), shortFileName, fullFileName, textComponent);
}
}
});
}
}
/**
* This method adds new tab on the desktop.
*/
public void addTab(final int tabId, final String shortFileName, final String fullFileName, final ITextComponent textComponent) {
if(SwingUtilities.isEventDispatchThread()) {
synchronized (theMutexObject) {
localAddTab(tabId, shortFileName, fullFileName, textComponent);
}
} else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
synchronized (theMutexObject) {
localAddTab(tabId, shortFileName, fullFileName, textComponent);
}
}
});
}
}
/**
* This method returns current text component.
*/
public ITextComponent getCurrentTextComponent() {
synchronized (theMutexObject) {
int index = this.tabs.getSelectedIndex();
for(Integer buf : this.comTabIdAndTab.keySet()) {
Integer value = this.comTabIdAndTab.get(buf);
if((int)value == index) {
return(this.comTabIdAndTextComponent.get(buf));
}
}
return(null);
}
}
/**
* This method returns current tab id, otherwise -1.
*/
public int getCurrentId() {
synchronized (theMutexObject) {
int index = this.tabs.getSelectedIndex();
for(Integer buf : this.comTabIdAndTab.keySet()) {
Integer value = this.comTabIdAndTab.get(buf);
if((int)value == index) {
return(buf);
}
}
return(-1);
}
}
/**
* This method returns current full file name, otherwise null.
*/
public String getCurrentFullFileName() {
synchronized (theMutexObject) {
int index = this.tabs.getSelectedIndex();
for(Integer buf : this.comTabIdAndTab.keySet()) {
Integer value = this.comTabIdAndTab.get(buf);
if((int)value == index) {
return(this.comTabIdAndFullFileName.get(buf));
}
}
return(null);
}
}
/**
* This method returns current short file name, otherwise null.
*/
public String getCurrentShortFileName() {
synchronized (theMutexObject) {
int index = this.tabs.getSelectedIndex();
for(Integer buf : this.comTabIdAndTab.keySet()) {
Integer value = this.comTabIdAndTab.get(buf);
if((int)value == index) {
return(this.comTabIdAndShortFileName.get(buf));
}
}
return(null);
}
}
/**
* This method returns text component.
*/
public ITextComponent getComponent(final int tabId) {
synchronized (theMutexObject) {
return(this.comTabIdAndTextComponent.get(tabId));
}
}
/**
* This method returns full file name.
*/
public String getFullFileNameAt(final int tabId) {
synchronized (theMutexObject) {
return(this.comTabIdAndFullFileName.get(tabId));
}
}
/**
* This method updates user interface theme.
*
*/
public void updateUITheme() {
synchronized (theMutexObject) {
Desktop.this.view.updateUI();
for(ITextComponent buf : this.comTabIdAndTextComponent.values()) {
buf.updateUITheme();
}
}
}
public void closeAllTab() {
if(SwingUtilities.isEventDispatchThread()) {
synchronized (theMutexObject) {
while(this.tabs.getTabCount() != 0) {
localCloseTab(0);
}
}
} else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
synchronized (theMutexObject) {
while(tabs.getTabCount() != 0) {
localCloseTab(0);
}
}
}
});
}
}
public void selectTabAt(final int tabId) {
if(SwingUtilities.isEventDispatchThread()) {
synchronized (theMutexObject) {
if(this.comTabIdAndTab.containsKey(tabId)) {
int index = this.comTabIdAndTab.get(tabId);
if(index >= 0 && index < this.tabs.getTabCount()) {
this.tabs.setSelectedIndex(index);
}
}
}
} else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
synchronized (theMutexObject) {
if(comTabIdAndTab.containsKey(tabId)) {
int index = comTabIdAndTab.get(tabId);
if(index >= 0 && index < tabs.getTabCount()) {
tabs.setSelectedIndex(index);
}
}
}
}
});
}
}
///////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
///////////////////////////////////////////////////////////////////////////
/**
* This method adds new tab on the desktop.
*/
private int localAddTab(int tabId, String shortFileName, String fullFileName, final ITextComponent textComponent) {
String localTitle = shortFileName;
ArrayList<Integer>array = this.tabTitleStatistic.get(shortFileName);
if(array == null) {
array = new ArrayList<Integer>();
array.add(0);
this.tabTitleStatistic.put(shortFileName, array);
} else {
if(array.size() == 1 && array.get(0) == 0) {
// renaming of existing tab
array.remove(0);
for(int i = 0 ; i < this.tabs.getTabCount(); i++) {
if(this.tabs.getTitleAt(i).equals(shortFileName)) {
this.tabs.setTitleAt(i, buildTabTitle(shortFileName, 1));
break;
}
}
array.add(1);
}
// find free index
int index = 1; // without zero
for(int i = 0 ; i < array.size() ; i++) {
if(!array.contains(index)) {
break;
}
index++;
}
array.add(index);
localTitle = buildTabTitle(localTitle, index);
}
// add information about new tab to data
this.tabs.addTab(localTitle, textComponent.getView());
this.comTabIdAndShortFileName.put(tabId, shortFileName);
this.comTabIdAndFullFileName.put(tabId, fullFileName);
this.comTabIdAndTab.put(tabId, this.tabs.getTabCount() - 1);
this.comTabIdAndTextComponent.put(tabId, textComponent);
// add close button
this.tabs.setTabComponentAt(this.tabs.getTabCount() - 1, new SimpleTabWithCloseButton(tabs, new ActionListener() {
public void actionPerformed(ActionEvent e) {
int tabIndex = Desktop.this.tabs.indexOfTabComponent((SimpleTabWithCloseButton)e.getSource());
localCloseTab(tabIndex);
}
}));
// update ui
this.view.updateUI();
// create and send event (opening of new aif)
if(this.parameter != null && this.parameter.userInterface != null) {
UIEventOpenNewAIF event = new UIEventOpenNewAIF(tabId, fullFileName, shortFileName);
this.parameter.userInterface.addEvent(event);
}
return(tabId);
}
/**
* This method closes tab.
* @param index - index of tab.
*/
private void localCloseTab(final int index) {
if(index < 0) return;
int tabId = -1;
for(Integer buf : this.comTabIdAndTab.keySet()) {
Integer value = this.comTabIdAndTab.get(buf);
if((int)value == index) {
String shortFileName = this.comTabIdAndShortFileName.get(buf);
String tabTitle = this.tabs.getTitleAt(index);
tabId = buf;
// clear tab statistic
ArrayList<Integer>array = this.tabTitleStatistic.get(shortFileName);
if(array != null) {
for(Integer bufInner : array) {
String title = buildTabTitle(shortFileName, bufInner);
if(title.equals(tabTitle)) {
array.remove(bufInner);
break;
}
}
if(array.size() == 0) {
this.tabTitleStatistic.remove(shortFileName);
}
}
// clear compositions
this.comTabIdAndFullFileName.remove(buf);
this.comTabIdAndShortFileName.remove(buf);
this.comTabIdAndTab.remove(buf);
this.comTabIdAndTextComponent.remove(buf);
// change next tab index
Map<Integer, Integer>bufMap = new HashMap<Integer, Integer>();
for(Integer bufInner : this.comTabIdAndTab.keySet()) {
Integer val = this.comTabIdAndTab.get(bufInner);
if((int)val > value) {
bufMap.put(bufInner, val - 1);
} else {
bufMap.put(bufInner, val);
}
}
this.comTabIdAndTab = bufMap;
break;
}
}
// update ui
this.tabs.remove(index);
this.view.updateUI();
// create and send event (closing of new aif)
if(tabId >= 0 && this.parameter != null && this.parameter.userInterface != null) {
UIEventCloseAIF event = new UIEventCloseAIF(tabId);
this.parameter.userInterface.addEvent(event);
}
}
private String buildTabTitle(final String title, final int number) {
if(number <= 0) return(title);
return(title + ":" + number);
}
/**
* This method returns next id for tab.
*/
private int getNextId() {
int a = counterId.incrementAndGet();
return(a);
}
}