001/* 002 * $Id: ViewManagerManager.java,v 1.14 2011/03/24 16:06:31 davep Exp $ 003 * 004 * This file is part of McIDAS-V 005 * 006 * Copyright 2007-2011 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 031package edu.wisc.ssec.mcidasv; 032 033import java.util.List; 034import java.util.Stack; 035 036import edu.wisc.ssec.mcidasv.ui.McvComponentHolder; 037import edu.wisc.ssec.mcidasv.ui.UIManager; 038 039import ucar.unidata.idv.IntegratedDataViewer; 040import ucar.unidata.idv.VMManager; 041import ucar.unidata.idv.ViewDescriptor; 042import ucar.unidata.idv.ViewManager; 043import ucar.unidata.idv.control.DisplayControlImpl; 044import ucar.unidata.ui.ComponentHolder; 045import ucar.unidata.util.GuiUtils; 046 047/** 048 * <p>McIDAS-V needs to manage ViewManagers in a slightly different way than 049 * the IDV. The key differences between the two are the way previously active 050 * ViewManagers are ordered and a (hopefully) more consistent way of handling 051 * active ViewManagers.</p> 052 * 053 * <p>The IDV only keeps track of the ViewManager used immediately before the 054 * current one. McV keeps track of the previously active ViewManagers in a 055 * stack. This mimics window z-ordering and always returns the user to the most 056 * recently active ViewManager upon removal of the active ViewManager.</p> 057 * 058 * <p>Newly created ViewManagers and their first layer now become the active 059 * ViewManager and layer. If there is only one ViewManager, it is now displayed 060 * as active instead of just implying it. When the active ViewManager is 061 * removed, the last active ViewManager and its first layer become active.</p> 062 * 063 * <p><b>A note to the future</b>: McV/IDV supports two notions of active and 064 * selected ViewManagers. Say you have NxN ViewManagers in a ComponentHolder, 065 * and you want to share views among some of these ViewManagers. When one of 066 * these shared ViewManagers is activated, should all of them become the active 067 * ViewManager? We're going to have to work out how to convey which 068 * ViewManagers are shared and active, and maybe more? Good luck!</p> 069 */ 070// TODO: should accesses to previousVMs should be synchronized? 071// TODO: should keep track of the ordering of active layers per VM as well. 072public class ViewManagerManager extends VMManager { 073 074 /** Whether or not to display debug messages. */ 075 private final boolean DEBUG = false; 076 077 /** The stack that stores the order of previously active ViewManagers. */ 078 private Stack<ViewManager> previousVMs = new Stack<ViewManager>(); 079 080 /** Convenient reference back to the UIManager. */ 081 private UIManager uiManager; 082 083 /** 084 * Yet another constructor. 085 */ 086 public ViewManagerManager(IntegratedDataViewer idv) { 087 super(idv); 088 uiManager = (UIManager)getIdvUIManager(); 089 } 090 091 /** 092 * Add the new view manager into the list if we don't have 093 * one with the {@link ViewDescriptor} of the new view manager 094 * already. 095 * 096 * @param newViewManager The new view manager 097 */ 098 @Override public void addViewManager(ViewManager newViewManager) { 099 super.addViewManager(newViewManager); 100 focusLayerControlsOn(newViewManager, false); 101 } 102 103 /** 104 * @return Reference to the stack of previously active ViewManagers. 105 */ 106 public Stack<ViewManager> getViewManagerOrder() { 107 return previousVMs; 108 } 109 110 /** 111 * Overridden so that McV can set the active ViewManager even if there is 112 * only one ViewManager. This is just a UI nicety; it'll allow the McV UI 113 * to show the active ViewManager no matter what. 114 * 115 * @return Always returns true. 116 */ 117 @Override public boolean haveMoreThanOneMainViewManager() { 118 return true; 119 } 120 121 /** 122 * Handles the removal of a ViewManager. McV needs to override this so that 123 * the stack of previously active ViewManagers is ordered properly. McV uses 124 * this method to make the ViewPanel respond immediately to the change. 125 * 126 * @param viewManager The ViewManager being removed. 127 */ 128 @Override public void removeViewManager(ViewManager viewManager) { 129 // the ordering of the stack must be preserved! this is the only chance 130 // to ensure the ordering if the incoming VM is inactive. 131 if (getLastActiveViewManager() != viewManager) { 132 previousVMs.remove(viewManager); 133 inspectStack("removing inactive vm"); 134 } 135 136 // now just sit back and let the IDV and setLastActiveViewManager work 137 // their magic. 138 super.removeViewManager(viewManager); 139 140 // inform UIManager that the VM needs to be dissociated from its 141 // ComponentHolder. 142 uiManager.removeViewManagerHolder(viewManager); 143 144 // force the layer controls tabs to layout the remaining components, 145 // but we don't want to bring it to the front! 146 uiManager.getViewPanel().getContents().validate(); 147 } 148 149 /** 150 * <p>This method is a bit strange. If the given ViewManager is null, then 151 * the IDV has removed the active ViewManager. McV will use the stack of 152 * last active ViewManagers to make the last active ViewManager active once 153 * again.</p> 154 * 155 * <p>If the given ViewManager is not null, but cannot be found in the stack 156 * of previously active ViewManagers, the IDV has created a new ViewManager 157 * and McV must push it on the stack.</p> 158 * 159 * <p>If the given ViewManager is not null and has been found in the stack, 160 * then the user has selected an inactive ViewManager. McV must remove the 161 * ViewManager from the stack and then push it back on top.</p> 162 * 163 * <p>These steps allow McV to make the behavior of closing tabs a bit more 164 * user-friendly. The user is always returned to whichever ViewManager was 165 * last active.</p> 166 * 167 * @param vm See above. :( 168 */ 169 // TODO: when you start removing the debug stuff, just convert the messages 170 // to comments. 171 @Override public void setLastActiveViewManager(ViewManager vm) { 172 String debugMsg = "created new vm"; 173 if (vm != null) { 174 if (previousVMs.search(vm) >= 0) { 175 debugMsg = "reset active vm"; 176 previousVMs.remove(vm); 177 focusLayerControlsOn(vm, false); 178 } 179 previousVMs.push(vm); 180 } else { 181 debugMsg = "removed active vm"; 182 183 ViewManager lastActive = getLastActiveViewManager(); 184 if (lastActive == null) 185 return; 186 187 lastActive.setLastActive(false); 188 189 previousVMs.pop(); 190 191 // if there are no more VMs, make sure the IDV code knows about it 192 // by setting the last active VM to null. 193 if (previousVMs.isEmpty()) { 194 super.setLastActiveViewManager(null); 195 return; 196 } 197 198 lastActive = previousVMs.peek(); 199 lastActive.setLastActive(true); 200 201 focusLayerControlsOn(lastActive, false); 202 } 203 204 inspectStack(debugMsg); 205 super.setLastActiveViewManager(previousVMs.peek()); 206 207 // start active tab testing 208 ComponentHolder holder = 209 uiManager.getViewManagerHolder(previousVMs.peek()); 210 if ((holder != null) && (holder instanceof McvComponentHolder)) { 211 ((McvComponentHolder)holder).setAsActiveTab(); 212 } 213 // stop active tab testing 214 } 215 216 /** 217 * <p>Overwrite the stack containing the ordering of previously active 218 * ViewManagers.</p> 219 * 220 * <p>Use this if you want to mess with the user's mind a little bit.</p> 221 * 222 * @param newOrder The stack containing the new ordering of ViewManagers. 223 */ 224 public void setViewManagerOrder(Stack<ViewManager> newOrder) { 225 previousVMs = newOrder; 226 } 227 228 /** 229 * Sets the active tab of the dashboard to the layer controls and makes the 230 * first layer (TODO: fix that!) of the given ViewManager the active layer. 231 * 232 * @param vm The ViewManager to make active. 233 * @param doShow Whether or not the layer controls should become the active 234 * tab in the dashboard. 235 */ 236 private void focusLayerControlsOn(ViewManager vm, boolean doShow) { 237 List<DisplayControlImpl> controls = vm.getControlsForLegend(); 238 if (controls != null && !controls.isEmpty()) { 239 DisplayControlImpl control = controls.get(0); 240 if (doShow) { 241 GuiUtils.showComponentInTabs(control.getOuterContents(), false); 242 } 243 } 244 } 245 246 /** 247 * Helper method that'll display the ordering of the stack and a helpful 248 * debug message! 249 */ 250 private void inspectStack(String msg) { 251 if (!DEBUG) { 252 return; 253 } 254 StringBuilder sb = new StringBuilder(this.hashCode()).append(": ").append(msg).append(": ["); 255 for (ViewManager vm : previousVMs) { 256 sb.append(vm.hashCode()).append(','); 257 } 258 System.out.println(sb.append("] Size=").append(previousVMs.size()).toString()); 259 } 260 261 /** 262 * Turns off layer visibility animation for all {@code ViewManager}s. This 263 * is typically only useful for when the user has removed all layers 264 * <i>without</i> turning off the layer animation setting. 265 */ 266 protected void disableAllLayerVizAnimations() { 267 for (ViewManager vm : getViewManagers()) { 268 vm.setAnimatedVisibilityCheckBox(false); 269 } 270 } 271}