at.spardat.xma.page.messagebox.DetailedMessageBox Maven / Gradle / Ivy
The newest version!
/*******************************************************************************
* Copyright (c) 2003, 2010 s IT Solutions AT Spardat GmbH .
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* s IT Solutions AT Spardat GmbH - initial API and implementation
*******************************************************************************/
// @(#) $Id: $
package at.spardat.xma.page.messagebox;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
/**
* The {@link DetailedMessageBox} will be able to present a detail message in a collapsible area.
* This information will contain:
*
* - All elements in
DetailedMessageBox.additionalInformationList
* - The formated Stacktrace located at
DetailedMessageBox.exception
*
* So the caller can add custom "additional information" to the details.
* There is no need to set buttons and icons from the caller side, so the MessageBox will have a default
* layout, button and icon set.
* The default style will be:
SWT.APPLICATION_MODAL | SWT.RESIZE
* Buttons:
*
* Show Details / Hide Details
* Copy text to Clipboard
* OK
*
* So we hope the name of the buttons wil be self-explanatory, but if there are any further questions, use
* the ToolTip to get detailed information about the buttons and their actions behind them.
* Icon:
* This Detailed MessageBox will be used to display an Error to the EndUser, and provide the feature to get
* detailed Information about the error. So there is no need to add another icon as the {@link SWT#ICON_ERROR}.
* There is also another extra feature, because this MessageBox can be resized, so feel free to click the
* Bottom-right corner of the Box and make it larger
*
* @author s3945
* @since 2.1.2
*/
public class DetailedMessageBox extends AbstractMessagebox {
/** the default height of the StackTrace element */
private static final int STACK_HEIGHT = 300;
/**
* this will be true, if the current used SWT version can display icons AND text on buttons.
*
* VERSION NUMBER
* 3.1.1 3139
* 3.2.2 3236
* 3.3.0 3346
* 3.3.2 3349
* 3.4.0 3448
* 3.4.1 3449
* 3.5.1 3555
*
*/
private static boolean DISPLAY_IMG_ON_BUTTONS = org.eclipse.swt.internal.Library.SWT_VERSION > 3139;
/** the used {@link Button} on the MessageBox */
private Button detailButton, copyButton;
/** the GUI representation of the information */
private Text infoText;
/** this boolean will remember the state of the information field, so it will know if the detail is opend or not. */
private boolean detailOpen;
/** the {@link Exception} contain the Stacktrace to present in details */
private Exception exception;
/** this {@link List} of Strings will contain all additional information provided by the caller */
private List additionalInformationList = new ArrayList();
/** internal usage of newline */
private static final String NEWLINE = System.getProperty("line.separator");
/** the default style of the {@link DetailedMessageBox} */
private static final int DEFAULT_STYLE = SWT.APPLICATION_MODAL | SWT.RESIZE| SWT.ICON_ERROR;
/** the internal cache of the text to display in the info box, because the copy context will be the same as the
* context to display in the box, and the string building should only be done once. */
private String infoTextContent;
/** all possible images added to the buttons, but can be null if SWT version will be not OK */
private Image copyImage, showDetailImage, hideDetailImage;
/**
* Creates a DetailedMessageBox with the following parameters:
* SWT.APPLICATION_MODAL
* SWT.RESIZE
* SWT.ICON_ERROR
* Locale = Default
* @param parent shell to use to display
*/
public DetailedMessageBox(Shell parent) {
super(parent, null, DEFAULT_STYLE);
}
/**
* Creates a DetailedMessageBox with the following parameters:
* SWT.APPLICATION_MODAL
* SWT.RESIZE
* SWT.ICON_ERROR
* @param parent shell to use to display
* @param locale the local to use to localize the buttons
*/
public DetailedMessageBox(Shell parent, Locale locale) {
super(parent, locale, DEFAULT_STYLE);
}
/**
* Creates a DetailedMessageBox with the following parameters:
* SWT.APPLICATION_MODAL
* SWT.RESIZE
* SWT.ICON_ERROR
* @param parent shell to use to display
* @param locale the local to use to localize the buttons
* @param exception the exception to display details for
*/
public DetailedMessageBox(Shell parent, Locale locale, Exception exception) {
super(parent, locale, DEFAULT_STYLE);
this.exception = exception;
}
/**
* Creates a DetailedMessageBox with the following parameters:
* @param parent shell to use to display
* @param locale the local to use to localize the buttons
* @param exception the exception to display details for
* @param style the style of dialog to construct
*/
public DetailedMessageBox(Shell parent, Locale locale, Exception exception, int style) {
super(parent, locale, style);
this.exception = exception;
}
/**
* This method will create all needed images to be able to use them on the buttons.
*/
private void initializeImages() {
if (DISPLAY_IMG_ON_BUTTONS) {
copyImage = new Image(getShell().getDisplay(), DetailedMessageBox.class.getClassLoader().getResourceAsStream("at/spardat/xma/page/messagebox/icon_copy.gif"));
showDetailImage = new Image(getShell().getDisplay(), DetailedMessageBox.class.getClassLoader().getResourceAsStream("at/spardat/xma/page/messagebox/icon_show_details.gif"));
hideDetailImage = new Image(getShell().getDisplay(), DetailedMessageBox.class.getClassLoader().getResourceAsStream("at/spardat/xma/page/messagebox/icon_hide_details.gif"));
}
}
/** {@inheritDoc} */
protected void shellDisposed() {
super.shellDisposed();
if (copyImage != null) { copyImage.dispose(); }
if (showDetailImage != null) { showDetailImage.dispose(); }
if (hideDetailImage != null) { hideDetailImage.dispose(); }
}
/**
* create the buttons
* @param lowerSeperator all buttons will be bottom attached to this separator
*/
private void createButtons(Label lowerSeparator) {
initializeImages();
detailButton = new Button(getShell(), SWT.PUSH);
detailButton.setText(getResourceBundle().getString("showdetails"));
detailButton.setToolTipText(getResourceBundle().getString("showdetails.tooltip"));
detailButton.addSelectionListener(getSelectionListener());
if (DISPLAY_IMG_ON_BUTTONS) {
detailButton.setImage(showDetailImage);
}
copyButton = new Button(getShell(), SWT.PUSH);
copyButton.setToolTipText(getResourceBundle().getString("copy.tooltip"));
copyButton.addSelectionListener(getSelectionListener());
if (DISPLAY_IMG_ON_BUTTONS) {
copyButton.setImage(copyImage);
}
copyButton.setText(getResourceBundle().getString("copy"));
// DETAIL BUTTON
FormData data = new FormData();
data.top = new FormAttachment(lowerSeparator, scaler.convertYToCurrent(5), SWT.BOTTOM);
data.left = new FormAttachment(0, 100, scaler.convertXToCurrent(10));
detailButton.setLayoutData(data);
// COPY BUTTON
data = new FormData();
data.top = new FormAttachment(lowerSeparator, scaler.convertYToCurrent(5), SWT.BOTTOM);
data.left = new FormAttachment(detailButton, scaler.convertXToCurrent(HORIZONTAL_OFFSET), SWT.RIGHT);
copyButton.setLayoutData(data);
}
protected void layoutButtonComposite() {
FormLayout layout = new FormLayout();
layout.marginBottom = scaler.convertYToCurrent(10);
getButtonComposite().setLayout(layout);
FormData data = new FormData();
data.top = new FormAttachment(getLowerSep(), 0, SWT.BOTTOM);
data.right = new FormAttachment(100, 100, 0);
getButtonComposite().setLayoutData(data);
}
/**
* This selectionListener will set the local variable "answer" to the
* selected Button or handle copy or display details.
* @return the {@link SelectionListener} used for the button
*/
private SelectionListener getSelectionListener() {
SelectionListener selectionListener = new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
Button selButton = (Button) e.widget;
boolean close = true;
if (selButton.equals(copyButton)) {
close = false; // by clicking the copy button, the MessageBox will not be closed
copyTextToClipboard();
} else if (selButton.equals(detailButton)) {
close = false; // by clicking the detail button, the MessageBox will not be closed
handleDetail();
}
if (close) {
getShell().close();
}
}
public void widgetDefaultSelected(SelectionEvent e) { }
};
return selectionListener;
}
/**
* This method will collect the context to copy to {@link Clipboard}.
* After successful copy this content to Clipboard, an {@link LocalizedMessageBox} with a
* information text will be displayed to the user.
*/
private void copyTextToClipboard() {
String text = getClipboardContent();
if (text != null && text.length() > 0) {
Clipboard clipboard = new Clipboard(getShell().getDisplay());
try {
clipboard.setContents(new Object[] { text }, new Transfer[] { TextTransfer.getInstance() });
LocalizedMessageBox msg = new LocalizedMessageBox(getShell(), getLocale(), SWT.OK | SWT.ICON_INFORMATION);
msg.setMessage(getResourceBundle().getString("copy.information"));
msg.open();
} finally {
clipboard.dispose();
}
}
}
/**
* @return the String to be copied into {@link Clipboard}.
*/
private String getClipboardContent() {
StringBuffer b = new StringBuffer();
String title = getTitle();
if(title!=null&&title.length()>0) b.append(title).append(NEWLINE).append(NEWLINE);
String message = getMessage();
if(message!=null&&message.length()>0) b.append(message).append(NEWLINE).append(NEWLINE);
b.append(getInfoTextContent());
return b.toString();
}
/**
* This method will handle the collapse and expand of the detail section.
*/
private void handleDetail() {
if (detailOpen) {
detailOpen = false;
layoutInfoText();
detailButton.setText(getResourceBundle().getString("showdetails"));
detailButton.setToolTipText(getResourceBundle().getString("showdetails.tooltip"));
if (DISPLAY_IMG_ON_BUTTONS) {
detailButton.setImage(showDetailImage);
}
} else {
detailOpen = true;
layoutInfoText();
infoText.setText(getInfoTextContent());
detailButton.setText(getResourceBundle().getString("hidedetails"));
detailButton.setToolTipText(getResourceBundle().getString("hidedetails.tooltip"));
if (DISPLAY_IMG_ON_BUTTONS) {
detailButton.setImage(hideDetailImage);
}
}
infoText.setVisible(detailOpen);
calcShellSize(false);
}
private void layoutInfoText() {
FormData infoTextData = new FormData();
infoTextData.left = new FormAttachment(0, 100, scaler.convertXToCurrent(10));
infoTextData.right = new FormAttachment(100, 100, 0);
if (detailOpen) {
infoTextData.top = new FormAttachment(getButtonComposite(), scaler.convertYToCurrent(5), SWT.BOTTOM);
} else {
infoTextData.top = new FormAttachment(getButtonComposite(), -40, SWT.BOTTOM);
}
infoTextData.bottom = new FormAttachment(100, 100, 0);
infoTextData.height = detailOpen ? scaler.convertYToCurrent(STACK_HEIGHT) : 1;
infoText.setLayoutData(infoTextData);
}
/**
* This will provide the text to display in detail section.
* The text will also add all additional information separated by a NEWLINE.
* @return the text to display in detail box
*/
private String getInfoTextContent() {
if (infoTextContent == null) {
StringBuffer b = new StringBuffer();
// append all additional infos to text
for(Iterator it=additionalInformationList.iterator();it.hasNext();) {
String addInfo = (String) it.next();
b.append(addInfo).append(NEWLINE);
}
// append the stack trace
final Writer result = new StringWriter();
final PrintWriter printWriter = new PrintWriter(result);
this.exception.printStackTrace(printWriter);
infoTextContent = b.append(result.toString()).toString();
}
return infoTextContent;
}
/** {@inheritDoc} */
protected void createWidgets() {
super.createWidgets();
createButtons(getLowerSep());
createInfoText(detailButton);
}
/**
* Create the GUI representation of the info box.
* @param lowerSep to attach
*/
private void createInfoText(Button topLayoutElement) {
infoText = new Text(getShell(), SWT.BORDER | SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL);
infoText.setVisible(false);
infoText.setEditable(false);
infoText.setText(getInfoTextContent());
FormData infoTextData = new FormData();
infoTextData.left = new FormAttachment(0, 100, scaler.convertXToCurrent(10));
infoTextData.right = new FormAttachment(100, 100, 0);
infoTextData.top = new FormAttachment(topLayoutElement, -40, SWT.BOTTOM);
infoTextData.height = detailOpen ? scaler.convertYToCurrent(STACK_HEIGHT) : 0;
infoTextData.bottom = new FormAttachment(100, 100, 0);
infoText.setLayoutData(infoTextData);
}
/**
* The given string will be displayed in detail box and also copied to {@link Clipboard}.
* @param additionalInformation the additionalInformationList to set
*/
public void addAdditionalInformation(String additionalInformation) {
this.additionalInformationList.add(additionalInformation);
}
protected Point getMinSize() {
int minWidth = scaler.convertXToCurrent(23) + detailButton.getSize().x + scaler.convertXToCurrent(HORIZONTAL_OFFSET);
minWidth += copyButton.getSize().x + scaler.convertXToCurrent(HORIZONTAL_OFFSET);
minWidth += getButtonComposite().getSize().x + scaler.convertXToCurrent(HORIZONTAL_OFFSET);
int okBottomEnd = getButtonComposite().getLocation().y + getButtonComposite().getSize().y;
int minHeight = okBottomEnd + scaler.convertYToCurrent(SHELL_DEFAULT_MIN_HEIGHT);
if(detailOpen) {
minHeight += scaler.convertYToCurrent(100);
}
return new Point(minWidth,minHeight);
}
protected int getButtonHeight() {
return detailButton.computeSize(SWT.DEFAULT,SWT.DEFAULT).y;
}
}