CodecExample.java

/**
 * In this example every dockable can be closed. When the dockable is added again later,
 * the dockable is docked as good as possible like it was docked before.
 * 
 * When the application is stopped, the workspace is saved.
 * When the application is restarted again, the workspace is recovered.
 * The dockables and docks are showed in the same state as when they were closed.
 * 
 * This example uses a float dock model.
 * The model can be saved with a dock model encoder.
 * The model can be reloaded with a dock model decoder.
 * 
 * @author Heidi Rakels
 */
public class CodecExample extends JPanel
{

	// Static fields.

	public static final int 			FRAME_WIDTH 	= 600;
	public static final int 			FRAME_HEIGHT 	= 450;
	public static final String 			SOURCE 			= "codec_1_1.dck";

	// Fields.

	/** The ID for the owner window. */
	private String 						frameId 		= "frame";
	/** The model with the docks and dockables. */
	private FloatDockModel 				dockModel;
	
	// Constructors.

	public CodecExample(JFrame frame)
	{
		super(new BorderLayout());

		// Listen when the frame is closed. The workspace should be saved.
		frame.addWindowListener(new WorkspaceSaver());

		// Create the content components.
		TextPanel textPanel1 = new TextPanel("I am window 1.");
		TextPanel textPanel2 = new TextPanel("I am window 2.");
		TextPanel textPanel3 = new TextPanel("I am window 3.");
		TextPanel textPanel4 = new TextPanel("I am window 4.");
		TextPanel textPanel5 = new TextPanel("I am window 5.");
		TextPanel textPanel6 = new TextPanel("I am window 6.");

		// Create the dockables around the content components.
		Icon icon = new ImageIcon(getClass().getResource("/com/javadocking/resources/images/text12.gif"));
		Dockable dockable1 = new DefaultDockable("Window1", textPanel1, "Window 1", icon);
		Dockable dockable2 = new DefaultDockable("Window2", textPanel2, "Window 2", icon);
		Dockable dockable3 = new DefaultDockable("Window3", textPanel3, "Window 3", icon);
		Dockable dockable4 = new DefaultDockable("Window4", textPanel4, "Window 4", icon);
		Dockable dockable5 = new DefaultDockable("Window5", textPanel5, "Window 5", icon);
		Dockable dockable6 = new DefaultDockable("Window6", textPanel6, "Window 6", icon);
		
		// Add close actions to the dockables.
		dockable1 = new StateActionDockable(dockable1, new DefaultDockableStateActionFactory(), DockableState.statesClosed());
		dockable2 = new StateActionDockable(dockable2, new DefaultDockableStateActionFactory(), DockableState.statesClosed());
		dockable3 = new StateActionDockable(dockable3, new DefaultDockableStateActionFactory(), DockableState.statesClosed());
		dockable4 = new StateActionDockable(dockable4, new DefaultDockableStateActionFactory(), DockableState.statesClosed());
		dockable5 = new StateActionDockable(dockable5, new DefaultDockableStateActionFactory(), DockableState.statesClosed());
		dockable6 = new StateActionDockable(dockable6, new DefaultDockableStateActionFactory(), DockableState.statesClosed());

		// Try to decode the dock model from file.
		DockModelPropertiesDecoder dockModelDecoder = new DockModelPropertiesDecoder();
		if (dockModelDecoder.canDecodeSource(SOURCE))
		{
			try 
			{
				// Create the map with the dockables, that the decoder needs.
				Map dockablesMap = new HashMap();
				dockablesMap.put(dockable1.getID(), dockable1);
				dockablesMap.put(dockable2.getID(), dockable2);
				dockablesMap.put(dockable3.getID(), dockable3);
				dockablesMap.put(dockable4.getID(), dockable4);			
				dockablesMap.put(dockable5.getID(), dockable5);			
				dockablesMap.put(dockable6.getID(), dockable6);			
								
				// Create the map with the owner windows, that the decoder needs.
				Map ownersMap = new HashMap();
				ownersMap.put(frameId, frame);
				
				// Create the map with the visualizers, that the decoder needs.
				Map visualizersMap = null;
//				visualizersMap.put("maximizer", maximizer);
//				visualizersMap.put("minimizer", minimizer);

				// Decode the file.
				dockModel = (FloatDockModel)dockModelDecoder.decode(SOURCE, dockablesMap, ownersMap, visualizersMap);
			}
			catch (FileNotFoundException fileNotFoundException){
				System.out.println("Could not find the file [" + SOURCE + "] with the saved dock model.");
				System.out.println("Continuing with the default dock model.");
			}
			catch (IOException ioException){
				System.out.println("Could not decode a dock model: [" + ioException + "].");
				ioException.printStackTrace();
				System.out.println("Continuing with the default dock model.");
			}
		}

		// These are the root docks.
		SplitDock splitDock = null;
		
		if (dockModel == null)
		{	

			// Create the dock model for the docks because they could not be retrieved from a file.
			dockModel = new FloatDockModel(SOURCE);
			dockModel.addOwner(frameId, frame);
			
			// Give the dock model to the docking manager.
			DockingManager.setDockModel(dockModel);
	
			// Create the child tab docks.
			TabDock leftTabDock = new TabDock();
			TabDock rightTabDock = new TabDock();
			
			// Add the dockables to the tab dock.
			leftTabDock.addDockable(dockable1, new Position(0));
			leftTabDock.addDockable(dockable2, new Position(1));
			rightTabDock.addDockable(dockable3, new Position(0));
			rightTabDock.addDockable(dockable4, new Position(1));

			// Create the split dock.
			splitDock = new SplitDock();
			
			// Add the child docks to the split dock at the left and right.
			splitDock.addChildDock(leftTabDock, new Position(Position.LEFT));
			splitDock.addChildDock(rightTabDock, new Position(Position.RIGHT));
			splitDock.setDividerLocation(290);

			// Add the root dock to the dock model.
			dockModel.addRootDock("splitDock", splitDock, frame);

			// Add the paths of the docked dockables to the model with the docking paths.
			addDockingPath(dockable1);
			addDockingPath(dockable2);
			addDockingPath(dockable3);
			addDockingPath(dockable4);

			// Add the path of the dockables that are not docked already.
			// We want dockable 5 to be docked, when it is made visible, where dockable 1 is docked.
			DockingPath dockingPathToCopy1 = DockingManager.getDockingPathModel().getDockingPath(dockable1.getID());
			DockingPath dockingPath5 = DefaultDockingPath.copyDockingPath(dockable5, dockingPathToCopy1);
			DockingManager.getDockingPathModel().add(dockingPath5);
			// We want dockable 6 to be docked, when it is made visible, where dockable 3 is docked.
			DockingPath dockingPathToCopy3 = DockingManager.getDockingPathModel().getDockingPath(dockable3.getID());
			DockingPath dockingPath6 = DefaultDockingPath.copyDockingPath(dockable6, dockingPathToCopy3);
			DockingManager.getDockingPathModel().add(dockingPath6);

		}
		else
		{
			
			// Get the root dock from the dock model.
			splitDock = (SplitDock)dockModel.getRootDock("splitDock");

		}
						
		// Add the root dock to the panel.
		add(splitDock, BorderLayout.CENTER);
		
		// Create the menubar.
		Dockable[] dockables = new Dockable[6];
		dockables[0] = dockable1;
		dockables[1] = dockable2;
		dockables[2] = dockable3;
		dockables[3] = dockable4;
		dockables[4] = dockable5;
		dockables[5] = dockable6;
		JMenuBar menuBar = createMenu(dockables);
		frame.setJMenuBar(menuBar);

	}

	/**
	 * Creates the menubar with two menus: File and Window.
	 * File has the Exit menu item. Window has check boxes for the dockables.
	 * 
	 * @param dockables		The dockables for which a menu item has to be created.
	 * @return				The created menu bar.
	 */
	private JMenuBar createMenu(Dockable[] dockables)
	{
		
		// Create the menu bar.
		JMenuBar menuBar = new JMenuBar();

		// Build the File menu.
		JMenu fileMenu = new JMenu("File");
		fileMenu.setMnemonic(KeyEvent.VK_F);
		fileMenu.getAccessibleContext().setAccessibleDescription("The file menu");
		menuBar.add(fileMenu);
		
		// Build the Window menu.
		JMenu windowMenu = new JMenu("Window");
		windowMenu.setMnemonic(KeyEvent.VK_W);
		windowMenu.getAccessibleContext().setAccessibleDescription("The window menu");
		menuBar.add(windowMenu);

		// The JMenuItem for File
		JMenuItem menuItem = new JMenuItem("Exit", KeyEvent.VK_E);
		menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, ActionEvent.ALT_MASK));
		menuItem.getAccessibleContext().setAccessibleDescription("Exit te application");
		menuItem.addActionListener(new ActionListener()
				{
					public void actionPerformed(ActionEvent arg0)
					{
						saveWorkspace();
						System.exit(0);
					}
				});
		fileMenu.add(menuItem);

		// Iterate over the dockables.
		for (int index = 0; index < dockables.length; index++)
		{
			// Create the check box menu for the dockable.
			JCheckBoxMenuItem cbMenuItem = new DockableMenuItem(dockables[index]);
			windowMenu.add(cbMenuItem);			
		}
		
		return menuBar;
		
	}
	
	/**
	 * Creates a docking path for the dockable. This path is added to the docking pah model of the docking
	 * manager.
	 * To create a docking path, the dock model should already be given to the docking manager.
	 * 
	 * @param 	dockable	The dockable for which to create a docking path.
	 * @return				The created docking path.
	 */
	private DockingPath addDockingPath(Dockable dockable)
	{

		if (dockable.getDock() != null)
		{
			// Create the docking path of the dockable.
			DockingPath dockingPath = DefaultDockingPath.createDockingPath(dockable);
			DockingManager.getDockingPathModel().add(dockingPath);
			return dockingPath;
		}

		return null;

	}

	private void saveWorkspace()
	{
		
		// Save the dock model.
		DockModelPropertiesEncoder encoder = new DockModelPropertiesEncoder();
		if (encoder.canSave(dockModel))
		{
			try
			{
				encoder.save(dockModel);
			}
			catch (Exception e)
			{
				System.out.println("Error while saving the dock model.");
				e.printStackTrace();
			}
		}
		else
		{
			System.out.println("Could not save the dock model.");
		}

	}
	
	/**
	 * A listener for window closing events. Saves the workspace, when the application window is closed.
	 * 
	 * @author Heidi Rakels.
	 */
	private class WorkspaceSaver implements WindowListener
	{
		
		public void windowClosing(WindowEvent windowEvent) 
		{
			saveWorkspace();
		}

		public void windowDeactivated(WindowEvent windowEvent) {}
		public void windowDeiconified(WindowEvent windowEvent) {}
		public void windowIconified(WindowEvent windowEvent) {}
		public void windowOpened(WindowEvent windowEvent) {}
		public void windowActivated(WindowEvent windowEvent) {}
		public void windowClosed(WindowEvent windowEvent) {}

	}

	/**
	 * A check box menu item to add or remove the dockable.
	 */
	private class DockableMenuItem extends JCheckBoxMenuItem
	{
		
		public DockableMenuItem(Dockable dockable)
		{
			super(dockable.getTitle(), dockable.getIcon());
			
			setSelected(dockable.getDock() != null);
			
			DockableMediator dockableMediator = new DockableMediator(dockable, this);
			dockable.addDockingListener(dockableMediator);
			addItemListener(dockableMediator);
		}
		
	}
	
	/**
	 * A listener that listens when menu items with dockables are selected and deselected.
	 * It also listens when dockables are closed or docked.
	 */
	private class DockableMediator implements ItemListener, DockingListener
	{
		
		private Dockable dockable;
		private Action closeAction;
		private Action restoreAction;
		private JMenuItem dockableMenuItem;
		
		public DockableMediator(Dockable dockable, JMenuItem dockableMenuItem) 
		{
			
			this.dockable = dockable;
			this.dockableMenuItem = dockableMenuItem;
			closeAction = new DefaultDockableStateAction(dockable, DockableState.CLOSED);
			restoreAction = new DefaultDockableStateAction(dockable, DockableState.NORMAL);

		}

		public void itemStateChanged(ItemEvent itemEvent)
		{
			
			dockable.removeDockingListener(this);
			if (itemEvent.getStateChange() == ItemEvent.DESELECTED)
			{
				// Close the dockable.
				closeAction.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "Close"));
			} 
			else 
			{
				// Restore the dockable.
				restoreAction.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "Restore"));
			}
			dockable.addDockingListener(this);
			
		}

		public void dockingChanged(DockingEvent dockingEvent) {
			if (dockingEvent.getDestinationDock() != null)
			{
				dockableMenuItem.removeItemListener(this);
				dockableMenuItem.setSelected(true);
				dockableMenuItem.addItemListener(this);	
			}
			else
			{
				dockableMenuItem.removeItemListener(this);
				dockableMenuItem.setSelected(false);
				dockableMenuItem.addItemListener(this);
			}
		}

		public void dockingWillChange(DockingEvent dockingEvent) {}

	}

	/**
	 * This is the class for the content.
	 */
	private class TextPanel extends JPanel implements DraggableContent
	{
		
		private JLabel label; 
		
		public TextPanel(String text)
		{
			super(new FlowLayout());
			
			// The panel.
			setMinimumSize(new Dimension(80,80));
			setPreferredSize(new Dimension(150,150));
			setBackground(Color.white);
			setBorder(BorderFactory.createLineBorder(Color.lightGray));
			
			// The label.
			label = new JLabel(text);
			label.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
			add(label);
		}
		
		// Implementations of DraggableContent.

		public void addDragListener(DragListener dragListener)
		{
			addMouseListener(dragListener);
			addMouseMotionListener(dragListener);
			label.addMouseListener(dragListener);
			label.addMouseMotionListener(dragListener);
		}
	}

	// Main method.

	public static void createAndShowGUI()
	{ 

		// Create the frame.
		JFrame frame = new JFrame("Encoder and Decoder");

		// Create the panel and add it to the frame.
		CodecExample panel = new CodecExample(frame);
		frame.getContentPane().add(panel);
		
		// Set the frame properties and show it.
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
		frame.setLocation((screenSize.width - FRAME_WIDTH) / 2, (screenSize.height - FRAME_HEIGHT) / 2);
		frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
		frame.setVisible(true);
		
	}

	public static void main(String args[]) 
	{
        Runnable doCreateAndShowGUI = new Runnable() 
        {
            public void run() 
            {
                createAndShowGUI();
            }
        };
        SwingUtilities.invokeLater(doCreateAndShowGUI);
    }
	
}