FileDocCategorySizeDatePackage
MessageSlideShell.javaAPI DocAzureus 3.0.3.439133Thu May 24 05:36:38 BST 2007org.gudy.azureus2.ui.swt.shells

MessageSlideShell

public class MessageSlideShell extends Object
+=====================================+ | +----+ | | |Icon| Big Bold Title | | +----+ | | Wrapping message text | | with optional URL links | | +-----+ | | |BGImg| XX more slideys.. | | | Icon| Closing in XX secs | | +-----+ [HideAll] [Details] [Hide] | +=====================================+
author
TuxPaper
created
Mar 7, 2006

Fields Summary
private static boolean
USE_SWT32_BG_SET
private static final boolean
DEBUG
private static final String
REGEX_URLHTML
private static final int
EDGE_GAP
Slide until there's this much gap between shell and edge of screen
private static final int
SHELL_DEF_WIDTH
Width used when BG image can't be loaded
private static final int
SHELL_MIN_HEIGHT
Standard height of the shell. Shell may grow depending on text
private static final int
SHELL_MAX_HEIGHT
Maximimum height of popup. If text is too long, the full text will be put into details.
private static final int
DETAILS_WIDTH
Width of the details shell
private static final int
DETAILS_HEIGHT
Height of the details shell
private static final AEMonitor
monitor
Synchronization for popupList
private static ArrayList
historyList
List of all popups ever created
private static int
currentPopupIndex
Current popup being displayed
private static int
firstUnreadMessage
Index of first message which the user has not seen (index) - set to -1 if we don't care. :)
private Shell
shell
Shell for popup
private Composite
cShell
Composite in shell
private Label
lblCloseIn
popup could and closing in xx seconds label
private Button
btnHideAll
Button that hides all slideys in the popupList. Visible only when there's more than 1 slidey
private Button
btnNext
Button to move to next message. Text changes from "Hide" to "Next" appropriately.
private boolean
bDelayPaused
paused state of auto-close delay
private ArrayList
disposeList
List of SWT objects needing disposal
private String
sDetails
Text to put into details popup
private int
idxHistory
Position this popup is in the history list
private Image
imgPopup
Constructors Summary
public MessageSlideShell(Display display, int iconID, String keyPrefix, String details, String[] textParams)
Open a popup using resource keys for title/text

param
display Display to create the shell on
param
iconID SWT.ICON_* constant for icon in top left
param
keyPrefix message bundle key prefix used to get title and text. Title will be keyPrefix + ".title", and text will be set to keyPrefix + ".text"
param
details actual text for details (not a key)
param
textParams any parameters for text
note
Display moved to end to remove conflict in constructors


	                                                                                                 	 
	      
			    
		this(display, iconID, MessageText.getString(keyPrefix + ".title"),
				MessageText.getString(keyPrefix + ".text", textParams), details);
	
public MessageSlideShell(Display display, int iconID, String keyPrefix, String details, String[] textParams, Object[] relatedObjects)

		this(display, iconID, MessageText.getString(keyPrefix + ".title"),
				MessageText.getString(keyPrefix + ".text", textParams), details,
				relatedObjects);
	
public MessageSlideShell(Display display, int iconID, String title, String text, String details)

		this(display, iconID, title, text, details, null);
	
public MessageSlideShell(Display display, int iconID, String title, String text, String details, Object[] relatedObjects)
Open Mr Slidey

param
display Display to create the shell on
param
iconID SWT.ICON_* constant for icon in top left
param
title Text to put in the title
param
text Text to put in the body
param
details Text displayed when the Details button is pressed. Null for disabled Details button.

		try {
			monitor.enter();

			PopupParams popupParams = new PopupParams(iconID, title, text, details,
					relatedObjects);
			historyList.add(popupParams);
			if (currentPopupIndex < 0) {
				create(display, popupParams, true);
			}
		} catch (Exception e) {
			Logger.log(new LogEvent(LogIDs.GUI, "Mr. Slidey Init", e));
			disposeShell(shell);
			Utils.disposeSWTObjects(disposeList);
		} finally {
			monitor.exit();
		}
	
private MessageSlideShell(Display display, PopupParams popupParams, boolean bSlide)

		create(display, popupParams, bSlide);
	
Methods Summary
private voidaddMouseTrackListener(Composite parent, MouseTrackListener listener)
Adds mousetracklistener to composite and all it's children

param
parent Composite to start at
param
listener Listener to add

		if (parent == null || listener == null || parent.isDisposed())
			return;

		parent.addMouseTrackListener(listener);
		Control[] children = parent.getChildren();
		for (int i = 0; i < children.length; i++) {
			Control control = children[i];
			if (control instanceof Composite)
				addMouseTrackListener((Composite) control, listener);
			else
				control.addMouseTrackListener(listener);
		}
	
private voidaddPaintListener(Composite parent, PaintListener listener, Color colorBG, boolean childrenOnly)

		if (parent == null || listener == null || parent.isDisposed())
			return;

		if (!childrenOnly) {
			parent.addPaintListener(listener);
			parent.setBackground(colorBG);
		}

		Control[] children = parent.getChildren();
		for (int i = 0; i < children.length; i++) {
			Control control = children[i];

			control.addPaintListener(listener);
			control.setBackground(colorBG);

			if (control instanceof Composite)
				addPaintListener((Composite) control, listener, colorBG, true);
		}
	
private voidcreate(Display display, org.gudy.azureus2.ui.swt.shells.MessageSlideShell$PopupParams popupParams, boolean bSlide)


		firstUnreadMessage = -1; // Reset the last read message counter.

		GridData gridData;
		int shellWidth;
		int style = SWT.ON_TOP;

		boolean bDisableSliding = COConfigurationManager.getBooleanParameter("GUI_SWT_DisableAlertSliding");
		if (bDisableSliding) {
			bSlide = false;
			style = SWT.NONE;
		}

		if (DEBUG)
			System.out.println("create " + (bSlide ? "SlideIn" : "") + ";"
					+ historyList.indexOf(popupParams) + ";");

		idxHistory = historyList.indexOf(popupParams);

		// 2 Assertions
		if (idxHistory < 0) {
			System.err.println("Not in popup history list");
			return;
		}

		if (currentPopupIndex == idxHistory) {
			System.err.println("Trying to open already opened!! " + idxHistory);
			return;
		}

		try {
			monitor.enter();
			currentPopupIndex = idxHistory;
		} finally {
			monitor.exit();
		}

		if (DEBUG)
			System.out.println("set currIdx = " + idxHistory);

		sDetails = popupParams.details;

		// Load Images
		// Disable BG Image on OSX
		if (imgPopup == null) {
			if (Constants.isOSX && (SWT.getVersion() < 3221 || !USE_SWT32_BG_SET)) {
				USE_SWT32_BG_SET = false;
				imgPopup = null;
			} else {
				imgPopup = ImageRepository.getImage("popup");
			}
		}
		Rectangle imgPopupBounds;
		if (imgPopup != null) {
			shellWidth = imgPopup.getBounds().width;
			imgPopupBounds = imgPopup.getBounds();
		} else {
			shellWidth = SHELL_DEF_WIDTH;
			imgPopupBounds = null;
		}
		Image imgIcon = null;
		switch (popupParams.iconID) {
			case SWT.ICON_ERROR:
				imgIcon = ImageRepository.getImage("error");
				break;

			case SWT.ICON_WARNING:
				imgIcon = ImageRepository.getImage("warning");
				break;

			case SWT.ICON_INFORMATION:
				imgIcon = ImageRepository.getImage("info");
				break;

			default:
				imgIcon = null;
				break;
		}

		// if there's a link, or the info is non-information,
		// disable timer and mouse watching
		bDelayPaused = UrlUtils.parseHTMLforURL(popupParams.text) != null
				|| popupParams.iconID != SWT.ICON_INFORMATION || !bSlide;
		// Pause the auto-close delay when mouse is over slidey
		// This will be applies to every control
		final MouseTrackAdapter mouseAdapter = bDelayPaused ? null
				: new MouseTrackAdapter() {
					public void mouseEnter(MouseEvent e) {
						bDelayPaused = true;
					}

					public void mouseExit(MouseEvent e) {
						bDelayPaused = false;
					}
				};

		// Create shell & widgets
		if (bDisableSliding) {
			UIFunctionsSWT uiFunctions = UIFunctionsManagerSWT.getUIFunctionsSWT();
			if (uiFunctions != null) {
				Shell mainShell = uiFunctions.getMainShell();
				if (mainShell != null) {
					shell = new Shell(mainShell, style);
				}
			}
		}
		if (shell == null) {
			shell = new Shell(display, style);
		}
		if (USE_SWT32_BG_SET) {
			try {
				shell.setBackgroundMode(SWT.INHERIT_DEFAULT);
			} catch (NoSuchMethodError e) {
				// Ignore
			} catch (NoSuchFieldError e2) {
				// ignore
			}
		}
		Utils.setShellIcon(shell);
		shell.setText(popupParams.title);

		UISkinnableSWTListener[] listeners = UISkinnableManagerSWT.getInstance().getSkinnableListeners(
				MessageSlideShell.class.toString());
		for (int i = 0; i < listeners.length; i++) {
			try {
				listeners[i].skinBeforeComponents(shell, this, popupParams.relatedTo);
			} catch (Exception e) {
				Debug.out(e);
			}
		}

		FormLayout shellLayout = new FormLayout();
		shell.setLayout(shellLayout);

		cShell = new Composite(shell, SWT.NULL);
		GridLayout layout = new GridLayout(3, false);
		cShell.setLayout(layout);

		FormData formData = new FormData();
		formData.left = new FormAttachment(0, 0);
		formData.right = new FormAttachment(100, 0);
		cShell.setLayoutData(formData);

		Label lblIcon = new Label(cShell, SWT.NONE);
		lblIcon.setImage(imgIcon);
		lblIcon.setLayoutData(new GridData());

		Label lblTitle = new Label(cShell, SWT.getVersion() < 3100 ? SWT.NONE
				: SWT.WRAP);
		gridData = new GridData(GridData.FILL_HORIZONTAL);
		if (SWT.getVersion() < 3100)
			gridData.widthHint = 140;
		lblTitle.setLayoutData(gridData);
		lblTitle.setForeground(display.getSystemColor(SWT.COLOR_BLACK));
		lblTitle.setText(popupParams.title);
		FontData[] fontData = lblTitle.getFont().getFontData();
		fontData[0].setStyle(SWT.BOLD);
		fontData[0].setHeight((int) (fontData[0].getHeight() * 1.5));
		Font boldFont = new Font(display, fontData);
		disposeList.add(boldFont);
		lblTitle.setFont(boldFont);

		final Button btnDetails = new Button(cShell, SWT.TOGGLE);
		btnDetails.setForeground(display.getSystemColor(SWT.COLOR_BLACK));
		Messages.setLanguageText(btnDetails, "popup.error.details");
		gridData = new GridData();
		btnDetails.setLayoutData(gridData);
		btnDetails.addListener(SWT.MouseUp, new Listener() {
			public void handleEvent(Event arg0) {
				try {
					boolean bShow = btnDetails.getSelection();
					if (bShow) {
						Shell detailsShell = new Shell(display, SWT.BORDER | SWT.ON_TOP);
						Utils.setShellIcon(detailsShell);
						detailsShell.setLayout(new FillLayout());
						StyledText textDetails = new StyledText(detailsShell, SWT.READ_ONLY
								| SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER);
						textDetails.setBackground(display.getSystemColor(SWT.COLOR_LIST_BACKGROUND));
						textDetails.setForeground(display.getSystemColor(SWT.COLOR_LIST_FOREGROUND));
						textDetails.setWordWrap(true);
						textDetails.setText(sDetails);
						detailsShell.layout();
						Rectangle shellBounds = shell.getBounds();
						detailsShell.setBounds(shellBounds.x + shellBounds.width
								- DETAILS_WIDTH, shellBounds.y - DETAILS_HEIGHT, DETAILS_WIDTH,
								DETAILS_HEIGHT);
						detailsShell.open();
						shell.setData("detailsShell", detailsShell);
						shell.addDisposeListener(new DisposeListener() {
							public void widgetDisposed(DisposeEvent e) {
								Shell detailsShell = (Shell) shell.getData("detailsShell");
								if (detailsShell != null && !detailsShell.isDisposed()) {
									detailsShell.dispose();
								}
							}
						});

						// disable auto-close on opening of details
						bDelayPaused = true;
						removeMouseTrackListener(shell, mouseAdapter);
					} else {
						Shell detailsShell = (Shell) shell.getData("detailsShell");
						if (detailsShell != null && !detailsShell.isDisposed()) {
							detailsShell.dispose();
						}
					}
				} catch (Exception e) {
					Logger.log(new LogEvent(LogIDs.GUI, "Mr. Slidey DetailsButton", e));
				}
			}
		});

		createLinkLabel(cShell, true, popupParams);

		lblCloseIn = new Label(cShell, SWT.TRAIL);
		lblCloseIn.setForeground(display.getSystemColor(SWT.COLOR_BLACK));
		// Ensure computeSize computes for 2 lined label
		lblCloseIn.setText("\n");
		gridData = new GridData(SWT.FILL, SWT.TOP, true, false);
		gridData.horizontalSpan = 3;
		lblCloseIn.setLayoutData(gridData);

		final Composite cButtons = new Composite(cShell, SWT.NULL);
		GridLayout gridLayout = new GridLayout();
		gridLayout.marginHeight = 0;
		gridLayout.marginWidth = 0;
		gridLayout.verticalSpacing = 0;
		if (Constants.isOSX)
			gridLayout.horizontalSpacing = 0;
		gridLayout.numColumns = (idxHistory > 0) ? 3 : 2;
		cButtons.setLayout(gridLayout);
		gridData = new GridData(GridData.HORIZONTAL_ALIGN_END
				| GridData.VERTICAL_ALIGN_CENTER);
		gridData.horizontalSpan = 3;
		cButtons.setLayoutData(gridData);

		btnHideAll = new Button(cButtons, SWT.PUSH);
		Messages.setLanguageText(btnHideAll, "popup.error.hideall");
		btnHideAll.setVisible(false);
		btnHideAll.setForeground(display.getSystemColor(SWT.COLOR_BLACK));
		// XXX SWT.Selection doesn't work on latest GTK (2.8.17) & SWT3.2 for ON_TOP
		btnHideAll.addListener(SWT.MouseUp, new Listener() {
			public void handleEvent(Event arg0) {
				cButtons.setEnabled(false);

				shell.dispose();
			}
		});

		if (idxHistory > 0) {
			final Button btnPrev = new Button(cButtons, SWT.PUSH);
			btnPrev.setForeground(display.getSystemColor(SWT.COLOR_BLACK));
			btnPrev.setText(MessageText.getString("popup.previous", new String[] {
				"" + idxHistory
			}));
			btnPrev.addListener(SWT.MouseUp, new Listener() {
				public void handleEvent(Event arg0) {
					disposeShell(shell);
					int idx = historyList.indexOf(popupParams) - 1;
					if (idx >= 0) {
						PopupParams item = (PopupParams) historyList.get(idx);
						showPopup(display, item, false);
						disposeShell(shell);
					}
				}
			});
		}

		btnNext = new Button(cButtons, SWT.PUSH);
		btnNext.setForeground(display.getSystemColor(SWT.COLOR_BLACK));
		int numAfter = historyList.size() - idxHistory - 1;
		setButtonNextText(numAfter);

		btnNext.addListener(SWT.MouseUp, new Listener() {
			public void handleEvent(Event arg0) {
				if (DEBUG)
					System.out.println("Next Pressed");

				if (idxHistory + 1 < historyList.size()) {
					showPopup(display, (PopupParams) historyList.get(idxHistory + 1),
							false);
				}

				disposeShell(shell);
			}
		});

		// Image has gap for text at the top (with image at bottom left)
		// trim top to height of shell 
		Point bestSize = cShell.computeSize(shellWidth, SWT.DEFAULT);
		if (bestSize.y < SHELL_MIN_HEIGHT)
			bestSize.y = SHELL_MIN_HEIGHT;
		else if (bestSize.y > SHELL_MAX_HEIGHT) {
			bestSize.y = SHELL_MAX_HEIGHT;
			if (sDetails == null) {
				sDetails = popupParams.text;
			} else {
				sDetails = popupParams.text + "\n===============\n" + sDetails;
			}
		}

		if (imgPopup != null) {
			// no text on the frog in the bottom left
			int bottomHeight = cButtons.computeSize(SWT.DEFAULT, SWT.DEFAULT).y
					+ lblCloseIn.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
			if (bottomHeight < 50)
				bestSize.y += 50 - bottomHeight;

			final Image imgBackground = new Image(display, bestSize.x, bestSize.y);

			disposeList.add(imgBackground);
			GC gc = new GC(imgBackground);
			int dstY = imgPopupBounds.height - bestSize.y;
			if (dstY < 0)
				dstY = 0;
			gc.drawImage(imgPopup, 0, dstY, imgPopupBounds.width,
					imgPopupBounds.height - dstY, 0, 0, bestSize.x, bestSize.y);
			gc.dispose();

			boolean bAlternateDrawing = true;
			if (USE_SWT32_BG_SET) {
				try {
					shell.setBackgroundImage(imgBackground);
					bAlternateDrawing = false;
				} catch (NoSuchMethodError e) {
				}
			}

			if (bAlternateDrawing) {
				// Drawing of BG Image for pre SWT 3.2

				cShell.addPaintListener(new PaintListener() {
					public void paintControl(PaintEvent e) {
						e.gc.drawImage(imgBackground, e.x, e.y, e.width, e.height, e.x,
								e.y, e.width, e.height);
					}
				});

				Color colorBG = display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
				final RGB bgRGB = colorBG.getRGB();

				PaintListener paintListener = new PaintListener() {
					// OSX: copyArea() causes a paint event, resulting in recursion
					boolean alreadyPainting = false;

					public void paintControl(PaintEvent e) {
						if (alreadyPainting || e.width <= 0 || e.height <= 0) {
							return;
						}

						alreadyPainting = true;

						try {
							Rectangle bounds = ((Control) e.widget).getBounds();

							Image img = new Image(display, e.width, e.height);
							e.gc.copyArea(img, e.x, e.y);

							e.gc.drawImage(imgBackground, -bounds.x, -bounds.y);

							// Set the background color to invisible.  img.setBackground
							// doesn't work, so change transparentPixel directly and roll
							// a new image
							ImageData data = img.getImageData();
							data.transparentPixel = data.palette.getPixel(bgRGB);
							Image imgTransparent = new Image(display, data);

							// This is an alternative way of setting the transparency.
							// Probably much slower

							//int bgIndex = data.palette.getPixel(bgRGB);
							//ImageData transparencyMask = data.getTransparencyMask();
							//for (int y = 0; y < data.height; y++) {
							//	for (int x = 0; x < data.width; x++) {
							//		if (bgIndex == data.getPixel(x, y))
							//			transparencyMask.setPixel(x, y, 0);
							//	}
							//}
							//
							//Image imgTransparent = new Image(display, data, transparencyMask);

							e.gc.drawImage(imgTransparent, 0, 0, e.width, e.height, e.x, e.y,
									e.width, e.height);

							img.dispose();
							imgTransparent.dispose();
						} finally {
							alreadyPainting = false;
						}
					}
				};

				shell.setBackground(colorBG);
				cShell.setBackground(colorBG);
				addPaintListener(cShell, paintListener, colorBG, true);
			}
		}

		Rectangle bounds = null;
		try {
			UIFunctionsSWT uiFunctions = UIFunctionsManagerSWT.getUIFunctionsSWT();
			if (uiFunctions != null) {
				Shell mainShell = uiFunctions.getMainShell();
				if (mainShell != null) {
					bounds = mainShell.getMonitor().getClientArea();
				}
			} else {
				Shell shell = display.getActiveShell();
				if (shell != null) {
					bounds = shell.getMonitor().getClientArea();
				}
			}
			if (bounds == null) {
				bounds = shell.getMonitor().getClientArea();
			}
		} catch (Exception e) {
		}
		if (bounds == null) {
			bounds = display.getClientArea();
		}

		Rectangle endBounds;
		if (bDisableSliding) {
			endBounds = new Rectangle(((bounds.x + bounds.width) / 2)
					- (bestSize.x / 2), ((bounds.y + bounds.height) / 2)
					- (bestSize.y / 2), bestSize.x, bestSize.y);
		} else {
			int boundsX2 = bounds.x + bounds.width;
			int boundsY2 = bounds.y + bounds.height;
			endBounds = shell.computeTrim(boundsX2 - bestSize.x, boundsY2
					- bestSize.y, bestSize.x, bestSize.y);

			// bottom and right trim will be off the edge, calulate this trim
			// and adjust it up and left (trim may not be the same size on all sides)
			int diff = (endBounds.x + endBounds.width) - boundsX2;
			if (diff >= 0)
				endBounds.x -= diff + EDGE_GAP;
			diff = (endBounds.y + endBounds.height) - boundsY2;
			if (diff >= 0) {
				endBounds.y -= diff + EDGE_GAP;
			}
			//System.out.println("best" + bestSize + ";mon" + bounds + ";end" + endBounds);
		}

		FormData data = new FormData(bestSize.x, bestSize.y);
		cShell.setLayoutData(data);

		btnDetails.setVisible(sDetails != null);
		if (sDetails == null) {
			gridData = new GridData();
			gridData.widthHint = 0;
			btnDetails.setLayoutData(gridData);
		}
		shell.layout();

		btnNext.setFocus();
		shell.addDisposeListener(new DisposeListener() {
			public void widgetDisposed(DisposeEvent e) {
				Utils.disposeSWTObjects(disposeList);

				if (currentPopupIndex == idxHistory) {
					if (DEBUG)
						System.out.println("Clear #" + currentPopupIndex + "/" + idxHistory);
					try {
						monitor.enter();
						currentPopupIndex = -1;
					} finally {
						monitor.exit();
					}
				}
			}
		});

		shell.addListener(SWT.Traverse, new Listener() {
			public void handleEvent(Event event) {
				if (event.detail == SWT.TRAVERSE_ESCAPE) {
					disposeShell(shell);
					event.doit = false;
				}
			}
		});

		if (mouseAdapter != null)
			addMouseTrackListener(shell, mouseAdapter);

		for (int i = 0; i < listeners.length; i++) {
			try {
				listeners[i].skinAfterComponents(shell, this, popupParams.relatedTo);
			} catch (Exception e) {
				Debug.out(e);
			}
		}

		runPopup(endBounds, idxHistory, bSlide);
	
private voidcreateLinkLabel(Composite shell, boolean tryLinkIfURLs, org.gudy.azureus2.ui.swt.shells.MessageSlideShell$PopupParams popupParams)

param
shell2
param
b
since
3.0.0.9


		Matcher matcher = Pattern.compile(REGEX_URLHTML, Pattern.CASE_INSENSITIVE).matcher(
				popupParams.text);
		boolean hasHTML = matcher.find();
		if (tryLinkIfURLs && hasHTML) {
			try {
				Link linkLabel = new Link(cShell, SWT.WRAP);
				GridData gridData = new GridData(GridData.FILL_BOTH);
				gridData.horizontalSpan = 3;
				linkLabel.setLayoutData(gridData);
				linkLabel.setForeground(shell.getDisplay().getSystemColor(
						SWT.COLOR_BLACK));
				linkLabel.setText(popupParams.text);
				linkLabel.addSelectionListener(new SelectionAdapter() {
					public void widgetSelected(SelectionEvent e) {
						if (e.text.endsWith(".torrent"))
							TorrentOpener.openTorrent(e.text);
						else
							Utils.launch(e.text);
					}
				});

				String tooltip = null;
				matcher.reset();
				while (matcher.find()) {
					if (tooltip == null)
						tooltip = "";
					else
						tooltip += "\n";
					tooltip += matcher.group(2) + ": " + matcher.group(1);
				}
				linkLabel.setToolTipText(tooltip);
			} catch (Throwable t) {
				createLinkLabel(shell, false, popupParams);
			}
		} else {
			// 3.0
			Label linkLabel = new Label(cShell, SWT.WRAP);
			GridData gridData = new GridData(GridData.FILL_BOTH);
			gridData.horizontalSpan = 3;
			linkLabel.setLayoutData(gridData);

			//<a href="http://atorre.s">test</A> and <a href="http://atorre.s">test2</A>

			if (hasHTML) {
  			matcher.reset();
  			popupParams.text = matcher.replaceAll("$2 ($1)");
  
  			if (sDetails == null) {
  				sDetails = popupParams.text;
  			} else {
  				sDetails = popupParams.text + "\n---------\n" + sDetails;
  			}
			}

			linkLabel.setForeground(shell.getDisplay().getSystemColor(SWT.COLOR_BLACK));
			linkLabel.setText(popupParams.text);
		}
	
public static voiddisplayLastMessage(Display display, boolean last_unread)

		display.asyncExec(new AERunnable() {
			public void runSupport() {
				if (historyList.isEmpty()) {
					return;
				}
				if (currentPopupIndex >= 0) {
					return;
				} // Already being displayed.
				int msg_index = firstUnreadMessage;
				if (!last_unread || msg_index == -1) {
					msg_index = historyList.size() - 1;
				}
				new MessageSlideShell(display,
						(PopupParams) historyList.get(msg_index), true);
			}
		});
	
private voiddisposeShell(Shell shell)

		if (shell == null || shell.isDisposed())
			return;

		Utils.execSWTThread(new AERunnable() {
			public void runSupport() {
				shell.dispose();
			}
		});
	
public ImagegetImgPopup()

return
the imgPopup

		return imgPopup;
	
public static voidmain(java.lang.String[] args)
Test

param
args

		final Display display = Display.getDefault();

		Shell shell = new Shell(display, SWT.DIALOG_TRIM);
		shell.setLayout(new FillLayout());
		Button btn = new Button(shell, SWT.PUSH);
		btn.addListener(SWT.Selection, new Listener() {
			public void handleEvent(Event event) {
				test(display);
			}
		});
		shell.open();

		while (!shell.isDisposed()) {
			if (!display.readAndDispatch()) {
				display.sleep();
			}
		}
	
public static voidrecordMessage(int iconID, java.lang.String title, java.lang.String text, java.lang.String details, java.lang.Object[] relatedTo)
Adds this message to the slide shell without forcing it to be displayed.

param
relatedTo

		try {
			monitor.enter();
			historyList.add(new PopupParams(iconID, title, text, details, relatedTo));
			if (firstUnreadMessage == -1) {
				firstUnreadMessage = historyList.size() - 1;
			}
		} finally {
			monitor.exit();
		}
	
private voidremoveMouseTrackListener(Composite parent, MouseTrackListener listener)
removes mousetracklistener from composite and all it's children

param
parent Composite to start at
param
listener Listener to remove

		if (parent == null || listener == null || parent.isDisposed())
			return;

		Control[] children = parent.getChildren();
		for (int i = 0; i < children.length; i++) {
			Control control = children[i];
			control.removeMouseTrackListener(listener);
			if (control instanceof Composite)
				removeMouseTrackListener((Composite) control, listener);
		}
	
private voidrunPopup(Rectangle endBounds, int idx, boolean bSlide)
Start the slid in, wait specified time while notifying user of impending auto-close, then slide out. Run on separate thread, so this method returns immediately

param
endBounds end location and size wanted
param
idx Index in historyList of popup (Used to calculate # prev, next)
param
bSlide Whether to slide in, or show immediately

		if (shell == null || shell.isDisposed())
			return;

		final Display display = shell.getDisplay();

		if (DEBUG)
			System.out.println("runPopup " + idx + ((bSlide) ? " Slide" : " Instant"));

		AEThread thread = new AEThread("Slidey", true) {
			private final static int PAUSE = 500;

			public void runSupport() {
				if (shell == null || shell.isDisposed())
					return;

				if (bSlide) {
					new SlideShell(shell, SWT.UP, endBounds).run();
				} else {
					Utils.execSWTThread(new AERunnable() {

						public void runSupport() {
							shell.setBounds(endBounds);
							shell.open();
						}
					});
				}

				int delayLeft = COConfigurationManager.getIntParameter("Message Popup Autoclose in Seconds") * 1000;
				final boolean autohide = (delayLeft != 0);

				long lastDelaySecs = 0;
				int lastNumPopups = -1;
				while ((!autohide || bDelayPaused || delayLeft > 0)
						&& !shell.isDisposed()) {
					int delayPausedOfs = (bDelayPaused ? 1 : 0);
					final long delaySecs = Math.round(delayLeft / 1000.0)
							+ delayPausedOfs;
					final int numPopups = historyList.size();
					if (lastDelaySecs != delaySecs || lastNumPopups != numPopups) {
						lastDelaySecs = delaySecs;
						lastNumPopups = numPopups;
						shell.getDisplay().asyncExec(new AERunnable() {
							public void runSupport() {
								String sText = "";

								if (lblCloseIn == null || lblCloseIn.isDisposed())
									return;

								lblCloseIn.setRedraw(false);
								if (!bDelayPaused && autohide)
									sText += MessageText.getString("popup.closing.in",
											new String[] {
												String.valueOf(delaySecs)
											});

								int numPopupsAfterUs = numPopups - idx - 1;
								boolean bHasMany = numPopupsAfterUs > 0;
								if (bHasMany) {
									sText += "\n";
									sText += MessageText.getString("popup.more.waiting",
											new String[] {
												String.valueOf(numPopupsAfterUs)
											});
								}

								lblCloseIn.setText(sText);

								if (btnHideAll.getVisible() != bHasMany) {
									cShell.setRedraw(false);
									btnHideAll.setVisible(bHasMany);
									lblCloseIn.getParent().layout(true);
									cShell.setRedraw(true);
								}

								setButtonNextText(numPopupsAfterUs);

								// Need to redraw to cause a paint
								lblCloseIn.setRedraw(true);
							}
						});
					}

					if (!bDelayPaused)
						delayLeft -= PAUSE;
					try {
						Thread.sleep(PAUSE);
					} catch (InterruptedException e) {
						delayLeft = 0;
					}
				}

				if (this.isInterrupted()) {
					// App closedown likely, boot out ASAP
					disposeShell(shell);
					return;
				}

				// Assume that if the shell was disposed during loop, it's on purpose
				// and that it has handled whether to show the next popup or not
				if (shell != null && !shell.isDisposed()) {
					if (idx + 1 < historyList.size()) {
						showPopup(display, (PopupParams) historyList.get(idx + 1), true);
					}

					// slide out current popup
					if (bSlide)
						new SlideShell(shell, SWT.RIGHT).run();

					disposeShell(shell);
				}
			}
		};
		thread.start();
	
private voidsetButtonNextText(int numAfter)

param
numAfter

		if (numAfter <= 0)
			Messages.setLanguageText(btnNext, "popup.error.hide");
		else
			Messages.setLanguageText(btnNext, "popup.next", new String[] {
				"" + numAfter
			});
		cShell.layout(true);
	
public voidsetImgPopup(Image imgPopup)

param
imgPopup the imgPopup to set

		this.imgPopup = imgPopup;
	
private voidshowPopup(Display display, org.gudy.azureus2.ui.swt.shells.MessageSlideShell$PopupParams item, boolean bSlide)
Show the popup with the specified parameters.

param
display Display to show on
param
item popup to display. Must already exist in historyList
param
bSlide Whether to slide in or show immediately

		Utils.execSWTThread(new AERunnable() {
			public void runSupport() {
				new MessageSlideShell(display, item, bSlide);
			}
		});
	
public static java.lang.StringstripOutHyperlinks(java.lang.String message)

		return Pattern.compile(REGEX_URLHTML, Pattern.CASE_INSENSITIVE).matcher(
				message).replaceAll("$2");
	
public static voidtest(Display display)


		ImageRepository.loadImages(display);

		String title = "This is the title that never ends, never ends!";
		String text = "This is a very long message with lots of information and "
				+ "stuff you really should read.  Are you still reading? Good, because "
				+ "reading <a href=\"http://moo.com\">stimulates</a> the mind and grows "
				+ "hair on your chest.\n\n  Unless you are a girl, then it makes you want "
				+ "to read more.  It's an endless cycle of reading that will never "
				+ "end.  Cursed is the long text that is in this test and may it fill"
				+ "every last line of the shell until there is no more.";

		// delay before running, to give eclipse time to finish up it's work
		// Otherwise, Mr Slidey is jumpy
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		//		MessagePopupShell shell = new MessagePopupShell(display,
		//				MessagePopupShell.ICON_INFO, "Title", text, "Details");

		new MessageSlideShell(display, SWT.ICON_INFORMATION,
				"Simple. . . . . . . . . . . . . . . . . . .", "Simple", (String) null);

		new MessageSlideShell(display, SWT.ICON_INFORMATION, title + "1", text,
				"Details: " + text);

		new MessageSlideShell(display, SWT.ICON_INFORMATION, "ShortTitle2",
				"ShortText", "Details");
		MessageSlideShell.waitUntilClosed();

		new MessageSlideShell(display, SWT.ICON_INFORMATION, "ShortTitle3",
				"ShortText", (String) null);
		for (int x = 0; x < 10; x++)
			text += "\n\n\n\n\n\n\n\nWow";
		new MessageSlideShell(display, SWT.ICON_INFORMATION, title + "4", text,
				"Details");

		new MessageSlideShell(display, SWT.ICON_ERROR, title + "5", text,
				(String) null);

		MessageSlideShell.waitUntilClosed();
	
public static voidwaitUntilClosed()
Waits until all slideys are closed before returning to caller.

		if (currentPopupIndex < 0)
			return;

		Display display = Display.getCurrent();
		while (currentPopupIndex >= 0) {
			if (!display.readAndDispatch())
				display.sleep();
		}