001    /*
002     * Copyright 2002-2008 the original author or authors.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005     * use this file except in compliance with the License. You may obtain a copy of
006     * the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013     * License for the specific language governing permissions and limitations under
014     * the License.
015     */
016    package org.springframework.richclient.application.docking.vldocking;
017    
018    import java.io.File;
019    import java.io.FileOutputStream;
020    import java.io.IOException;
021    import java.io.InputStream;
022    import java.io.OutputStream;
023    
024    import javax.swing.JComponent;
025    import javax.xml.parsers.ParserConfigurationException;
026    
027    import org.springframework.core.io.Resource;
028    import org.springframework.richclient.application.ApplicationWindow;
029    import org.springframework.richclient.application.PageComponent;
030    import org.springframework.richclient.application.PageDescriptor;
031    import org.springframework.richclient.application.PageLayoutBuilder;
032    import org.springframework.richclient.application.ViewDescriptor;
033    import org.springframework.richclient.application.support.AbstractApplicationPage;
034    import org.xml.sax.SAXException;
035    
036    import com.vlsolutions.swing.docking.DockKey;
037    import com.vlsolutions.swing.docking.Dockable;
038    import com.vlsolutions.swing.docking.DockableResolver;
039    import com.vlsolutions.swing.docking.DockableState;
040    import com.vlsolutions.swing.docking.DockingContext;
041    import com.vlsolutions.swing.docking.DockingDesktop;
042    import com.vlsolutions.swing.docking.event.DockableSelectionEvent;
043    import com.vlsolutions.swing.docking.event.DockableSelectionListener;
044    import com.vlsolutions.swing.docking.event.DockableStateChangeEvent;
045    import com.vlsolutions.swing.docking.event.DockableStateChangeListener;
046    import com.vlsolutions.swing.docking.event.DockableStateWillChangeEvent;
047    import com.vlsolutions.swing.docking.event.DockableStateWillChangeListener;
048    
049    /**
050     * @author Rogan Dawes
051     */
052    public class VLDockingApplicationPage extends AbstractApplicationPage implements PageLayoutBuilder {
053    
054        private DockingDesktop desktop;
055    
056        private DockingContext dockingContext;
057    
058        private VLDockingLayoutManager layoutManager = null;
059    
060        private boolean resolving = false;
061    
062        private Resource initialLayout = null;
063    
064        public VLDockingApplicationPage(ApplicationWindow window, PageDescriptor pageDescriptor) {
065            super(window, pageDescriptor);
066            if (pageDescriptor instanceof VLDockingPageDescriptor) {
067                VLDockingPageDescriptor descriptor = (VLDockingPageDescriptor) pageDescriptor;
068                setLayoutManager(descriptor.getLayoutManager());
069                setInitialLayout(descriptor.getInitialLayout());
070            }
071        }
072    
073        protected PageComponent getPageComponent(Dockable dockable) {
074            if (dockable instanceof ViewDescriptorDockable)
075                return ((ViewDescriptorDockable) dockable).getPageComponent();
076            return null;
077        }
078    
079        protected Dockable getDockable(PageComponent pageComponent) {
080            DockableState[] states = desktop.getDockables();
081            for (int i = 0; i < states.length; i++) {
082                Dockable dockable = states[i].getDockable();
083                PageComponent pc = getPageComponent(dockable);
084                if (pc == pageComponent)
085                    return dockable;
086            }
087            return null;
088        }
089    
090        protected boolean giveFocusTo(PageComponent pageComponent) {
091            Dockable dockable = getDockable(pageComponent);
092            if (dockable == null) {
093                return false;
094            }
095            // Don't request focus here, the DockingDesktop already shifts focus. If requesting focus at this point, 
096            //  the DockingDesktop catches this event and fires another focus event. This might cause loops when 
097            // maximizing/minimizing/restoring because at that point a remove of the component is done which shifts
098            // focus and after setting the correct docking state, a focus request is done. 
099            // see RCP-558
100            return true;
101        }
102    
103        public void addView(String viewDescriptorId) {
104            showView(viewDescriptorId);
105        }
106    
107        protected void doAddPageComponent(PageComponent pageComponent) {
108            if (resolving)
109                return;
110            pageComponent.getControl();
111            Dockable dockable = getDockable(pageComponent);
112            if (dockable != null)
113                return;
114            dockable = createDockable(pageComponent);
115            getLayoutManager().addDockable(desktop, dockable);
116        }
117    
118        protected Dockable createDockable(PageComponent pageComponent) {
119            return createDockable(getViewDescriptor(pageComponent.getId()), pageComponent);
120        }
121    
122        protected Dockable createDockable(ViewDescriptor descriptor, PageComponent pageComponent) {
123            return new ViewDescriptorDockable(descriptor, pageComponent);
124        }
125    
126        protected void doRemovePageComponent(PageComponent pageComponent) {
127            Dockable dockable = getDockable(pageComponent);
128            if (dockable != null) {
129                getLayoutManager().removeDockable(desktop, dockable);
130            }
131        }
132    
133        protected JComponent createControl() {
134            String name = getPageDescriptor().getId();
135            desktop = new DockingDesktop(name, getDockingContext());
136            desktop.setName(name);
137            DockableListener listener = new DockableListener();
138            desktop.addDockableStateChangeListener(listener);
139            desktop.addDockableStateWillChangeListener(listener);
140            desktop.addDockableSelectionListener(listener);
141    
142            if (initialLayout != null) {
143                try {
144                    InputStream in = initialLayout.getInputStream();
145                    desktop.getContext().readXML(in);
146                    in.close();
147                } catch (IOException ioe) {
148                    logger.warn("Error reading workspace layout " + initialLayout + ", using defaults", ioe);
149                    getPageDescriptor().buildInitialLayout(this);
150                } catch (SAXException saxe) {
151                    logger.warn("Error parsing workspace layout " + initialLayout + ", using defaults", saxe);
152                    getPageDescriptor().buildInitialLayout(this);
153                } catch (ParserConfigurationException pce) {
154                    logger.warn("Error parsing workspace layout " + initialLayout + ", using defaults", pce);
155                    getPageDescriptor().buildInitialLayout(this);
156                }
157                if (desktop.getDockables().length == 0) {
158                    getPageDescriptor().buildInitialLayout(this);
159                }
160            } else {
161                getPageDescriptor().buildInitialLayout(this);
162            }
163            return desktop;
164        }
165    
166        protected void updatePageComponentProperties(PageComponent pageComponent) {
167            Dockable dockable = getDockable(pageComponent);
168            DockKey dockKey = dockable.getDockKey();
169    
170            if (pageComponent.getIcon() != null) {
171                dockKey.setIcon(pageComponent.getIcon());
172            }
173            dockKey.setName(pageComponent.getDisplayName());
174            dockKey.setTooltip(pageComponent.getCaption());
175        }
176    
177        /**
178         * @return the dockingContext
179         */
180        public DockingContext getDockingContext() {
181            if (this.dockingContext == null) {
182                this.dockingContext = new DockingContext();
183                this.dockingContext.setDockableResolver(new ViewDescriptorResolver());
184            }
185            return this.dockingContext;
186        }
187    
188        /**
189         * @return the layoutManager
190         */
191        private VLDockingLayoutManager getLayoutManager() {
192            if (this.layoutManager == null) {
193                layoutManager = new DefaultLayoutManager();
194            }
195            return this.layoutManager;
196        }
197    
198        /**
199         * @param layoutManager
200         *            the layoutManager to set
201         */
202        public void setLayoutManager(VLDockingLayoutManager layoutManager) {
203            this.layoutManager = layoutManager;
204        }
205    
206        /**
207         * @param initialLayout
208         *            the initialLayout to set
209         */
210        public void setInitialLayout(Resource initialLayout) {
211            this.initialLayout = initialLayout;
212        }
213    
214        public boolean close() {
215            // try to save the layout if it came from a file we can write to
216            if (initialLayout != null) {
217                File file = null;
218                try {
219                    file = initialLayout.getFile();
220                    if (file.canWrite()) {
221                        try {
222                            OutputStream out = new FileOutputStream(file);
223                            desktop.getContext().writeXML(out);
224                            out.close();
225                        } catch (IOException ioe) {
226                            logger.warn("Error saving desktop layout to " + file, ioe);
227                        }
228                    }
229                } catch (IOException ioe) {
230                }
231            }
232            return super.close();
233        }
234    
235        private class DockableListener implements DockableStateChangeListener, DockableStateWillChangeListener,
236                DockableSelectionListener {
237    
238            /*
239             * (non-Javadoc)
240             * 
241             * @see com.vlsolutions.swing.docking.event.DockableStateWillChangeListener#dockableStateWillChange(com.vlsolutions.swing.docking.event.DockableStateWillChangeEvent)
242             */
243            public void dockableStateWillChange(DockableStateWillChangeEvent event) {
244                DockableState futureState = event.getFutureState();
245                if (futureState.isClosed()) {
246                    Dockable dockable = futureState.getDockable();
247                    if (dockable instanceof ViewDescriptorDockable) {
248                        ViewDescriptorDockable vdd = (ViewDescriptorDockable) dockable;
249                        PageComponent pc = vdd.getPageComponent();
250                        if (!pc.canClose())
251                            event.cancel();
252                    }
253                }
254            }
255    
256            /*
257             * (non-Javadoc)
258             * 
259             * @see com.vlsolutions.swing.docking.event.DockableStateChangeListener#dockableStateChanged(com.vlsolutions.swing.docking.event.DockableStateChangeEvent)
260             */
261            public void dockableStateChanged(DockableStateChangeEvent event) {
262                DockableState previousState = event.getPreviousState();
263                DockableState newState = event.getNewState();
264                Dockable dockable = newState.getDockable();
265                PageComponent pc = getPageComponent(dockable);
266                if (pc == null)
267                    return;
268                if (previousState != null && !previousState.isClosed() && newState.isClosed()) {
269                    pc.getContext().getPage().close(pc);
270                }
271            }
272    
273            /*
274             * (non-Javadoc)
275             * 
276             * @see com.vlsolutions.swing.docking.event.DockableSelectionListener#selectionChanged(com.vlsolutions.swing.docking.event.DockableSelectionEvent)
277             */
278            public void selectionChanged(DockableSelectionEvent e) {
279                Dockable dockable = e.getSelectedDockable();
280                if (dockable != null) {
281                    PageComponent pc = getPageComponent(dockable);
282                    if (pc != null)
283                        setActiveComponent(pc);
284                }
285            }
286    
287        }
288    
289        private class ViewDescriptorResolver implements DockableResolver {
290    
291            public Dockable resolveDockable(String keyName) {
292                ViewDescriptor descriptor = getViewDescriptor(keyName);
293                if (descriptor == null)
294                    return null;
295                PageComponent pageComponent = createPageComponent(descriptor);
296                resolving = true;
297                addPageComponent(pageComponent);
298                resolving = false;
299                Dockable dockable = createDockable(descriptor, pageComponent);
300                return dockable;
301            }
302    
303        }
304    
305        private class DefaultLayoutManager implements VLDockingLayoutManager {
306    
307            /*
308             * (non-Javadoc)
309             * 
310             * @see org.springframework.richclient.application.vldocking.VLDockingLayoutManager#addDockable(com.vlsolutions.swing.docking.DockingDesktop,
311             *      com.vlsolutions.swing.docking.Dockable)
312             */
313            public void addDockable(DockingDesktop desktop, Dockable dockable) {
314                desktop.addDockable(dockable);
315            }
316    
317            /*
318             * (non-Javadoc)
319             * 
320             * @see org.springframework.richclient.application.vldocking.VLDockingLayoutManager#removeDockable(com.vlsolutions.swing.docking.DockingDesktop,
321             *      com.vlsolutions.swing.docking.Dockable)
322             */
323            public void removeDockable(DockingDesktop desktop, Dockable dockable) {
324                desktop.remove(dockable);
325            }
326    
327        }
328    
329    }