/*
* Freeplane - mind map editor
* Copyright (C) 2013 Joerg Mueller, Daniel Polansky, Christian Foltin, Dimitry Polivaev
*
* This file is modified by Dimitry Polivaev in 2013.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.freeplane.main.application;
import java.awt.Component;
import java.awt.Container;
import java.awt.Frame;
import java.awt.dnd.DropTarget;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Vector;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JDesktopPane;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;
import net.infonode.docking.DockingWindow;
import net.infonode.docking.DockingWindowAdapter;
import net.infonode.docking.OperationAbortedException;
import net.infonode.docking.RootWindow;
import net.infonode.docking.TabWindow;
import net.infonode.docking.View;
import net.infonode.docking.properties.DockingWindowProperties;
import net.infonode.docking.properties.RootWindowProperties;
import net.infonode.docking.theme.BlueHighlightDockingTheme;
import net.infonode.docking.util.DockingUtil;
import net.infonode.tabbedpanel.TabAreaProperties;
import net.infonode.tabbedpanel.TabAreaVisiblePolicy;
import net.infonode.tabbedpanel.TabDropDownListVisiblePolicy;
import net.infonode.tabbedpanel.TabLayoutPolicy;
import net.infonode.tabbedpanel.TabbedPanelProperties;
import net.infonode.util.Direction;
import org.apache.commons.codec.binary.Base64;
import org.freeplane.core.resources.ResourceController;
import org.freeplane.core.util.LogUtils;
import org.freeplane.features.mode.Controller;
import org.freeplane.features.ui.IMapViewChangeListener;
import org.freeplane.features.url.mindmapmode.FileOpener;
import org.freeplane.view.swing.map.MapView;
import org.freeplane.view.swing.map.NodeView;
import org.freeplane.view.swing.ui.DefaultMapMouseListener;
class MapViewDockingWindows implements IMapViewChangeListener {
// // final private Controller controller;
private static final String OPENED_NOW = "openedNow_1.3.04";
private RootWindow rootWindow = null;
final private Vector<Component> mapViews;
private boolean mPaneSelectionUpdate = true;
private boolean loadingLayoutFromObjectInpusStream;
private byte[] emptyConfigurations;
private final MapViewSerializer viewSerializer;
public MapViewDockingWindows() {
viewSerializer = new MapViewSerializer();
rootWindow = new RootWindow(viewSerializer);
RootWindowProperties rootWindowProperties = rootWindow.getRootWindowProperties();
rootWindowProperties.addSuperObject(new BlueHighlightDockingTheme().getRootWindowProperties());
rootWindowProperties.getWindowAreaProperties().setBackgroundColor(UIManager.getColor("Panel.background"));
rootWindow.getWindowBar(Direction.DOWN).setEnabled(true);
try {
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
ObjectOutputStream wrapper = new ObjectOutputStream(byteStream);
rootWindow.write(wrapper);
wrapper.close();
emptyConfigurations = byteStream.toByteArray();
}
catch (IOException e1) {
}
removeDesktopPaneAccelerators();
mapViews = new Vector<Component>();
final FileOpener fileOpener = new FileOpener();
new DropTarget(rootWindow, fileOpener);
rootWindow.addMouseListener(new DefaultMapMouseListener());
final Controller controller = Controller.getCurrentController();
controller.getMapViewManager().addMapViewChangeListener(this);
rootWindow.addListener(new DockingWindowAdapter(){
@Override
public void viewFocusChanged(View previouslyFocusedView, View focusedView) {
if(previouslyFocusedView != null && focusedView != null){
Component containedMapView = getContainedMapView(focusedView);
viewSelectionChanged(containedMapView);
}
}
@Override
public void windowClosing(DockingWindow window) throws OperationAbortedException {
for(Component mapViewComponent : mapViews.toArray(new Component[]{}))
if(SwingUtilities.isDescendingFrom(mapViewComponent, window))
if (!Controller.getCurrentController().getMapViewManager().close(mapViewComponent, false))
throw new OperationAbortedException("can not close view");
}
@Override
public void windowAdded(final DockingWindow addedToWindow, final DockingWindow addedWindow) {
if(addedWindow instanceof TabWindow) {
final DockingWindowProperties windowProperties = addedWindow.getWindowProperties();
windowProperties.setDockEnabled(false);
windowProperties.setUndockEnabled(false);
final TabAreaProperties tabAreaProperties = ((TabWindow)addedWindow).getTabWindowProperties().getTabbedPanelProperties().getTabAreaProperties();
if (addedToWindow == rootWindow)
tabAreaProperties.setTabAreaVisiblePolicy(TabAreaVisiblePolicy.MORE_THAN_ONE_TAB);
else
tabAreaProperties.setTabAreaVisiblePolicy(TabAreaVisiblePolicy.ALWAYS);
}
setTabPolicies(addedWindow);
}
private void setTabPolicies(final DockingWindow window) {
if(window instanceof TabWindow){
TabbedPanelProperties tabbedPanelProperties = ((TabWindow)window).getTabWindowProperties().getTabbedPanelProperties();
if(! tabbedPanelProperties.getTabLayoutPolicy().equals(TabLayoutPolicy.COMPRESSION))
tabbedPanelProperties.setTabLayoutPolicy(TabLayoutPolicy.COMPRESSION);
if(! tabbedPanelProperties.getTabDropDownListVisiblePolicy().equals(TabDropDownListVisiblePolicy.MORE_THAN_ONE_TAB))
tabbedPanelProperties.setTabDropDownListVisiblePolicy(TabDropDownListVisiblePolicy.MORE_THAN_ONE_TAB);
}
for(int i = 0; i < window.getChildWindowCount(); i++){
setTabPolicies(window.getChildWindow(i));
}
}
@Override
public void windowRemoved(DockingWindow removedFromWindow, DockingWindow removedWindow) {
if(removedWindow instanceof TabWindow) {
if (removedFromWindow == rootWindow) {
final TabAreaProperties tabAreaProperties = ((TabWindow)removedWindow).getTabWindowProperties().getTabbedPanelProperties().getTabAreaProperties();
tabAreaProperties.setTabAreaVisiblePolicy(TabAreaVisiblePolicy.ALWAYS);
}
}
}
});
new InternalFrameAdapter() {
@Override
public void internalFrameClosing(InternalFrameEvent e) {
}
};
}
private void removeDesktopPaneAccelerators() {
final InputMap map = new InputMap();
rootWindow.setInputMap(JDesktopPane.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, map);
}
private DockingWindow getLastFocusedChildWindow(DockingWindow parentWindow) {
DockingWindow lastFocusedChildWindow = parentWindow.getLastFocusedChildWindow();
if(lastFocusedChildWindow == null)
return parentWindow;
else
return getLastFocusedChildWindow(lastFocusedChildWindow);
}
public void afterViewChange(final Component pOldMap, final Component pNewMap) {
if (pNewMap == null) {
return;
}
if(! loadingLayoutFromObjectInpusStream) {
for (int i = 0; i < mapViews.size(); ++i) {
if (mapViews.get(i) == pNewMap) {
View dockedView = getContainingDockedWindow(pNewMap);
if(dockedView.isMinimized())
dockedView.restore();
else
dockedView.restoreFocus();
focusMapViewLater((MapView) pNewMap);
return;
}
}
addDockedWindow(pNewMap);
}
else if(mapViews.contains(pNewMap))
return;
mapViews.add(pNewMap);
}
static private View getContainingDockedWindow(final Component pNewMap) {
return (View) SwingUtilities.getAncestorOfClass(View.class, pNewMap);
}
protected void addDockedView(View dynamicView) {
DockingWindow lastFocusedChildWindow = getLastFocusedChildWindow(rootWindow);
if(lastFocusedChildWindow == null) {
DockingUtil.addWindow(dynamicView, rootWindow);
}
else{
Container parent = SwingUtilities.getAncestorOfClass(DockingWindow.class, lastFocusedChildWindow);
if(parent instanceof TabWindow)
((TabWindow)parent).addTab(dynamicView);
else
DockingUtil.addWindow(dynamicView, lastFocusedChildWindow.getRootWindow());
}
}
static Component getContainedMapView(View dockedWindow) {
JScrollPane scrollPane = (JScrollPane) dockedWindow.getComponent();
Component view = scrollPane.getViewport().getView();
return view;
}
private void addDockedWindow(final Component pNewMap) {
final View viewFrame = viewSerializer.newDockedView(pNewMap);
addDockedView(viewFrame);
}
public void afterViewClose(final Component pOldMapView) {
for (int i = 0; i < mapViews.size(); ++i) {
if (mapViews.get(i) == pOldMapView) {
mPaneSelectionUpdate = false;
getContainingDockedWindow(pOldMapView).close();
mapViews.remove(i);
mPaneSelectionUpdate = true;
rootWindow.repaint();
return;
}
}
}
public void afterViewCreated(final Component mapView) {
}
public void beforeViewChange(final Component pOldMapView, final Component pNewMapView) {
}
private void viewSelectionChanged(final Component mapView) {
if (!mPaneSelectionUpdate) {
return;
}
Controller controller = Controller.getCurrentController();
if (mapView != controller.getMapViewManager().getMapViewComponent()) {
controller.getMapViewManager().changeToMapView(mapView.getName());
}
}
public JComponent getMapPane() {
return rootWindow;
}
public void saveLayout(){
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
try {
ObjectOutputStream objectStream = new ObjectOutputStream(byteStream);
rootWindow.write(objectStream);
objectStream.close();
String encodedBytes = Base64.encodeBase64String(byteStream.toByteArray());
ResourceController.getResourceController().setProperty(OPENED_NOW, encodedBytes);
}
catch (IOException e) {
e.printStackTrace();
}
}
public void loadLayout(){
String encodedBytes = ResourceController.getResourceController().getProperty(OPENED_NOW, null);
if(encodedBytes != null){
byte[] bytes = Base64.decodeBase64(encodedBytes);
ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes);
try {
loadingLayoutFromObjectInpusStream = true;
rootWindow.read(new ObjectInputStream(byteStream));
}
catch (Exception e) {
LogUtils.severe(e);
try {
rootWindow.read(new ObjectInputStream(new ByteArrayInputStream(emptyConfigurations)));
}
catch (IOException e1) {
}
}
finally{
viewSerializer.removeDummyViews();
loadingLayoutFromObjectInpusStream = false;
}
}
}
public void focusMapViewLater(final MapView mapView) {
Timer timer = new Timer(40, new ActionListener() {
int retryCount = 5;
public void actionPerformed(final ActionEvent event) {
final Timer eventTimer = (Timer)event.getSource();
focusMapLater(mapView, eventTimer);
}
private void focusMapLater(final MapView mapView, final Timer eventTimer) {
if(mapView.isShowing() && Controller.getCurrentController().getMapViewManager().getMapViewComponent() == mapView){
final NodeView selected = mapView.getSelected();
if(selected != null){
final Frame frame = JOptionPane.getFrameForComponent(mapView);
if (frame.isFocused())
selected.requestFocusInWindow();
else
frame.addWindowFocusListener(new WindowAdapter() {
@Override
public void windowGainedFocus(WindowEvent e) {
frame.removeWindowFocusListener(this);
selected.requestFocusInWindow();
retryCount = 2;
eventTimer.start();
}
});
}
}
if(retryCount > 1){
retryCount--;
eventTimer.start();
}
}
});
timer.setRepeats(false);
timer.start();
}
public void setTitle() {
if(loadingLayoutFromObjectInpusStream)
return;
for (Component mapViewComponent: mapViews) {
if (mapViewComponent instanceof MapView ) {
MapView mapView = (MapView)mapViewComponent;
String name = mapView.getName();
String title;
if(mapView.getModel().isSaved())
title = name;
else
title = name + " *";
View containingDockedWindow = getContainingDockedWindow(mapViewComponent);
containingDockedWindow.getViewProperties().setTitle(title);
}
}
}
}