gt:         synchronized (mTreeChangeListeners) {
context: /*
   * Copyright (C) 2010 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.android.hierarchyviewerlib.models;
  
  import com.android.hierarchyviewerlib.device.ViewNode;
  import com.android.hierarchyviewerlib.device.Window;
  import com.android.hierarchyviewerlib.ui.util.DrawableViewNode;
  import com.android.hierarchyviewerlib.ui.util.DrawableViewNode.Point;
  import com.android.hierarchyviewerlib.ui.util.DrawableViewNode.Rectangle;
  
  import java.util.ArrayList;
  
  public class TreeViewModel {
      public static final double MAX_ZOOM = 2;
  
      public static final double MIN_ZOOM = 0.2;
  
      private Window mWindow;
  
      private DrawableViewNode mTree;
  
      private DrawableViewNode mSelectedNode;
  
      private Rectangle mViewport;
  
      private double mZoom;
  
      private final ArrayList<ITreeChangeListener> mTreeChangeListeners =
              new ArrayList<ITreeChangeListener>();
  
      private static TreeViewModel sModel;
  
      public static TreeViewModel getModel() {
          if (sModel == null) {
              sModel = new TreeViewModel();
          }
          return sModel;
      }
  
      public void setData(Window window, ViewNode viewNode) {
          synchronized (this) {
              if (mTree != null) {
                  mTree.viewNode.dispose();
              }
              this.mWindow = window;
              if (viewNode == null) {
                  mTree = null;
              } else {
                  mTree = new DrawableViewNode(viewNode);
                  mTree.setLeft();
                  mTree.placeRoot();
              }
              mViewport = null;
              mZoom = 1;
              mSelectedNode = null;
          }
          notifyTreeChanged();
      }
  
      public void setSelection(DrawableViewNode selectedNode) {
          synchronized (this) {
              this.mSelectedNode = selectedNode;
          }
          notifySelectionChanged();
      }
  
      public void setViewport(Rectangle viewport) {
          synchronized (this) {
              this.mViewport = viewport;
          }
          notifyViewportChanged();
      }
  
      public void setZoom(double newZoom) {
          Point zoomPoint = null;
          synchronized (this) {
              if (mTree != null && mViewport != null) {
                  zoomPoint =
                          new Point(mViewport.x + mViewport.width / 2, mViewport.y + mViewport.height / 2);
              }
          }
          zoomOnPoint(newZoom, zoomPoint);
      }
  
      public void zoomOnPoint(double newZoom, Point zoomPoint) {
          synchronized (this) {
              if (mTree != null && this.mViewport != null) {
                  if (newZoom < MIN_ZOOM) {
                      newZoom = MIN_ZOOM;
                  }
                  if (newZoom > MAX_ZOOM) {
                      newZoom = MAX_ZOOM;
                  }
                  mViewport.x = zoomPoint.x - (zoomPoint.x - mViewport.x) * mZoom / newZoom;
                  mViewport.y = zoomPoint.y - (zoomPoint.y - mViewport.y) * mZoom / newZoom;
                  mViewport.width = mViewport.width * mZoom / newZoom;
                  mViewport.height = mViewport.height * mZoom / newZoom;
                  mZoom = newZoom;
              }
          }
          notifyZoomChanged();
      }
  
      public DrawableViewNode getTree() {
          synchronized (this) {
              return mTree;
          }
      }
  
      public Window getWindow() {
          synchronized (this) {
              return mWindow;
          }
      }
  
      public Rectangle getViewport() {
          synchronized (this) {
              return mViewport;
          }
      }
  
      public double getZoom() {
          synchronized (this) {
              return mZoom;
          }
      }
  
      public DrawableViewNode getSelection() {
          synchronized (this) {
              return mSelectedNode;
          }
      }
  
      public static interface ITreeChangeListener {
          public void treeChanged();
  
          public void selectionChanged();
  
          public void viewportChanged();
  
          public void zoomChanged();
      }
  
      private ITreeChangeListener[] getTreeChangeListenerList() {
          ITreeChangeListener[] listeners = null;
          synchronized (mTreeChangeListeners) {
              if (mTreeChangeListeners.size() == 0) {
                  return null;
              }
              listeners =
                      mTreeChangeListeners.toArray(new ITreeChangeListener[mTreeChangeListeners.size()]);
          }
          return listeners;
      }
  
      public void notifyTreeChanged() {
          ITreeChangeListener[] listeners = getTreeChangeListenerList();
          if (listeners != null) {
              for (int i = 0; i < listeners.length; i++) {
                  listeners[i].treeChanged();
              }
          }
      }
  
      public void notifySelectionChanged() {
          ITreeChangeListener[] listeners = getTreeChangeListenerList();
          if (listeners != null) {
              for (int i = 0; i < listeners.length; i++) {
                  listeners[i].selectionChanged();
              }
          }
      }
  
      public void notifyViewportChanged() {
          ITreeChangeListener[] listeners = getTreeChangeListenerList();
          if (listeners != null) {
              for (int i = 0; i < listeners.length; i++) {
                  listeners[i].viewportChanged();
              }
          }
      }
  
      public void notifyZoomChanged() {
          ITreeChangeListener[] listeners = getTreeChangeListenerList();
          if (listeners != null) {
              for (int i = 0; i < listeners.length; i++) {
                  listeners[i].zoomChanged();
              }
          }
      }
  
      public void addTreeChangeListener(ITreeChangeListener listener) {
          synchronized (mTreeChangeListeners) {
              mTreeChangeListeners.add(listener);
          }
      }
  
      public void removeTreeChangeListener(ITreeChangeListener listener) {
  