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 }