Package com.starlight.ui

Source Code of com.starlight.ui.LongCallDialog$CallThread

/*
* Copyright(c) 2002-2010, Rob Eden
* All rights reserved.
*/
package com.starlight.ui;

import com.starlight.locale.ResourceKey;
import com.starlight.thread.SharedThreadPool;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.applet.Applet;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.lang.reflect.InvocationTargetException;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicReference;


/**
* Class used to keep the GUI responsive while performing (possibly) long tasks.
*/
public class LongCallDialog {
  public static final long DEFAULT_SHOW_DIALOG_DELAY = 500L;

  private static final int DEFAULT_WIDTH = 300;

  /** Timer used for all calls. */
  private static Timer timer = new Timer( false );


  /**
   * Do a Swing-based call in which some action is done outside the EDT and then action
   * is done inside the EDT with the result.
   * <p>
   * This method may be called either from inside or outside the EDT.
   */
  public static <E> void doSwingWork( final Callable<E> outside_edt_callable,
    final ResultHandler<E> edt_result_handler, final ResourceKey<String> dialog_title,
    final ResourceKey<String> message, final Component parent,
    final boolean allow_cancel ) {

    if ( SwingUtilities.isEventDispatchThread() ) {
      SharedThreadPool.INSTANCE.execute( new Runnable() {
        @Override
        public void run() {
          doSwingWork( outside_edt_callable, edt_result_handler,
            dialog_title, message, parent, allow_cancel );
        }
      } );
      return;
    }

    try {
      doCall( new Callable<Void>() {
        @Override
        public Void call() throws Exception {
          final E result = outside_edt_callable.call();

          SwingUtilities.invokeLater( new Runnable() {
            @Override
            public void run() {
              edt_result_handler.run( result );
            }
          });

          return null;
        }
      }, dialog_title, message, parent, DEFAULT_SHOW_DIALOG_DELAY, allow_cancel );
    }
    catch( Throwable t ) {
      if ( t instanceof  ExecutionException ) {
        t = t.getCause();
      }

      final Throwable t_final = t;
      SwingUtilities.invokeLater( new Runnable() {
        @Override
        public void run() {
          edt_result_handler.error( t_final );
        }
      } );
    }
  }



  /**
   * Used to present feedback and leave the UI responsive when making a long network or
   * other time consuming call. This version does not pass back exceptions or return
   * values and so can be called from within the EventDispatchThread (because another
   * thread will be spawned in that case). This will return a FutureResult object which
   * can be used to get the result of the call at a later time.
   *
   * @param callable      The task that might take a significant amount of time.
   * @param dialog_title    The title of the dialog.
   * @param message      The message on the dialog.
   * @param parent      The parent component, if any.
   */
  public static <E> FutureTask<E> doFutureCall( final Callable<E> callable,
    final ResourceKey<String> dialog_title, final ResourceKey<String> message,
    final Component parent ) {

    // Note: CAN be called from the EDT

    Callable<E> inner_callable = new Callable<E>() {
      public E call() throws Exception {
        return doCall( callable, dialog_title, message, parent );
      }
    };

    final FutureTask<E> result = new FutureTask<E>( inner_callable );

    SharedThreadPool.INSTANCE.execute( result );

    return result;
  }


  /**
   * Used to present feedback and leave the UI responsive when making a long network or
   * other time consuming call. This must be called outside of the EventDispatchThread
   * and will throw an IllegalStateException if that's not done.
   *
   * @param callable      The task that might take a significant amount of time.
   * @param dialog_title    The title of the dialog.
   * @param message      The message on the dialog.
   * @param parent      The parent component, if any.
   *
   * @return          The value returned from the Callable.
   *
   * @throws InterruptedException  If the user cancels while the task
   *                       is being queued.
   * @throws java.util.concurrent.ExecutionException  A wrapper exception that's thrown
   *                                      if the Callable throws an exception.
   * @throws IllegalStateException    If called from within the EventDispatchThread.
   */
  public static <E> E doCall( Callable<E> callable, ResourceKey<String> dialog_title,
    ResourceKey<String> message, Component parent )
    throws InterruptedException, ExecutionException {

    return doCall( callable, dialog_title, message, parent, DEFAULT_SHOW_DIALOG_DELAY );
  }

  /**
   * Used to present feedback and leave the UI responsive when making a long network or
   * other time consuming call. This must be called outside of the EventDispatchThread
   * and will throw an IllegalStateException if that's not done.
   *
   * @param callable      The task that might take a significant amount of time.
   * @param dialog_title    The title of the dialog.
   * @param message      The message on the dialog.
   * @param parent      The parent component, if any.
   *
   * @return          The value returned from the Callable.
   *
   * @throws InterruptedException  If the user cancels while the task
   *                       is being queued.
   * @throws java.util.concurrent.ExecutionException  A wrapper exception that's thrown
   *                                      if the Callable throws an exception.
   * @throws IllegalStateException    If called from within the EventDispatchThread.
   */
  public static <E> E doCall( Callable<E> callable, ResourceKey<String> dialog_title,
    ResourceKey<String> message, Component parent, long dialog_display_delay )
    throws InterruptedException, ExecutionException {

    return doCall( callable, dialog_title, message, parent, dialog_display_delay,
      true );
  }

  /**
   * Used to present feedback and leave the UI responsive when making a long network or
   * other time consuming call. This must be called outside of the EventDispatchThread
   * and will throw an IllegalStateException if that's not done.
   *
   * @param callable      The task that might take a significant amount of time.
   * @param dialog_title    The title of the dialog.
   * @param message      The message on the dialog.
   * @param parent      The parent component, if any.
   * @param allow_cancel    True if the cancel button should be enabled. If pressed,
   *               the thread running the task will be hidden.
   *
   * @return          The value returned from the Callable.
   *
   * @throws InterruptedException      If the user cancels while the task
   *                     is being queued.
   * @throws java.util.concurrent.ExecutionException      A wrapper exception that's thrown if the
   *                     Callable throws an exception.
   * @throws IllegalStateException    If called from within the EventDispatchThread.
   */
  public static <E> E doCall( Callable<E> callable,
    final ResourceKey<String> dialog_title,
    final ResourceKey<String> message, final Component parent,
    final long dialog_display_delay, final boolean allow_cancel )
    throws InterruptedException, ExecutionException {

    if ( SwingUtilities.isEventDispatchThread() ) {
      throw new IllegalStateException(
        "Call cannot be made from within the EventDispatchThread." );
    }

    final AtomicReference<ActivityDialog> dialog_slot =
      new AtomicReference<ActivityDialog>();
    final Window window;
    Component root = parent == null ? null : SwingUtilities.getRoot( parent );
    if ( root instanceof Applet || root == null ) window = null;
    else window = (Window) root;

    try {
      SwingUtilities.invokeAndWait( new Runnable() {
        @Override
        public void run() {
          ActivityDialog dialog;
          if ( window instanceof Dialog ) {
            dialog = new ActivityDialog( ( Dialog ) window, dialog_title,
              message, allow_cancel );
          }
          else if ( window instanceof Frame ) {
            dialog = new ActivityDialog( ( Frame ) window, dialog_title,
              message, allow_cancel );
          }
          else {
            dialog = new ActivityDialog( ( Frame ) null, dialog_title,
              message, allow_cancel );
          }
          dialog_slot.set( dialog );
        }
      } );
    }
    catch ( InvocationTargetException e ) {
      throw new ExecutionException( e.getCause() );
    }

    final ActivityDialog dialog = dialog_slot.get();

    TimerTask show_dialog_task = new TimerTask() {
      public void run() {
        dialog.setVisible( true );
      }
    };

    FutureTask<E> future = new FutureTask<E>( callable );
    CallThread<E> call_thread =
      new CallThread<E>( future, dialog, callable.getClass().getName() );
    call_thread.start();

    dialog.setThreadsToInterrupt( new Thread[] { Thread.currentThread(), call_thread } );

    if ( dialog_display_delay > 0 ) {
      timer.schedule( show_dialog_task, dialog_display_delay );
    }
    else {
      // This will block until the dialog is hidden
      show_dialog_task.run();
    }

    try {
      return future.get();
    }
    finally {
      show_dialog_task.cancel();

      SwingUtilities.invokeLater( new Runnable() {
        @Override
        public void run() {
          dialog.setVisible( false );
          dialog.dispose();
        }
      } );
    }
  }


  private static class ActivityDialog extends JDialog implements ActionListener {

    private Thread[] threads_to_interrupt;

    public ActivityDialog( Dialog owner, ResourceKey<String> title,
      ResourceKey<String> message, boolean allow_cancel ) throws HeadlessException {

      super( owner, title.getValue(), ModalityType.DOCUMENT_MODAL );

      init( message, allow_cancel );

      center( owner );
    }

    public ActivityDialog( Frame owner, ResourceKey<String> title,
      ResourceKey<String> message, boolean allow_cancel ) throws HeadlessException {

      super( owner, title.getValue(), ModalityType.DOCUMENT_MODAL );

      init( message, allow_cancel );

      center( owner );
    }


    void setThreadsToInterrupt( Thread[] threads_to_interrupt ) {
      this.threads_to_interrupt = threads_to_interrupt;
    }


    private void init( ResourceKey<String> message, boolean allow_cancel ) {
      setDefaultCloseOperation( JDialog.DO_NOTHING_ON_CLOSE );

      JComponent content_pane = ( JComponent ) getContentPane();

      content_pane.setLayout( new BorderLayout() );

      JLabel label = new MultiLineLabel( message.getValue() );
      label.setBorder( new EmptyBorder( 0, 5, 5, 5 ) );

      JProgressBar prog = new JProgressBar();
      prog.setIndeterminate( true );


      JPanel center_panel = new JPanel( new BorderLayout( 10, 0 ) );
      JLabel icon_label = new JLabel( UIManager.getIcon( "OptionPane.informationIcon" ) );
      center_panel.add( icon_label, BorderLayout.LINE_START );

      JPanel message_panel = new JPanel( new BorderLayout() );
      message_panel.add( label, BorderLayout.CENTER );
      message_panel.add( prog, BorderLayout.PAGE_END );
      center_panel.add( message_panel, BorderLayout.CENTER );
      content_pane.add( center_panel, BorderLayout.CENTER );


      JPanel button_panel = new JPanel( new FlowLayout( FlowLayout.TRAILING ) );
      JButton cancel = new JButton( "Cancel" );

      cancel.setVisible( allow_cancel );
      if ( allow_cancel ) {
        addWindowListener( new WindowAdapter() {
          public void windowClosing( WindowEvent e ) {
            actionPerformed( null );
          }
        } );
      }

      cancel.addActionListener( this );
      button_panel.add( cancel );
      content_pane.add( button_panel, BorderLayout.PAGE_END );

      content_pane.setBorder( new EmptyBorder( 10, 10, 10, 10 ) );

      setResizable( false );

      pack();

      setSize( Math.max( DEFAULT_WIDTH, getWidth() ), getHeight() );
    }


    private void center( Window parent ) {
      if ( parent == null ) {
        WindowKit.centerWindow( this );
      }
      else {
        WindowKit.centerWindowOnWindow( this, parent );
      }
    }


    public void actionPerformed( ActionEvent e ) {
      for( int i = 0; i < threads_to_interrupt.length; i++ ) {
        threads_to_interrupt[ i ].interrupt();
      }
    }
  }


  private static class CallThread<E> extends Thread {
    private final FutureTask<E> future;
    private final JDialog dialog;

    CallThread( FutureTask<E> future, JDialog dialog, String name ) {
      super( "LongCallDialog CallThread: " + name );

      this.future = future;
      this.dialog = dialog;
    }

    public void run() {
      try {
        future.run();
      }
      finally {
        SwingUtilities.invokeLater( new Runnable() {
          public void run() {
            dialog.setVisible( false );
          }
        } );
      }
    }
  }


//  public static void main( String[] args ) throws Exception {
//    String arg = args.length > 0 ? args[ 0 ] : "30000";
//
//    final long time = Long.parseLong( arg );
//
//    doCall( new Callable() {
//      public Object call() throws Exception {
//        Thread.sleep( time );
//        return null;
//      }
//    }, new NxBundleKey( "Test Title" ), new NxBundleKey( "Test Message" ), null, 0,
//      true );
//
//    System.exit( 0 );
//  }
}
TOP

Related Classes of com.starlight.ui.LongCallDialog$CallThread

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.