//----------------------------------------------------------------------------
// COMPONENT NAME: LPEX Editor
//
// © Copyright IBM Corporation 2004, 2007
// All Rights Reserved.
//
// DESCRIPTION:
// SetDateAction - sample user-defined action, command (setDate)
//----------------------------------------------------------------------------

package com.ibm.lpex.samples;

import com.ibm.lpex.core.LpexAction;
import com.ibm.lpex.core.LpexCommand;
import com.ibm.lpex.core.LpexDocumentLocation;
import com.ibm.lpex.core.LpexStringTokenizer;
import com.ibm.lpex.core.LpexView;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Sample action <b>setDate</b> - set the sequence-numbers date in the selected
 * range.  Use this action to modify the textual part of the sequence numbers in
 * all the currently-selected visible lines.  The user is prompted for the new
 * text.  Note that LPEX doesn't record changes to the sequence numbers in its
 * undo stack.
 *
 * <p>Here is the SetDateAction
 * <a href="doc-files/SetDateAction.java.html">source code</a>.</p>
 *
 * <p>To run this sample:
 * <ul>
 *  <li>Define this user action via an editor preference page, where available,
 *   or from the editor command line:
 *   <pre>set actionClass.setDate com.ibm.lpex.samples.SetDateAction</pre></li>
 *  <li>Run it from the editor command line:
 *   <pre>action setDate</pre>
 *   or associate it with a key (here, <b>Ctrl+T</b> in the text area):
 *   <pre>set keyAction.c-t setDate</pre></li>
 * </ul></p>
 *
 * @see com.ibm.lpex.samples All the samples
 */
public class SetDateAction implements LpexAction
{
 /**
  * Sample command <b>setDate</b> - set the sequence-numbers date in the
  * selected range.  Use this command to modify the textual part of the sequence
  * numbers in all the currently-selected visible lines.  If you do not specify
  * the new text via the command parameter, you will be prompted for it.  Note
  * that LPEX doesn't record changes to the sequence numbers in its undo stack.
  *
  * <p>This is an alternative to the <b>setDate</b> sample action, for cases in
  * which the user prefers the command approach, or is running out of available
  * keys to assign to actions.</p>
  *
  * <p>Defined as a separate, named class inside {@link SetDateAction} (where
  * you can also see its <a href="doc-files/SetDateAction.java.html#line47">
  * source code</a>), it can be registered in the editor with, for example:
  * <pre>   set commandClass.setDate com.ibm.lpex.samples.SetDateAction$SetDate</pre></p>
  * and run from the command line:
  * <pre>   setDate [ <i>newDate</i> ]</pre></p>
  */
 public static class SetDate implements LpexCommand
 {
  /**
   * Runs this command.
   * Uses the specified date, or otherwise prompts the user for one, and updates
   * the sequence numbers of all the visible selected lines.
   *
   * @param lpexView the document view in which the command was issued
   * @param parameters optional new date to set
   */
  public boolean doCommand(LpexView lpexView, String parameters)
  {
   if (lpexView != null)
    {
     if ("?".equals(parameters.trim())) // command help
      {
       lpexView.doCommand("set messageText Syntax: setDate [<new date>]");
       return true;
      }

     if (lpexView.queryOn("readonly"))
      {
       lpexView.doDefaultCommand("set messageText Document is read only.");
      }
     else if (sequenceNumbersTextLength(lpexView) == 0)
      {
       lpexView.doDefaultCommand("set messageText Document has no sequence-numbers text part.");
      }
     else if (lpexView.currentElement() == 0)
      {
       lpexView.doDefaultCommand("set messageText No visible elements.");
      }
     else
      {
       // (a) use the user-supplied parameter as the new date
       if (parameters.length() != 0)
        {
         return doSetDateCommand.doCommand(lpexView, parameters);
        }

       // (b) no parameters: do what the SetDateAction does (prompt user, etc.)
       return internalDoAction(lpexView);
      }
    }

   return true;
  }
 }


 /**
  * Runs the action.
  * Prompts the user for a new date, then updates the sequence numbers for all
  * the visible selected lines.
  *
  * <p>Uses the <b>input</b> built-in editor command, which in turn issues the
  * <b>doSetDate</b> helper command defined in here.  It checks for, and if
  * needed registers, an instance of the <b>doSetDate</b> command in the view.</p>
  */
 public void doAction(LpexView lpexView)
 {
  internalDoAction(lpexView);
 }

 /**
  * Returns the availability of this action.
  * The action can be run in any visible, writable view of a document with
  * sequence numbers which have a textual part.
  */
 public boolean available(LpexView lpexView)
 {
  return canSetSequenceNumbersText(lpexView);
 }

 /**
  * Variation of SetDateAction that can be used, for example from a user profile,
  * when the <b>doSetDate</b> helper command has already been defined in the view.
  * It does not check for, nor defines, the <b>doSetDate</b> command.
  */
 public static LpexAction setDateAction = new LpexAction() {
  public void doAction(LpexView lpexView) {
   LpexView.doGlobalCommand("set status");
   lpexView.doDefaultCommand(getInputCommandString(lpexView));
   }
  public boolean available(LpexView lpexView) {
   return canSetSequenceNumbersText(lpexView);
   }
  };

 /**
  * Helper command, issued by our prompt's <b>input</b> command, to set the new
  * sequence-numbers text (specified in <code>parameters</code>), in all the
  * visible selected lines, or in the current line if there is no selection.
  * This command must be registered in a view with the name <b>doSetDate</b>.
  */
 public static LpexCommand doSetDateCommand = new LpexCommand() {
  public boolean doCommand(LpexView lpexView, String parameters) {
   // determine the range of elements to set
   int firstElement, lastElement;
   // 1.- visible selection in this view
   if (lpexView.queryOn("block.inView") && lpexView.queryOn("block.anythingSelected"))
    {
     int linesBeforeStart = lpexView.queryInt("lines.beforeStart");
     firstElement = lpexView.queryInt("block.topElement") - linesBeforeStart;
     lastElement  = lpexView.queryInt("block.bottomElement") - linesBeforeStart;
    }
   // 2.- no selected text (is visible) in this view
   else
    {
     firstElement = lastElement = lpexView.currentElement();
    }

   // record whether any changes done
   boolean dirty = false;

   // go through all visible lines in range
   LpexDocumentLocation loc = new LpexDocumentLocation(1, 1);
   for (int i = firstElement; i <= lastElement; i++)
    {
     if (!lpexView.show(i))
      {
       loc.element = i;
       if (lpexView.queryOn("visible", loc))
        {
         if (!dirty)
          {
           dirty = !parameters.equals(lpexView.query("sequenceText"));
          }
         lpexView.doCommand(loc, "set sequenceText " + parameters);
        }
      }
    }

   // some editor applications won't save a document unless it's marked dirty
   if (dirty)
    {
     lpexView.doCommand("set dirty on");
    }

   return true;
   }
  };

 // Optionally registers helper command, prompts user, sets sequence numbers.
 private static boolean internalDoAction(LpexView lpexView)
 {
  // ensure our set-date command is defined in the view in which this action runs
  if (lpexView.command("doSetDate") != doSetDateCommand)
   {
    lpexView.defineCommand("doSetDate", doSetDateCommand);
   }

  // prompt user for the new date, update visible selected lines
  LpexView.doGlobalCommand("set status");
  return lpexView.doDefaultCommand(getInputCommandString(lpexView));
 }

 // Returns the input command to use for prompting and setting the date (see
 // the "input" editor command), e.g., input "New date:" "040630" "doSetDate ".
 private static String getInputCommandString(LpexView lpexView)
 {
  int sequenceNumbersTextLength = sequenceNumbersTextLength(lpexView);

  // prompt - specify YYMMDD if the sequence numbers are iSeries style
  String prompt = (sequenceNumbersTextLength == 6)? "New date (YYMMDD):" : "New date:";
  prompt = LpexStringTokenizer.addQuotes(prompt);

  // prompt initialization - default text if set / today's date if room for it / 0s
  String initialText = lpexView.query("current.sequenceDefaultText");
  if (initialText == null || initialText.trim().length() == 0)
   {
    // iSeries style - use today's date, in YYMMDD format
    if (sequenceNumbersTextLength == 6)
     {
      initialText = new SimpleDateFormat("yyMMdd").format(new Date());
     }
    // otherwise - initialize to 0s
    else
     {
      initialText = "0";
      while (initialText.length() < sequenceNumbersTextLength)
       {
        initialText += '0';
       }
     }
   }
  initialText = LpexStringTokenizer.addQuotes(initialText);

  // build the entire "input" command
  return "input " + prompt + ' ' + initialText + " \"doSetDate \"";
 }

 // Checks whether we can set sequence-numbers text in the given view:
 // this should be the writable, visible view of a document with sequence
 // numbers which have a textual part.
 private static boolean canSetSequenceNumbersText(LpexView lpexView)
 {
  return lpexView.currentElement() != 0 && !lpexView.queryOn("readonly") &&
         sequenceNumbersTextLength(lpexView) != 0;
 }

 // Returns the length of the document's sequence-numbers text part.
 private static int sequenceNumbersTextLength(LpexView lpexView)
 {
  return Integer.parseInt(lpexView.query("current.sequenceNumbers").split(" ")[3]);
 }
}