/*
* Copyright (C) 2014 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.tools.idea.run;
import com.intellij.debugger.DebuggerInvocationUtil;
import com.intellij.debugger.DebuggerManagerEx;
import com.intellij.debugger.actions.DebuggerAction;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
import com.intellij.debugger.ui.tree.ValueDescriptor;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.progress.util.ProgressWindowWithNotification;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.Value;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
/**
* {@link ViewBitmapAction} provides the ability to view an android Bitmap object. When this action
* is invoked, the bitmap is downloaded from the target and displayed in a pop-up.
*/
public class ViewBitmapAction extends DebuggerAction {
@Override
public void actionPerformed(AnActionEvent e) {
final DataContext actionContext = e.getDataContext();
final DebuggerTreeNodeImpl node = getSelectedNode(actionContext);
if (node == null) {
return;
}
final Value bitmapValue = getValue(node);
if (bitmapValue == null) {
return;
}
final Project project = CommonDataKeys.PROJECT.getData(actionContext);
if (project == null) {
return;
}
final DebuggerManagerEx debuggerManager = DebuggerManagerEx.getInstanceEx(project);
if(debuggerManager == null) {
return;
}
final DebuggerContextImpl debuggerContext = debuggerManager.getContext();
if (debuggerContext == null || debuggerContext.getDebuggerSession() == null) {
return;
}
final ProgressWindowWithNotification progressWindow = new ProgressWindowWithNotification(true, project);
final String variableName = node.getText() == null ? "Bitmap" : node.getText().toString();
DownloadBitmapCommand.CompletionCallback onComplete = new DownloadBitmapCommand.CompletionCallback() {
@Override
public void bitmapDownloaded(@NotNull final BufferedImage image) {
DebuggerInvocationUtil.swingInvokeLater(project, new Runnable() {
@Override
public void run() {
JComponent imageComponent = new JComponent() {
@Override
protected void paintComponent(Graphics g) {
g.drawImage(image, 0, 0, null);
}
};
JBPopup popup = JBPopupFactory.getInstance()
.createComponentPopupBuilder(imageComponent, null)
.setProject(project)
.setTitle(variableName)
.setResizable(true)
.setMovable(true)
.setFocusable(true)
.setMinSize(new Dimension(image.getWidth(), image.getHeight()))
.setShowBorder(true)
.createPopup();
popup.show(JBPopupFactory.getInstance().guessBestPopupLocation(actionContext));
}
});
}
@Override
public void error(@NotNull final String message) {
DebuggerInvocationUtil.swingInvokeLater(project, new Runnable() {
@Override
public void run() {
JBPopup popup = JBPopupFactory.getInstance().createMessage(message);
popup.show(JBPopupFactory.getInstance().guessBestPopupLocation(actionContext));
}
});
}
};
DownloadBitmapCommand downloadBitmapCommand =
new DownloadBitmapCommand(bitmapValue, debuggerContext, onComplete, progressWindow);
progressWindow.setTitle("Retrieving Bitmap...");
debuggerContext.getDebugProcess().getManagerThread().startProgress(downloadBitmapCommand, progressWindow);
}
// display this action only if the selected node is a Bitmap
@Override
public void update(AnActionEvent e) {
Presentation presentation = e.getPresentation();
DataContext actionContext = e.getDataContext();
DebuggerTreeNodeImpl node = getSelectedNode(actionContext);
Value value = getValue(node);
// TODO: should this be a check on the type or the value?
if (!(value instanceof ObjectReference)) {
presentation.setVisible(false);
return;
}
if (!DownloadBitmapCommand.isSupportedBitmap(value)) {
presentation.setVisible(false);
} else {
presentation.setText("View Bitmap");
presentation.setVisible(true);
presentation.setEnabled(true);
}
}
@Nullable
private static Value getValue(@Nullable DebuggerTreeNodeImpl node) {
if (node == null) {
return null;
}
NodeDescriptorImpl descriptor = node.getDescriptor();
if (!(descriptor instanceof ValueDescriptor)) {
return null;
}
return ((ValueDescriptor)descriptor).getValue();
}
}