Package org.gudy.azureus2.ui.swt.shells

Source Code of org.gudy.azureus2.ui.swt.shells.MessageSlideShell

/*
* Created on Mar 7, 2006 10:42:32 PM
* Copyright (C) 2006 Aelitis, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*
* AELITIS, SAS au capital de 46,603.30 euros
* 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
*/
package org.gudy.azureus2.ui.swt.shells;

import java.util.ArrayList;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;

import org.gudy.azureus2.core3.config.COConfigurationManager;
import org.gudy.azureus2.core3.internat.MessageText;
import org.gudy.azureus2.core3.logging.LogEvent;
import org.gudy.azureus2.core3.logging.LogIDs;
import org.gudy.azureus2.core3.logging.Logger;
import org.gudy.azureus2.core3.util.*;
import org.gudy.azureus2.ui.swt.Messages;
import org.gudy.azureus2.ui.swt.Utils;
import org.gudy.azureus2.ui.swt.mainwindow.ClipboardCopy;
import org.gudy.azureus2.ui.swt.shells.GCStringPrinter.URLInfo;

import com.aelitis.azureus.ui.swt.*;
import com.aelitis.azureus.ui.swt.imageloader.ImageLoader;
import com.aelitis.azureus.ui.swt.utils.ColorCache;

/**
*
* +=====================================+
* | +----+                              |
* | |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
*
*/
public class MessageSlideShell
{
  private static final boolean DEBUG = false;

  /** Slide until there's this much gap between shell and edge of screen */
  private final static int EDGE_GAP = 0;

  /** Width used when BG image can't be loaded */
  private final static int SHELL_DEF_WIDTH = 280;

  /** Standard height of the shell.  Shell may grow depending on text */
  private final static int SHELL_MIN_HEIGHT = 150;

  /** Maximimum height of popup.  If text is too long, the full text will be
   * put into details.
   */
  private final static int SHELL_MAX_HEIGHT = 330;

  /** Width of the details shell */
  private final static int DETAILS_WIDTH = 550;

  /** Height of the details shell */
  private final static int DETAILS_HEIGHT = 180;

  /** Synchronization for popupList */
  private final static AEMonitor monitor = new AEMonitor("slidey_mon");

  /** List of all popups ever created */
  private static ArrayList<PopupParams> historyList = new ArrayList<PopupParams>();
 
  /** Current popup being displayed */
  private static int currentPopupIndex = -1;

  /** Index of first message which the user has not seen (index) - set to -1 if we don't care. :) **/
  private static int firstUnreadMessage = -1;

  /** Shell for popup */
  private Shell shell;

  /** Composite in shell */
  private Composite cShell;

  /** popup could and closing in xx seconds label */
  private Label lblCloseIn;

  /** Button that hides all slideys in the popupList.  Visible only when there's
   * more than 1 slidey
   */
  private Button btnHideAll;

  /** Button to move to next message.  Text changes from "Hide" to "Next"
   * appropriately.
   */
  private Button btnNext;

  /** paused state of auto-close delay */
  private boolean bDelayPaused = false;

  /** List of SWT objects needing disposal */
  private ArrayList disposeList = new ArrayList();

  /** Text to put into details popup */
  private String sDetails;

  /** Position this popup is in the history list */
  private int idxHistory;

  private Image imgPopup;

  protected Color colorURL;

  private Color colorFG;

  private int shellWidth;

  /** 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
   */
  public MessageSlideShell(Display display, int iconID, String keyPrefix,
      String details, String[] textParams, int timeoutSecs ) {
    this(display, iconID, MessageText.getString(keyPrefix + ".title"),
        MessageText.getString(keyPrefix + ".text", textParams), details, timeoutSecs);
  }

  /**
   *
   * @param display
   * @param iconID
   * @param keyPrefix
   * @param details
   * @param textParams
   * @param relatedObjects
   * @param timeoutSecs = -1 -> use default timeout, 0 -> no timeout, other -> timeout in secs
   */
  public MessageSlideShell(Display display, int iconID, String keyPrefix,
      String details, String[] textParams, Object[] relatedObjects,
      int timeoutSecs ) {
    this(display, iconID, MessageText.getString(keyPrefix + ".title"),
        MessageText.getString(keyPrefix + ".text", textParams), details,
        relatedObjects, timeoutSecs);
  }

  public MessageSlideShell(Display display, int iconID, String title,
      String text, String details, int timeoutSecs) {
    this(display, iconID, title, text, details, null, timeoutSecs);
  }

  /**
   * 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.
   * @param timeoutSecs = -1 -> use default timeout, 0 -> no timeout, other -> timeout in secs
   */
  public MessageSlideShell(Display display, int iconID, String title,
      String text, String details, Object[] relatedObjects, int timeoutSecs ) {
    try {
      monitor.enter();

      PopupParams popupParams = new PopupParams(iconID, title, text, details,
          relatedObjects, timeoutSecs );
      addToHistory(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();
    }
  }

  /**
   * @param popupParams
   *
   * @since 4.1.0.5
   */
  private static void addToHistory(PopupParams popupParams) {
    monitor.enter();
    try {
      historyList.add(popupParams);
    } finally {
      monitor.exit();
    }
  }

  private MessageSlideShell(Display display, PopupParams popupParams,
      boolean bSlide) {
    create(display, popupParams, bSlide);
  }

  public static void displayLastMessage(final Display display,
      final 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, historyList.get(msg_index), true);
      }
    });
  }

  /**
   * Adds this message to the slide shell without forcing it to be displayed.
   * @param relatedTo
   */
  public static void recordMessage(int iconID, String title, String text,
      String details, Object[] relatedTo, int timeoutSecs ) {
    try {
      monitor.enter();
      addToHistory(new PopupParams(iconID, title, text, details, relatedTo, timeoutSecs));
      if (firstUnreadMessage == -1) {
        firstUnreadMessage = historyList.size() - 1;
      }
    } finally {
      monitor.exit();
    }
  }

  private void create(final Display display, final PopupParams popupParams,
      boolean bSlide) {

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

    GridData gridData;
    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
    Image imgIcon = popupParams.iconID <= 0 ? null
        : display.getSystemImage(popupParams.iconID);

    /*
     * If forceTimer is true then we always show the counter for auto-closing the shell;
     * otherwise proceed to the more fine-grained logic
     */
      // if there's a link, or the info is non-information,
      // disable timer and mouse watching

    bDelayPaused = 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);
    }
    try {
      shell.setBackgroundMode(SWT.INHERIT_DEFAULT);
    } catch (NoSuchMethodError e) {
      // Ignore
    } catch (NoSuchFieldError e2) {
      // ignore
    }
    Utils.setShellIcon(shell);
    if (popupParams.title != null) {
      shell.setText(popupParams.title);
    }

    // Disable BG Image on OSX
    if (imgPopup == null) {
      imgPopup = ImageLoader.getInstance().getImage("popup");
      shell.addDisposeListener(new DisposeListener() {
        public void widgetDisposed(DisposeEvent e) {
          ImageLoader.getInstance().releaseImage("popup");
        }
      });
    }
    Rectangle imgPopupBounds;
    if (imgPopup != null) {
      shellWidth = imgPopup.getBounds().width;
     
      /*
       * KN: The buttons at the bottom of the shell has an automatic horizontal spacing on OSX;
       * compensating the shellWidth so that the buttons will not overlap the image at the bottom left
       * of the shell
       */
      if (true == Constants.isOSX) {
        shellWidth += 30;
      }
      imgPopupBounds = imgPopup.getBounds();
    } else {
      shellWidth = SHELL_DEF_WIDTH;
      imgPopupBounds = null;
    }

    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);
      }
    }

    if (colorFG == null) {
      colorFG = display.getSystemColor(SWT.COLOR_BLACK);
    }

    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());

    if (popupParams.title != null) {
      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(colorFG);
      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(colorFG);
    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, popupParams);

    lblCloseIn = new Label(cShell, SWT.TRAIL);
    lblCloseIn.setForeground(colorFG);
    // 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, 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;
      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);
      }
    }

    int  timeoutSecs;
   
    if ( popupParams.timeoutSecs < 0 ){
   
      timeoutSecs = COConfigurationManager.getIntParameter("Message Popup Autoclose in Seconds");
     
    }else{
     
      timeoutSecs = popupParams.timeoutSecs;
    }
   
    runPopup(endBounds, idxHistory, bSlide, timeoutSecs );
  }

  /**
   * @param shell2
   * @param b
   *
   * @since 3.0.0.9
   */
  private void createLinkLabel(Composite shell, final PopupParams popupParams) {

    final Canvas canvas = new Canvas(shell, SWT.None) {
      public Point computeSize(int wHint, int hHint, boolean changed) {
        Rectangle area = new Rectangle(0, 0, shellWidth, 5000);
        GC gc = new GC(this);
        GCStringPrinter sp = new GCStringPrinter(gc, popupParams.text, area,
            true, false, SWT.WRAP | SWT.TOP);
        sp.calculateMetrics();
        gc.dispose();
        Point size = sp.getCalculatedSize();
        return size;
      }
    };

    Listener l = new Listener() {
      GCStringPrinter sp;

      public void handleEvent(Event e) {
        if (e.type == SWT.Paint) {
          Rectangle area = canvas.getClientArea();
          sp = new GCStringPrinter(e.gc, popupParams.text, area, true, false,
              SWT.WRAP | SWT.TOP);
          sp.setUrlColor(ColorCache.getColor(e.gc.getDevice(), "#0000ff"));
          if (colorURL != null) {
            sp.setUrlColor(colorURL);
          }
          if (colorFG != null) {
            e.gc.setForeground(colorFG);
          }
          sp.printString();
        } else if (e.type == SWT.MouseMove) {
          if (sp != null) {
            URLInfo hitUrl = sp.getHitUrl(e.x, e.y);
            if (hitUrl != null) {
              canvas.setCursor(canvas.getDisplay().getSystemCursor(
                  SWT.CURSOR_HAND));
              canvas.setToolTipText(hitUrl.url);
            } else {
              canvas.setCursor(canvas.getDisplay().getSystemCursor(
                  SWT.CURSOR_ARROW));
              canvas.setToolTipText(null);
            }
          }
        } else if (e.type == SWT.MouseUp) {
          if (sp != null) {
            URLInfo hitUrl = sp.getHitUrl(e.x, e.y);
            if (hitUrl != null && !hitUrl.url.startsWith(":")) {
              Utils.launch(hitUrl.url);
            }
          }
        }
      }
    };
    canvas.addListener(SWT.Paint, l);
    canvas.addListener(SWT.MouseMove, l);
    canvas.addListener(SWT.MouseUp, l);

    ClipboardCopy.addCopyToClipMenu(canvas,
        new ClipboardCopy.copyToClipProvider() {
          public String getText() {
            return (popupParams.title + "\n\n" + popupParams.text);
          }
        });

    GridData gridData = new GridData(GridData.FILL_BOTH);
    gridData.horizontalSpan = 3;
    canvas.setLayoutData(gridData);
  }

  /**
   * @param numAfter
   */
  private void setButtonNextText(int numAfter) {
    if (numAfter <= 0)
      Messages.setLanguageText(btnNext, "popup.error.hide");
    else
      Messages.setLanguageText(btnNext, "popup.next", new String[] {
        "" + numAfter
      });
    cShell.layout(true);
  }

  /**
   * 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
   */
  private void showPopup(final Display display, final PopupParams item,
      final boolean bSlide) {
    Utils.execSWTThread(new AERunnable() {
      public void runSupport() {
        new MessageSlideShell(display, item, bSlide);
      }
    });
  }

  /**
   * Adds mousetracklistener to composite and all it's children
   *
   * @param parent Composite to start at
   * @param listener Listener to add
   */
  private void addMouseTrackListener(Composite parent,
      MouseTrackListener listener) {
    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 void addPaintListener(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);
    }
  }

  /**
   * removes mousetracklistener from composite and all it's children
   *
   * @param parent Composite to start at
   * @param listener Listener to remove
   */
  private void removeMouseTrackListener(Composite parent,
      MouseTrackListener listener) {
    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);
    }
  }

  /**
   * 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
   */
  private void runPopup(final Rectangle endBounds, final int idx,
      final boolean bSlide, final int timeoutSecs ) {
    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 ShellSlider(shell, SWT.UP, endBounds).run();
        } else {
          Utils.execSWTThread(new AERunnable() {

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

        int delayLeft = timeoutSecs * 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, historyList.get(idx + 1), true);
          }

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

          disposeShell(shell);
        }
      }
    };
    thread.start();
  }

  private void disposeShell(final Shell shell) {
    if (shell == null || shell.isDisposed())
      return;

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

  /**
   * Waits until all slideys are closed before returning to caller.
   */
  public static void waitUntilClosed() {
    if (currentPopupIndex < 0)
      return;

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

  public static class PopupParams
  {
    public int iconID;

    public String title;

    public String text;

    public String details;

    public long addedOn;

    public Object[] relatedTo;

    public int  timeoutSecs;
   
    /**
     * @param iconID
     * @param title
     * @param text
     * @param details
     */
    public PopupParams(int iconID, String title, String text, String details, int timeoutSecs ) {
      this.iconID = iconID;
      this.title = title;
      this.text = text;
      this.details = details;
      this.timeoutSecs = timeoutSecs;
      addedOn = System.currentTimeMillis();
    }

    /**
     * @param iconID2
     * @param title2
     * @param text2
     * @param details2
     * @param relatedTo
     */
    public PopupParams(int iconID, String title, String text, String details,
        Object[] relatedTo, int timeoutSecs ) {
      this(iconID, title, text, details, timeoutSecs );
      this.relatedTo = relatedTo;
    }
  }

  /**
   * Test
   *
   * @param args
   */
  public static void main(String[] 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 void test(Display 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, -1);

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

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

    new MessageSlideShell(display, SWT.ICON_INFORMATION, "ShortTitle3",
        "ShortText", (String) null, -1);
    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", -1);

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

    MessageSlideShell.waitUntilClosed();
  }

  /**
   * @return the imgPopup
   */
  public Image getImgPopup() {
    return imgPopup;
  }

  /**
   * @param imgPopup the imgPopup to set
   */
  public void setImgPopup(Image imgPopup) {
    this.imgPopup = imgPopup;
  }

  public Color getUrlColor() {
    return colorURL;
  }

  public void setUrlColor(Color urlColor) {
    this.colorURL = urlColor;
  }

  public Color getColorFG() {
    return colorFG;
  }

  public void setColorFG(Color colorFG) {
    this.colorFG = colorFG;
  }
}
TOP

Related Classes of org.gudy.azureus2.ui.swt.shells.MessageSlideShell

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.