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 }