blob: b4a50d48566e275be9010638e7a3bc33e6fdec2f [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.netbeans.modules.progress.ui;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Image;
import java.awt.LayoutManager;
import java.awt.event.ActionEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import org.netbeans.modules.progress.spi.InternalHandle;
import org.netbeans.modules.progress.spi.ProgressEvent;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.util.ImageUtilities;
import org.openide.util.NbBundle;
/**
*
* @author mkleint
*/
public class ListComponent extends JPanel {
private NbProgressBar bar;
private JLabel mainLabel;
private JLabel dynaLabel;
private JButton closeButton;
private InternalHandle handle;
private boolean watched;
private Action cancelAction;
private Color selectBgColor;
private Color selectFgColor;
private Color bgColor;
private Color fgColor;
// private Color dynaFgColor;
private int mainHeight;
private int dynaHeight;
/** Creates a new instance of ListComponent */
public ListComponent(InternalHandle hndl) {
setFocusable(true);
setRequestFocusEnabled(true);
// setVerifyInputWhenFocusTarget(false);
mainLabel = new JLabel();
dynaLabel = new JLabel();
// in gtk, the panel is non-opague, meaning we cannot color background
// #59419
setOpaque(true);
dynaLabel.setFont(dynaLabel.getFont().deriveFont((float) (dynaLabel.getFont().getSize() - 2)));
bar = new NbProgressBar();
handle = hndl;
Color bg = UIManager.getColor("nbProgressBar.popupText.background");
if (bg != null) {
setBackground(bg);
mainLabel.setBackground(bg);
dynaLabel.setBackground(bg);
}
bgColor = getBackground();
Color dynaFg = UIManager.getColor("nbProgressBar.popupDynaText.foreground");
if (dynaFg != null) {
dynaLabel.setForeground(dynaFg);
}
// dynaFgColor = dynaLabel.getForeground();
fgColor = UIManager.getColor("nbProgressBar.popupText.foreground");
if (fgColor != null) {
mainLabel.setForeground(fgColor);
}
fgColor = mainLabel.getForeground();
selectBgColor = UIManager.getColor("nbProgressBar.popupText.selectBackground");
if (selectBgColor == null) {
selectBgColor = UIManager.getColor("List.selectionBackground");
}
selectFgColor = UIManager.getColor("nbProgressBar.popupText.selectForeground");
if (selectFgColor == null) {
selectFgColor = UIManager.getColor("List.selectionForeground");
}
bar.setToolTipText(NbBundle.getMessage(ListComponent.class, "ListComponent.bar.tooltip"));
bar.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
//start figure out height
mainLabel.setText("XYZ");
dynaLabel.setText("XYZ");
mainHeight = mainLabel.getPreferredSize().height;
dynaHeight = dynaLabel.getPreferredSize().height;
mainLabel.setText(null);
dynaLabel.setText(null);
//end figure out height
setLayout(new CustomLayout());
add(mainLabel);
add(bar);
MListener list = new MListener();
if (handle.isAllowCancel()) {
cancelAction = new CancelAction(false);
closeButton = new JButton(cancelAction);
closeButton.setBorderPainted(false);
closeButton.setBorder(BorderFactory.createEmptyBorder());
closeButton.setOpaque(false);
closeButton.setContentAreaFilled(false);
closeButton.setFocusable(false);
Image img = (Image)UIManager.get("nb.progress.cancel.icon");
if( null != img ) {
closeButton.setIcon( new ImageIcon( img ) );
}
img = (Image)UIManager.get("nb.progress.cancel.icon.mouseover");
if( null != img ) {
closeButton.setRolloverEnabled(true);
closeButton.setRolloverIcon( new ImageIcon( img ) );
}
img = (Image)UIManager.get("nb.progress.cancel.icon.pressed");
if( null != img ) {
closeButton.setPressedIcon( new ImageIcon( img ) );
}
closeButton.setToolTipText(NbBundle.getMessage(ListComponent.class, "ListComponent.btnClose.tooltip"));
add(closeButton);
if (handle.getState() != InternalHandle.STATE_RUNNING) {
closeButton.setEnabled(false);
}
}
add(dynaLabel);
setBorder(BorderFactory.createEmptyBorder());
addMouseListener(list);
bar.addMouseListener(list);
mainLabel.addMouseListener(list);
dynaLabel.addMouseListener(list);
if (handle.isAllowCancel()) {
closeButton.addMouseListener(list);
}
mainLabel.setText(handle.getDisplayName());
mainLabel.setToolTipText(mainLabel.getText());
NbProgressBar.setupBar(handle, bar);
addFocusListener(new FocusListener() {
public void focusGained(java.awt.event.FocusEvent e) {
if (!e.isTemporary()) {
setBackground(selectBgColor);
mainLabel.setBackground(selectBgColor);
dynaLabel.setBackground(selectBgColor);
mainLabel.setForeground(selectFgColor);
// TODO assuming now that dynalabel has always the same foreground color
// seems to be the case according to the spec
scrollRectToVisible(getBounds());
}
}
public void focusLost(java.awt.event.FocusEvent e) {
if (!e.isTemporary()) {
setBackground(bgColor);
mainLabel.setBackground(bgColor);
dynaLabel.setBackground(bgColor);
mainLabel.setForeground(fgColor);
// TODO assuming now that dynalabel has always the same foreground color
// seems to be the case according to the spec
}
}
});
}
Action getCancelAction() {
return cancelAction;
}
InternalHandle getHandle() {
return handle;
}
void processProgressEvent(ProgressEvent event) {
if (event.getType() == ProgressEvent.TYPE_PROGRESS ||
event.getType() == ProgressEvent.TYPE_SWITCH ||
event.getType() == ProgressEvent.TYPE_SILENT) {
if (event.getSource() != handle) {
throw new IllegalStateException();
}
if (event.isSwitched()) {
NbProgressBar.setupBar(event.getSource(), bar);
}
if (event.getWorkunitsDone() > 0) {
bar.setValue(event.getWorkunitsDone());
}
bar.setString(StatusLineComponent.getBarString(event.getPercentageDone(), event.getEstimatedCompletion()));
if (event.getMessage() != null) {
dynaLabel.setText(event.getMessage());
}
if (event.getSource().getState() == InternalHandle.STATE_REQUEST_STOP) {
closeButton.setEnabled(false);
}
if (event.getDisplayName() != null) {
mainLabel.setText(event.getDisplayName());
}
} else {
throw new IllegalStateException();
}
}
void markAsActive(boolean sel) {
if (sel == watched) {
return;
}
watched = sel;
if (sel) {
mainLabel.setFont(mainLabel.getFont().deriveFont(Font.BOLD));
} else {
mainLabel.setFont(mainLabel.getFont().deriveFont(Font.PLAIN));
}
if (mainLabel.isVisible()) {
mainLabel.repaint();
}
}
//Called to clear the progressbar
void clearProgressBarOSX() {
//EMI: On OSX, an animation thread is started when the progressbar is created, even if not displayed (or added to a container).
// The following line is needed to kill the animation thread otherwise this tends to be alive for the rest of the JVM execution
// pumping a lot of repaing events in the event queue (in my tests, about 50% of the CPU while idle).
// The culprit apple.laf.CUIAquaProgressBar$Animator was discovered with the normal Profiler, while Tim Boudreau told me about a similar
// problem with OSX and the pulsating button (that also has a thread for that animation).
// Finally, btrace and this IDEA bug report (http://www.jetbrains.net/jira/browse/IDEADEV-25376) connected the dots.
if (bar != null) {
bar.getUI().uninstallUI(bar);
}
}
private class MListener extends MouseAdapter {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getSource() == bar) {
handle.requestExplicitSelection();
// markAsSelected(true);
}
if (e.getClickCount() > 1 && (e.getSource() == mainLabel || e.getSource() == dynaLabel)) {
handle.requestView();
}
if (e.getButton() != MouseEvent.BUTTON1) {
showMenu(e);
} else {
ListComponent.this.requestFocus();
}
// System.out.println("list component requesting focus..");
}
}
private void showMenu(MouseEvent e) {
if (!isShowing()) {
return;
}
JPopupMenu popup = new JPopupMenu();
//mark teh popup for the status line awt listener
popup.setName("progresspopup");
popup.add(new ViewAction());
popup.add(new WatchAction());
popup.add(new CancelAction(true));
popup.show((Component)e.getSource(), e.getX(), e.getY());
}
private class CancelAction extends AbstractAction {
CancelAction(boolean text) {
if (text) {
putValue(Action.NAME, NbBundle.getMessage(ListComponent.class, "StatusLineComponent.Cancel"));
putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0));
} else {
Image icon = (Image)UIManager.get("nb.progress.cancel.icon");
if (icon == null) {
// for custom L&F?
icon = ImageUtilities.loadImage("org/netbeans/progress/module/resources/buton.png");
}
putValue(Action.SMALL_ICON, new ImageIcon(icon));
}
setEnabled(handle == null ? false : handle.isAllowCancel());
}
public void actionPerformed(ActionEvent actionEvent) {
if (handle.getState() == InternalHandle.STATE_RUNNING) {
String message = NbBundle.getMessage(ListComponent.class, "Cancel_Question", handle.getDisplayName());
String title = NbBundle.getMessage(ListComponent.class, "Cancel_Question_Title");
NotifyDescriptor dd = new NotifyDescriptor(message, title,
NotifyDescriptor.YES_NO_OPTION,
NotifyDescriptor.QUESTION_MESSAGE, null, null);
Object retType = DialogDisplayer.getDefault().notify(dd);
if (retType == NotifyDescriptor.YES_OPTION) {
handle.requestCancel();
}
}
}
}
private class ViewAction extends AbstractAction {
public ViewAction() {
putValue(Action.NAME, NbBundle.getMessage(ListComponent.class, "StatusLineComponent.View"));
setEnabled(handle == null ? false : handle.isAllowView());
putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0));
}
public void actionPerformed(ActionEvent actionEvent) {
if (handle != null) {
handle.requestView();
}
}
}
private class WatchAction extends AbstractAction {
public WatchAction() {
putValue(Action.NAME, NbBundle.getMessage(ListComponent.class, "ListComponent.Watch"));
putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0));
setEnabled(true);
}
public void actionPerformed(ActionEvent actionEvent) {
if (handle != null) {
handle.requestExplicitSelection();
}
}
}
private static final int UPPERMARGIN = 3;
private static final int LEFTMARGIN = 2;
private static final int BOTTOMMARGIN = 2;
private static final int BETWEENTEXTMARGIN = 3;
static final int ITEM_WIDTH = 600;
private class CustomLayout implements LayoutManager {
/**
* If the layout manager uses a per-component string,
* adds the component <code>comp</code> to the layout,
* associating it
* with the string specified by <code>name</code>.
*
* @param name the string to be associated with the component
* @param comp the component to be added
*/
public void addLayoutComponent(String name, java.awt.Component comp) {
}
/**
* Calculates the preferred size dimensions for the specified
* container, given the components it contains.
* @param parent the container to be laid out
*
* @see #minimumLayoutSize
*/
public Dimension preferredLayoutSize(java.awt.Container parent) {
int height = UPPERMARGIN + mainHeight + BETWEENTEXTMARGIN + dynaHeight + BOTTOMMARGIN;
return new Dimension(ITEM_WIDTH, height);
}
/**
*
* Lays out the specified container.
* @param parent the container to be laid out
*/
public void layoutContainer(java.awt.Container parent) {
int parentWidth = parent.getWidth();
int parentHeight = parent.getHeight();
int offset = parentWidth - 18;
if (closeButton != null) {
closeButton.setBounds(offset, UPPERMARGIN, 18, mainHeight);
}
// have the bar approx 30 percent of the width
int barOffset = offset - (ITEM_WIDTH / 3);
bar.setBounds(barOffset, UPPERMARGIN, offset - barOffset, mainHeight);
mainLabel.setBounds(LEFTMARGIN, UPPERMARGIN, barOffset - LEFTMARGIN, mainHeight);
dynaLabel.setBounds(LEFTMARGIN, mainHeight + UPPERMARGIN + BETWEENTEXTMARGIN,
parentWidth - LEFTMARGIN, dynaHeight);
}
/**
*
* Calculates the minimum size dimensions for the specified
* container, given the components it contains.
* @param parent the component to be laid out
* @see #preferredLayoutSize
*/
public Dimension minimumLayoutSize(java.awt.Container parent) {
return preferredLayoutSize(parent);
}
/**
* Removes the specified component from the layout.
* @param comp the component to be removed
*/
public void removeLayoutComponent(java.awt.Component comp) {
}
}
}