/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.esa.snap.netbeans.docwin;
import org.esa.snap.netbeans.tile.TileUtilities;
import org.esa.snap.netbeans.tile.Tileable;
import org.netbeans.swing.tabcontrol.DefaultTabDataModel;
import org.netbeans.swing.tabcontrol.TabData;
import org.netbeans.swing.tabcontrol.TabDisplayer;
import org.netbeans.swing.tabcontrol.TabbedContainer;
import org.netbeans.swing.tabcontrol.WinsysInfoForTabbedContainer;
import org.netbeans.swing.tabcontrol.event.TabActionEvent;
import org.openide.DialogDescriptor;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.awt.ActionID;
import org.openide.awt.UndoRedo;
import org.openide.util.Lookup;
import org.openide.util.lookup.Lookups;
import org.openide.util.lookup.ProxyLookup;
import org.openide.windows.Mode;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.InternalFrameEvent;
import javax.swing.event.InternalFrameListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.Vector;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import static org.openide.util.NbBundle.Messages;
/**
* A top-level window which hosts an internal desktop in which other windows can be floated as internal frames.
*
* @author Norman Fomferra
* @since 1.0
*/
@TopComponent.Description(
preferredID = "WorkspaceTopComponent",
persistenceType = TopComponent.PERSISTENCE_ONLY_OPENED
)
@TopComponent.Registration(
mode = "editor",
openAtStartup = false
)
@ActionID(
category = "Window",
id = "org.esa.snap.netbeans.docwin.WorkspaceTopComponent"
)
@TopComponent.OpenActionRegistration(
displayName = "#CTL_WorkspaceTopComponentNameBase",
preferredID = "WorkspaceTopComponent"
)
@Messages({
"CTL_WorkspaceTopComponentNameBase=Workspace",
"CTL_WorkspaceTopComponentDescription=Provides an internal desktop for document windows",
})
public class WorkspaceTopComponent extends TopComponent implements WindowContainer<TopComponent>, Tileable {
public final static String ID = WorkspaceTopComponent.class.getSimpleName();
private static final Logger LOG = Logger.getLogger(WorkspaceTopComponent.class.getName());
private final Map<TabData, JInternalFrame> tabToFrameMap;
private final Map<JInternalFrame, TabData> frameToTabMap;
private final Map<Object, Rectangle> idToBoundsMap;
private final ActionListener tabActionListener;
private final InternalFrameListener internalFrameListener;
private final PropertyChangeListener propertyChangeListener;
private final FrameProxyLookup lookup;
private TabbedContainer tabbedContainer;
private JDesktopPane desktopPane;
public WorkspaceTopComponent() {
this(Bundle.CTL_WorkspaceTopComponentNameBase());
}
public WorkspaceTopComponent(String displayName) {
frameToTabMap = new HashMap<>();
tabToFrameMap = new HashMap<>();
idToBoundsMap = new HashMap<>();
tabActionListener = new TabActionListener();
internalFrameListener = new MyInternalFrameListener();
propertyChangeListener = new MyPropertyChangeListener();
lookup = new FrameProxyLookup();
associateLookup(new ProxyLookup(Lookups.fixed(this), lookup));
initComponents();
setName(displayName);
setDisplayName(displayName);
setToolTipText(Bundle.CTL_WorkspaceTopComponentDescription());
}
public static WorkspaceTopComponent findShowingInstance() {
TopComponent activated = WindowManager.getDefault().getRegistry().getActivated();
if (activated instanceof WorkspaceTopComponent) {
return (WorkspaceTopComponent) activated;
}
List<WorkspaceTopComponent> showingWorkspaces = findShowingInstances();
if (!showingWorkspaces.isEmpty()) {
return showingWorkspaces.get(0);
}
return null;
}
public static List<WorkspaceTopComponent> findShowingInstances() {
return WindowUtilities.getOpened(WorkspaceTopComponent.class).filter(Component::isShowing).collect(Collectors.toList());
}
private void initComponents() {
setLayout(new BorderLayout());
DefaultTabDataModel tabDataModel = new DefaultTabDataModel();
tabbedContainer = new TabbedContainer(tabDataModel,
TabbedContainer.TYPE_EDITOR,
WinsysInfoForTabbedContainer.getDefault(new MyWinsysInfoForTabbedContainer()));
tabbedContainer.setVisible(false);
desktopPane = new JDesktopPane();
add(tabbedContainer, BorderLayout.NORTH);
add(desktopPane, BorderLayout.CENTER);
}
@Override
public UndoRedo getUndoRedo() {
TopComponent topComponent = getActiveTopComponent();
if (topComponent != null) {
return topComponent.getUndoRedo();
}
return super.getUndoRedo();
}
@Override
public TopComponent getSelectedWindow() {
return getActiveTopComponent();
}
@Override
public List<TopComponent> getOpenedWindows() {
return getTopComponents();
}
/**
* @return The currently active window.
*/
public TopComponent getActiveTopComponent() {
JInternalFrame selectedFrame = desktopPane.getSelectedFrame();
return selectedFrame != null ? getTopComponent(selectedFrame) : null;
}
/**
* Gets all windows contained in this one.
*
* @return The list of all windows which may be empty.
*/
public List<TopComponent> getTopComponents() {
List<TabData> tabs = tabbedContainer.getModel().getTabs();
List<TopComponent> topComponents = new ArrayList<>();
for (TabData tab : tabs) {
JInternalFrame internalFrame = tabToFrameMap.get(tab);
topComponents.add(getTopComponent(internalFrame));
}
return topComponents;
}
@Override
public boolean canTile() {
return frameToTabMap.size() >= 2;
}
@Override
public void tileEvenly() {
new TileEvenlyAction().actionPerformed(null);
}
@Override
public void tileHorizontally() {
new TileHorizontallyAction().actionPerformed(null);
}
@Override
public void tileVertically() {
new TileVerticallyAction().actionPerformed(null);
}
@Override
public void tileSingle() {
new TileSingleAction().actionPerformed(null);
}
/**
* Adds an internal window to this workspace window. The method actually "floats" the window as an internal frame into an
* internal desktop pane. If the window already exists, it's internal frame will be activated.
*
* @param topComponent The window to add.
*/
@Messages("CTL_WorkspaceTopComponentFrameUnnamed=Unnamed")
public void addTopComponent(TopComponent topComponent) {
// If the window already exists, activate it and return.
List<TabData> tabs = tabbedContainer.getModel().getTabs();
for (TabData tab : tabs) {
JInternalFrame internalFrame = tabToFrameMap.get(tab);
if (topComponent == getTopComponent(internalFrame)) {
try {
internalFrame.setSelected(true);
} catch (PropertyVetoException e) {
// ok
}
return;
}
}
// Make sure, topComponent is closed and not any longer controlled by NB's WindowManager
if (topComponent.isOpened()) {
topComponent.close();
}
String displayName = topComponent.getDisplayName();
if (displayName == null) {
displayName = Bundle.CTL_WorkspaceTopComponentFrameUnnamed();
}
JInternalFrame internalFrame = new JInternalFrame(displayName, true, true, true, true);
Image iconImage = topComponent.getIcon();
ImageIcon imageIcon = null;
if (iconImage != null) {
imageIcon = new ImageIcon(iconImage);
internalFrame.setFrameIcon(imageIcon);
}
// Note: The following dummyComponent with preferred size (-1, 4) allows for using the tabbedContainer as
// a *thin*, empty tabbed bar on top of the desktopPane.
JComponent dummyComponent = new JPanel();
dummyComponent.setPreferredSize(new Dimension(-1, 4));
//dummyComponent.setSize(new Dimension(-1, 4));
TabData tab = new TabData(dummyComponent, imageIcon, displayName, null);
frameToTabMap.put(internalFrame, tab);
tabToFrameMap.put(tab, internalFrame);
internalFrame.setContentPane(topComponent);
Object internalFrameID = getInternalFrameID(topComponent);
Rectangle bounds = idToBoundsMap.get(internalFrameID);
if (bounds == null) {
int count = frameToTabMap.size() % 5;
bounds = new Rectangle(count * 24, count * 24, 400, 400);
}
internalFrame.setBounds(bounds);
tabbedContainer.getModel().addTab(tabbedContainer.getModel().size(), tab);
tabbedContainer.setVisible(true);
desktopPane.add(internalFrame);
internalFrame.addInternalFrameListener(internalFrameListener);
internalFrame.setVisible(true);
try {
internalFrame.setSelected(true);
} catch (PropertyVetoException e) {
// ok
}
topComponent.addPropertyChangeListener(propertyChangeListener);
}
/**
* Removes an internal window from this workspace window and closes the associated internal frame.
*
* @param topComponent The window to be removed.
* @return {@code true} on success
*/
public boolean removeTopComponent(TopComponent topComponent) {
JInternalFrame internalFrame = getInternalFrame(topComponent);
return internalFrame != null && closeInternalFrame(internalFrame) == topComponent;
}
/**
* Gets extra actions, e.g. for floating a docked window or group into a workspace.
*
* @param topComponent The document window.
* @return The extra actions.
*/
public static Action[] getExtraActions(TopComponent topComponent) {
return new Action[]{
new FloatIntoWorkspaceAction(topComponent),
new FloatGroupIntoWorkspaceAction(topComponent)
};
}
/**
* Adds various "tile window" actions to the base class' set of actions.
*
* @return Array of actions for this component.
*/
@Override
public Action[] getActions() {
Action[] actions = super.getActions();
ArrayList<Action> actionList = new ArrayList<>();
actionList.add(new RenameWorkspaceAction());
if (actions.length > 0) {
actionList.add(null);
actionList.addAll(Arrays.asList(actions));
}
if (tabbedContainer.getTabCount() > 0) {
actionList.add(null);
actionList.add(new TileEvenlyAction());
actionList.add(new TileHorizontallyAction());
actionList.add(new TileVerticallyAction());
}
return actionList.toArray(new Action[actionList.size()]);
}
// CHECKME: How does NB Platform use this method? What is its use?
// See https://netbeans.org/bugzilla/show_bug.cgi?id=209051
@Override
public SubComponent[] getSubComponents() {
Map<Object, JInternalFrame> internalFrameMap = new HashMap<>();
SubComponent[] subComponents = new SubComponent[tabbedContainer.getTabCount()];
ActionListener activator = actionEvent -> {
JInternalFrame internalFrame = internalFrameMap.get(actionEvent.getSource());
try {
internalFrame.setSelected(true);
} catch (PropertyVetoException e1) {
// ok
}
internalFrame.requestFocusInWindow();
};
for (int i = 0; i < subComponents.length; i++) {
TabData tab = tabbedContainer.getModel().getTab(i);
JInternalFrame internalFrame = tabToFrameMap.get(tab);
SubComponent subComponent = new SubComponent(internalFrame.getTitle(),
internalFrame.getToolTipText(),
activator,
internalFrame.isSelected(),
getTopComponent(internalFrame).getLookup(),
internalFrame.isShowing());
internalFrameMap.put(subComponent, internalFrame);
subComponents[i] = subComponent;
}
return subComponents;
}
@Override
protected void componentOpened() {
tabbedContainer.addActionListener(tabActionListener);
}
@Override
protected void componentClosed() {
tabbedContainer.removeActionListener(tabActionListener);
for (JInternalFrame internalFrame : desktopPane.getAllFrames()) {
internalFrame.dispose();
}
}
@Override
protected void componentActivated() {
JInternalFrame selectedFrame = desktopPane.getSelectedFrame();
if (selectedFrame != null) {
TabData tab = frameToTabMap.get(selectedFrame);
int tabIndex = tabbedContainer.getModel().indexOf(tab);
if (tabIndex >= 0) {
tabbedContainer.getSelectionModel().setSelectedIndex(tabIndex);
}
selectedFrame.requestFocusInWindow();
notifyActivated(getTopComponent(selectedFrame));
} else {
int tabIndex = tabbedContainer.getSelectionModel().getSelectedIndex();
if (tabIndex >= 0) {
TabData tab = tabbedContainer.getModel().getTab(tabIndex);
selectedFrame = tabToFrameMap.get(tab);
if (!selectedFrame.isSelected()) {
try {
selectedFrame.setSelected(true);
} catch (PropertyVetoException e) {
// ok
}
}
}
}
}
@Override
protected void componentDeactivated() {
JInternalFrame selectedFrame = desktopPane.getSelectedFrame();
if (selectedFrame != null) {
notifyDeactivated(getTopComponent(selectedFrame));
}
}
@Override
protected void componentShowing() {
for (JInternalFrame internalFrame : desktopPane.getAllFrames()) {
NotifiableComponent.get(getTopComponent(internalFrame)).componentShowing();
}
}
@Override
protected void componentHidden() {
for (JInternalFrame internalFrame : desktopPane.getAllFrames()) {
NotifiableComponent.get(getTopComponent(internalFrame)).componentHidden();
}
}
@SuppressWarnings("UnusedDeclaration")
void writeProperties(java.util.Properties p) {
// better to version settings since initial version as advocated at
// http://wiki.apidesign.org/wiki/PropertyFiles
p.setProperty("version", "1.0");
// store your settings
}
@SuppressWarnings("UnusedDeclaration")
void readProperties(java.util.Properties p) {
String version = p.getProperty("version");
// read your settings according to their version
}
private TopComponent closeInternalFrame(JInternalFrame internalFrame) {
return closeInternalFrame(internalFrame, true);
}
private TopComponent closeInternalFrame(JInternalFrame internalFrame, boolean removeTab) {
internalFrame.removeInternalFrameListener(internalFrameListener);
TopComponent topComponent = getTopComponent(internalFrame);
topComponent.removePropertyChangeListener(propertyChangeListener);
Object internalFrameID = getInternalFrameID(topComponent);
idToBoundsMap.put(internalFrameID, new Rectangle(internalFrame.getBounds()));
TabData tab = frameToTabMap.get(internalFrame);
if (tab != null) {
if (removeTab) {
int tabIndex = tabbedContainer.getModel().indexOf(tab);
if (tabIndex >= 0) {
tabbedContainer.getModel().removeTab(tabIndex);
}
}
tabToFrameMap.remove(tab);
}
frameToTabMap.remove(internalFrame);
internalFrame.dispose();
desktopPane.remove(internalFrame);
if (desktopPane.getComponentCount() == 0) {
tabbedContainer.setVisible(false);
}
// make sure the topComponent's parent is not the internalFrame which we just closed
internalFrame.setContentPane(new TopComponent());
return topComponent;
}
private TopComponent dockInternalFrame(JInternalFrame internalFrame) {
TopComponent topComponent = closeInternalFrame(internalFrame, true);
Mode mode = WindowManager.getDefault().findMode("editor");
mode.dockInto(topComponent);
if (!topComponent.isOpened()) {
topComponent.open();
}
return topComponent;
}
private TopComponent getTopComponent(JInternalFrame internalFrame) {
return (TopComponent) internalFrame.getContentPane();
}
private JInternalFrame getInternalFrame(TopComponent topComponent) {
for (JInternalFrame internalFrame : frameToTabMap.keySet()) {
if (topComponent == getTopComponent(internalFrame)) {
return internalFrame;
}
}
return null;
}
private JInternalFrame getInternalFrame(int tabIndex) {
TabData tab = tabbedContainer.getModel().getTab(tabIndex);
return tabToFrameMap.get(tab);
}
private Object getInternalFrameID(TopComponent topComponent) {
Object internalFrameID = topComponent.getClientProperty("internalFrameID");
if (internalFrameID == null) {
internalFrameID = "IF" + Long.toHexString(new Random().nextLong());
topComponent.putClientProperty("internalFrameID", internalFrameID);
}
return internalFrameID;
}
private void notifyOpened(TopComponent topComponent) {
NotifiableComponent.get(topComponent).componentOpened();
if (topComponent instanceof DocumentWindow) {
DocumentWindowManager.getDefault().addOpenedWindow((DocumentWindow) topComponent);
}
}
private void notifyClosed(TopComponent topComponent) {
NotifiableComponent.get(topComponent).componentClosed();
if (topComponent instanceof DocumentWindow) {
DocumentWindowManager.getDefault().removeOpenedWindow((DocumentWindow) topComponent);
}
}
private void notifyActivated(TopComponent topComponent) {
NotifiableComponent.get(topComponent).componentActivated();
if (topComponent instanceof DocumentWindow) {
DocumentWindowManager.getDefault().setSelectedWindow((DocumentWindow) topComponent);
}
}
private void notifyDeactivated(TopComponent topComponent) {
NotifiableComponent.get(topComponent).componentDeactivated();
}
public boolean requestActiveTopComponent(TopComponent topComponent) {
JInternalFrame internalFrame = getInternalFrame(topComponent);
if (internalFrame != null) {
try {
internalFrame.setSelected(true);
return true;
} catch (PropertyVetoException e) {
// fail
}
}
return false;
}
/**
* Used to listen to actions invoked on the tabbedContainer
*/
private class TabActionListener implements ActionListener {
private final Map<String, Action> tabActions;
private TabActionListener() {
tabActions = new HashMap<>();
initTabActions();
}
@Override
public void actionPerformed(ActionEvent actionEvent) {
Action action = tabActions.get(actionEvent.getActionCommand());
if (action != null) {
action.actionPerformed(actionEvent);
}
}
private void initTabActions() {
addTabAction(new TabAction("close") {
@Override
public void tabActionPerformed(TabActionEvent actionEvent) {
// Note: the tab UI is already removed, but the tabData is still in the model. NetBeans will remove it later.
int tabIndex = actionEvent.getTabIndex();
JInternalFrame internalFrame = getInternalFrame(tabIndex);
if (internalFrame != null) {
closeInternalFrame(internalFrame, false);
}
}
});
addTabAction(new TabAction("select") {
@Override
public void tabActionPerformed(TabActionEvent actionEvent) {
int tabIndex = actionEvent.getTabIndex();
JInternalFrame internalFrame = getInternalFrame(tabIndex);
if (internalFrame != null) {
try {
if (internalFrame.isIcon()) {
internalFrame.setIcon(false);
}
internalFrame.setSelected(true);
} catch (PropertyVetoException e) {
// ok
}
}
}
});
addTabAction(new TabAction("maximize") {
@Override
public void tabActionPerformed(TabActionEvent actionEvent) {
int tabIndex = actionEvent.getTabIndex();
new MaximizeWindowAction(tabIndex).actionPerformed(actionEvent);
}
});
addTabAction(new TabAction("popup") {
@Override
public void tabActionPerformed(TabActionEvent actionEvent) {
int tabCount = tabbedContainer.getTabCount();
if (tabCount == 0) {
return;
}
int tabIndex = actionEvent.getTabIndex();
//System.out.println("tabIndex = " + tabIndex);
JPopupMenu popupMenu = new JPopupMenu();
if (tabIndex >= 0) {
popupMenu.add(new CloseWindowAction(tabIndex));
}
if (tabCount > 1) {
popupMenu.add(new CloseAllWindowsAction());
}
if (tabIndex >= 0 && tabCount > 1) {
popupMenu.add(new CloseOtherWindowsAction(tabIndex));
}
if (tabIndex >= 0 || tabCount > 1) {
popupMenu.addSeparator();
if (tabIndex >= 0) {
popupMenu.add(new MaximizeWindowAction(tabIndex));
popupMenu.add(new DockAction(tabIndex));
}
if (tabCount > 1) {
popupMenu.add(new DockGroupAction());
}
}
//if (tabIndex >= 0) {
// popupMenu.addSeparator();
// popupMenu.add(new CloneWindowAction(tabIndex));
//}
if (tabCount > 1) {
popupMenu.addSeparator();
popupMenu.add(new TileEvenlyAction());
popupMenu.add(new TileHorizontallyAction());
popupMenu.add(new TileVerticallyAction());
}
popupMenu.show(tabbedContainer, actionEvent.getMouseEvent().getX(), actionEvent.getMouseEvent().getY());
}
});
}
private void addTabAction(Action action) {
tabActions.put((String) action.getValue(Action.ACTION_COMMAND_KEY), action);
}
}
private abstract class TabAction extends AbstractAction {
private TabAction(String name) {
super(name);
putValue(Action.ACTION_COMMAND_KEY, name);
}
@Override
public void actionPerformed(ActionEvent e) {
if (e instanceof TabActionEvent) {
tabActionPerformed((TabActionEvent) e);
}
}
public abstract void tabActionPerformed(TabActionEvent e);
}
@Messages("CTL_RenameActionName=Rename")
private class RenameWorkspaceAction extends AbstractAction {
public RenameWorkspaceAction() {
super(Bundle.CTL_RenameActionName());
}
@Override
public void actionPerformed(ActionEvent e) {
NotifyDescriptor.InputLine d = new NotifyDescriptor.InputLine("Name:", "Rename Workspace");
d.setInputText(WorkspaceTopComponent.this.getDisplayName());
Object result = DialogDisplayer.getDefault().notify(d);
if (NotifyDescriptor.OK_OPTION.equals(result)) {
WorkspaceTopComponent.this.setDisplayName(d.getInputText());
}
}
}
@Messages("CTL_CloseWindowActionName=Close")
private class CloseWindowAction extends AbstractAction {
private final int tabIndex;
public CloseWindowAction(int tabIndex) {
super(Bundle.CTL_CloseWindowActionName());
this.tabIndex = tabIndex;
}
@Override
public void actionPerformed(ActionEvent e) {
TabData tab = tabbedContainer.getModel().getTab(tabIndex);
JInternalFrame internalFrame = tabToFrameMap.get(tab);
closeInternalFrame(internalFrame);
}
}
@Messages("CTL_CloseAllWindowsActionName=Close All")
private class CloseAllWindowsAction extends AbstractAction {
public CloseAllWindowsAction() {
super(Bundle.CTL_CloseAllWindowsActionName());
}
@Override
public void actionPerformed(ActionEvent e) {
new CloseOtherWindowsAction(-1).actionPerformed(e);
}
}
@Messages("CTL_CloseOtherWindowsActionName=Close Others")
private class CloseOtherWindowsAction extends AbstractAction {
private final int tabIndex;
public CloseOtherWindowsAction(int tabIndex) {
super(Bundle.CTL_CloseOtherWindowsActionName());
this.tabIndex = tabIndex;
}
@Override
public void actionPerformed(ActionEvent e) {
TabData tab = tabbedContainer.getModel().getTab(tabIndex);
JInternalFrame selectedInternalFrame = tabToFrameMap.get(tab);
Set<JInternalFrame> internalFrameSet = frameToTabMap.keySet();
JInternalFrame[] internalFrames = internalFrameSet.toArray(new JInternalFrame[internalFrameSet.size()]);
for (JInternalFrame internalFrame : internalFrames) {
if (internalFrame != selectedInternalFrame) {
closeInternalFrame(internalFrame);
}
}
}
}
@Messages("CTL_MaximizeWindowActionName=Maximise")
private class MaximizeWindowAction extends AbstractAction {
private final int tabIndex;
public MaximizeWindowAction(int tabIndex) {
super(Bundle.CTL_MaximizeWindowActionName());
this.tabIndex = tabIndex;
}
@Override
public void actionPerformed(ActionEvent e) {
TabData tab = tabbedContainer.getModel().getTab(tabIndex);
JInternalFrame internalFrame = tabToFrameMap.get(tab);
try {
internalFrame.setMaximum(true);
} catch (PropertyVetoException e1) {
// ok
}
}
}
@Messages("CTL_TileEvenlyActionName=Tile Evenly")
private class TileEvenlyAction extends AbstractAction {
public TileEvenlyAction() {
super(Bundle.CTL_TileEvenlyActionName());
}
@Override
public void actionPerformed(ActionEvent e) {
int desktopWidth = desktopPane.getWidth();
int desktopHeight = desktopPane.getHeight();
int windowCount = frameToTabMap.size();
Dimension matrixSize = TileUtilities.computeMatrixSizeForEqualAreaTiling(windowCount);
int windowWidth = desktopWidth / matrixSize.width;
int windowHeight = desktopHeight / matrixSize.height;
List<TabData> tabs = tabbedContainer.getModel().getTabs();
int windowIndex = 0;
for (int j = 0; j < matrixSize.height; j++) {
for (int i = 0; i < matrixSize.width; i++) {
if (windowIndex < windowCount) {
TabData tab = tabs.get(windowIndex);
JInternalFrame internalFrame = tabToFrameMap.get(tab);
internalFrame.setBounds(i * windowWidth, j * windowHeight, windowWidth, windowHeight);
}
windowIndex++;
}
}
}
}
@Messages("CTL_TileHorizontallyActionName=Tile Horizontally")
private class TileHorizontallyAction extends AbstractAction {
public TileHorizontallyAction() {
super(Bundle.CTL_TileHorizontallyActionName());
}
@Override
public void actionPerformed(ActionEvent e) {
int desktopWidth = desktopPane.getWidth();
int desktopHeight = desktopPane.getHeight();
int windowCount = frameToTabMap.size();
int windowWidth = desktopWidth / windowCount;
List<TabData> tabs = tabbedContainer.getModel().getTabs();
for (int windowIndex = 0; windowIndex < windowCount; windowIndex++) {
TabData tab = tabs.get(windowIndex);
JInternalFrame internalFrame = tabToFrameMap.get(tab);
internalFrame.setBounds(windowIndex * windowWidth, 0, windowWidth, desktopHeight);
}
}
}
@Messages("CTL_TileVerticallyActionName=Tile Vertically")
private class TileVerticallyAction extends AbstractAction {
public TileVerticallyAction() {
super(Bundle.CTL_TileVerticallyActionName());
}
@Override
public void actionPerformed(ActionEvent e) {
int desktopWidth = desktopPane.getWidth();
int desktopHeight = desktopPane.getHeight();
int windowCount = frameToTabMap.size();
int windowHeight = desktopHeight / windowCount;
List<TabData> tabs = tabbedContainer.getModel().getTabs();
for (int windowIndex = 0; windowIndex < windowCount; windowIndex++) {
TabData tab = tabs.get(windowIndex);
JInternalFrame internalFrame = tabToFrameMap.get(tab);
internalFrame.setBounds(0, windowIndex * windowHeight, desktopWidth, windowHeight);
}
}
}
@Messages("CTL_TileSingleActionName=Tile Single")
private class TileSingleAction extends AbstractAction {
public TileSingleAction() {
super(Bundle.CTL_TileSingleActionName());
}
@Override
public void actionPerformed(ActionEvent e) {
int desktopWidth = desktopPane.getWidth();
int desktopHeight = desktopPane.getHeight();
JInternalFrame[] internalFrames = desktopPane.getAllFrames();
for (JInternalFrame internalFrame : internalFrames) {
internalFrame.setBounds(0, 0, desktopWidth, desktopHeight);
}
}
}
@Messages("CTL_DockActionName=Dock")
private class DockAction extends AbstractAction {
private int tabIndex;
public DockAction(int tabIndex) {
super(Bundle.CTL_DockActionName());
this.tabIndex = tabIndex;
}
@Override
public void actionPerformed(ActionEvent e) {
TabData tab = tabbedContainer.getModel().getTab(tabIndex);
JInternalFrame internalFrame = tabToFrameMap.get(tab);
TopComponent topComponent = dockInternalFrame(internalFrame);
if (topComponent != null) {
topComponent.requestActive();
}
}
}
@Messages("CTL_DockGroupActionName=Dock Group")
private class DockGroupAction extends AbstractAction {
public DockGroupAction() {
super(Bundle.CTL_DockGroupActionName());
}
@Override
public void actionPerformed(ActionEvent e) {
Set<JInternalFrame> internalFrameSet = frameToTabMap.keySet();
JInternalFrame[] internalFrames = internalFrameSet.toArray(new JInternalFrame[internalFrameSet.size()]);
TopComponent topComponent = null;
for (JInternalFrame internalFrame : internalFrames) {
topComponent = dockInternalFrame(internalFrame);
}
if (topComponent != null) {
topComponent.requestActive();
}
}
}
@Messages({
"CTL_FloatIntoWorkspaceActionName=Float into Workspace",
"LBL_FloatIntoWorkspaceActionName=Workspaces:",
"CTL_FloatIntoWorkspaceActionTitle=Select Workspace",
})
public static class FloatIntoWorkspaceAction extends AbstractAction {
private TopComponent window;
public FloatIntoWorkspaceAction(TopComponent window) {
super(Bundle.CTL_FloatIntoWorkspaceActionName());
this.window = window;
}
@Override
public void actionPerformed(ActionEvent e) {
WorkspaceTopComponent workspaceTopComponent = promptForWorkspaces();
if (workspaceTopComponent != null) {
workspaceTopComponent.requestActive();
workspaceTopComponent.addTopComponent(window);
}
}
static WorkspaceTopComponent promptForWorkspaces() {
List<WorkspaceTopComponent> workspaces = WindowUtilities.getOpened(WorkspaceTopComponent.class).collect(Collectors.toList());
WorkspaceTopComponent workspaceTopComponent = null;
if (workspaces.size() == 1) {
workspaceTopComponent = workspaces.get(0);
} else if (workspaces.size() > 1) {
List<String> displayNames = workspaces.stream()
.map(WorkspaceTopComponent::getDisplayName)
.collect(Collectors.toList());
JPanel panel = new JPanel(new BorderLayout(2, 2));
panel.setBorder(new EmptyBorder(8, 8, 8, 8));
panel.add(new JLabel(Bundle.LBL_FloatIntoWorkspaceActionName()), BorderLayout.NORTH);
JList<Object> listComponent = new JList<>(new Vector<>(displayNames));
listComponent.setVisibleRowCount(6);
listComponent.setSelectedIndex(0);
panel.add(new JScrollPane(listComponent));
DialogDescriptor dd = new DialogDescriptor(panel, Bundle.CTL_FloatIntoWorkspaceActionTitle());
DialogDisplayer.getDefault().createDialog(dd).setVisible(true);
Object result = dd.getValue();
if (DialogDescriptor.OK_OPTION.equals(result)) {
int selectedIndex = listComponent.getSelectedIndex();
if (selectedIndex < 0) {
selectedIndex = 0;
}
workspaceTopComponent = workspaces.get(selectedIndex);
}
} else {
TopComponent topComponent = WindowManager.getDefault().findTopComponent(ID);
if (topComponent instanceof WorkspaceTopComponent) {
workspaceTopComponent = (WorkspaceTopComponent) topComponent;
workspaceTopComponent.open();
}
}
return workspaceTopComponent;
}
}
@Messages("CTL_FloatGroupIntoWorkspaceActionName=Float Group into Workspace")
public static class FloatGroupIntoWorkspaceAction extends AbstractAction {
private TopComponent window;
public FloatGroupIntoWorkspaceAction(TopComponent window) {
super(Bundle.CTL_FloatGroupIntoWorkspaceActionName());
this.window = window;
}
@Override
public void actionPerformed(ActionEvent e) {
WorkspaceTopComponent workspaceTopComponent = FloatIntoWorkspaceAction.promptForWorkspaces();
if (workspaceTopComponent != null) {
workspaceTopComponent.requestActive();
Mode mode = WindowManager.getDefault().findMode(window);
if (mode != null) {
TopComponent[] topComponents = WindowManager.getDefault().getOpenedTopComponents(mode);
for (TopComponent topComponent : topComponents) {
if (!(topComponent instanceof WorkspaceTopComponent)) {
workspaceTopComponent.addTopComponent(topComponent);
}
}
}
}
}
}
private static class FrameProxyLookup extends ProxyLookup {
void setLookup(Lookup lookup) {
setLookups(lookup);
}
}
private class MyInternalFrameListener implements InternalFrameListener {
@Override
public void internalFrameOpened(InternalFrameEvent e) {
//LOG.fine("internalFrameOpened: e = " + e);
tabbedContainer.updateUI();
notifyOpened(getTopComponent(e.getInternalFrame()));
}
@Override
public void internalFrameClosing(InternalFrameEvent e) {
//LOG.fine("internalFrameClosing: e = " + e);
// do nothing
}
@Override
public void internalFrameClosed(InternalFrameEvent e) {
//LOG.fine("internalFrameClosed: e = " + e);
JInternalFrame internalFrame = e.getInternalFrame();
if (frameToTabMap.containsKey(internalFrame)) {
closeInternalFrame(internalFrame);
}
tabbedContainer.updateUI();
notifyClosed(getTopComponent(internalFrame));
}
@Override
public void internalFrameActivated(InternalFrameEvent e) {
//LOG.fine("internalFrameActivated: e = " + e);
// Synchronise tab selection state, if not already done
JInternalFrame internalFrame = e.getInternalFrame();
TabData selectedTab = frameToTabMap.get(internalFrame);
int selectedTabIndex = tabbedContainer.getSelectionModel().getSelectedIndex();
List<TabData> tabs = tabbedContainer.getModel().getTabs();
for (int i = 0; i < tabs.size(); i++) {
TabData tab = tabs.get(i);
if (tab == selectedTab && selectedTabIndex != i) {
tabbedContainer.getSelectionModel().setSelectedIndex(i);
break;
}
}
tabbedContainer.updateUI();
TopComponent topComponent = getTopComponent(internalFrame);
// Publish lookup contents of selected frame to parent window
lookup.setLookup(topComponent.getLookup());
// Publish activated nodes, if any
setActivatedNodes(topComponent.getActivatedNodes());
// May not really be required
if (WorkspaceTopComponent.this != WindowManager.getDefault().getRegistry().getActivated()) {
WorkspaceTopComponent.this.requestActive();
}
notifyActivated(topComponent);
}
@Override
public void internalFrameDeactivated(InternalFrameEvent e) {
//LOG.fine("internalFrameDeactivated: e = " + e);
tabbedContainer.updateUI();
lookup.setLookup(Lookup.EMPTY);
notifyDeactivated(getTopComponent(e.getInternalFrame()));
}
@Override
public void internalFrameIconified(InternalFrameEvent e) {
//LOG.fine("internalFrameIconified: e = " + e);
tabbedContainer.updateUI();
TopComponent topComponent = getTopComponent(e.getInternalFrame());
NotifiableComponent.get(topComponent).componentHidden();
}
@Override
public void internalFrameDeiconified(InternalFrameEvent e) {
//LOG.fine("internalFrameDeiconified: e = " + e);
tabbedContainer.updateUI();
TopComponent topComponent = getTopComponent(e.getInternalFrame());
NotifiableComponent.get(topComponent).componentShowing();
}
}
/**
* Allows telling the tabbedContainer if a tab component is maximized.
*/
private class MyWinsysInfoForTabbedContainer extends WinsysInfoForTabbedContainer {
@Override
public Object getOrientation(Component comp) {
return TabDisplayer.ORIENTATION_CENTER;
}
@Override
public boolean inMaximizedMode(Component comp) {
JInternalFrame internalFrame = desktopPane.getSelectedFrame();
return internalFrame != null && internalFrame.isMaximum();
}
}
private class MyPropertyChangeListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent event) {
TopComponent source = (TopComponent) event.getSource();
JInternalFrame frame = getInternalFrame(source);
if ("icon".equals(event.getPropertyName())) {
Image icon = source.getIcon();
if (icon != null) {
frame.setFrameIcon(new ImageIcon(icon));
} else {
frame.setFrameIcon(null);
}
TabData tab = frameToTabMap.get(frame);
assert tab != null;
int i = tabbedContainer.getModel().indexOf(tab);
if (i >= 0) {
if (icon != null) {
tabbedContainer.getModel().setIcon(i, new ImageIcon(icon));
} else {
tabbedContainer.getModel().setIcon(i, null);
}
}
} else if ("displayName".equals(event.getPropertyName())) {
String displayName = source.getDisplayName();
frame.setTitle(displayName);
TabData tab = frameToTabMap.get(frame);
assert tab != null;
int i = tabbedContainer.getModel().indexOf(tab);
if (i >= 0) {
tabbedContainer.getModel().setText(i, displayName);
}
}
}
}
}