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