Java tutorial
/* * Copyright 2013-2016 Erwin Mller <erwin.mueller@deventm.org> * * This file is part of prefdialog-misc-swing. * * prefdialog-misc-swing is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * prefdialog-misc-swing 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 Lesser General Public License * along with prefdialog-misc-swing. If not, see <http://www.gnu.org/licenses/>. */ package com.anrisoftware.prefdialog.miscswing.docks.dockingframes.core; import java.awt.Component; import java.awt.Window; import java.beans.PropertyChangeListener; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import javax.inject.Inject; import javax.swing.SwingWorker; import javax.swing.event.ChangeListener; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.event.EventListenerSupport; import bibliothek.gui.dock.common.CControl; import bibliothek.gui.dock.common.CWorkingArea; import bibliothek.gui.dock.common.MultipleCDockable; import bibliothek.gui.dock.common.SingleCDockable; import bibliothek.gui.dock.common.event.CControlListener; import bibliothek.gui.dock.common.event.CFocusListener; import bibliothek.gui.dock.common.intern.AbstractCDockable; import bibliothek.gui.dock.common.intern.CDockable; import bibliothek.gui.dock.common.theme.ThemeMap; import bibliothek.gui.dock.themes.ThemeManager; import bibliothek.gui.dock.util.DirectWindowProvider; import bibliothek.gui.dock.util.NullWindowProvider; import bibliothek.gui.dock.util.WindowProvider; import com.anrisoftware.prefdialog.miscswing.awtcheck.OnAwt; import com.anrisoftware.prefdialog.miscswing.docks.api.Dock; import com.anrisoftware.prefdialog.miscswing.docks.api.DockFactory; import com.anrisoftware.prefdialog.miscswing.docks.api.EditorDockWindow; import com.anrisoftware.prefdialog.miscswing.docks.api.FocusChangedEvent; import com.anrisoftware.prefdialog.miscswing.docks.api.LayoutException; import com.anrisoftware.prefdialog.miscswing.docks.api.LayoutInterruptedException; import com.anrisoftware.prefdialog.miscswing.docks.api.LayoutListener; import com.anrisoftware.prefdialog.miscswing.docks.api.LayoutLoadingException; import com.anrisoftware.prefdialog.miscswing.docks.api.LayoutSavingException; import com.anrisoftware.prefdialog.miscswing.docks.api.LayoutTask; import com.anrisoftware.prefdialog.miscswing.docks.api.ViewDockWindow; import com.anrisoftware.prefdialog.miscswing.docks.dockingframes.layoutloader.LoadLayoutWorkerFactory; import com.anrisoftware.prefdialog.miscswing.docks.dockingframes.layoutsaver.SaveLayoutWorkerFactory; import com.anrisoftware.prefdialog.miscswing.docks.layouts.dockingframes.DefaultLayoutTask; /** * Docking frames docks. * * @author Erwin Mueller, erwin.mueller@deventm.org * @since 3.0 */ public class DockingFramesDock implements Dock { private static final String WORK_AREA_ID = "work"; private final EventListenerSupport<LayoutListener> layoutListeners; private final Map<SingleCDockable, ViewDockWindow> viewDocks; private final Map<MultipleCDockable, EditorDockWindow> editorDocks; private final EventListenerSupport<ChangeListener> changeListeners; private final CFocusListener editorsFocusListener; private final CControlListener controlListener; @Inject private DockingFramesDockLogger log; @Inject private SaveLayoutWorkerFactory saveFactory; @Inject private LoadLayoutWorkerFactory loadFactory; @Inject private DefaultLayoutTask defaultLayoutTask; private CControl control; private CWorkingArea workingArea; private DockingFramesLayoutTask currentLayout; /** * @see DockFactory#create() */ DockingFramesDock() { this.layoutListeners = new EventListenerSupport<LayoutListener>(LayoutListener.class); this.viewDocks = new ConcurrentHashMap<SingleCDockable, ViewDockWindow>(); this.editorDocks = new ConcurrentHashMap<MultipleCDockable, EditorDockWindow>(); this.changeListeners = new EventListenerSupport<ChangeListener>(ChangeListener.class); this.editorsFocusListener = new CFocusListener() { @Override public void focusLost(CDockable dockable) { fireEditorDockFocusLost(dockable); } @Override public void focusGained(CDockable dockable) { fireEditorDockFocusGained(dockable); } }; this.controlListener = new CControlListener() { @Override public void removed(CControl control, CDockable dockable) { editorDocks.remove(dockable); } @Override public void opened(CControl control, CDockable dockable) { } @Override public void closed(CControl control, CDockable dockable) { } @Override public void added(CControl control, CDockable dockable) { } }; } private void fireEditorDockFocusGained(CDockable dockable) { EditorDockWindow editor = editorDocks.get(dockable); FocusChangedEvent e = new FocusChangedEvent(editor, true); changeListeners.fire().stateChanged(e); } private void fireEditorDockFocusLost(CDockable dockable) { EditorDockWindow editor = editorDocks.get(dockable); FocusChangedEvent e = new FocusChangedEvent(editor, false); changeListeners.fire().stateChanged(e); } @OnAwt @Override public Dock createDock(Window frame) { WindowProvider provider; if (frame == null) { provider = new NullWindowProvider(); } else { provider = new DirectWindowProvider(frame); } this.control = new CControl(provider); this.workingArea = control.createWorkingArea(WORK_AREA_ID); applyLayout(defaultLayoutTask); control.addControlListener(controlListener); return this; } /** * Returns the theme manager so the user interface of the dock can be * modified. * * @return the {@link ThemeManager}. */ public ThemeManager getThemeManager() { return control.getController().getThemeManager(); } @Override public Component getAWTComponent() { return control.getContentArea(); } @Override @OnAwt public void addViewDock(ViewDockWindow dock) { SingleCDockable dockable = currentLayout.addView(control, dock); dock.setDockable(dockable); viewDocks.put(dockable, dock); } @Override @OnAwt public void addEditorDock(EditorDockWindow dock) { MultipleCDockable dockable = currentLayout.addEditor(workingArea, dock); dock.setDockable(dockable); editorDocks.put(dockable, dock); dockable.addFocusListener(editorsFocusListener); } /** * @throws ClassCastException * if the specified task is not of type * {@link DockingFramesLayoutTask}. */ @Override @OnAwt public synchronized void applyLayout(LayoutTask layout) { this.currentLayout = (DockingFramesLayoutTask) layout; currentLayout.setupLayout(control, workingArea, viewDocks); } @Override public synchronized void saveLayout(String name, File file, PropertyChangeListener... listeners) throws LayoutException { try { FileOutputStream stream = new FileOutputStream(file); doSaveLayout(name, stream, listeners); log.layoutSaved(this, name, file); } catch (FileNotFoundException e) { throw new LayoutLoadingException(name, e); } } @Override public synchronized void saveLayout(String name, OutputStream stream, PropertyChangeListener... listeners) throws LayoutException { doSaveLayout(name, stream, listeners); } @Override public synchronized void loadLayout(String name, File file, PropertyChangeListener... listeners) throws LayoutException { try { FileInputStream stream = new FileInputStream(file); doLoadLayout(name, stream, listeners); log.layoutLoaded(this, name, file); } catch (FileNotFoundException e) { throw new LayoutLoadingException(name, e); } } @Override public synchronized void loadLayout(String name, InputStream stream, PropertyChangeListener... listeners) throws LayoutException { doLoadLayout(name, stream, listeners); } @Override public LayoutTask getCurrentLayout() { return currentLayout; } @Override public void requestFocus(EditorDockWindow dock) { MultipleCDockable dockable = findDockable(dock); if (dockable == null) { return; } if (dockable instanceof AbstractCDockable) { ((AbstractCDockable) dockable).toFront(); } } private MultipleCDockable findDockable(EditorDockWindow dock) { for (Map.Entry<MultipleCDockable, EditorDockWindow> entry : editorDocks.entrySet()) { if (entry.getValue().getId().equals(dock.getId())) { return entry.getKey(); } } return null; } /** * Sets the current layout without applying it. * * @param currentLayout * the new current layout. * * @throws ClassCastException * if the specified task is not of type * {@link DockingFramesLayoutTask}. */ public void setCurrentLayout(LayoutTask currentLayout) { this.currentLayout = (DockingFramesLayoutTask) currentLayout; } @Override public void setTheme(final String name) { ThemeMap themes = control.getThemes(); themes.select(name); } @Override public void addLayoutListener(LayoutListener listener) { layoutListeners.addListener(listener); } @Override public void removeLayoutListener(LayoutListener listener) { layoutListeners.removeListener(listener); } @Override public void addStateChangedListener(ChangeListener listener) { changeListeners.addListener(listener); } @Override public void removeStateChangedListener(ChangeListener listener) { changeListeners.removeListener(listener); } @Override public String toString() { return new ToStringBuilder(this).toString(); } private void doSaveLayout(String name, OutputStream stream, PropertyChangeListener... listeners) throws LayoutException { try { SwingWorker<OutputStream, OutputStream> worker = saveFactory.create(layoutListeners, this, name, control, stream); for (PropertyChangeListener l : listeners) { worker.addPropertyChangeListener(l); } worker.execute(); worker.get(); } catch (ExecutionException e) { throw new LayoutSavingException(name, e.getCause()); } catch (InterruptedException e) { throw new LayoutInterruptedException(name, e); } } private void doLoadLayout(String name, InputStream stream, PropertyChangeListener... listeners) throws LayoutInterruptedException, LayoutLoadingException { try { SwingWorker<InputStream, InputStream> worker = loadFactory.create(layoutListeners, this, name, control, stream); for (PropertyChangeListener l : listeners) { worker.addPropertyChangeListener(l); } worker.execute(); worker.get(); } catch (InterruptedException e) { throw new LayoutInterruptedException(name, e); } catch (ExecutionException e) { throw new LayoutLoadingException(name, e.getCause()); } } }