001 /* 002 * $Id: ViewManagerManager.java,v 1.16 2012/02/19 17:35:53 davep Exp $ 003 * 004 * This file is part of McIDAS-V 005 * 006 * Copyright 2007-2012 007 * Space Science and Engineering Center (SSEC) 008 * University of Wisconsin - Madison 009 * 1225 W. Dayton Street, Madison, WI 53706, USA 010 * https://www.ssec.wisc.edu/mcidas 011 * 012 * All Rights Reserved 013 * 014 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 015 * some McIDAS-V source code is based on IDV and VisAD source code. 016 * 017 * McIDAS-V is free software; you can redistribute it and/or modify 018 * it under the terms of the GNU Lesser Public License as published by 019 * the Free Software Foundation; either version 3 of the License, or 020 * (at your option) any later version. 021 * 022 * McIDAS-V is distributed in the hope that it will be useful, 023 * but WITHOUT ANY WARRANTY; without even the implied warranty of 024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 025 * GNU Lesser Public License for more details. 026 * 027 * You should have received a copy of the GNU Lesser Public License 028 * along with this program. If not, see http://www.gnu.org/licenses. 029 */ 030 031 package edu.wisc.ssec.mcidasv; 032 033 import static edu.wisc.ssec.mcidasv.util.CollectionHelpers.newHashSet; 034 035 import java.util.List; 036 import java.util.Set; 037 import java.util.Stack; 038 039 import org.slf4j.Logger; 040 import org.slf4j.LoggerFactory; 041 042 import edu.wisc.ssec.mcidasv.ui.McvComponentHolder; 043 import edu.wisc.ssec.mcidasv.ui.UIManager; 044 045 import ucar.unidata.idv.IntegratedDataViewer; 046 import ucar.unidata.idv.VMManager; 047 import ucar.unidata.idv.ViewDescriptor; 048 import ucar.unidata.idv.ViewManager; 049 import ucar.unidata.idv.control.DisplayControlImpl; 050 import ucar.unidata.ui.ComponentGroup; 051 import ucar.unidata.ui.ComponentHolder; 052 import ucar.unidata.util.GuiUtils; 053 054 /** 055 * <p>McIDAS-V needs to manage ViewManagers in a slightly different way than 056 * the IDV. The key differences between the two are the way previously active 057 * ViewManagers are ordered and a (hopefully) more consistent way of handling 058 * active ViewManagers.</p> 059 * 060 * <p>The IDV only keeps track of the ViewManager used immediately before the 061 * current one. McV keeps track of the previously active ViewManagers in a 062 * stack. This mimics window z-ordering and always returns the user to the most 063 * recently active ViewManager upon removal of the active ViewManager.</p> 064 * 065 * <p>Newly created ViewManagers and their first layer now become the active 066 * ViewManager and layer. If there is only one ViewManager, it is now displayed 067 * as active instead of just implying it. When the active ViewManager is 068 * removed, the last active ViewManager and its first layer become active.</p> 069 * 070 * <p><b>A note to the future</b>: McV/IDV supports two notions of active and 071 * selected ViewManagers. Say you have NxN ViewManagers in a ComponentHolder, 072 * and you want to share views among some of these ViewManagers. When one of 073 * these shared ViewManagers is activated, should all of them become the active 074 * ViewManager? We're going to have to work out how to convey which 075 * ViewManagers are shared and active, and maybe more? Good luck!</p> 076 */ 077 // TODO: should accesses to previousVMs should be synchronized? 078 // TODO: should keep track of the ordering of active layers per VM as well. 079 public class ViewManagerManager extends VMManager { 080 081 private static final Logger logger = LoggerFactory.getLogger(ViewManagerManager.class); 082 083 /** Whether or not to display debug messages. */ 084 private final boolean DEBUG = false; 085 086 /** The stack that stores the order of previously active ViewManagers. */ 087 private final Stack<ViewManager> previousVMs = new Stack<ViewManager>(); 088 089 /** Convenient reference back to the UIManager. */ 090 private UIManager uiManager; 091 092 /** 093 * Yet another constructor. 094 */ 095 public ViewManagerManager(IntegratedDataViewer idv) { 096 super(idv); 097 uiManager = (UIManager)getIdvUIManager(); 098 } 099 100 public int getViewManagerCount() { 101 return getViewManagers().size(); 102 } 103 104 /** 105 * Add the new view manager into the list if we don't have 106 * one with the {@link ViewDescriptor} of the new view manager 107 * already. 108 * 109 * @param newViewManager The new view manager 110 */ 111 @Override public void addViewManager(ViewManager newViewManager) { 112 super.addViewManager(newViewManager); 113 focusLayerControlsOn(newViewManager, false); 114 } 115 116 /** 117 * @return Reference to the stack of previously active ViewManagers. 118 */ 119 public Stack<ViewManager> getViewManagerOrder() { 120 return previousVMs; 121 } 122 123 /** 124 * Overridden so that McV can set the active ViewManager even if there is 125 * only one ViewManager. This is just a UI nicety; it'll allow the McV UI 126 * to show the active ViewManager no matter what. 127 * 128 * @return Always returns true. 129 */ 130 @Override public boolean haveMoreThanOneMainViewManager() { 131 return true; 132 } 133 134 /** 135 * Handles the removal of a ViewManager. McV needs to override this so that 136 * the stack of previously active ViewManagers is ordered properly. McV uses 137 * this method to make the ViewPanel respond immediately to the change. 138 * 139 * @param viewManager The ViewManager being removed. 140 */ 141 @Override public void removeViewManager(ViewManager viewManager) { 142 // the ordering of the stack must be preserved! this is the only chance 143 // to ensure the ordering if the incoming VM is inactive. 144 if (getLastActiveViewManager() != viewManager) { 145 previousVMs.remove(viewManager); 146 inspectStack("removing inactive vm"); 147 } 148 149 // now just sit back and let the IDV and setLastActiveViewManager work 150 // their magic. 151 super.removeViewManager(viewManager); 152 153 // inform UIManager that the VM needs to be dissociated from its 154 // ComponentHolder. 155 uiManager.removeViewManagerHolder(viewManager); 156 157 // force the layer controls tabs to layout the remaining components, 158 // but we don't want to bring it to the front! 159 uiManager.getViewPanel().getContents().validate(); 160 } 161 162 /** 163 * <p>This method is a bit strange. If the given ViewManager is null, then 164 * the IDV has removed the active ViewManager. McV will use the stack of 165 * last active ViewManagers to make the last active ViewManager active once 166 * again.</p> 167 * 168 * <p>If the given ViewManager is not null, but cannot be found in the stack 169 * of previously active ViewManagers, the IDV has created a new ViewManager 170 * and McV must push it on the stack.</p> 171 * 172 * <p>If the given ViewManager is not null and has been found in the stack, 173 * then the user has selected an inactive ViewManager. McV must remove the 174 * ViewManager from the stack and then push it back on top.</p> 175 * 176 * <p>These steps allow McV to make the behavior of closing tabs a bit more 177 * user-friendly. The user is always returned to whichever ViewManager was 178 * last active.</p> 179 * 180 * @param vm See above. :( 181 */ 182 // TODO: when you start removing the debug stuff, just convert the messages 183 // to comments. 184 @Override public void setLastActiveViewManager(ViewManager vm) { 185 String debugMsg = "created new vm"; 186 if (vm != null) { 187 if (previousVMs.search(vm) >= 0) { 188 debugMsg = "reset active vm"; 189 previousVMs.remove(vm); 190 focusLayerControlsOn(vm, false); 191 } 192 previousVMs.push(vm); 193 } else { 194 debugMsg = "removed active vm"; 195 196 ViewManager lastActive = getLastActiveViewManager(); 197 if (lastActive == null) 198 return; 199 200 lastActive.setLastActive(false); 201 202 previousVMs.pop(); 203 204 // if there are no more VMs, make sure the IDV code knows about it 205 // by setting the last active VM to null. 206 if (previousVMs.isEmpty()) { 207 super.setLastActiveViewManager(null); 208 return; 209 } 210 211 lastActive = previousVMs.peek(); 212 lastActive.setLastActive(true); 213 214 focusLayerControlsOn(lastActive, false); 215 } 216 217 inspectStack(debugMsg); 218 super.setLastActiveViewManager(previousVMs.peek()); 219 220 // start active tab testing 221 ComponentHolder holder = 222 uiManager.getViewManagerHolder(previousVMs.peek()); 223 if ((holder != null) && (holder instanceof McvComponentHolder)) { 224 ((McvComponentHolder)holder).setAsActiveTab(); 225 } 226 // stop active tab testing 227 } 228 229 /** 230 * <p>Overwrite the stack containing the ordering of previously active 231 * ViewManagers.</p> 232 * 233 * <p>Use this if you want to mess with the user's mind a little bit.</p> 234 * 235 * @param newOrder The stack containing the new ordering of ViewManagers. 236 */ 237 public void setViewManagerOrder(Stack<ViewManager> newOrder) { 238 previousVMs.clear(); 239 previousVMs.addAll(newOrder); 240 } 241 242 public int getComponentHolderCount() { 243 return -1; 244 } 245 246 public int getComponentGroupCount() { 247 // should be the same as the number of windows (or perhaps numWindows-1). 248 return -1; 249 } 250 251 /** 252 * Sets the active tab of the dashboard to the layer controls and makes the 253 * first layer (TODO: fix that!) of the given ViewManager the active layer. 254 * 255 * @param vm The ViewManager to make active. 256 * @param doShow Whether or not the layer controls should become the active 257 * tab in the dashboard. 258 */ 259 private void focusLayerControlsOn(ViewManager vm, boolean doShow) { 260 List<DisplayControlImpl> controls = vm.getControlsForLegend(); 261 if (controls != null && !controls.isEmpty()) { 262 DisplayControlImpl control = controls.get(0); 263 if (doShow) { 264 GuiUtils.showComponentInTabs(control.getOuterContents(), false); 265 } 266 } 267 } 268 269 /** 270 * Helper method that'll display the ordering of the stack and a helpful 271 * debug message! 272 */ 273 private void inspectStack(String msg) { 274 if (!DEBUG) { 275 return; 276 } 277 StringBuilder sb = new StringBuilder(this.hashCode()).append(": ").append(msg).append(": ["); 278 for (ViewManager vm : previousVMs) { 279 sb.append(vm.hashCode()).append(','); 280 } 281 logger.trace(sb.append("] Size=").append(previousVMs.size()).toString()); 282 } 283 284 /** 285 * Turns off layer visibility animation for all {@code ViewManager}s. This 286 * is typically only useful for when the user has removed all layers 287 * <i>without</i> turning off the layer animation setting. 288 */ 289 protected void disableAllLayerVizAnimations() { 290 for (ViewManager vm : getViewManagers()) { 291 vm.setAnimatedVisibilityCheckBox(false); 292 } 293 } 294 }