/*
 * Copyright 2000-2009 JetBrains s.r.o.
 *
 * 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.
 */

/*
 * @author Eugene Zhuravlev
 */
package com.intellij.debugger.ui.impl;

import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.impl.DebuggerStateManager;
import com.intellij.debugger.ui.impl.watch.DebuggerTree;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.keymap.KeymapManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.wm.ex.IdeFocusTraversalPolicy;
import com.intellij.ui.PopupHandler;
import com.intellij.util.Alarm;
import com.intellij.xdebugger.impl.actions.XDebuggerActions;
import com.sun.jdi.VMDisconnectedException;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;

public abstract class DebuggerTreePanel extends UpdatableDebuggerView implements DataProvider {
  public static final DataKey<DebuggerTreePanel> DATA_KEY = DataKey.create("DebuggerPanel");
  
  private final Alarm myRebuildAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
  protected DebuggerTree myTree;

  public DebuggerTreePanel(Project project, DebuggerStateManager stateManager) {
    super(project, stateManager);
    myTree = createTreeView();

    final PopupHandler popupHandler = new PopupHandler() {
      @Override
      public void invokePopup(Component comp, int x, int y) {
        ActionPopupMenu popupMenu = createPopupMenu();
        if (popupMenu != null) {
          myTree.myTipManager.registerPopup(popupMenu.getComponent()).show(comp, x, y);
        }
      }
    };
    myTree.addMouseListener(popupHandler);

    setFocusTraversalPolicy(new IdeFocusTraversalPolicy() {
      @Override
      public Component getDefaultComponentImpl(Container focusCycleRoot) {
        return myTree;
      }
    });

    registerDisposable(new Disposable() {
      @Override
      public void dispose() {
        myTree.removeMouseListener(popupHandler);
      }
    });

    final Shortcut[] shortcuts = KeymapManager.getInstance().getActiveKeymap().getShortcuts("ToggleBookmark");
    final CustomShortcutSet shortcutSet = shortcuts.length > 0? new CustomShortcutSet(shortcuts) : new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_F11, 0));
    overrideShortcut(myTree, XDebuggerActions.MARK_OBJECT, shortcutSet);
  }

  protected abstract DebuggerTree createTreeView();


  @Override
  protected void rebuild(int event) {
    myRebuildAlarm.cancelAllRequests();
    myRebuildAlarm.addRequest(new Runnable() {
      @Override
      public void run() {
        try {
          final DebuggerContextImpl context = getContext();
          if(context.getDebuggerSession() != null) {
            getTree().rebuild(context);
          }
        }
        catch (VMDisconnectedException ignored) {
          // ignored
        }
      }
    }, 100, ModalityState.NON_MODAL);
  }

  @Override
  public void dispose() {
    Disposer.dispose(myRebuildAlarm);
    try {
      super.dispose();
    }
    finally {
      final DebuggerTree tree = myTree;
      if (tree != null) {
        Disposer.dispose(tree);
      }
      // prevent mem leak from inside Swing
      myTree = null;
    }
  }


  protected abstract ActionPopupMenu createPopupMenu();

  public final DebuggerTree getTree() {
    return myTree;
  }

  public void clear() {
    myTree.removeAllChildren();
  }

  @Override
  public Object getData(String dataId) {
    if (DATA_KEY.is(dataId)) {
      return this;
    }
    return null;
  }

  @Override
  public void requestFocus() {
    getTree().requestFocus();
  }
}

@@ -926,63 +926,8 @@
 .*;%0A
-import com.intellij.openapi.application.ModalityState;%0A
 impo
@@ -964,32 +964,32 @@
 .KeymapManager;%0A
+
 import com.intel
@@ -1181,16 +1181,22 @@
 ij.util.
+Single
 Alarm;%0Ai
@@ -1582,16 +1582,22 @@
 e final 
+Single
 Alarm my
@@ -1619,47 +1619,327 @@
 new 
-Alarm(Alarm.ThreadToUse.SWING_THREAD
+SingleAlarm(new Runnable() %7B%0A    @Override%0A    public void run() %7B%0A      try %7B%0A        final DebuggerContextImpl context = getContext();%0A        if(context.getDebuggerSession() != null) %7B%0A          getTree().rebuild(context);%0A        %7D%0A      %7D%0A      catch (VMDisconnectedException ignored) %7B%0A      %7D%0A%0A    %7D%0A  %7D, 100
 );%0A
+%0A
   pr
@@ -3251,17 +3251,16 @@
 iew();%0A%0A
-%0A
   @Overr
@@ -3331,424 +3331,18 @@
 celA
-llRequests();%0A    myRebuildAlarm.addRequest(new Runnable() %7B%0A      @Override%0A      public void run() %7B%0A        try %7B%0A          final DebuggerContextImpl context = getContext();%0A          if(context.getDebuggerSession() != null) %7B%0A            getTree().rebuild(context);%0A          %7D%0A        %7D%0A        catch (VMDisconnectedException ignored) %7B%0A          // ignored%0A        %7D%0A      %7D%0A    %7D, 100, ModalityState.NON_MODAL
+ndRequest(
 );%0A 

use SingleAlarm