package data.stdforms;

import sale.*;

import data.*;
import data.swing.*;
import data.stdforms.twotableformsheet.*;

import users.*;

import util.swing.*;

import java.util.*;

import javax.swing.*;
import javax.swing.event.*;

/**
  * A FormSheet that will display the contents of two data containers, a source and a destination, and will
  * allow the user to move items between the two.
  *
  * <p>Source and destination are displayed in a tabular form. The data containers that are supported as source
  * or destination are: {@link Catalog}, {@link Stock}, {@link DataBasket}. There will be two buttons that
  * allow to move items from the source into the destination table and vice-versa. If at least one of source
  * and destination is a {@link CountingStock} there will also be an input line where the user can specify how
  * many items are to be moved. The actual moving will be implemented as sub-process of the process that
  * displays the FormSheet. The concrete sub-process implementations are provided by {@link MoveStrategy}
  * strategy objects.</p>
  *
  * <p>A quite comprehensive set of <code>create()</code> functions is provided to allow to easily create
  * TwoTableFormSheets by simply supplying some parameters.</p>
  *
  * @author Steffen Zschaler
  * @version 2.0 20/08/1999
  * @since v2.0
  */
public class TwoTableFormSheet extends FormSheet {

  /**
    * The strategy used when moving items between source and destination.
    *
    * @serial
    */
  private MoveStrategy m_ms;

  /**
    * The gate at which the FormSheet is displayed.
    *
    * @serial
    */
  private UIGate m_uigGate;

  /**
    * The {@link TableModel} of the left table displayed.
    */
  private transient util.swing.AbstractTableModel m_atmLeftModel;

  /**
    * The {@link TableModel} of the right table displayed.
    */
  private transient util.swing.AbstractTableModel m_atmRightModel;

  /**
    * Reference to the currently selected index.
    *
    * @serial
    */
  private final int[] m_anLeftSelection = new int[] { -1 };

  /**
    * Reference to the currently selected index.
    *
    * @serial
    */
  private final int[] m_anRightSelection = new int[] { -1 };

  /**
    * Create a new TwoTableFormSheet. Instead of calling this constructor directly, use one of the many
    * <code>create()</code> functions provided.
    *
    * @param sCaption the caption of the FormSheet.
    * @param fscc the content creator to be used.
    * @param uigGate the gate at which to display the FormSheet.
    * @param ms the strategy to be used when moving items between source and destination.
    */
  protected TwoTableFormSheet (String sCaption,
                               FormSheetContentCreator fscc,
                               UIGate uigGate,
                               MoveStrategy ms) {
    super (sCaption,
           (JComponent) null,
           true);

    m_ms = ms;
    setGate (uigGate);

    addContentCreator (fscc);
  }

  /**
    * Get the record currently selected in the left table.
    *
    * <p>The actual class of the record depends on the concrete type of TableModel used. See the TableModel's
    * <code>getRecord()</code> method for details.</p>
    */
  public Object getLeftSelectedRecord() {
    return m_atmLeftModel.getRecord (m_anLeftSelection[0]);
  }

  /**
    * Get the record currently selected in the right table.
    *
    * <p>The actual class of the record depends on the concrete type of TableModel used. See the TableModel's
    * <code>getRecord()</code> method for details.</p>
    */
  public Object getRightSelectedRecord() {
    return m_atmRightModel.getRecord (m_anRightSelection[0]);
  }

  /**
    * Get the strategy used when moving items between source and destination.
    *
    * @override Never
    */
  public MoveStrategy getStrategy() {
    return m_ms;
  }

  /**
    * Set the strategy to be used when moving items between source and destination. Note, that the new
    * strategy's {@link MoveStrategy#canMoveToDest} and {@link MoveStrategy#canMoveToSource} methods will
    * have no effect. Also, it is your responsibility to make sure, that the actual class of the strategy
    * matches the source/destination combination.
    *
    * @param ms the new strategy
    *
    * @override Never
    */
  public void setStrategy (MoveStrategy ms) {
    m_ms = ms;
  }

  /**
    * Get the gate at which the FormSheet is being displayed.
    *
    * @override Never
    */
  public UIGate getGate() {
    return m_uigGate;
  }

  /**
    * Set the gate at which to display the FormSheet. The FormSheet will be moved to the new gate, i.e.
    * {@link UIGate#setFormSheet} will be called with the FormSheet as a parameter.
    *
    * @override Never
    */
  public void setGate (UIGate uigGate) {
    if (m_uigGate != null) {
      m_uigGate.setFormSheet (null);
    }

    m_uigGate = uigGate;

    if (m_uigGate != null) {
      m_uigGate.setFormSheet (this);
    }
  }

  // 1. CountingStock -> CountingStock

  /**
    * Create and return a new TwoTableFormSheet where source and destination are CountingStocks.
    *
    * <p>There will be an input line where the user can specify how many items to move with the next action.
    * </p>
    *
    * @param sCaption the caption of the FormSheet.
    * @param csSource the source Stock.
    * @param csDest the destination Stock.
    * @param db the DataBasket relative to which to perform all operations.
    * @param uigGate the Gate at which the FormSheet will be displayed. If this is <code>null</code> it must be
    * {@link #setGate set} before actually using the FormSheet.
    * @param cmpSource a comparator defining the source sorting order. The objects to be compared will be the
    * keys of the individual items. If <code>null</code> the ordering will be the natural ordering of the keys.
    * @param cmpDest a comparator defining the destination sorting order. The objects to be compared will be
    * the keys of the individual items. If <code>null</code> the ordering will be the natural ordering of the
    * keys.
    * @param fShowZerosSource if false, source lines containing '0' in the &quot;Count&quot; column will be
    * hidden.
    * @param fShowZerosDest if false, destination lines containing '0' in the &quot;Count&quot; column will be
    * hidden.
    * @param tedSource a TableEntryDescriptor that can split individual
    * {@link CountingStockTableModel.Record CountingStockTableModel records} into a table's cells. It will be
    * used for the source table. If <code>null</code> and <code>csSource</code> is a {@link MoneyBag} it
    * defaults to a {@link DefaultMoneyBagItemTED} using <code>csSource.getCatalog()</code> to format values.
    * Otherwise, it defaults to a {@link DefaultCountingStockItemTED}.
    * @param tedDest a TableEntryDescriptor that can split individual
    * {@link CountingStockTableModel.Record CountingStockTableModel records} into a table's cells. It will be
    * used for the destination table. If <code>null</code> and <code>csDest</code> is a {@link MoneyBag} it
    * defaults to a {@link DefaultMoneyBagItemTED} using <code>csDest.getCatalog()</code> to format values.
    * Otherwise, it defaults to a {@link DefaultCountingStockItemTED}.
    * @param cscssMoveStrategy the strategy to be used when moving items between source and destination. If
    * <code>null</code>, defaults to a {@link CSCSStrategy} object.
    */
  public static TwoTableFormSheet create (String sCaption,
                                          final CountingStock csSource,
                                          final CountingStock csDest,
                                          final DataBasket db,
                                          UIGate uigGate,
                                          final Comparator cmpSource,
                                          final Comparator cmpDest,
                                          final boolean fShowZerosSource,
                                          final boolean fShowZerosDest,
                                          final TableEntryDescriptor tedSource,
                                          final TableEntryDescriptor tedDest,
                                          CSCSStrategy cscssMoveStrategy) {

    if (csSource.getCatalog (db) != csDest.getCatalog (db)) {
      throw new CatalogConflictException();
    }

    FormSheetContentCreator fscc = new FormSheetContentCreator() {
      protected void createFormSheetContent (FormSheet fs) {

        final TwoTableFormSheet ttfs = (TwoTableFormSheet) fs;

        final int[] anCounter = { 1 };

        JCountingStockTable jcstSource = new JCountingStockTable (csSource,
                                                                  db,
                                                                  cmpSource,
                                                                  fShowZerosSource,
                                                                  ((tedSource != null)?
                                                                   (tedSource):
                                                                   ((csSource instanceof MoneyBag)?
                                                                    (new DefaultMoneyBagItemTED ((Currency) csSource.getCatalog (db))):
                                                                    (new DefaultCountingStockItemTED()))));
        jcstSource.setSelectionObserver (ttfs.m_anLeftSelection);
        final util.swing.AbstractTableModel atmSource = (util.swing.AbstractTableModel) jcstSource.getModel();

        ttfs.m_atmLeftModel = atmSource;

        JCountingStockTable jcstDest = new JCountingStockTable (csDest,
                                                                db,
                                                                cmpDest,
                                                                fShowZerosDest,
                                                                ((tedDest != null)?
                                                                 (tedDest):
                                                                 ((csDest instanceof MoneyBag)?
                                                                  (new DefaultMoneyBagItemTED ((Currency) csDest.getCatalog (db))):
                                                                  (new DefaultCountingStockItemTED()))));
        jcstDest.setSelectionObserver (ttfs.m_anRightSelection);
        final util.swing.AbstractTableModel atmDest = (util.swing.AbstractTableModel) jcstDest.getModel();

        ttfs.m_atmRightModel = atmDest;

        JTextField jtf = new JIntInput (anCounter, 1, 1, Integer.MAX_VALUE);

        JButton jbRight = new JButton (getResourceText (BUTTON_RIGHT));
        JButton jbLeft = new JButton (getResourceText (BUTTON_LEFT));

        JPanel jpForm = new JPanel();
        jpForm.setLayout (new BoxLayout (jpForm, BoxLayout.X_AXIS));

        jpForm.add (new JScrollPane (jcstSource));

        jpForm.add (createCentralBox (jbRight, jbLeft, jtf, ttfs.getStrategy()));

        jpForm.add (new JScrollPane (jcstDest));

        if (ttfs.getStrategy().canMoveToDest()) {
          jbRight.addActionListener (new ActionActionListener (fs) {
            public void doAction (SaleProcess p, final SalesPoint sp) {
              CountingStockTableModel.Record r = (CountingStockTableModel.Record) atmSource.getRecord (ttfs.m_anLeftSelection[0]);

              if (r != null) {
                UIGate uig = ttfs.getGate();
                if (uig != null) {
                  uig.setNextTransition (((CSCSStrategy) ttfs.getStrategy()).getMoveToDestProcess (p, sp, csSource, csDest, db, r.getDescriptor(), anCounter[0], ttfs));
                }
              }
            }
          });
        }

        if (ttfs.getStrategy().canMoveToSource()) {
          jbLeft.addActionListener (new ActionActionListener (fs) {
            public void doAction (SaleProcess p, final SalesPoint sp) {
              CountingStockTableModel.Record r = (CountingStockTableModel.Record) atmDest.getRecord (ttfs.m_anRightSelection[0]);

              if (r != null) {
                UIGate uig = ttfs.getGate();
                if (uig != null) {
                  uig.setNextTransition (((CSCSStrategy) ttfs.getStrategy()).getMoveToSourceProcess (p, sp, csSource, csDest, db, r.getDescriptor(), anCounter[0], ttfs));
                }
              }
            }
          });
        }

        fs.setComponent (jpForm);
      }
    };

    cscssMoveStrategy = ((cscssMoveStrategy != null)?
                         (cscssMoveStrategy):
                         (new CSCSStrategy()));

    return new TwoTableFormSheet (sCaption,
                                  fscc,
                                  uigGate,
                                  cscssMoveStrategy);
  }

  /**
    * Create and return a new TwoTableFormSheet where source and destination are CountingStocks.
    *
    * <p>Calls the appropriate fully parameterized function, passing default values (i.e. <code>null</code> or
    * false) for the missing parameters.</p>
    *
    * @param sCaption the caption of the FormSheet.
    * @param csSource the source Stock.
    * @param csDest the destination Stock.
    * @param db the DataBasket relative to which to perform all operations.
    * @param uigGate the Gate at which the FormSheet will be displayed. If this is <code>null</code> it must be
    * {@link #setGate set} before actually using the FormSheet.
    */
  public static TwoTableFormSheet create (String sCaption,
                                          CountingStock csSource,
                                          CountingStock csDest,
                                          DataBasket db,
                                          UIGate uigGate) {
    return create (sCaption,
                   csSource,
                   csDest,
                   db,
                   uigGate,
                   null,
                   null,
                   false,
                   false,
                   null,
                   null,
                   null);
  }

  // 2. StoringStock -> StoringStock

  /**
    * Create and return a new TwoTableFormSheet where source and destination are StoringStocks.
    *
    * @param sCaption the caption of the FormSheet.
    * @param ssSource the source Stock.
    * @param ssDest the destination Stock.
    * @param db the DataBasket relative to which to perform all operations.
    * @param uigGate the Gate at which the FormSheet will be displayed. If this is <code>null</code> it must be
    * {@link #setGate set} before actually using the FormSheet.
    * @param cmpSource a comparator defining the source sorting order. The objects to be compared will be the
    * individual items. If <code>null</code> the ordering will be the natural ordering of the items.
    * @param cmpDest a comparator defining the destination sorting order. The objects to be compared will be
    * the individual items. If <code>null</code> the ordering will be the natural ordering of the items.
    * @param tedSource a TableEntryDescriptor that can split individual {@link StockItem StockItems} into a
    * table's cells. It will be used for the source table. If <code>null</code> it defaults to a
    * {@link DefaultStockItemTED}.
    * @param tedDest a TableEntryDescriptor that can split individual {@link StockItem StockItems} into a
    * table's cells. It will be used for the destination table. If <code>null</code> it defaults to a
    * {@link DefaultStockItemTED}.
    * @param sssssMoveStrategy the strategy to be used when moving items between source and destination. If
    * <code>null</code>, defaults to a {@link SSSSStrategy} object.
    */
  public static TwoTableFormSheet create (String sCaption,
                                          final StoringStock ssSource,
                                          final StoringStock ssDest,
                                          final DataBasket db,
                                          UIGate uigGate,
                                          final Comparator cmpSource,
                                          final Comparator cmpDest,
                                          final TableEntryDescriptor tedSource,
                                          final TableEntryDescriptor tedDest,
                                          SSSSStrategy sssssMoveStrategy) {
    FormSheetContentCreator fscc = new FormSheetContentCreator() {
      protected void createFormSheetContent (FormSheet fs) {
        final TwoTableFormSheet ttfs = (TwoTableFormSheet) fs;

        JStoringStockTable jsstSource = new JStoringStockTable (ssSource,
                                                                db,
                                                                cmpSource,
                                                                ((tedSource != null)?
                                                                 (tedSource):
                                                                 (new DefaultStockItemTED())));
        jsstSource.setSelectionObserver (ttfs.m_anLeftSelection);
        final util.swing.AbstractTableModel atmSource = (util.swing.AbstractTableModel) jsstSource.getModel();

        ttfs.m_atmLeftModel = atmSource;

        JStoringStockTable jsstDest = new JStoringStockTable (ssDest,
                                                              db,
                                                              cmpDest,
                                                              ((tedDest != null)?
                                                               (tedDest):
                                                               (new DefaultStockItemTED())));
        jsstDest.setSelectionObserver (ttfs.m_anRightSelection);
        final util.swing.AbstractTableModel atmDest = (util.swing.AbstractTableModel) jsstDest.getModel();

        ttfs.m_atmRightModel = atmDest;

        JButton jbRight = new JButton (getResourceText (BUTTON_RIGHT));
        JButton jbLeft = new JButton (getResourceText (BUTTON_LEFT));

        JPanel jpForm = new JPanel();
        jpForm.setLayout (new BoxLayout (jpForm, BoxLayout.X_AXIS));

        jpForm.add (new JScrollPane (jsstSource));

        jpForm.add (createCentralBox (jbRight, jbLeft, null, ttfs.getStrategy()));

        jpForm.add (new JScrollPane (jsstDest));

        if (ttfs.getStrategy().canMoveToDest()) {
          jbRight.addActionListener (new ActionActionListener (fs) {
            public void doAction (SaleProcess p, final SalesPoint sp) {
              StockItem si = (StockItem) atmSource.getRecord (ttfs.m_anLeftSelection[0]);

              if (si != null) {
                UIGate uig = ttfs.getGate();
                if (uig != null) {
                  uig.setNextTransition (((SSSSStrategy) ttfs.getStrategy()).getMoveToDestProcess (p, sp, ssSource, ssDest, db, si, ttfs));
                }
              }
            }
          });
        }

        if (ttfs.getStrategy().canMoveToSource()) {
          jbLeft.addActionListener (new ActionActionListener (fs) {
            public void doAction (SaleProcess p, final SalesPoint sp) {
              StockItem si = (StockItem) atmDest.getRecord (ttfs.m_anRightSelection[0]);

              if (si != null) {
                UIGate uig = ttfs.getGate();
                if (uig != null) {
                  uig.setNextTransition (((SSSSStrategy) ttfs.getStrategy()).getMoveToSourceProcess (p, sp, ssSource, ssDest, db, si, ttfs));
                }
              }
            }
          });
        }

        fs.setComponent (jpForm);
      }
    };

    sssssMoveStrategy = ((sssssMoveStrategy != null)?
                         (sssssMoveStrategy):
                         (new SSSSStrategy()));

    return new TwoTableFormSheet (sCaption,
                                  fscc,
                                  uigGate,
                                  sssssMoveStrategy);
  }

  /**
    * Create and return a new TwoTableFormSheet where source and destination are StoringStocks.
    *
    * <p>Calls the appropriate fully parameterized function, passing <code>null</code> for the missing
    * parameters.</p>
    *
    * @param sCaption the caption of the FormSheet.
    * @param ssSource the source Stock.
    * @param ssDest the destination Stock.
    * @param db the DataBasket relative to which to perform all operations.
    * @param uigGate the Gate at which the FormSheet will be displayed. If this is <code>null</code> it must be
    * {@link #setGate set} before actually using the FormSheet.
    */
  public static TwoTableFormSheet create (String sCaption,
                                          StoringStock ssSource,
                                          StoringStock ssDest,
                                          DataBasket db,
                                          UIGate uigGate) {

    return create (sCaption,
                   ssSource,
                   ssDest,
                   db,
                   uigGate,
                   null,
                   null,
                   null,
                   null,
                   null);
  }

  // 3. CountingStock -> DataBasket

  /**
    * Create and return a new TwoTableFormSheet where the source is a CountingStock and the destination is a
    * DataBasket.
    *
    * <p>There will be an input line where the user can specify how many items to move with the next action.
    * </p>
    *
    * @param sCaption the caption of the FormSheet.
    * @param csSource the source Stock.
    * @param dbDest the destination DataBasket.
    * @param uigGate the Gate at which the FormSheet will be displayed. If this is <code>null</code> it must be
    * {@link #setGate set} before actually using the FormSheet.
    * @param cmpSource a comparator defining the source sorting order. The objects to be compared will be the
    * keys of the individual items. If <code>null</code> the ordering will be the natural ordering of the keys.
    * @param cmpDest a comparator defining the destination sorting order. The objects to be compared will be
    * DataBasketEntries. If <code>null</code> the entries will be ordered first by their main and then by their
    * secondary keys.
    * @param fShowZeros if false, source lines containing '0' in the &quot;Count&quot; column will be hidden.
    * @param tedSource a TableEntryDescriptor that can split individual
    * {@link CountingStockTableModel.Record CountingStockTableModel records} into a table's cells. It will be
    * used for the source table. If <code>null</code> and <code>csSource</code> is a {@link MoneyBag} it
    * defaults to a {@link DefaultMoneyBagItemTED} using <code>csSource.getCatalog()</code> to format values.
    * Otherwise, it defaults to a {@link DefaultCountingStockItemTED}.
    * @param tedDest a TableEntryDescriptor that can split individual {@link DataBasketEntry DataBasketEntries}
    * into a table's cells. It will be used for the destination table. If <code>null</code>  it defaults to a
    * {@link DefaultCountingStockDBETableEntryDescriptor}.
    * @param csdbsMoveStrategy the strategy to be used when moving items between source and destination. If
    * <code>null</code>, defaults to a {@link CSDBStrategy} object.
    */
  public static TwoTableFormSheet create (String sCaption,
                                          final CountingStock csSource,
                                          final DataBasket dbDest,
                                          UIGate uigGate,
                                          final Comparator cmpSource,
                                          final Comparator cmpDest,
                                          final boolean fShowZeros,
                                          final TableEntryDescriptor tedSource,
                                          final TableEntryDescriptor tedDest,
                                          CSDBStrategy csdbsMoveStrategy) {

    FormSheetContentCreator fscc = new FormSheetContentCreator() {
      protected void createFormSheetContent (FormSheet fs) {
        final TwoTableFormSheet ttfs = (TwoTableFormSheet) fs;

        final int[] anCounter = { 1 };

        JCountingStockTable jcstSource = new JCountingStockTable (csSource,
                                                                  dbDest,
                                                                  cmpSource,
                                                                  fShowZeros,
                                                                  ((tedSource != null)?
                                                                   (tedSource):
                                                                   ((csSource instanceof MoneyBag)?
                                                                    (new DefaultMoneyBagItemTED ((Currency) csSource.getCatalog (dbDest))):
                                                                    (new DefaultCountingStockItemTED()))));
        jcstSource.setSelectionObserver (ttfs.m_anLeftSelection);
        final util.swing.AbstractTableModel atmSource = (util.swing.AbstractTableModel) jcstSource.getModel();

        ttfs.m_atmLeftModel = atmSource;

        JDataBasketTable jdbtDest = new JDataBasketTable (dbDest,
                                                          DataBasketConditionImpl.allStockItemsWithSource (csSource),
                                                          new CountingStockDBEGrouper(),
                                                          cmpDest,
                                                          ((tedDest != null)?
                                                           (tedDest):
                                                           (new DefaultCountingStockDBETableEntryDescriptor())));
        jdbtDest.setSelectionObserver (ttfs.m_anRightSelection);
        final util.swing.AbstractTableModel atmDest = (util.swing.AbstractTableModel) jdbtDest.getModel();

        ttfs.m_atmRightModel = atmDest;

        JTextField jtf = new JIntInput (anCounter, 1, 1, Integer.MAX_VALUE);

        JButton jbRight = new JButton (getResourceText (BUTTON_RIGHT));
        JButton jbLeft = new JButton (getResourceText (BUTTON_LEFT));

        JPanel jpForm = new JPanel();
        jpForm.setLayout (new BoxLayout (jpForm, BoxLayout.X_AXIS));

        jpForm.add (new JScrollPane (jcstSource));

        jpForm.add (createCentralBox (jbRight, jbLeft, jtf, ttfs.getStrategy()));

        jpForm.add (new JScrollPane (jdbtDest));

        if (ttfs.getStrategy().canMoveToDest()) {
          jbRight.addActionListener (new ActionActionListener (fs) {
            public void doAction (SaleProcess p, final SalesPoint sp) {
              CountingStockTableModel.Record r = (CountingStockTableModel.Record) atmSource.getRecord (ttfs.m_anLeftSelection[0]);

              if (r != null) {
                UIGate uig = ttfs.getGate();
                if (uig != null) {
                  uig.setNextTransition (((CSDBStrategy) ttfs.getStrategy()).getMoveToDestProcess (p, sp, csSource, dbDest, r.getDescriptor(), anCounter[0], ttfs));
                }
              }
            }
          });
        }

        if (ttfs.getStrategy().canMoveToSource()) {
          jbLeft.addActionListener (new ActionActionListener (fs) {
            public void doAction (SaleProcess p, final SalesPoint sp) {
              DataBasketEntry dbe = (DataBasketEntry) atmDest.getRecord (ttfs.m_anRightSelection[0]);

              if (dbe != null) {
                UIGate uig = ttfs.getGate();
                if (uig != null) {
                  uig.setNextTransition (((CSDBStrategy) ttfs.getStrategy()).getMoveToSourceProcess (p, sp, csSource, dbDest, dbe, anCounter[0], ttfs));
                }
              }
            }
          });
        }

        fs.setComponent (jpForm);
      }
    };

    csdbsMoveStrategy = ((csdbsMoveStrategy != null)?
                         (csdbsMoveStrategy):
                         (new CSDBStrategy()));

    return new TwoTableFormSheet (sCaption,
                                  fscc,
                                  uigGate,
                                  csdbsMoveStrategy);
  }

  /**
    * Create and return a new TwoTableFormSheet where the source is a CountingStocks and the destination is a
    * DataBasket.
    *
    * <p>Calls the appropriate fully parameterized function, passing default values (i.e. <code>null</code> or
    * false) for the missing parameters.</p>
    *
    * @param sCaption the caption of the FormSheet.
    * @param csSource the source Stock.
    * @param dbDest the destination DataBasket.
    * @param uigGate the Gate at which the FormSheet will be displayed. If this is <code>null</code> it must be
    * {@link #setGate set} before actually using the FormSheet.
    */
  public static TwoTableFormSheet create (String sCaption,
                                          CountingStock csSource,
                                          DataBasket dbDest,
                                          UIGate uigGate) {
    return create (sCaption,
                   csSource,
                   dbDest,
                   uigGate,
                   null,
                   null,
                   false,
                   null,
                   null,
                   null);
  }

  // 4. DataBasket -> CountingStock

  /**
    * Create and return a new TwoTableFormSheet where the source is a DataBasket and the destination is a
    * CountingStock.
    *
    * <p>There will be an input line where the user can specify how many items to move with the next action.
    * </p>
    *
    * @param sCaption the caption of the FormSheet.
    * @param dbSource the source DataBasket.
    * @param csDest the destination Stock.
    * @param uigGate the Gate at which the FormSheet will be displayed. If this is <code>null</code> it must be
    * {@link #setGate set} before actually using the FormSheet.
    * @param cmpSource a comparator defining the source sorting order. The objects to be compared will be
    * DataBasketEntries. If <code>null</code> the entries will be ordered first by their main and then by their
    * secondary keys.
    * @param cmpDest a comparator defining the destination sorting order. The objects to be compared will be
    * the keys of the individual items. If <code>null</code> the ordering will be the natural ordering of the
    * keys.
    * @param fShowZeros if false, destination lines containing '0' in the &quot;Count&quot; column will be
    * hidden.
    * @param tedSource a TableEntryDescriptor that can split individual
    * {@link DataBasketEntry DataBasketEntries} into a table's cells. It will be used for the source table.
    * If <code>null</code>  it defaults to a {@link DefaultCountingStockDBETableEntryDescriptor}.
    * @param tedDest a TableEntryDescriptor that can split individual
    * {@link CountingStockTableModel.Record CountingStockTableModel records} into a table's cells. It will be
    * used for the destination table. If <code>null</code> and <code>csDest</code> is a {@link MoneyBag} it
    * defaults to a {@link DefaultMoneyBagItemTED} using <code>csDest.getCatalog()</code> to format values.
    * Otherwise, it defaults to a {@link DefaultCountingStockItemTED}.
    * @param dbcssMoveStrategy the strategy to be used when moving items between source and destination. If
    * <code>null</code>, defaults to a {@link DBCSStrategy} object.
    */
  public static TwoTableFormSheet create (String sCaption,
                                          final DataBasket dbSource,
                                          final CountingStock csDest,
                                          UIGate uigGate,
                                          final Comparator cmpSource,
                                          final Comparator cmpDest,
                                          final boolean fShowZeros,
                                          final TableEntryDescriptor tedSource,
                                          final TableEntryDescriptor tedDest,
                                          DBCSStrategy dbcssMoveStrategy) {

    FormSheetContentCreator fscc = new FormSheetContentCreator() {
      protected void createFormSheetContent (FormSheet fs) {
        final TwoTableFormSheet ttfs = (TwoTableFormSheet) fs;

        final int[] anCounter = { 1 };

        JDataBasketTable jdbtSource = new JDataBasketTable (dbSource,
                                                            new DataBasketConditionImpl (DataBasketKeys.STOCK_ITEM_MAIN_KEY,
                                                                                         null,
                                                                                         null,
                                                                                         null,
                                                                                         null) {
                                                              public boolean match (DataBasketEntry dbe) {
                                                                return (dbe.getDestination() == null);
                                                              }
                                                            },
                                                            new CountingStockDBEGrouper(),
                                                            cmpSource,
                                                            ((tedSource != null)?
                                                            (tedSource):
                                                            (new DefaultCountingStockDBETableEntryDescriptor())));
        jdbtSource.setSelectionObserver (ttfs.m_anLeftSelection);
        final util.swing.AbstractTableModel atmSource = (util.swing.AbstractTableModel) jdbtSource.getModel();

        ttfs.m_atmLeftModel = atmSource;

        JCountingStockTable jcstDest = new JCountingStockTable (csDest,
                                                                dbSource,
                                                                cmpDest,
                                                                fShowZeros,
                                                                ((tedDest != null)?
                                                                 (tedDest):
                                                                 ((csDest instanceof MoneyBag)?
                                                                  (new DefaultMoneyBagItemTED ((Currency) csDest.getCatalog (dbSource))):
                                                                  (new DefaultCountingStockItemTED()))));
        jcstDest.setSelectionObserver (ttfs.m_anRightSelection);
        final util.swing.AbstractTableModel atmDest = (util.swing.AbstractTableModel) jcstDest.getModel();

        ttfs.m_atmRightModel = atmDest;

        JTextField jtf = new JIntInput (anCounter, 1, 1, Integer.MAX_VALUE);

        JButton jbRight = new JButton (getResourceText (BUTTON_RIGHT));
        JButton jbLeft = new JButton (getResourceText (BUTTON_LEFT));

        JPanel jpForm = new JPanel();
        jpForm.setLayout (new BoxLayout (jpForm, BoxLayout.X_AXIS));

        jpForm.add (new JScrollPane (jdbtSource));

        jpForm.add (createCentralBox (jbRight, jbLeft, jtf, ttfs.getStrategy()));

        jpForm.add (new JScrollPane (jcstDest));

        if (ttfs.getStrategy().canMoveToDest()) {
          jbRight.addActionListener (new ActionActionListener (fs) {
            public void doAction (SaleProcess p, final SalesPoint sp) {
              DataBasketEntry dbe = (DataBasketEntry) atmSource.getRecord (ttfs.m_anLeftSelection[0]);

              if (dbe != null) {
                UIGate uig = ttfs.getGate();
                if (uig != null) {
                  uig.setNextTransition (((DBCSStrategy) ttfs.getStrategy()).getMoveToDestProcess (p, sp, dbSource, csDest, dbe, anCounter[0], ttfs));
                }
              }
            }
          });
        }

        if (ttfs.getStrategy().canMoveToSource()) {
          jbLeft.addActionListener (new ActionActionListener (fs) {
            public void doAction (SaleProcess p, final SalesPoint sp) {
              CountingStockTableModel.Record r = (CountingStockTableModel.Record) atmDest.getRecord (ttfs.m_anRightSelection[0]);

              if (r != null) {
                UIGate uig = ttfs.getGate();
                if (uig != null) {
                  uig.setNextTransition (((DBCSStrategy) ttfs.getStrategy()).getMoveToSourceProcess (p, sp, dbSource, csDest, r.getDescriptor(), anCounter[0], ttfs));
                }
              }
            }
          });
        }

        fs.setComponent (jpForm);
      }
    };

    dbcssMoveStrategy = ((dbcssMoveStrategy != null)?
                         (dbcssMoveStrategy):
                         (new DBCSStrategy()));

    return new TwoTableFormSheet (sCaption,
                                  fscc,
                                  uigGate,
                                  dbcssMoveStrategy);
  }

  /**
    * Create and return a new TwoTableFormSheet where the source is a DataBasket and the destination is a
    * CountingStock.
    *
    * <p>Calls the appropriate fully parameterized function, passing default values (i.e. <code>null</code> or
    * false) for the missing parameters.</p>
    *
    * @param sCaption the caption of the FormSheet.
    * @param dbSource the source DataBasket.
    * @param csDest the destination Stock.
    * @param uigGate the Gate at which the FormSheet will be displayed. If this is <code>null</code> it must be
    * {@link #setGate set} before actually using the FormSheet.
    */
  public static TwoTableFormSheet create (String sCaption,
                                          DataBasket dbSource,
                                          CountingStock csDest,
                                          UIGate uigGate) {
    return create (sCaption,
                   dbSource,
                   csDest,
                   uigGate,
                   null,
                   null,
                   false,
                   null,
                   null,
                   null);
  }

  // 5. StoringStock -> DataBasket

  /**
    * Create and return a new TwoTableFormSheet where the source is a StoringStock and the destination is a
    * DataBasket.
    *
    * @param sCaption the caption of the FormSheet.
    * @param ssSource the source Stock.
    * @param dbDest the destination DataBasket.
    * @param uigGate the Gate at which the FormSheet will be displayed. If this is <code>null</code> it must be
    * {@link #setGate set} before actually using the FormSheet.
    * @param cmpSource a comparator defining the source sorting order. The objects to be compared will be the
    * individual items. If <code>null</code> the ordering will be the natural ordering of the items.
    * @param cmpDest a comparator defining the destination sorting order. The objects to be compared will be
    * DataBasketEntries. If <code>null</code> the entries will be ordered first by their main and then by their
    * secondary keys.
    * @param tedSource a TableEntryDescriptor that can split individual {@link StockItem StockItems} into a
    * table's cells. It will be used for the source table. If <code>null</code> it defaults to a
    * {@link DefaultStockItemTED}.
    * @param tedDest a TableEntryDescriptor that can split individual {@link DataBasketEntry DataBasketEntries}
    * into a table's cells. It will be used for the destination table. If <code>null</code>  it defaults to a
    * {@link DefaultStoringStockDBETableEntryDescriptor}.
    * @param ssdbsMoveStrategy the strategy to be used when moving items between source and destination. If
    * <code>null</code>, defaults to a {@link SSDBStrategy} object.
    */
  public static TwoTableFormSheet create (String sCaption,
                                          final StoringStock ssSource,
                                          final DataBasket dbDest,
                                          UIGate uigGate,
                                          final Comparator cmpSource,
                                          final Comparator cmpDest,
                                          final TableEntryDescriptor tedSource,
                                          final TableEntryDescriptor tedDest,
                                          SSDBStrategy ssdbsMoveStrategy) {
    FormSheetContentCreator fscc = new FormSheetContentCreator() {
      protected void createFormSheetContent (FormSheet fs) {
        final TwoTableFormSheet ttfs = (TwoTableFormSheet) fs;

        JStoringStockTable jsstSource = new JStoringStockTable (ssSource,
                                                                dbDest,
                                                                cmpSource,
                                                                ((tedSource != null)?
                                                                 (tedSource):
                                                                 (new DefaultStockItemTED())));
        jsstSource.setSelectionObserver (ttfs.m_anLeftSelection);
        final util.swing.AbstractTableModel atmSource = (util.swing.AbstractTableModel) jsstSource.getModel();

        ttfs.m_atmLeftModel = atmSource;

        JDataBasketTable jdbtDest = new JDataBasketTable (dbDest,
                                                          DataBasketConditionImpl.allStockItemsWithSource (ssSource),
                                                          NOPDataBasketEntryGrouper.NO_GROUPS,
                                                          cmpDest,
                                                          ((tedDest != null)?
                                                           (tedDest):
                                                           (new DefaultStoringStockDBETableEntryDescriptor())));
        jdbtDest.setSelectionObserver (ttfs.m_anRightSelection);
        final util.swing.AbstractTableModel atmDest = (util.swing.AbstractTableModel) jdbtDest.getModel();

        ttfs.m_atmRightModel = atmDest;

        JButton jbRight = new JButton (getResourceText (BUTTON_RIGHT));
        JButton jbLeft = new JButton (getResourceText (BUTTON_LEFT));

        JPanel jpForm = new JPanel();
        jpForm.setLayout (new BoxLayout (jpForm, BoxLayout.X_AXIS));

        jpForm.add (new JScrollPane (jsstSource));

        jpForm.add (createCentralBox (jbRight, jbLeft, null, ttfs.getStrategy()));

        jpForm.add (new JScrollPane (jdbtDest));

        if (ttfs.getStrategy().canMoveToDest()) {
          jbRight.addActionListener (new ActionActionListener (fs) {
            public void doAction (SaleProcess p, final SalesPoint sp) {
              final StockItem si = (StockItem) atmSource.getRecord (ttfs.m_anLeftSelection[0]);

              if (si != null) {
                UIGate uig = ttfs.getGate();
                if (uig != null) {
                  uig.setNextTransition (((SSDBStrategy) ttfs.getStrategy()).getMoveToDestProcess (p, sp, ssSource, dbDest, si, ttfs));
                }
              }
            }
          });
        }

        if (ttfs.getStrategy().canMoveToSource()) {
          jbLeft.addActionListener (new ActionActionListener (fs) {
            public void doAction (SaleProcess p, final SalesPoint sp) {
              final DataBasketEntry dbe = (DataBasketEntry) atmDest.getRecord (ttfs.m_anRightSelection[0]);

              if (dbe != null) {
                UIGate uig = ttfs.getGate();
                if (uig != null) {
                  uig.setNextTransition (((SSDBStrategy) ttfs.getStrategy()).getMoveToSourceProcess (p, sp, ssSource, dbDest, dbe, ttfs));
                }
              }
            }
          });
        }

        fs.setComponent (jpForm);
      }
    };

    ssdbsMoveStrategy = ((ssdbsMoveStrategy != null)?
                         (ssdbsMoveStrategy):
                         (new SSDBStrategy()));

    return new TwoTableFormSheet (sCaption,
                                  fscc,
                                  uigGate,
                                  ssdbsMoveStrategy);
  }

  /**
    * Create and return a new TwoTableFormSheet where the source is a StoringStock and the destination is a
    * DataBasket.
    *
    * <p>Calls the appropriate fully parameterized function, passing <code>null</code> for the missing
    * parameters.</p>
    *
    * @param sCaption the caption of the FormSheet.
    * @param ssSource the source Stock.
    * @param dbDest the destination DataBasket.
    * @param uigGate the Gate at which the FormSheet will be displayed. If this is <code>null</code> it must be
    * {@link #setGate set} before actually using the FormSheet.
    */
  public static TwoTableFormSheet create (String sCaption,
                                          StoringStock ssSource,
                                          DataBasket dbDest,
                                          UIGate uigGate) {
    return create (sCaption,
                   ssSource,
                   dbDest,
                   uigGate,
                   null,
                   null,
                   null,
                   null,
                   null);
  }

  // 6. DataBasket -> StoringStock

  /**
    * Create and return a new TwoTableFormSheet where the source is a DataBasket and the destination is a
    * StoringStock.
    *
    * @param sCaption the caption of the FormSheet.
    * @param dbSource the source DataBasket.
    * @param ssDest the destination Stock.
    * @param uigGate the Gate at which the FormSheet will be displayed. If this is <code>null</code> it must be
    * {@link #setGate set} before actually using the FormSheet.
    * @param cmpSource a comparator defining the source sorting order. The objects to be compared will be
    * DataBasketEntries. If <code>null</code> the entries will be ordered first by their main and then by their
    * secondary keys.
    * @param cmpDest a comparator defining the destination sorting order. The objects to be compared will be
    * the individual items. If <code>null</code> the ordering will be the natural ordering of the items.
    * @param tedSource a TableEntryDescriptor that can split individual
    * {@link DataBasketEntry DataBasketEntries} into a table's cells. It will be used for the source table. If
    * <code>null</code>  it defaults to a {@link DefaultStoringStockDBETableEntryDescriptor}.
    * @param tedDest a TableEntryDescriptor that can split individual {@link StockItem StockItems} into a
    * table's cells. It will be used for the destination table. If <code>null</code> it defaults to a
    * {@link DefaultStockItemTED}.
    * @param dbsssMoveStrategy the strategy to be used when moving items between source and destination. If
    * <code>null</code>, defaults to a {@link DBSSStrategy} object.
    */
  public static TwoTableFormSheet create (String sCaption,
                                          final DataBasket dbSource,
                                          final StoringStock ssDest,
                                          UIGate uigGate,
                                          final Comparator cmpSource,
                                          final Comparator cmpDest,
                                          final TableEntryDescriptor tedSource,
                                          final TableEntryDescriptor tedDest,
                                          DBSSStrategy dbsssMoveStrategy) {
    FormSheetContentCreator fscc = new FormSheetContentCreator() {
      protected void createFormSheetContent (FormSheet fs) {
        final TwoTableFormSheet ttfs = (TwoTableFormSheet) fs;

        JDataBasketTable jdbtSource = new JDataBasketTable (dbSource,
                                                            new DataBasketConditionImpl (DataBasketKeys.STOCK_ITEM_MAIN_KEY,
                                                                                         null,
                                                                                         null,
                                                                                         null,
                                                                                         null) {
                                                              public boolean match (DataBasketEntry dbe) {
                                                                return (dbe.getDestination() == null);
                                                              }
                                                            },
                                                            NOPDataBasketEntryGrouper.NO_GROUPS,
                                                            cmpSource,
                                                            ((tedSource != null)?
                                                             (tedSource):
                                                             (new DefaultStoringStockDBETableEntryDescriptor())));
        jdbtSource.setSelectionObserver (ttfs.m_anLeftSelection);
        final util.swing.AbstractTableModel atmSource = (util.swing.AbstractTableModel) jdbtSource.getModel();

        ttfs.m_atmLeftModel = atmSource;

        JStoringStockTable jsstDest = new JStoringStockTable (ssDest,
                                                              dbSource,
                                                              cmpDest,
                                                              ((tedDest != null)?
                                                               (tedDest):
                                                               (new DefaultStockItemTED())));
        jsstDest.setSelectionObserver (ttfs.m_anRightSelection);
        final util.swing.AbstractTableModel atmDest = (util.swing.AbstractTableModel) jsstDest.getModel();

        ttfs.m_atmRightModel = atmDest;

        JButton jbRight = new JButton (getResourceText (BUTTON_RIGHT));
        JButton jbLeft = new JButton (getResourceText (BUTTON_LEFT));

        JPanel jpForm = new JPanel();
        jpForm.setLayout (new BoxLayout (jpForm, BoxLayout.X_AXIS));

        jpForm.add (new JScrollPane (jdbtSource));

        jpForm.add (createCentralBox (jbRight, jbLeft, null, ttfs.getStrategy()));

        jpForm.add (new JScrollPane (jsstDest));

        if (ttfs.getStrategy().canMoveToDest()) {
          jbRight.addActionListener (new ActionActionListener (fs) {
            public void doAction (SaleProcess p, final SalesPoint sp) {
              final DataBasketEntry dbe = (DataBasketEntry) atmSource.getRecord (ttfs.m_anLeftSelection[0]);

              if (dbe != null) {
                UIGate uig = ttfs.getGate();
                if (uig != null) {
                  uig.setNextTransition (((DBSSStrategy) ttfs.getStrategy()).getMoveToDestProcess (p, sp, dbSource, ssDest, dbe, ttfs));
                }
              }
            }
          });
        }

        if (ttfs.getStrategy().canMoveToSource()) {
          jbLeft.addActionListener (new ActionActionListener (fs) {
            public void doAction (SaleProcess p, final SalesPoint sp) {
              final StockItem si = (StockItem) atmDest.getRecord (ttfs.m_anRightSelection[0]);

              if (si != null) {
                UIGate uig = ttfs.getGate();
                if (uig != null) {
                  uig.setNextTransition (((DBSSStrategy) ttfs.getStrategy()).getMoveToSourceProcess (p, sp, dbSource, ssDest, si, ttfs));
                }
              }
            }
          });
        }

        fs.setComponent (jpForm);
      }
    };

    dbsssMoveStrategy = ((dbsssMoveStrategy != null)?
                         (dbsssMoveStrategy):
                         (new DBSSStrategy()));

    return new TwoTableFormSheet (sCaption,
                                  fscc,
                                  uigGate,
                                  dbsssMoveStrategy);
  }

  /**
    * Create and return a new TwoTableFormSheet where the source is a DataBasket and the destination is a
    * StoringStock.
    *
    * <p>Calls the appropriate fully parameterized function, passing <code>null</code> for the missing
    * parameters.</p>
    *
    * @param sCaption the caption of the FormSheet.
    * @param dbSource the source DataBasket.
    * @param ssDest the destination Stock.
    * @param uigGate the Gate at which the FormSheet will be displayed. If this is <code>null</code> it must be
    * {@link #setGate set} before actually using the FormSheet.
    */
  public static TwoTableFormSheet create (String sCaption,
                                          DataBasket dbSource,
                                          StoringStock ssDest,
                                          UIGate uigGate) {
    return create (sCaption,
                   dbSource,
                   ssDest,
                   uigGate,
                   null,
                   null,
                   null,
                   null,
                   null);
  }

  // 7. Catalog -> Catalog

  /**
    * Create and return a new TwoTableFormSheet where source and destination are Catalogs.
    *
    * @param sCaption the caption of the FormSheet.
    * @param cSource the source Catalog.
    * @param cDest the destination Catalog.
    * @param uigGate the Gate at which the FormSheet will be displayed. If this is <code>null</code> it must be
    * {@link #setGate set} before actually using the FormSheet.
    * @param cmpSource a comparator defining the source sorting order. The items to be compared are
    * {@link CatalogItem CatalogItems}. If <code>null</code> the items will be sorted according to their
    * natural ordering.
    * @param cmpDest a comparator defining the destination sorting order. The items to be compared are
    * {@link CatalogItem CatalogItems}. If <code>null</code> the items will be sorted according to their
    * natural ordering.
    * @param tedSource a TableEntryDescriptor that can split CatalogItems into a table's cells. It will be used
    * for the source table. If <code>null</code> and <code>cSource</code> is a {@link Currency} it defaults to
    * a {@link DefaultCurrencyItemTED} using <code>cSource</code> to format values. Otherwise, it defaults to a
    * {@link DefaultCatalogItemTED}.
    * @param tedDest a TableEntryDescriptor that can split CatalogItems into a table's cells. It will be used
    * for the destination table. If <code>null</code> and <code>cDest</code> is a {@link Currency} it defaults
    * to a {@link DefaultCurrencyItemTED} using <code>cDest</code> to format values. Otherwise, it defaults to
    * a {@link DefaultCatalogItemTED}.
    * @param ccsMoveStrategy the strategy to be used when moving items between source and destination. If
    * <code>null</code> it defaults to a {@link CCStrategy} object.
    */
  public static TwoTableFormSheet create (String sCaption,
                                          final Catalog cSource,
                                          final Catalog cDest,
                                          final DataBasket db,
                                          UIGate uigGate,
                                          final Comparator cmpSource,
                                          final Comparator cmpDest,
                                          final TableEntryDescriptor tedSource,
                                          final TableEntryDescriptor tedDest,
                                          CCStrategy ccsMoveStrategy) {
    FormSheetContentCreator fscc = new FormSheetContentCreator() {
      protected void createFormSheetContent (FormSheet fs) {
        final TwoTableFormSheet ttfs = (TwoTableFormSheet) fs;

        JCatalogTable jctSource = new JCatalogTable (cSource,
                                                     db,
                                                     cmpSource,
                                                     ((tedSource != null)?
                                                      (tedSource):
                                                      ((cSource instanceof Currency)?
                                                       (new DefaultCurrencyItemTED ((Currency) cSource)):
                                                       (new DefaultCatalogItemTED()))));
        jctSource.setSelectionObserver (ttfs.m_anLeftSelection);
        final util.swing.AbstractTableModel atmSource = (util.swing.AbstractTableModel) jctSource.getModel();

        ttfs.m_atmLeftModel = atmSource;

        JCatalogTable jctDest = new JCatalogTable (cDest,
                                                   db,
                                                   cmpDest,
                                                   ((tedDest != null)?
                                                    (tedDest):
                                                    ((cDest instanceof Currency)?
                                                     (new DefaultCurrencyItemTED ((Currency) cDest)):
                                                     (new DefaultCatalogItemTED()))));
        jctDest.setSelectionObserver (ttfs.m_anRightSelection);
        final util.swing.AbstractTableModel atmDest = (util.swing.AbstractTableModel) jctDest.getModel();

        ttfs.m_atmRightModel = atmDest;

        JButton jbRight = new JButton (getResourceText (BUTTON_RIGHT));
        JButton jbLeft = new JButton (getResourceText (BUTTON_LEFT));

        JPanel jpForm = new JPanel();
        jpForm.setLayout (new BoxLayout (jpForm, BoxLayout.X_AXIS));

        jpForm.add (new JScrollPane (jctSource));

        jpForm.add (createCentralBox (jbRight, jbLeft, null, ttfs.getStrategy()));

        jpForm.add (new JScrollPane (jctDest));

        if (ttfs.getStrategy().canMoveToDest()) {
          jbRight.addActionListener (new ActionActionListener (fs) {
            public void doAction (SaleProcess p, final SalesPoint sp) {
              final CatalogItem ci = (CatalogItem) atmSource.getRecord (ttfs.m_anLeftSelection[0]);

              if (ci != null) {
                UIGate uig = ttfs.getGate();
                if (uig != null) {
                  uig.setNextTransition (((CCStrategy) ttfs.getStrategy()).getMoveToDestProcess (p, sp, cSource, cDest, db, ci, ttfs));
                }
              }
            }
          });
        }

        if (ttfs.getStrategy().canMoveToSource()) {
          jbLeft.addActionListener (new ActionActionListener (fs) {
            public void doAction (SaleProcess p, final SalesPoint sp) {
              final CatalogItem ci = (CatalogItem) atmDest.getRecord (ttfs.m_anRightSelection[0]);

              if (ci != null) {
                UIGate uig = ttfs.getGate();
                if (uig != null) {
                  uig.setNextTransition (((CCStrategy) ttfs.getStrategy()).getMoveToSourceProcess (p, sp, cSource, cDest, db, ci, ttfs));
                }
              }
            }
          });
        }

        fs.setComponent (jpForm);
      }
    };

    ccsMoveStrategy = ((ccsMoveStrategy != null)?
                       (ccsMoveStrategy):
                       (new CCStrategy()));

    return new TwoTableFormSheet (sCaption,
                                  fscc,
                                  uigGate,
                                  ccsMoveStrategy);
  }

  /**
    * Create and return a new TwoTableFormSheet where source and destination are Catalogs.
    *
    * <p>Calls the appropriate fully parameterized function, passing <code>null</code> for the missing
    * parameters.</p>
    *
    * @param sCaption the caption of the FormSheet.
    * @param cSource the source Catalog.
    * @param cDest the destination Catalog.
    * @param uigGate the Gate at which the FormSheet will be displayed. If this is <code>null</code> it must be
    * {@link #setGate set} before actually using the FormSheet.
    */
  public static TwoTableFormSheet create (String sCaption,
                                          Catalog cSource,
                                          Catalog cDest,
                                          DataBasket db,
                                          UIGate uigGate) {
    return create (sCaption,
                   cSource,
                   cDest,
                   db,
                   uigGate,
                   null,
                   null,
                   null,
                   null,
                   null);
  }

  // 8. Catalog -> DataBasket

  /**
    * Create and return a new TwoTableFormSheet where the source is a Catalog and the destination is a
    * DataBasket.
    *
    * @param sCaption the caption of the FormSheet.
    * @param cSource the source Catalog.
    * @param dbDest the destination Databasket.
    * @param uigGate the Gate at which the FormSheet will be displayed. If this is <code>null</code> it must be
    * {@link #setGate set} before actually using the FormSheet.
    * @param cmpSource a comparator defining the source sorting order. The items to be compared are
    * {@link CatalogItem CatalogItems}. If <code>null</code> the items will be sorted according to their
    * natural ordering.
    * @param cmpDest a comparator defining the destination sorting order. The objects to be compared will be
    * DataBasketEntries. If <code>null</code> the entries will be ordered first by their main and then by their
    * secondary keys.
    * @param tedSource a TableEntryDescriptor that can split CatalogItems into a table's cells. It will be used
    * for the source table. If <code>null</code> and <code>cSource</code> is a {@link Currency} it defaults to
    * a {@link DefaultCurrencyItemTED} using <code>cSource</code> to format values. Otherwise, it defaults to a
    * {@link DefaultCatalogItemTED}.
    * @param tedDest a TableEntryDescriptor that can split individual {@link DataBasketEntry DataBasketEntries}
    * into a table's cells. It will be used for the destination table. If <code>null</code>  it defaults to a
    * {@link DefaultCatalogItemDBETableEntryDescriptor}.
    * @param cdbsMoveStrategy the strategy to be used when moving items between source and destination. If
    * <code>null</code> it defaults to a {@link CDBStrategy} object.
    */
  public static TwoTableFormSheet create (String sCaption,
                                          final Catalog cSource,
                                          final DataBasket dbDest,
                                          UIGate uigGate,
                                          final Comparator cmpSource,
                                          final Comparator cmpDest,
                                          final TableEntryDescriptor tedSource,
                                          final TableEntryDescriptor tedDest,
                                          CDBStrategy cdbsMoveStrategy) {

    FormSheetContentCreator fscc = new FormSheetContentCreator() {
      protected void createFormSheetContent (FormSheet fs) {
        final TwoTableFormSheet ttfs = (TwoTableFormSheet) fs;

        JCatalogTable jctSource = new JCatalogTable (cSource,
                                                     dbDest,
                                                     cmpSource,
                                                     ((tedSource != null)?
                                                      (tedSource):
                                                      ((cSource instanceof Currency)?
                                                       (new DefaultCurrencyItemTED ((Currency) cSource)):
                                                       (new DefaultCatalogItemTED()))));
        jctSource.setSelectionObserver (ttfs.m_anLeftSelection);
        final util.swing.AbstractTableModel atmSource = (util.swing.AbstractTableModel) jctSource.getModel();

        ttfs.m_atmLeftModel = atmSource;

        JDataBasketTable jdbtDest = new JDataBasketTable (dbDest,
                                                          DataBasketConditionImpl.allCatalogItemsWithSource (cSource),
                                                          null,
                                                          cmpDest,
                                                          ((tedDest != null)?
                                                           (tedDest):
                                                           (new DefaultCatalogItemDBETableEntryDescriptor())));
        jdbtDest.setSelectionObserver (ttfs.m_anRightSelection);
        final util.swing.AbstractTableModel atmDest = (util.swing.AbstractTableModel) jdbtDest.getModel();

        ttfs.m_atmRightModel = atmDest;

        JButton jbRight = new JButton (getResourceText (BUTTON_RIGHT));
        JButton jbLeft = new JButton (getResourceText (BUTTON_LEFT));

        JPanel jpForm = new JPanel();
        jpForm.setLayout (new BoxLayout (jpForm, BoxLayout.X_AXIS));

        jpForm.add (new JScrollPane (jctSource));

        jpForm.add (createCentralBox (jbRight, jbLeft, null, ttfs.getStrategy()));

        jpForm.add (new JScrollPane (jdbtDest));

        if (ttfs.getStrategy().canMoveToDest()) {
          jbRight.addActionListener (new ActionActionListener (fs) {
            public void doAction (SaleProcess p, final SalesPoint sp) {
              CatalogItem ci = (CatalogItem) atmSource.getRecord (ttfs.m_anLeftSelection[0]);

              if (ci != null) {
                UIGate uig = ttfs.getGate();
                if (uig != null) {
                  uig.setNextTransition (((CDBStrategy) ttfs.getStrategy()).getMoveToDestProcess (p, sp, cSource, dbDest, ci, ttfs));
                }
              }
            }
          });
        }

        if (ttfs.getStrategy().canMoveToSource()) {
          jbLeft.addActionListener (new ActionActionListener (fs) {
            public void doAction (SaleProcess p, final SalesPoint sp) {
              DataBasketEntry dbe = (DataBasketEntry) atmDest.getRecord (ttfs.m_anRightSelection[0]);

              if (dbe != null) {
                CatalogItem ci = (CatalogItem) dbe.getValue();

                if (ci != null) {
                  UIGate uig = ttfs.getGate();
                  if (uig != null) {
                    uig.setNextTransition (((CDBStrategy) ttfs.getStrategy()).getMoveToSourceProcess (p, sp, cSource, dbDest, ci, ttfs));
                  }
                }
              }
            }
          });
        }

        fs.setComponent (jpForm);
      }
    };

    cdbsMoveStrategy = ((cdbsMoveStrategy != null)?
                        (cdbsMoveStrategy):
                        (new CDBStrategy()));

    return new TwoTableFormSheet (sCaption,
                                  fscc,
                                  uigGate,
                                  cdbsMoveStrategy);
  }

  /**
    * Create and return a new TwoTableFormSheet where the source is a Catalog and the destination is a
    * DataBasket.
    *
    * <p>Calls the appropriate fully parameterized function, passing <code>null</code> for the missing
    * parameters.</p>
    *
    * @param sCaption the caption of the FormSheet.
    * @param cSource the source Catalog.
    * @param dbDest the destination Databasket.
    * @param uigGate the Gate at which the FormSheet will be displayed. If this is <code>null</code> it must be
    * {@link #setGate set} before actually using the FormSheet.
    */
  public static TwoTableFormSheet create (String sCaption,
                                          final Catalog cSource,
                                          final DataBasket dbDest,
                                          UIGate uigGate) {
  return create (sCaption,
                 cSource,
                 dbDest,
                 uigGate,
                 null,
                 null,
                 null,
                 null,
                 null);
  }

  // 9. DataBasket -> Catalog

  /**
    * Create and return a new TwoTableFormSheet where the source is a DataBasket and the destination is a
    * Catalog.
    *
    * @param sCaption the caption of the FormSheet.
    * @param dbSource the source Databasket.
    * @param cDest the destination Catalog.
    * @param uigGate the Gate at which the FormSheet will be displayed. If this is <code>null</code> it must be
    * {@link #setGate set} before actually using the FormSheet.
    * @param cmpSource a comparator defining the source sorting order. The objects to be compared will be
    * DataBasketEntries. If <code>null</code> the entries will be ordered first by their main and then by their
    * secondary keys.
    * @param cmpSource a comparator defining the destination sorting order. The items to be compared are
    * {@link CatalogItem CatalogItems}. If <code>null</code> the items will be sorted according to their
    * natural ordering.
    * @param tedSource a TableEntryDescriptor that can split individual
    * {@link DataBasketEntry DataBasketEntries} into a table's cells. It will be used for the source table.
    * If <code>null</code>  it defaults to a {@link DefaultCatalogItemDBETableEntryDescriptor}.
    * @param tedDest a TableEntryDescriptor that can split CatalogItems into a table's cells. It will be used
    * for the destination table. If <code>null</code> and <code>cDest</code> is a {@link Currency} it defaults
    * to a {@link DefaultCurrencyItemTED} using <code>cDest</code> to format values. Otherwise, it defaults to
    * a {@link DefaultCatalogItemTED}.
    * @param dbcsMoveStrategy the strategy to be used when moving items between source and destination. If
    * <code>null</code> it defaults to a {@link DBCStrategy} object.
    */
  public static TwoTableFormSheet create (String sCaption,
                                          final DataBasket dbSource,
                                          final Catalog cDest,
                                          UIGate uigGate,
                                          final Comparator cmpSource,
                                          final Comparator cmpDest,
                                          final TableEntryDescriptor tedSource,
                                          final TableEntryDescriptor tedDest,
                                          DBCStrategy dbcsMoveStrategy) {

    FormSheetContentCreator fscc = new FormSheetContentCreator() {
      protected void createFormSheetContent (FormSheet fs) {
        final TwoTableFormSheet ttfs = (TwoTableFormSheet) fs;

        JDataBasketTable jdbtSource = new JDataBasketTable (dbSource,
                                                            new DataBasketConditionImpl (DataBasketKeys.CATALOG_ITEM_MAIN_KEY,
                                                                                         null,
                                                                                         null,
                                                                                         null,
                                                                                         null) {
                                                              public boolean match (DataBasketEntry dbe) {
                                                                return (dbe.getDestination() == null);
                                                              }
                                                            },
                                                            null,
                                                            cmpSource,
                                                            ((tedSource != null)?
                                                            (tedSource):
                                                            (new DefaultCountingStockDBETableEntryDescriptor())));
        jdbtSource.setSelectionObserver (ttfs.m_anLeftSelection);
        final util.swing.AbstractTableModel atmSource = (util.swing.AbstractTableModel) jdbtSource.getModel();

        ttfs.m_atmLeftModel = atmSource;

        JCatalogTable jctDest = new JCatalogTable (cDest,
                                                   dbSource,
                                                   cmpDest,
                                                   ((tedDest != null)?
                                                    (tedDest):
                                                    ((cDest instanceof Currency)?
                                                     (new DefaultCurrencyItemTED ((Currency) cDest)):
                                                     (new DefaultCatalogItemTED()))));
        jctDest.setSelectionObserver (ttfs.m_anRightSelection);
        final util.swing.AbstractTableModel atmDest = (util.swing.AbstractTableModel) jctDest.getModel();

        ttfs.m_atmRightModel = atmDest;

        JButton jbRight = new JButton (getResourceText (BUTTON_RIGHT));
        JButton jbLeft = new JButton (getResourceText (BUTTON_LEFT));

        JPanel jpForm = new JPanel();
        jpForm.setLayout (new BoxLayout (jpForm, BoxLayout.X_AXIS));

        jpForm.add (new JScrollPane (jdbtSource));

        jpForm.add (createCentralBox (jbRight, jbLeft, null, ttfs.getStrategy()));

        jpForm.add (new JScrollPane (jctDest));

        if (ttfs.getStrategy().canMoveToDest()) {
          jbRight.addActionListener (new ActionActionListener (fs) {
            public void doAction (SaleProcess p, final SalesPoint sp) {
              CatalogItem ci = (CatalogItem) ((DataBasketEntry) atmSource.getRecord (ttfs.m_anLeftSelection[0])).getValue();

              if (ci != null) {
                UIGate uig = ttfs.getGate();
                if (uig != null) {
                  uig.setNextTransition (((DBCStrategy) ttfs.getStrategy()).getMoveToDestProcess (p, sp, dbSource, cDest, ci, ttfs));
                }
              }
            }
          });
        }

        if (ttfs.getStrategy().canMoveToSource()) {
          jbLeft.addActionListener (new ActionActionListener (fs) {
            public void doAction (SaleProcess p, final SalesPoint sp) {
              CatalogItem ci = (CatalogItem) atmDest.getRecord (ttfs.m_anRightSelection[0]);

              if (ci != null) {
                UIGate uig = ttfs.getGate();
                if (uig != null) {
                  uig.setNextTransition (((DBCStrategy) ttfs.getStrategy()).getMoveToSourceProcess (p, sp, dbSource, cDest, ci, ttfs));
                }
              }
            }
          });
        }

        fs.setComponent (jpForm);
      }
    };

    dbcsMoveStrategy = ((dbcsMoveStrategy != null)?
                        (dbcsMoveStrategy):
                        (new DBCStrategy()));

    return new TwoTableFormSheet (sCaption,
                                  fscc,
                                  uigGate,
                                  dbcsMoveStrategy);
  }

  /**
    * Create and return a new TwoTableFormSheet where the source is a DataBasket and the destination is a
    * Catalog.
    *
    * <p>Calls the appropriate fully parameterized function, passing <code>null</code> for the missing
    * parameters.</p>
    *
    * @param sCaption the caption of the FormSheet.
    * @param dbSource the source Databasket.
    * @param cDest the destination Catalog.
    * @param uigGate the Gate at which the FormSheet will be displayed. If this is <code>null</code> it must be
    * {@link #setGate set} before actually using the FormSheet.
    */
  public static TwoTableFormSheet create (String sCaption,
                                          DataBasket dbSource,
                                          Catalog cDest,
                                          UIGate uigGate) {
    return create (sCaption,
                   dbSource,
                   cDest,
                   uigGate,
                   null,
                   null,
                   null,
                   null,
                   null);
  }

  // 10. Catalog -> StoringStock

  /**
    * Create and return a new TwoTableFormSheet where the source is a Catalog and the destination is a
    * StoringStock.
    *
    * @param sCaption the caption of the FormSheet.
    * @param cSource the source Catalog.
    * @param ssDest the destination Stock.
    * @param uigGate the Gate at which the FormSheet will be displayed. If this is <code>null</code> it must be
    * {@link #setGate set} before actually using the FormSheet.
    * @param cmpSource a comparator defining the source sorting order. The items to be compared are
    * {@link CatalogItem CatalogItems}. If <code>null</code> the items will be sorted according to their
    * natural ordering.
    * @param cmpDest a comparator defining the destination sorting order. The objects to be compared will be
    * the individual items. If <code>null</code> the ordering will be the natural ordering of the items.
    * @param tedSource a TableEntryDescriptor that can split CatalogItems into a table's cells. It will be used
    * for the source table. If <code>null</code> and <code>cSource</code> is a {@link Currency} it defaults to
    * a {@link DefaultCurrencyItemTED} using <code>cSource</code> to format values. Otherwise, it defaults to a
    * {@link DefaultCatalogItemTED}.
    * @param tedDest a TableEntryDescriptor that can split individual {@link StockItem StockItems} into a
    * table's cells. It will be used for the destination table. If <code>null</code> it defaults to a
    * {@link DefaultStockItemTED}.
    * @param csssMoveStrategy the strategy to be used when moving items between source and destination.
    * <strong>Attention</strong>: Must not be <code>null</code>!
    */
  public static TwoTableFormSheet create (String sCaption,
                                          final Catalog cSource,
                                          final StoringStock ssDest,
                                          final DataBasket db,
                                          UIGate uigGate,
                                          final Comparator cmpSource,
                                          final Comparator cmpDest,
                                          final TableEntryDescriptor tedSource,
                                          final TableEntryDescriptor tedDest,
                                          CSSStrategy csssMoveStrategy) {  // EXCEPTION : STRATEGY MUST NOT BE NULL!!!
    FormSheetContentCreator fscc = new FormSheetContentCreator() {
      protected void createFormSheetContent (FormSheet fs) {
        final TwoTableFormSheet ttfs = (TwoTableFormSheet) fs;

        JCatalogTable jctSource = new JCatalogTable (cSource,
                                                     db,
                                                     cmpSource,
                                                     ((tedSource != null)?
                                                      (tedSource):
                                                      ((cSource instanceof Currency)?
                                                       (new DefaultCurrencyItemTED ((Currency) cSource)):
                                                       (new DefaultCatalogItemTED()))));
        jctSource.setSelectionObserver (ttfs.m_anLeftSelection);
        final util.swing.AbstractTableModel atmSource = (util.swing.AbstractTableModel) jctSource.getModel();

        ttfs.m_atmLeftModel = atmSource;

        JStoringStockTable jsstDest = new JStoringStockTable (ssDest,
                                                              db,
                                                              cmpDest,
                                                              ((tedDest != null)?
                                                               (tedDest):
                                                               (new DefaultStockItemTED())));
        jsstDest.setSelectionObserver (ttfs.m_anRightSelection);
        final util.swing.AbstractTableModel atmDest = (util.swing.AbstractTableModel) jsstDest.getModel();

        ttfs.m_atmRightModel = atmDest;

        JButton jbRight = new JButton (getResourceText (BUTTON_RIGHT));
        JButton jbLeft = new JButton (getResourceText (BUTTON_LEFT));

        JPanel jpForm = new JPanel();
        jpForm.setLayout (new BoxLayout (jpForm, BoxLayout.X_AXIS));

        jpForm.add (new JScrollPane (jctSource));

        jpForm.add (createCentralBox (jbRight, jbLeft, null, ttfs.getStrategy()));

        jpForm.add (new JScrollPane (jsstDest));

        if (ttfs.getStrategy().canMoveToDest()) {
          jbRight.addActionListener (new ActionActionListener (fs) {
            public void doAction (SaleProcess p, final SalesPoint sp) {
              CatalogItem ci = (CatalogItem) atmSource.getRecord (ttfs.m_anLeftSelection[0]);

              if (ci != null) {
                UIGate uig = ttfs.getGate();
                if (uig != null) {
                  uig.setNextTransition (((CSSStrategy) ttfs.getStrategy()).getMoveToDestProcess (p, sp, cSource, ssDest, db, ci, ttfs));
                }
              }
            }
          });
        }

        if (ttfs.getStrategy().canMoveToSource()) {
          jbLeft.addActionListener (new ActionActionListener (fs) {
            public void doAction (SaleProcess p, final SalesPoint sp) {
              StockItem si = (StockItem) atmDest.getRecord (ttfs.m_anRightSelection[0]);

              if (si != null) {
                UIGate uig = ttfs.getGate();
                if (uig != null) {
                  uig.setNextTransition (((CSSStrategy) ttfs.getStrategy()).getMoveToSourceProcess (p, sp, cSource, ssDest, db, si, ttfs));
                }
              }
            }
          });
        }

        fs.setComponent (jpForm);
      }
    };

    return new TwoTableFormSheet (sCaption,
                                  fscc,
                                  uigGate,
                                  csssMoveStrategy);
  }

  /**
    * Create and return a new TwoTableFormSheet where the source is a Catalog and the destination is a
    * StoringStock.
    *
    * <p>Calls the appropriate fully parameterized function, passing <code>null</code> for the missing
    * parameters.</p>
    *
    * @param sCaption the caption of the FormSheet.
    * @param cSource the source Catalog.
    * @param ssDest the destination Stock.
    * @param uigGate the Gate at which the FormSheet will be displayed. If this is <code>null</code> it must be
    * {@link #setGate set} before actually using the FormSheet.
    * @param csssMoveStrategy the strategy to be used when moving items between source and destination.
    * <strong>Attention</strong>: Must not be <code>null</code>!
    */
  public static TwoTableFormSheet create (String sCaption,
                                          Catalog cSource,
                                          StoringStock ssDest,
                                          DataBasket db,
                                          UIGate uigGate,
                                          CSSStrategy csssMoveStrategy) {  // EXCEPTION : STRATEGY MUST NOT BE NULL!!!
    return create (sCaption,
                   cSource,
                   ssDest,
                   db,
                   uigGate,
                   null,
                   null,
                   null,
                   null,
                   csssMoveStrategy);
  }

  // 11. Catalog -> CountingStock

  /**
    * Create and return a new TwoTableFormSheet where the source is a Catalog and the destination is a
    * CountingStock.
    *
    * @param sCaption the caption of the FormSheet.
    * @param cSource the source Catalog.
    * @param ssDest the destination Stock.
    * @param uigGate the Gate at which the FormSheet will be displayed. If this is <code>null</code> it must be
    * {@link #setGate set} before actually using the FormSheet.
    * @param cmpSource a comparator defining the source sorting order. The items to be compared are
    * {@link CatalogItem CatalogItems}. If <code>null</code> the items will be sorted according to their
    * natural ordering.
    * @param cmpDest a comparator defining the destination sorting order. The objects to be compared will be
    * the keys of the individual items. If <code>null</code> the ordering will be the natural ordering of the
    * keys.
    * @param fShowZeros if false, destination lines containing '0' in the &quot;Count&quot; column will be
    * hidden.
    * @param tedSource a TableEntryDescriptor that can split CatalogItems into a table's cells. It will be used
    * for the source table. If <code>null</code> and <code>cSource</code> is a {@link Currency} it defaults to
    * a {@link DefaultCurrencyItemTED} using <code>cSource</code> to format values. Otherwise, it defaults to a
    * {@link DefaultCatalogItemTED}.
    * @param tedDest a TableEntryDescriptor that can split individual
    * {@link CountingStockTableModel.Record CountingStockTableModel records} into a table's cells. It will be
    * used for the destination table. If <code>null</code> and <code>csDest</code> is a {@link MoneyBag} it
    * defaults to a {@link DefaultMoneyBagItemTED} using <code>csDest.getCatalog()</code> to format values.
    * Otherwise, it defaults to a {@link DefaultCountingStockItemTED}.
    * @param ccssMoveStrategy the strategy to be used when moving items between source and destination. If
    * <code>null</code>, defaults to a {@link CCSStrategy} object.
    */
  public static TwoTableFormSheet create (String sCaption,
                                          final Catalog cSource,
                                          final CountingStock csDest,
                                          final DataBasket db,
                                          UIGate uigGate,
                                          final Comparator cmpSource,
                                          final Comparator cmpDest,
                                          final boolean fShowZeros,
                                          final TableEntryDescriptor tedSource,
                                          final TableEntryDescriptor tedDest,
                                          CCSStrategy ccssMoveStrategy) {
    FormSheetContentCreator fscc = new FormSheetContentCreator() {
      protected void createFormSheetContent (FormSheet fs) {
        final TwoTableFormSheet ttfs = (TwoTableFormSheet) fs;

        final int[] anCounter = { 1 };

        JCatalogTable jctSource = new JCatalogTable (cSource,
                                                     db,
                                                     cmpSource,
                                                     ((tedSource != null)?
                                                      (tedSource):
                                                      ((cSource instanceof Currency)?
                                                       (new DefaultCurrencyItemTED ((Currency) cSource)):
                                                       (new DefaultCatalogItemTED()))));
        jctSource.setSelectionObserver (ttfs.m_anLeftSelection);
        final util.swing.AbstractTableModel atmSource = (util.swing.AbstractTableModel) jctSource.getModel();

        ttfs.m_atmLeftModel = atmSource;

        JCountingStockTable jcstDest = new JCountingStockTable (csDest,
                                                                db,
                                                                cmpDest,
                                                                fShowZeros,
                                                                ((tedDest != null)?
                                                                 (tedDest):
                                                                 ((csDest instanceof MoneyBag)?
                                                                  (new DefaultMoneyBagItemTED ((Currency) csDest.getCatalog (db))):
                                                                  (new DefaultCountingStockItemTED()))));
        jcstDest.setSelectionObserver (ttfs.m_anRightSelection);
        final util.swing.AbstractTableModel atmDest = (util.swing.AbstractTableModel) jcstDest.getModel();

        ttfs.m_atmRightModel = atmDest;

        JTextField jtf = new JIntInput (anCounter, 1, 1, Integer.MAX_VALUE);

        JButton jbRight = new JButton (getResourceText (BUTTON_RIGHT));
        JButton jbLeft = new JButton (getResourceText (BUTTON_LEFT));

        JPanel jpForm = new JPanel();
        jpForm.setLayout (new BoxLayout (jpForm, BoxLayout.X_AXIS));

        jpForm.add (new JScrollPane (jctSource));

        jpForm.add (createCentralBox (jbRight, jbLeft, jtf, ttfs.getStrategy()));

        jpForm.add (new JScrollPane (jcstDest));

        if (ttfs.getStrategy().canMoveToDest()) {
          jbRight.addActionListener (new ActionActionListener (fs) {
            public void doAction (SaleProcess p, final SalesPoint sp) {
              CatalogItem ci = (CatalogItem) atmSource.getRecord (ttfs.m_anLeftSelection[0]);

              if (ci != null) {
                UIGate uig = ttfs.getGate();
                if (uig != null) {
                  uig.setNextTransition (((CCSStrategy) ttfs.getStrategy()).getMoveToDestProcess (p, sp, cSource, csDest, db, ci, anCounter[0], ttfs));
                }
              }
            }
          });
        }

        if (ttfs.getStrategy().canMoveToSource()) {
          jbLeft.addActionListener (new ActionActionListener (fs) {
            public void doAction (SaleProcess p, final SalesPoint sp) {
              CountingStockTableModel.Record r = (CountingStockTableModel.Record) atmDest.getRecord (ttfs.m_anRightSelection[0]);

              if (r != null) {
                UIGate uig = ttfs.getGate();
                if (uig != null) {
                  uig.setNextTransition (((CCSStrategy) ttfs.getStrategy()).getMoveToSourceProcess (p, sp, cSource, csDest, db, r.getDescriptor(), anCounter[0], ttfs));
                }
              }
            }
          });
        }

        fs.setComponent (jpForm);
      }
    };

    ccssMoveStrategy = ((ccssMoveStrategy != null)?
                        (ccssMoveStrategy):
                        (new CCSStrategy()));

    return new TwoTableFormSheet (sCaption,
                                  fscc,
                                  uigGate,
                                  ccssMoveStrategy);
  }

  /**
    * Create and return a new TwoTableFormSheet where the source is a Catalog and the destination is a
    * CountingStock.
    *
    * <p>Calls the appropriate fully parameterized function, passing default values (i.e. <code>null</code> or
    * false) for the missing parameters.</p>
    *
    * @param sCaption the caption of the FormSheet.
    * @param cSource the source Catalog.
    * @param ssDest the destination Stock.
    * @param uigGate the Gate at which the FormSheet will be displayed. If this is <code>null</code> it must be
    * {@link #setGate set} before actually using the FormSheet.
    */
  public static TwoTableFormSheet create (String sCaption,
                                          Catalog cSource,
                                          CountingStock csDest,
                                          DataBasket db,
                                          UIGate uigGate) {
  return create (sCaption,
                 cSource,
                 csDest,
                 db,
                 uigGate,
                 null,
                 null,
                 false,
                 null,
                 null,
                 null);
  }

  // helper routines...

  /**
    * Internal helper function creating the central box with the two buttons and the input line.
    */
  private static final Box createCentralBox (JButton jbRight,
                                             JButton jbLeft,
                                             JTextField jtf,
                                             MoveStrategy ms) {

    Box b = Box.createVerticalBox();
    b.add (Box.createGlue());

    if (jtf != null) {
      jtf.setMaximumSize (new java.awt.Dimension (jbRight.getMaximumSize().width * 2 - 1, jtf.getMinimumSize().height));
      b.add (jtf);
    }

    if (ms.canMoveToDest()) {
      b.add (jbRight);
    }

    if (ms.canMoveToSource()) {
      b.add (jbLeft);
    }
    b.add (Box.createGlue());

    Box bButtonBar = Box.createHorizontalBox();
    bButtonBar.add (Box.createGlue());
    bButtonBar.add (b);
    bButtonBar.add (Box.createGlue());

    return bButtonBar;
  }

  // resource stuff...

  /**
    * Resource identifier of the '<<' button's label. Is &quot;2TableFormSheet.button.left&quot;
    */
  public static final String BUTTON_LEFT = "2TableFormSheet.button.left";

  /**
    * Resource identifier of the '>>' button's label. Is &quot;2TableFormSheet.button.right&quot;
    */
  public static final String BUTTON_RIGHT = "2TableFormSheet.button.right";

  /**
    * The resource bundle that knows the buttons' labels.
    */
  private static ResourceBundle s_rbTexts = new ListResourceBundle() {
    protected Object[][] getContents() {
      return s_aaoContents;
    }

    private final Object[][] s_aaoContents = {
      {BUTTON_RIGHT, ">>"},
      {BUTTON_LEFT, "<<"}
    };
  };

  /**
    * Monitor synchronizing access to the resource.
    */
  private static final Object s_oResourceLock = new Object();

  /**
    * Set the resource bundle that knows the labels for the buttons.
    *
    * <p>The resource must contain Strings for all items needed by TwoTableFormSheet, specifically
    * {@link #BUTTON_LEFT} and {@link #BUTTON_RIGHT}.</p>
    */
  public static final void setTextResource (ResourceBundle rb) {
    synchronized (s_oResourceLock) {
      s_rbTexts = rb;
    }
  }

  /**
    * Get a String from the global TwoTableFormSheet resource bundle.
    *
    * @param sKey the key for which to find the text.
    */
  public static final String getResourceText (String sKey) {
    synchronized (s_oResourceLock) {
      return s_rbTexts.getString (sKey);
    }
  }
}