/** 
 *  Yudit Unicode Editor Source File
 *
 *  GNU Copyright (C) 1997-2006  Gaspar Sinai <gaspar@yudit.org>  
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License, version 2,
 *  dated June 1991. See file COPYYING for details.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "gui/SYudit.h"
#include "gui/Main.h"
#include "stoolkit/SEncoder.h"
#include "stoolkit/SIO.h"
#include "stoolkit/STypes.h"
#include "stoolkit/SIOStream.h"
#include "swidget/SUniPrint.h"
#include "swidget/SIconFactory.h"
#include <swindow/SPrinter.h>
#include <swindow/SScriptProcessor.h>
#include <stdlib.h>

static SString translateHighlightMode (const SString& hl);
static SString getPrintHelp ();
static int getLineNumber (const SString& s);

#define SGC_DOUBLE_BUFFER  true

/**
 * A caret that redraws itself differently for lr and rl text 
 */
SYudit::SYudit (const SStringVector& args, const SProperties& props)
{
  // No use to make this main window double buffered 
  // because it almost only redraws its borders and that's it 
// This is done in SFrame.
//  setThisDoubleBuffered (false);
  SPanel::setAllDoubleBuffered (SGC_DOUBLE_BUFFER);
  // preparse args
  unsigned int i;
  for (i=1; i<args.size(); i++)
  {
    if (args[i] == SString("--"))
    {
      break;
    }
    else if (args[i] == SString("-ndb"))
    {
      SPanel::setAllDoubleBuffered (false);
    }
    else if (args[i] == SString("-db"))
    {
      SPanel::setAllDoubleBuffered (true);
    }
  }

  freeHandShown = false;
  freehand = 0;
  freeHandHeight = 0;
  setLayout (SLayout (SLocation (0,0), SLocation (3000,2000)));
  originalProperties = props;

  /* TODO: move this to config file */
  helpStrings.put ("hilfe", "de");
  SEncoder kana("Kana");
  SEncoder utf8("utf-8-s");
  helpStrings.put (utf8.encode (kana.decode ("HERUPU", false)), "ja");
  SEncoder hung("Hungarian");
  helpStrings.put ("segit", "hu");
  helpStrings.put (utf8.encode (hung.decode ("segi't", false)), "hu");
  helpStrings.put (utf8.encode (hung.decode ("segi'tse'g", false)), "hu");
  SEncoder yidd("Yiddish");
  SV_UCS4 yi0;
  yi0.append (SD_CD_RLO);
  yi0.append (yidd.decode ("gehilF", false));
  yi0.append (SD_CD_PDF);
  SV_UCS4 yi1;
  yi1.append (SD_CD_RLO);
  yi1.append (yidd.decode ("helF", false));
  yi1.append (SD_CD_PDF);
  helpStrings.put (utf8.encode (yi0), "yi");
  helpStrings.put (utf8.encode (yi1), "yi");
  helpStrings.put (utf8.encode (yidd.decode ("gehilF", false)), "yi");
  helpStrings.put (utf8.encode (yidd.decode ("helF", false)), "yi");
  helpStrings.put ("ayuda", "es");

  configFile = props["yudit.config"];

  char fnc[64];
  /* get an unused filename */
  for (unsigned int fn=0; fn<100000; fn++)
  {
   if (fn==0)
   {
     sprintf (fnc, "untitled.txt"); 
   }
   else
   {
     sprintf (fnc, "untitled%d.txt", fn); 
   }
   currentFileName = SString (fnc);

   SFile cfile (currentFileName);
   if (cfile.size() < 0) break;
  }  
  lastprinterOption = props["yudit.default.printer.options"];
  lastpreviewOption ="-e ";
  lastpreviewOption.append (props["yudit.default.preview.command"]);
  lastpreviewOption.replaceAll("\\", "/");
  lastpreviewOption.replaceAll("//", "/");
  lastencoding=props["yudit.default.filetype"];

  setFrameListener (this);
  command = new STextEdit ();
  SString commandFont ("default");
  if (props.get("yudit.command.font"))
  {
    commandFont = props["yudit.command.font"];
  }
  SString commandFontSizeString ("16");
  if (props.get("yudit.command.fontsize"))
  {
    commandFontSizeString = props["yudit.command.fontsize"];
  }
  commandFontSizeString.append ((char)0);
  double commandFontSize=0;
  sscanf (commandFontSizeString.array(), "%lf", &commandFontSize);

  if (commandFontSize < 2.0) commandFontSize = 2.0;
  command->setFont (commandFont, commandFontSize);
  unsigned int commandHeight = command->getPreferredSize().height;
  command->setMultiline (false);
  command->setLineEndMark (false);

  messagebar = new SMessageBar (props);
  unsigned int msgbarHeight = messagebar->getPreferredSize().height;


  toolbar = new SToolBar (props);
  unsigned int toobarHeight = toolbar->getPreferredSize().height;

  toolbar->setLayout (
    SLayout (
     SLocation (2,0),
     SLocation (2998, toobarHeight+0),
     SLocation (0, 0),
     SLocation (100, 0)
    )
  );
  toolbar->setListener (this);
  

  messagelabel = new SMessageLabel (props);
  unsigned int messagelabeHeight = messagelabel->getPreferredSize().height;

  messagelabel->setLayout (
    SLayout (
      SLocation (2, 1998-messagelabeHeight),
      SLocation (2998, 1998),
      SLocation (0, 100),
      SLocation (100, 100)
    )
  );
  command->setLayout (
    SLayout (
      SLocation (2, 1998-commandHeight-messagelabeHeight),
      SLocation (2998, 1998-messagelabeHeight),
      SLocation (0, 100),
      SLocation (100, 100)
    )
  );

  messagebar->setLayout (
    SLayout (
     SLocation (2,1998-msgbarHeight-commandHeight-messagelabeHeight),
     SLocation (2998, 1998-commandHeight-messagelabeHeight),
     SLocation (0, 100),
     SLocation (100, 100)
    )
  );

  sliderLayout =  SLayout (
      SLocation (2978, toobarHeight+2),
      SLocation (2998, 1998 - commandHeight - msgbarHeight - messagelabeHeight),
      SLocation (100, 0),
      SLocation (100, 100)
  );

  slider = new SSlider ();
  slider->setLayout (sliderLayout);
  /* on mac somehow it did not want to resize - force it */
  slider->resize (SDimension (10, 200));
  
  SString currentFont = toolbar->fonts[toolbar->currentFont];
  SString currentFontSize = toolbar->fontsizes[toolbar->currentFontSize];
  SString currentInput = toolbar->inputs[toolbar->currentInput];
  SString currentParagraphSeparator = toolbar->paragraphBreaks[toolbar->currentParagraphSeparator];
  

  editor = new STextEdit ();
  editor->addSyntaxListener (toolbar);

  currentFontSize.append ((char)0);
  double df;
  if (sscanf (currentFontSize.array(), "%lf", &df)==0 || df < 2.0) df = 2.0;
  editor->setFont (currentFont, df);

  editor->setMultiline (true);
  editor->setLineEndMark (true);
  editor->setDocumentEmbedding (toolbar->currentEmbedding);

  editorLayout = 
    SLayout (
      SLocation (2, toobarHeight+2),
      SLocation (2978, 1998 - commandHeight - msgbarHeight - messagelabeHeight),
      SLocation (0, 0),
      SLocation (100, 100)
    );
  editor->setLayout (editorLayout);

  /*SGC */
  SString inputStyle = originalProperties["yudit.editor.xinputs.style"];

  /* allowed styles */
  if (inputStyle=="preedit-over-status-under" 
       || inputStyle == "preedit-under-status-under"
       || inputStyle=="preedit-over-status-over"
       || inputStyle == "preedit-root-status-root" )
  {
    SProperties p;
    SColor cbg(originalProperties["yudit.background"]);
    SColor cfg(originalProperties["yudit.label.foreground"]);

    sprintf (fnc, "%lu,%lu", (unsigned long)cbg.getValue(),
      (unsigned long)cfg.getValue());
    p.put ("InputClientColor", fnc);

    SColor sbg(originalProperties["yudit.background"]);
    SColor sfg(originalProperties["yudit.title.foreground"]);

    sprintf (fnc, "%lu,%lu", (unsigned long)sbg.getValue(),
      (unsigned long)sfg.getValue());
    p.put ("InputStatusColor", fnc);
    editor->setXIMProperties (p);

    p.put ("InputStyle", inputStyle);
    editor->setXIMProperties (p);
  }
  else
  {
    fprintf (stderr, "Yudit: unknown yudit.editor.xinputs.style=%*.*s\n",
       SSARGS(inputStyle));
  }

  /* showInputStatus has set up the xim properties for editor */
  editor->setInputMethod (currentInput);

  if (currentInput.size() > 2) 
  {
    if (currentInput != SS_KINPUT2_IM  
       && currentInput != SS_WINDOWS_IM
       && currentInput[0] != 'x' 
       && currentInput[1] != 'X')
    {
      command->setInputMethod (currentInput);
    }
  }
  if (props["yudit.editor.showbreak"]=="true")
  {
    editor->setLineEndMark (true);
  }
  else
  {
    editor->setLineEndMark (false);
  }
  if (props["yudit.editor.wordwrap"]=="true")
  {
    editor->setWordWrap (true);
  }
  else
  {
    editor->setWordWrap (false);
  }
  SSyntaxColors syn;
  syn.colors[(unsigned int) SSyntax::SD_NONE] = SColor ("cyan");
/*
  got rid of none. For errors and none we use the default fg.
    = props.getProperty ("yudit.editor.syntax.none.foreground", "green");
*/

  syn.colors[(unsigned int) SSyntax::SD_ERROR] 
    = props.getProperty ("yudit.editor.syntax.error.foreground", "OrangeRed3");
  syn.colors[(unsigned int) SSyntax::SD_NUMBER] 
    = props.getProperty ("yudit.editor.syntax.number.foreground", "orange");
  syn.colors[(unsigned int) SSyntax::SD_STRING] 
    = props.getProperty ("yudit.editor.syntax.string.foreground", "magenta");
  syn.colors[(unsigned int) SSyntax::SD_COMMENT] 
    = props.getProperty ("yudit.editor.syntax.comment.foreground", "lightgray");
  syn.colors[(unsigned int) SSyntax::SD_KEYWORD] 
    = props.getProperty ("yudit.editor.syntax.token.foreground", "yellow");
  syn.colors[(unsigned int) SSyntax::SD_VARIABLE] 
    = props.getProperty ("yudit.editor.syntax.variable.foreground", "red");
  syn.colors[(unsigned int) SSyntax::SD_DEFINE] 
    = props.getProperty ("yudit.editor.syntax.define.foreground", "cyan");
  syn.colors[(unsigned int) SSyntax::SD_CONTROL] 
    = props.getProperty ("yudit.editor.syntax.control.foreground", "CornflowerBlue");

  editor->setSyntaxColors (syn);

  const SString& hl = props["yudit.editor.syntax"];
  SString mode = translateHighlightMode (hl);
  if (mode.size() >= 0) 
  {
    editor->setSyntax (mode);
  }

  SEditor e(currentParagraphSeparator);
  editor->setEditor (e);
  messagebar->setFontSize(df);
  command->setEditor(SEditor());
  
  add (slider);
  add (editor);
  add (command);
  add (toolbar);
  add (messagebar);
  add (messagelabel);

  setBackground (props["yudit.background"]);
  editor->setTextBackground (props["yudit.editor.background"]);
  editor->setForeground (props["yudit.editor.left.foreground"],
            props["yudit.editor.right.foreground"]);
  editor->setCaretForeground (props["yudit.editor.caret.left.foreground"],
            props["yudit.editor.caret.right.foreground"]);
  command->setTextBackground (props["yudit.command.background"]);
  command->setForeground (props["yudit.command.left.foreground"],
            props["yudit.command.right.foreground"]);
  command->setCaretForeground (props["yudit.command.caret.left.foreground"],
            props["yudit.command.caret.right.foreground"]);
  slider->setSliderBackground (props["yudit.slider.background"]);

  SDimension pw = toolbar->getPreferredSize();
  /* get the dimension */
  SDimension ms(props["yudit.default.geometry"]);
  //if (ms.width < pw.width + 10) ms.width = pw.width + 10;
  //if (ms.height < pw.height + 10) ms.width = pw.height + 10;
  if (ms.width <  150) ms.width = 150;
  if (ms.height < 100) ms.height =  100;
  resize (ms);
  setMinimumSize (SDimension (150, 100));

  caretNow = STextIndex (0,0);
  messagebar->setRow(1);
  messagebar->setColumn (0);
  editor->setCursorIndex(SCursorIndex(0,0));
  editor->addTextEditLS (this);
  command->addTextEditLS (this);
  command->setHistorySize(30);

  kmapDialog = 0;
  fileDialog = 0;
  highlightDialog = 0;
  textDialog = 0;

#ifdef USE_WINAPI
  SToolBar::SFormatIndex ft=SToolBar::SS_FORMAT_DOS;
#else 
  SToolBar::SFormatIndex ft=SToolBar::SS_FORMAT_UNIX;
#endif
  toolbar->setParagraphSeparator (ft, true);
  int lineNum = -1;
  if (args.size())
  {
    SString comm ("open -yes ");
    SString enc ((lastencoding.size()==0)?SString("utf-8"):lastencoding);
    SString filename;
    bool help=false;
    unsigned int i;
    for (i=1; i<args.size(); i++)
    {
      if (args[i] == SString("--"))
      {
        if (i+1 < args.size())
        {
          filename = args[i+1];
        }
        break;
      }
      // +NUMBER
      else if (getLineNumber (args[i]) > 0)
      {
        lineNum = getLineNumber (args[i]);
      }
      else if (args[i] == SString("-e") && i+1 < args.size())
      {
        i++;
        enc = args[i];
      }
      else if (args[i] == SString("-help") || args[i] == SString("-h"))
      {
        help = true;
      }
      else if (args[i] == SString("-ndb") || args[i] == SString("-db"))
      {
        // already processed
      }
      else if (args[i] == SString("-us"))
      {
        SScriptProcessor::support(true);
      }
      else if (args[i] == SString("-nus"))
      {
        SScriptProcessor::support(false);
      }
      else if (args[i] == SString("-noinit"))
      {
        SScriptProcessor::doInit(false);
      }
      else if ((args[i] == SString("-d") || args[i] == SString("-geometry")) 
	      && i+1 < args.size()) {
        i++;
	ms = SDimension (args[i]);  
        //if (ms.width < pw.width + 10) ms.width = pw.width + 10;
        //if (ms.height < pw.height + 10) ms.width = pw.height + 10;
        if (ms.width < 150) ms.width =  150;
        if (ms.height < 100) ms.width =  100;
        resize (ms);
      }
      else 
      {
        filename = args[i];
        break;
      }
    }
    if (help)
    {
      filename = SString("help");
      command->putHistory (filename);
    }
    else
    {
       filename = composeFileNameCommand (comm, enc, 
            completeFileName(filename));
       /* now it comes handy - args starts with 1 */
       unsigned int j;
       for (j=args.size(); j>i; j--)
       {
          SString ss = composeFileNameCommand (comm, enc, 
                completeFileName (args[j-1]));
          command->putHistory (ss);
       }
    }
    SStringVector v;
    v.smartSplit (filename);
    load (v, filename);
  }

  toolbar->setFileName(currentFileName, lastencoding); 
  toolbar->setHighlightName (editor->getHighlightName());

  setFileTitle ();
  SIcon* icon = SIconFactory::getIcon("Yudit");
  setApplicationImage (icon->getImage ());

//fprintf (stderr, "SYudit.cpp EDITOR FOCUS REQUEST\n");
  setDropListener (this, SStringVector ("text/uri-list,text/plain"));
  showFreeHand (currentInput=="freehand");
  editor->setFocus ();
  if (lineNum > 0)
  {
    editor->setCursorIndex(SCursorIndex((unsigned int)(lineNum-1), 0, true));
  }
  
}

SYudit::~SYudit ()
{
}
bool
SYudit::close (SPanel* comp)
{

  if (textDialog != 0 && textDialog->isShown())
  {
    textDialog->center();
    return false;
  }
  bool ret = false;
  if (toolbar->modified)
  {
    createTextDialog ();
    ret = textDialog->getInput (
        "Yudit: Exit", translate ("Unsaved changes.\nExit anyway?\n"),
        STextDialog::SS_QUESTION); 
    if (ret)
    {
       saveProperties();
       exit (0);
    }
    return false;
  }
  if (toolbar->printing)
  {
    createTextDialog ();
    ret = textDialog->getInput (
        "Yudit: Exit", translate ("Pending printing job.\nExit anyway?\n"),
        SDialog::SS_QUESTION); 
    if (ret)
    {
       saveProperties();
       exit (0);
    }
  }
  saveProperties();
  exit (0);
} 

/**
 * Listening to toolbar.
 */
void
SYudit::buttonPressed (void* src, int which, bool accel)
{
  if (!toolbar->printing) messagelabel->setMessage("");
  if (textDialog != 0 && textDialog->isShown()) return;
  SString currentFont = toolbar->fonts[toolbar->currentFont];
  SString currentFontSize = toolbar->fontsizes[toolbar->currentFontSize];
  SString currentInput = toolbar->inputs[toolbar->currentInput];
  SString currentParagraphSeparator = toolbar->paragraphBreaks[toolbar->currentParagraphSeparator];
  currentFontSize.append ((char)0);
  double df;
  SString pstr;
  SStringVector pstrVector;
  if (sscanf (currentFontSize.array(), "%lf", &df)==0 || df < 2.0) df = 2.0;
  bool ok = false;
  int dire = false;
  bool wasmodified = toolbar->modified;
 
  switch (which)
  {
  /* FIXME */
  case SToolBar::SS_OPEN:
    if (!accel)
    {
       createFileDialog();
       if (toolbar->modified)
       {
         createTextDialog();
         ok = textDialog->getInput (
          "Yudit: Open", translate ("Unsaved changes.\nOpen anyway?\n"),
           STextDialog::SS_QUESTION); 
         if (!ok)
         {
           return;
         }
       }
       fileDialog->setFileName (currentFileName);
       fileDialog->setFileType (lastencoding);
       ok = fileDialog->getInput ("Yudit: Open", false, false);
       if (!ok)
       {
          return;
       }
       pstr.clear();
       pstr.append ("open ");
       pstr.append ("-yes ");
       pstr.append ("-e ");
       pstr.append (fileDialog->getFileType());
       pstr.append (" ");
       pstr.append (quoteFileName (fileDialog->getFileName()));
       command->setText (pstr);
       command->setCursorIndex (SCursorIndex(0,10000));
       command->putHistory (pstr);
       pstrVector.smartSplit(pstr);
       load (pstrVector, pstr);
       return;
    }
    if (toolbar->modified)
    {
      messagelabel->setMessage (translate ("Unsaved changes. Enter discards!"), SMessageLabel::SS_ERR);
      SString str = composeFileNameCommand ("open -yes ", 
         lastencoding, currentFileName);
      command->setText(str);
      command->setCursorIndex (SCursorIndex(0,10000));
    }
    else
    {
      messagelabel->setMessage (translate ("open -yes -e encoding file"), 
         SMessageLabel::SS_INFO);
      SString str = composeFileNameCommand (
             "open ", lastencoding, currentFileName);
      command->setText(str);
      command->setCursorIndex (SCursorIndex(0,10000));
    }
    command->setFocus();
    break;
  case SToolBar::SS_SAVE:
    if (!accel)
    {
       createFileDialog();
       fileDialog->setFileName (currentFileName);
       fileDialog->setFileType (lastencoding);
       pstr = fileDialog->getFileName();
       ok = fileDialog->getInput ("Yudit: Save", false, false);
       if (!ok)
       {
          return;
       }
       if (pstr != fileDialog->getFileName() &&
          newFileExists (fileDialog->getFileName()))
       {
         createTextDialog();
         ok = textDialog->getInput (
          "Yudit: Save", translate ("File exists.\nSave anyway?"),
           STextDialog::SS_QUESTION); 
         if (!ok)
         {
           return;
         }
       }
       pstr.clear();
       pstr.append ("save ");
       pstr.append ("-yes ");
       pstr.append ("-e ");
       pstr.append (fileDialog->getFileType());
       pstr.append (" ");
       pstr.append (quoteFileName (fileDialog->getFileName()));
       command->setText (pstr);
       command->setCursorIndex (SCursorIndex(0,10000));
       command->putHistory (pstr);
       pstrVector.smartSplit(pstr);
       save (pstrVector, pstr);
       return;
    }
    messagelabel->setMessage (translate ("save -e encoding file"), 
       SMessageLabel::SS_INFO);
    {
      SString str = composeFileNameCommand (
             "save ", lastencoding, currentFileName);;
      command->setText(str);
      command->setCursorIndex (SCursorIndex(0,10000));
    }
    command->setFocus();
    break;
  case SToolBar::SS_FIND:
    messagelabel->setMessage (translate ("find string"), 
       SMessageLabel::SS_INFO);
    command->setText("find ");
    command->setCursorIndex (SCursorIndex(0,10000));
    command->setFocus();
    break;
  case SToolBar::SS_GOTO:
    messagelabel->setMessage (translate ("go line [column]"), 
       SMessageLabel::SS_INFO);
    command->setText("go ");
    command->setCursorIndex (SCursorIndex(0,10000));
    command->setFocus();
    break;
  case SToolBar::SS_PRINT:
    messagelabel->setMessage (getPrintHelp (), SMessageLabel::SS_INFO);
    pstr.append ("print ");
    pstr.append (lastprinterOption);
    command->setText(pstr);
    command->setCursorIndex (SCursorIndex(0,10000));
    command->setFocus();
    break; 
  case SToolBar::SS_PRINT_PREVIEW:
    messagelabel->setMessage (translate ("print -e program"), 
       SMessageLabel::SS_INFO);
    pstr.append ("print ");
    pstr.append (lastpreviewOption);
    command->setText(pstr);
    command->setCursorIndex (SCursorIndex(0,10000));
    command->setFocus();
    break; 
  case SToolBar::SS_UNDO:
    if (!editor->undo())
    {
      messagelabel->setMessage (translate ("Nothing to undo."), 
         SMessageLabel::SS_WARN);
      toolbar->setModified (false);
    }
    toolbar->setEmbedding(editor->getDocumentEmbedding(), false);
    break;
  case SToolBar::SS_REDO: 
    if (!editor->redo())
    {
      messagelabel->setMessage (translate ("Nothing to redo."), 
         SMessageLabel::SS_WARN);
    }
    toolbar->setEmbedding(editor->getDocumentEmbedding(), false);
    break;
  case SToolBar::SS_DOCUMENT_EMBEDDING: 
    editor->setDocumentEmbedding (toolbar->currentEmbedding);
    break;
  case SToolBar::SS_SET_OVERRIDE: 
    /* change it back */ 
    dire = editor->changeDirection (toolbar->overrideLR ? SS_DR_LO : SS_DR_RO);
    if (dire)
    {
      messagelabel->setMessage("");
    }
    else 
    {
      /* leave it back */ 
      if (!wasmodified) toolbar->setModified (true);
      messagelabel->setMessage (
        translate ("Direction of selected text has been set."), 
        SMessageLabel::SS_INFO);
    }
    break;
  case SToolBar::SS_YIELD_EMBEDDING: 
    dire = editor->changeDirection  (toolbar->currentLR ? SS_DR_L : SS_DR_R);
    if (dire)
    {
      messagelabel->setMessage("");
    }
    else 
    {
      /* leave it back */ 
      if (!wasmodified) toolbar->setModified (true);
      messagelabel->setMessage (
        translate ("Direction of selected text has been re-set."), 
        SMessageLabel::SS_INFO);
    }
    break;
    
  case SToolBar::SS_SET_EMBEDDING: 
    /* change it back */ 
    dire = editor->changeDirection (toolbar->embedLR ? SS_DR_LE : SS_DR_RE);
    if (dire)
    {
      messagelabel->setMessage("");
    }
    else 
    {
      /* leave it back */ 
      if (!wasmodified) toolbar->setModified (true);
      messagelabel->setMessage (
        translate ("Direction of selected text has been set."), 
        SMessageLabel::SS_INFO);
    }
    break;
  case SToolBar::SS_MAGNIFY_PLUS: 
  case SToolBar::SS_MAGNIFY_MINUS: 
    messagebar->setFontSize(df);
    editor->setFontSize (df);
    if (freehand) freehand->setFontSize (df);
    break;
  case SToolBar::SS_FONT: 
    editor->setFont (currentFont, df);
    if (freehand) freehand->setFont (currentFont, df);
    break;
  case SToolBar::SS_INPUT: 
    if (!accel)
    {
       createKMapDialog ();
       ok = kmapDialog->getInput ("Yudit: KMap Setup",
            toolbar->inputs, toolbar->currentInput);
       if (!ok || kmapDialog->getSelectedKMap() < 0) break;
       toolbar->inputChanged(kmapDialog->getKMapList (), 
          kmapDialog->getSelectedKMap());
       currentInput =  toolbar->inputs[toolbar->currentInput];
       userProps.put ("yudit.editor.inputs", toolbar->inputs.join (","));
       userProps.put ("yudit.editor.input", currentInput);
       saveProperties();
    }
    editor->setInputMethod (currentInput);
    showFreeHand (currentInput=="freehand");
   
    if (currentInput != SS_KINPUT2_IM
       && currentInput != SS_WINDOWS_IM
       && currentInput[0] != 'x' 
       && currentInput[1] != 'X')
    {
      command->setInputMethod (currentInput);
    }
    else
    {
      command->setInputMethod ("straight");
    }
    /* update glyph info */
    caretMoved();
    break;
  case SToolBar::SS_PARAGRAPH_BREAK: 
    editor->setParagraphSeparator (currentParagraphSeparator, true);
    break;
  case SToolBar::SS_HIGHLIGHTING:
    if (!accel)
    {
       createHighlightDialog();
       SString gotMode = editor->getHighlightName();
       highlightDialog->setHighlightName (gotMode);
       ok = highlightDialog->getInput ("Yudit: Highlighting");
       if (!ok)
       {
          return;
       }
       SString hlmode = highlightDialog->getHighlightName();
       pstr.clear();
       pstr.append ("syntax ");
       pstr.append (" ");
       //pstr.append (quoteFileName (hlmode));
       pstr.append (hlmode);
       //command->setText (pstr);
       //command->setCursorIndex (SCursorIndex(0,10000));
       if (setHighlight (hlmode))
       {
         command->putHistory(pstr);
       }
       return;
    }
    else
    {
      SString gotMode = editor->getHighlightName();
      SString cmd ("syntax");
      cmd.append (" ");
      cmd.append (gotMode);
      command->setText (cmd);
      command->setCursorIndex (SCursorIndex(0,10000));
      command->setFocus();
    }
    break;
  default:
    break;
  }
}

bool
SYudit::setHighlight (const SString& syntaxMode)
{
  SString smode;
  if ((smode = translateHighlightMode (syntaxMode)) == "")
  {
    SString err = translate ("usage: ");
    err.append ("syntax none|simple|simple-dark|hunspell:en_US|...");
    messagelabel->setMessage (err, SMessageLabel::SS_ERR);
    return false;
  }
  SString oldM = editor->getHighlightName ();
  editor->setSyntax (smode);
  SString gotMode = editor->getSyntaxName();
  if (gotMode != smode && smode != "simple" 
      && smode != "simple-dark" && smode != "none")
  {
    SString err = translate ("Can not set syntax: ");
    err.append (smode);
    messagelabel->setMessage (err, SMessageLabel::SS_ERR);
    editor->setSyntax (oldM);
    return false;
  }
  // dont display these
  toolbar->setHighlightName (editor->getHighlightName());

  userProps.put ("yudit.editor.syntax", smode);
  return true;
}

void
SYudit::buttonLeave (void* src, int which)
{
}

void
SYudit::buttonEnter (void* src, int which)
{
}


/**
 * This is the STextEditLS
 */
void
SYudit::textChanged (void *source)
{
  if (source == editor)
  {
    if (!toolbar->printing) messagelabel->setMessage("");
    toolbar->setModified (true);
    return;
  }
}

void
SYudit::textEntered (void *source)
{
  if (!toolbar->printing) messagelabel->setMessage("");
  if (source != command) return;
  SString text =  command->getText();
  if (text.size()==0)
  {
    editor->setFocus();
    return;
  }
  SStringVector cv;
  cv.smartSplit (text);
  if (cv.size()==2 && cv[0]=="quit" && cv[1] == "-yes")
  {
    exit (0);
  }
  if (cv.size()==1 && cv[0] == "quit")
  {
    if (toolbar->modified)
    {
      messagelabel->setMessage (translate ("Unsaved changes. Enter quits!"), 
         SMessageLabel::SS_ERR);
      command->setText("quit -yes");
      command->setCursorIndex (SCursorIndex(0,10000));
      command->setFocus();
      return;
    }
    if (toolbar->printing)
    {
      messagelabel->setMessage (translate ("Pending printing job. Enter quits!"), 
         SMessageLabel::SS_ERR);
      command->setText("quit -yes");
      command->setCursorIndex (SCursorIndex(0,10000));
      command->setFocus();
      return;
    }
    exit (0);
  }
  command->putHistory(text);
  if (cv.size()>0 && cv[0] == "print")
  {
    print (cv, text);
    return;
  }
  if ((cv.size()>0 && (cv[0] == "help" || cv[0] == "test" || 
       helpStrings.get(cv[0]) != 0)) 
     || (cv.size() > 1 && cv[0] == "howto"))
  {
    load (cv, text);
    return;
  }
  if (cv.size()>0 && (cv[0] == "open" || cv[0] == "load"))
  {
    load (cv, text);
    return;
  }
  if (cv.size()>0 && (cv[0] == "configure" || cv[0] == "config"))
  {
    load (cv, text);
    return;
  }
  if (cv.size()>0 && (cv[0] == "save" || cv[0] == "save"))
  {
    save (cv, text);
    return;
  }
  if (cv.size()>0 && cv[0] == "go")
  {
    goLineRow (cv, text);
    return;
  }
  if (cv.size()>0 && cv[0] == "find")
  {
    find (cv, text);
    return;
  }
  if (cv.size()>0 && cv[0] == "replace")
  {
    replace (cv, text);
    return;
  }
  if (cv.size() > 0 && cv[0] == "syntax")
  {
    SString smode;
    if (cv.size() != 2)
    {
      SString err = translate ("usage: ");
      err.append ("syntax none|simple|simple-dark|hunspell:en_US... ");
      messagelabel->setMessage (err, SMessageLabel::SS_ERR);
      return;
    }
    if (setHighlight (cv[1]))
    {
      editor->setFocus();
      command->putHistory(text);
    }
    return;
  }
  if (cv.size() > 0 && cv[0] == "wordwrap")
  {
    if (cv.size() != 2 || (cv[1] != "true" && cv[1] != "false"))
    {
      SString err = translate ("usage: ");
      err.append ("wordwrap true|false");
      messagelabel->setMessage (err, SMessageLabel::SS_ERR);
      return;
    }
    if (cv[1]=="true")
    {
      editor->setWordWrap (true);
    }
    else
    {
      editor->setWordWrap (false);
    }
    userProps.put ("yudit.editor.wordwrap", cv[1]);
    editor->setFocus();
    command->putHistory(text);
    return;
  }
  SString mess ("syntax error: ");
  mess.append (SS_LB_LRE);
  mess.append ("'");
  mess.append (text);
  mess.append ("'");
  mess.append (SS_LB_PDF);
  messagelabel->setMessage (mess, SMessageLabel::SS_ERR);
  command->clear();
  command->historyEnd();
  //editor->setFocus();
}

void 
SYudit::print (const SStringVector& args, const SString& str)
{
  if (toolbar->printing) return;
  SString executable;
  SString printer;
  SString filename;
  SString hsize("10");
  unsigned int i;
  bool shownl = false;
  bool islpr = false;
  double fontSize = messagebar->fontSize;

  SPrinter::SMedia media = SPrinter::A4;
  SPrinter::SOrientation orientation = SPrinter::PORTRAIT;

  for (i=1; i<args.size(); i++)
  {
    if ((args[i] == "-printer" || args[i] =="-p") && i+1 < args.size())
    {
      i++;
      printer = args[i];
    }
    else if ((args[i] == "-hsize") && i+1 < args.size())
    {
      i++;
      hsize = args[i];
    }
    else if ((args[i] == "-size") && i+1 < args.size())
    {
      i++;
      SString sarg = args[i];
      sarg.append ((char)0);
      sscanf (sarg.array(), "%lf", &fontSize);
      if (fontSize > 1000.0)
      {
        fontSize = 1000.0;
      }
      if (fontSize < 1.0)
      {
        fontSize = 1.0;
      }
    }
    else if ((args[i] == "-out" || args[i] =="-o") && i+1 < args.size())
    {
      i++;
      filename = completeFileName (args[i]);
    }
    else if ((args[i] == "-m" || args[i] =="-media") && i+1 < args.size())
    {
      i++;
      if (args[i] == "A3")
      {
        media = SPrinter::A3;
      }
      else if (args[i] == "A4")
      {
        media = SPrinter::A4;
      }
      else if (args[i] == "A5")
      {
        media = SPrinter::A5;
      }
      else if (args[i] == "B4")
      {
        media = SPrinter::B4;
      }
      else if (args[i] == "B5")
      {
        media = SPrinter::B5;
      }
      else if (args[i] == "Executive")
      {
        media = SPrinter::Executive;
      }
      else if (args[i] == "Folio")
      {
        media = SPrinter::Folio;
      }
      else if (args[i] == "Ledger")
      {
        media = SPrinter::Ledger;
      }
      else if (args[i] == "Legal")
      {
        media = SPrinter::Legal;
      }
      else if (args[i] == "Letter")
      {
        media = SPrinter::Letter;
      }
      else if (args[i] == "Quarto")
      {
        media = SPrinter::Quarto;
      }
      else if (args[i] == "Statement")
      {
        media = SPrinter::Statement;
      }
      else if (args[i] == "Tabloid")
      {
        media = SPrinter::Tabloid;
      }
      else
      {
        media = SPrinter::A4;
      }
    }
    else if (args[i] == "-exec" && i+1 < args.size())
    {
      i++;
      executable = args[i];
      int ind = str.find("-exec");
      if (ind > 0)
      {
        /* strlen ("-exec")*/
        ind += 5;
        while (ind < (int) str.size() 
              && (str[(unsigned int)ind] == ' '
                  || str[(unsigned int)ind] == '\t')) ind++;
        if (ind < (int) str.size())
        {
          executable = SString (&str.array()[(unsigned int)ind], 
             str.size()-(unsigned int)ind);
          i = args.size();
          break;
        }
      }
    }
    else if (args[i] =="-e" && i+1 < args.size())
    {
      /* strlen ("-e")*/
      i++;
      executable = args[i];
      int ind = str.find("-e");
      if (ind > 0)
      {
        ind += 2;
        while (ind < (int) str.size() 
              && (str[(unsigned int)ind] == ' '
                  || str[(unsigned int)ind] == '\t')) ind++;
        if (ind < (int) str.size())
        {
          executable = SString (&str.array()[(unsigned int)ind], 
             str.size()-(unsigned int)ind);
          i = args.size();
          break;
        }
      }
    }
    else if (args[i] == "-break" || args[i] =="-b")
    {
      shownl = true;
    }
    else if (args[i] == "-L" || args[i] =="-landscape")
    {
      orientation = SPrinter::LANDSCAPE;
    }
    else
    {
      break;
    }
  }
  hsize.append ((char)0);
  double hsized=10.0;
  sscanf (hsize.array(), "%lf", &hsized);
  if (hsized < 2.0) hsized = 1.0;
  if (hsized > 100.0) hsized = 100.0;

  if (filename.size()==0 && executable.size()==0)
  {
     islpr = true;

#ifdef USE_WINAPI
     executable  = originalProperties["yudit.default.preview.command"];
     executable.replaceAll("\\", "/");
     executable.replaceAll("//", "/");
     if (!addMytoolPrefix (&executable)) return;
#else 
     executable = "lpr";
#endif
  }
  if (i<args.size() || (filename.size()!=0 && executable.size()!=0)
     || (filename.size()!=0 && printer.size() > 0))
  {
    messagelabel->setMessage (getPrintHelp(), SMessageLabel::SS_ERR);
    return;
  }
  SOutputStream os;
  if (executable.size())
  {
    if (!islpr && !addMytoolPrefix (&executable)) return;
    if (printer.size())
    {
      executable.append (" -P ");
      executable.append (printer);
    }
    //fprintf (stderr, "executable [%*.*s]\n", SSARGS (executable));
    SPipe p(executable);
    os = p.getOutputStream();
    if (!os.isOK())
    {
      SString ems ("Can not execute: ");
      ems.append (SS_LB_LRE);
      ems.append ("'");
      ems.append (executable);
      ems.append ("'");
      ems.append (SS_LB_PDF);
      messagelabel->setMessage (ems, SMessageLabel::SS_ERR);
      return;
    }
  }
  else
  {
    SFile f(filename);
    os = f.getOutputStream();
    if (!os.isOK())
    {
      SString ems ("Can not open: ");
      ems.append (SS_LB_LRE);
      ems.append ("'");
      ems.append (filename);
      ems.append ("'");
      ems.append (SS_LB_PDF);
      messagelabel->setMessage (ems, SMessageLabel::SS_ERR);
    }
  }
  SWriter writer(os);
  SString fontName = toolbar->fonts[toolbar->currentFont];
  
  SPrinter p (writer, SPrinter::POSTSCRIPT, media, orientation);
  SUniPrint uniPrint(p, fontName, hsized, fontName, fontSize);

  SString text = editor->getSelectedText();
  SString sel = currentFileName;
  if (text.size() == 0)
  { 
    text = editor->getText();
  }
  else
  {
    sel.append (" (excerpt)");
  }

  uniPrint.setDocumentEmbedding (toolbar->currentEmbedding);
  uniPrint.setLineEndMark(shownl);
  uniPrint.setWordWrap (editor->getWordWrap());
  toolbar->setPrinting(true);

  command->clear();
  command->historyEnd();
  editor->setFocus();
  messagelabel->setMessage (
     translate ("Printing..."),
      SMessageLabel::SS_NONE);
  if (!uniPrint.print (sel, text, true))
  {
      toolbar->setPrinting (false);
      SString ems ("Printing failed on '");
      ems.append (executable);
      ems.append (filename);
      ems.append ("'");
      messagelabel->setMessage (ems, SMessageLabel::SS_ERR);
      return;
  }
  int pagecount = uniPrint.getPageCount();
  char a[64];
  if (pagecount >= 1)
  {
    SString tr(translate("Printed %d page(s)."));
    tr.append ((char)0);
    sprintf (a, tr.array(), pagecount);
    if (uniPrint.hasNative())
    {
      SString ms ("Bitmap font printed (bad printing quality).");
      ms.append (a);
      messagelabel->setMessage (ms, SMessageLabel::SS_WARN);
    }
    else
    {
      SString ms (a);
      messagelabel->setMessage (ms, SMessageLabel::SS_INFO);
    }
  }
  command->clear();
  command->historyEnd();
 
  SString  op;
  for (i=strlen ("print "); i<str.size(); i++)
  {
    op.append ((char)str[i]);
  }
  if (executable.size() && !islpr && args.size() != 1)
  {
    lastpreviewOption = op;
  }
  else
  {
    lastprinterOption = op;
    //fprintf (stderr, "lastprinterOption=[%*.*s] str=[%*.*s]\n", 
     //   SSARGS(lastprinterOption), SSARGS (str));
  }
  toolbar->setPrinting (false);
}

void 
SYudit::load (const SStringVector& args, const SString& str)
{
  caretIndex.put (currentFileName, caretNow);
  SString encode="utf-8";
  bool hasyes = false;
  SString filename;
  unsigned int i;
  SString internal;
  for (i=1; i<args.size(); i++)
  {
    if (args[i] == "-e" && i+1 < args.size())
    {
      i++;
      encode = args[i];
    }
    else if (args[i] =="-yes")
    {
      hasyes = true;
    }
    else if (i+1 == args.size())
    {
      filename = completeFileName(args[i]);
    }
    else
    {
      break;
    }
  }
  if (args[0] =="help" || helpStrings.get (args[0]) != 0)
  {
    internal =  getPrefix();
    internal.append ("/doc/");
    SString lang = getLanguage();
    if (helpStrings.get (args[0]) != 0) 
    {
      lang = helpStrings[args[0]];
    }
    if (lang.size())
    {
      SString nd = internal;
      nd.append (lang);
      nd.append ("/");
      nd.append ("FAQ.TXT");
      SFile file (nd);
      if (file.size() > 0)
      {
        internal.append (lang);
        internal.append ("/");
      }
    }
    
    internal.append ("FAQ.TXT");
    i = args.size();
    encode = "utf-8";
    filename = internal;
  }

  if (args[0] =="howto" && args.size() >1)
  {
    internal =  getPrefix();
    internal.append ("/doc/");
    SString lang = getLanguage();
    if (helpStrings.get (args[0]) != 0) 
    {
      lang = helpStrings[args[0]];
    }
    if (lang.size())
    {
      SString nd = internal;
      nd.append (lang);
      nd.append ("/");
      nd.append ("HOWTO-");
      nd.append (args[1]);
      nd.append (".txt");
      SFile file (nd);
      if (file.size() > 0)
      {
        internal.append (lang);
        internal.append ("/");
      }
    }
    internal.append ("HOWTO-");
    internal.append (args[1]);
    internal.append (".txt");
    i = args.size();
    encode = "utf-8";
    filename = internal;
  }

  if (args[0] =="test")
  {
    internal =  getPrefix();
    internal.append ("/doc/");
    internal.append ("TestPage.txt");
    i = args.size();
    encode = "utf-8";
    filename = internal;
  }
  if (args[0] =="configure" || args[0] =="config")
  {
    SString afile = currentFileName;
    currentFileName = "/dev/null";
    /* trick to save properties */
    saveProperties();
    currentFileName = afile;
    SString internal = getHome();;
    internal.append ("/.yudit/yudit.properties");
    i = args.size();
    encode = "utf-8";
    filename = internal;
  }

  if (filename.size()==0)
  {
    if (i<args.size())
    {
      messagelabel->setMessage (translate ("usage: open -e utf-8 -yes filename"), 
          SMessageLabel::SS_ERR);
      return;
    }
    filename = currentFileName;
  }
  if (!hasyes && toolbar->modified)
  {
    messagelabel->setMessage (translate ("Unsaved changes. Try -yes option"),
       SMessageLabel::SS_ERR);
    return;
  }
  if (!hasyes && toolbar->printing)
  {
    messagelabel->setMessage (translate ("Pending printing job. Try -yes option"),
       SMessageLabel::SS_ERR);
    return;
  }

  SEncoder utf8("utf-8-s"); 
  SEncoder enc(encode);
  if (!enc.isOK())
  {
    SString ems = translate ("Unknown encoding: ");
    
    ems.append (SS_LB_LRE);
    ems.append ("'"); 
    ems.append (encode); 
    ems.append ("'");
    ems.append (SS_LB_PDF);

    messagelabel->setMessage (ems, SMessageLabel::SS_ERR);
    return;
  }
  SFile f(filename);
  SFileImage im = f.getFileImage();
  if (f.size()<0 || im.size()<0 || im.array()==0)
  {
    if (hasyes)
    {
      lastencoding = encode;
      command->clear();
      currentFileName = filename;
      toolbar->setFileName(currentFileName, lastencoding); 
      setFileTitle ();

      editor->clear();
      editor->setFocus();
      toolbar->setModified(false);
    }
    SString ems = translate ("Can not read: ");
    ems.append (SS_LB_LRE);
    ems.append ("'"); 
    ems.append (filename); 
    ems.append ("'");
    ems.append (SS_LB_PDF);
    messagelabel->setMessage (ems, SMessageLabel::SS_ERR);
    return;
  }
  
  SString text (im.array(), f.size());
//fprintf (stderr, "BOTTLE\n");
  SV_UCS4 ucs4 = enc.decode (text);
//fprintf (stderr, "BOTTLE - END\n");
  bool sft = false;
#ifdef USE_WINAPI
  SToolBar::SFormatIndex ft=SToolBar::SS_FORMAT_DOS;
#else 
  SToolBar::SFormatIndex ft=SToolBar::SS_FORMAT_UNIX;
#endif
  bool hadlinebreak = false;
  if (ucs4.size() 
    || ucs4[ucs4.size()-1] == (SS_UCS4) '\r' 
    || ucs4[ucs4.size()-1] == (SS_UCS4) '\n' 
    || ucs4[ucs4.size()-1] == (SS_UCS4) 0x2029) 
  { 
    switch (ucs4[ucs4.size()-1])
    {
    case '\n': 
      ft =  (ucs4.size()>1 && ucs4[ucs4.size()-2]=='\r')
         ? SToolBar::SS_FORMAT_DOS : SToolBar::SS_FORMAT_UNIX; 
      hadlinebreak = true;
      break;
    case '\r': ft = SToolBar::SS_FORMAT_MAC; 
      hadlinebreak = true;
      break;
    case 0x2029: ft = SToolBar::SS_FORMAT_PS;
      hadlinebreak = true;
      break;
    }
  }
  else
  {
    ft = SToolBar::SS_FORMAT_UNIX;
  }
  sft = (toolbar->currentParagraphSeparator != (unsigned int) ft);
  text = utf8.encode (ucs4);
  editor->clear();
  editor->setText(text);
  editor->setCursorIndex(SCursorIndex(0,0));

  SString ems = translate ("Opened: ");
  ems.append (SS_LB_LRE);
  ems.append ("'"); 
  ems.append (filename); 
  ems.append ("'");
  ems.append (SS_LB_PDF);
  ems.append (".");
  if (sft)
  {
    ems.append (translate (" Line Break:"));
    ems.append (translate (toolbar->paragraphBreakStrings[ft]));
  }
  if (!hadlinebreak)
  {
    ems.append (translate(" Incomplete Last Line..."));
  }

  if (sft)
  {
    toolbar->setParagraphSeparator ((int) ft, false);
    SString currentParagraphSeparator = 
         toolbar->paragraphBreaks[toolbar->currentParagraphSeparator];
    editor->setParagraphSeparator (currentParagraphSeparator, false);
  }
  
  toolbar->setModified (false);
  editor->setFocus();

  /*FIXME: load and set currentFileName */
  lastencoding = encode;
  command->clear();
  currentFileName = filename;

  toolbar->setFileName(currentFileName, lastencoding);
  setFileTitle ();

  messagelabel->setMessage (ems, 
       sft ?  SMessageLabel::SS_WARN : SMessageLabel::SS_INFO);
  const STextIndex* sindex = caretIndex.get (filename);
  if (sindex)
  {
    editor->setCursorIndex(SCursorIndex(sindex->line, sindex->index));
  }
  editor->setFocus();
}

void 
SYudit::save (const SStringVector& args, const SString& str)
{
  SString encode="utf-8";
  SString filename;
  unsigned int i;
  bool hasyes = false;
  for (i=1; i<args.size(); i++)
  {
    if (args[i] == "-e" && i+1 < args.size())
    {
      i++;
      encode = args[i];
    }
    else if (i+1 == args.size())
    {
      filename = completeFileName (args[i]);
    }
    else if (args[i] =="-yes")
    {
      hasyes = true;
    }
    else
    {
      break;
    }
  }
  if (filename.size()==0)
  {
    if (i<args.size())
    {
      messagelabel->setMessage (translate ("usage: save -e utf-8 -yes filename"), 
          SMessageLabel::SS_ERR);
      return;
    }
    filename = completeFileName (currentFileName);
  }

  if (!hasyes && newFileExists (filename))
  {
    SString ems = translate ("File exists. Use -yes option.");
    messagelabel->setMessage (ems, SMessageLabel::SS_ERR);
    return;
  }

  SEncoder utf8("utf-8-s"); 
  SEncoder enc(encode);
  if (!enc.isOK())
  {
    SString ems = translate ("Unknown encoding: ");
    ems.append (SS_LB_LRE);
    ems.append ("'"); 
    ems.append (encode); 
    ems.append ("'");
    ems.append (SS_LB_PDF);
    messagelabel->setMessage (ems, SMessageLabel::SS_ERR);
    return;
  }
  SFile f(filename);
  SOutputStream os = f.getOutputStream();  
  if (!os.isOK())
  {
    SString ems = translate ("Can not write: ");
    ems.append (SS_LB_LRE);
    ems.append ("'"); 
    ems.append (filename); 
    ems.append ("'");
    ems.append (SS_LB_PDF);
    messagelabel->setMessage (ems, SMessageLabel::SS_ERR);
    return;
  }
  SWriter writer(os);
  SString text = editor->getText();
  SV_UCS4 ucs4 = utf8.decode (text);
  bool completed = false;
  if (ucs4.size() 
    && ucs4[ucs4.size()-1] != (SS_UCS4) '\r' 
    && ucs4[ucs4.size()-1] != (SS_UCS4) '\n' 
    && ucs4[ucs4.size()-1] != (SS_UCS4) 0x2029)
  { 
    completed = true;
  }
  text = enc.encode (ucs4);
  if ((text.size() > 0 && !writer.write (text)) || !writer.close())
  {
    SString ems = translate ("Error while writing: ");
    ems.append (SS_LB_LRE);
    ems.append ("'"); 
    ems.append (filename); 
    ems.append ("'");
    ems.append (SS_LB_PDF);
    messagelabel->setMessage (ems, SMessageLabel::SS_ERR);
    return;
  }
  lastencoding = encode;
  currentFileName = filename;
  toolbar->setFileName(currentFileName, lastencoding);
  setFileTitle();

  command->clear();
  command->historyEnd();

  SString ems = translate ("Wrote: ");
  ems.append (SS_LB_LRE);
  ems.append ("'"); 
  ems.append (filename); 
  ems.append ("'");
  ems.append (SS_LB_PDF);
  ems.append (".");
  if (completed) ems.append (translate(" Incomplete Last Line..."));
  messagelabel->setMessage (ems, 
         (completed) ? SMessageLabel::SS_WARN : SMessageLabel::SS_INFO);
  toolbar->setModified (false);
  editor->setFocus();
  caretIndex.put (currentFileName, caretNow);
}

/**
 * move the cursor
 */
void
SYudit::goLineRow (const SStringVector& args, const SString& str)
{
  if (args.size () < 2 || args.size() > 3) 
  {
    SString ems = translate ("usage: go line [column] ");
    messagelabel->setMessage (ems, SMessageLabel::SS_ERR);
    return;
  }
  SString l=args[1];
  SString c;
  if (args.size()==3) c=args[2];
  l.append ((char)0);
  c.append ((char)0);
  int li=0;
  int ci=0;
  sscanf (l.array(), "%d", &li);
  sscanf (c.array(), "%d", &ci);
  if (li <= 0)
  {
    editor->setCursorIndex(SCursorIndex(0, 0, true));
  }
  else if (ci <= 0)
  {
    editor->setCursorIndex(SCursorIndex((unsigned int)(li-1), 0, true));
  }
  else
  {
    editor->setCursorIndex(SCursorIndex((unsigned int)(li-1),
       (unsigned int) (ci-1), false));
  }
}

/**
 * find text
 */
void
SYudit::find (const SStringVector& args, const SString& str)
{
  if (args.size () != 2) 
  {
    SString ems = translate ("usage: find text");
    messagelabel->setMessage (ems, SMessageLabel::SS_ERR);
    return;
  }
  if (!editor->find (args[1]))
  {
    SString ems = translate ("search string not found.");
    ems.append (SS_LB_LRE);
    ems.append ("'");
    ems.append (args[1]);
    ems.append ("'");
    ems.append (SS_LB_PDF);
    messagelabel->setMessage (ems, SMessageLabel::SS_ERR);
    return;
  }
}

/**
 * replace text
 */
void
SYudit::replace (const SStringVector& args, const SString& str)
{
  if (args.size () != 3 || args[1].size()==0) 
  {
    SString ems = translate ("usage: replace original new");
    messagelabel->setMessage (ems, SMessageLabel::SS_ERR);
    return;
  }
  if (!editor->replace (args[1], args[2]))
  {
    SString ems = translate ("search string not found.");
    ems.append (SS_LB_LRE);
    ems.append ("'");
    ems.append (args[1]);
    ems.append ("'");
    ems.append (SS_LB_PDF);
    messagelabel->setMessage (ems, SMessageLabel::SS_ERR);
  }
  return;
}

void
SYudit::focusOutRequest (void *source)
{
  if (source == editor)
  {
    command->setFocus();
  }
  else
  {
    editor->setFocus();
  }
}
void
SYudit::focusChanged (void *source, bool in)
{
}
void
SYudit::caretMoved (void *source, 
   unsigned int _line, unsigned int _column, bool _before)
{
  if (source != editor) return;
  caretMoved();
}

void 
SYudit::caretMoved ()
{
  STextIndex ti = editor->getCaretArrowFrom ();

  if (ti.line != caretNow.line) messagebar->setRow(ti.line+1);
  if (ti.index != caretNow.index) messagebar->setColumn (ti.index);

  if ((caretNow.line != ti.line || caretNow.index != ti.index) 
      && !toolbar->printing)
  {
    messagelabel->setMessage("");
  }
  caretNow = ti;
  
  toolbar->directionChanged (editor->isEmbedStateLR());

  if (ti.index == 0)
  {
    messagebar->setGlyphInfo (0, SString());
  }
  else
  {
    STextIndex tn (ti.line, ti.index-1);
    const SGlyph* g = editor->glyphAt(tn);
    SV_UCS4 chars;
    if (g!=0)
    {
      chars = g->getChars();
    }
    messagebar->setGlyphInfo (g, editor->encode (chars));
  }
}

/**
 * compose an escaped  file name.
 */
SString
SYudit::composeFileNameCommand (const SString& prefix, 
        const SString &encoding, const SString& file)
{
  SString ret (prefix);
  ret.append ("-e ");
  ret.append (encoding);
  ret.append (" ");
  ret.append (quoteFileName (file));
  return SString(ret);
}

SString
SYudit::quoteFileName (const SString& file)
{
  SString ret;
  if (file.find(" ") < 0)
  {
    ret.append (file);
  }
  else if (file.find ("\"") < 0)
  {
    ret.append ("\"");
    ret.append (file);
    ret.append ("\"");
  }
  else
  {
    ret.append ("\'");
    ret.append (file);
    ret.append ("\'");
  }
  return SString(ret);
}

SString
SYudit::completeFileName(const SString& _file)
{
  /* home directory substitution ~/ */
  SString file(_file);
  if (file.size() > 2 && file[0] == '~' && file[1]=='/')
  {
    file.remove (0, 2);
    SString homedir = getHome();
    homedir.append ("/");
    file.insert (0, homedir);
  }
  return SString(file);
}

/**
 * return false if it is not a new file or the file does not exist.
 */
bool
SYudit::newFileExists (const SString& filename)
{
  if (filename == currentFileName) return false;
  SFile f (filename);
  if (f.size() <0) return false;
  return true;
}

void
SYudit::saveProperties ()
{
  /* do not save proeprties if it is being edited */
  SString cfile = getHome();;
  cfile.append ("/.yudit/yudit.properties");
  if (currentFileName == cfile) return;

  //userProps.put ("yudit.default.printer", lastprinterOption);
  //userProps.put ("yudit.default.previewer", lastpreviewOption);

  SString s;
  s = toolbar->fonts[toolbar->currentFont];
  userProps.put ("yudit.editor.font", s);
  s = toolbar->inputs[toolbar->currentInput];
  userProps.put ("yudit.editor.input", s);
  s = toolbar->fontsizes[toolbar->currentFontSize];
  userProps.put ("yudit.editor.fontsize", s);
  s = "";
  s.print ((unsigned int) getSize().width);
  s.append ("x");
  s.print ((unsigned int) getSize().height);
  userProps.put ("yudit.default.geometry", s);
  if (freehand)
  {
    SString s = freehand->getConverter();
    if (s.size())
    {
      userProps.put ("yudit.freehand.converter", s);
    }
  }

  /* create a property file that has the original properties and 
   * the ones that I changed.
   */
  /* create a property list that has changes since last save */
  SProperties changes;
  for (unsigned int i=0; i<userProps.size(); i++)
  {
    for (unsigned int j=0; j<userProps.size(i); j++)
    {
      if (userProps.get(i, j) ==0) continue;
      const SString& key = userProps.key(i, j);
      const SString& vle = *userProps.get(i, j);
      if (originalProperties.get (key) == 0)
      {
         changes.put (key, vle);
      }
      else  if (vle != originalProperties[key])
      {
         changes.put (key, vle);
      }
    }
  }
  SProperties full = originalProperties;
  SProperties current;
  ::loadProperties (configFile, &current);
  /* just save back everything - change in yudit 2.5 */
  full.merge (current);
  /* merge in changes */
  full.merge (changes); 
  full.put ("00HEADER.version.yudit", SD_YUDIT_VERSION);

  /* That stupid old properties header comment. */
  const SString *pstr = full.get ("yudit.background.#");
  /* compatibility. remove this. */
  if (pstr!=0)
  { 
    /* Test it */
    if (pstr->find ("#   Autosave will eliminate all comments and '\\' (line joiner) characters.") > 0)
    {
      SString a;
      a.append ("# From version 2.7.6 Yudit preserves comment lines\n");
      a.append ("# that start with a '#' and preceed the property  \n");
      a.append ("# line they refer to. ");
      full.put ("yudit.background.#", a);
    }
  }

  if (!::saveProperties (configFile, full))
  {
    SString str = translate ("Could not save preferences");
    str.append (" ");
    str.append (configFile);
    messagelabel->setMessage (str, SMessageLabel::SS_ERR);
    fprintf (stderr, "Could not save properties into %*.*s.\n",
       SSARGS(configFile));
  }
}

bool
SYudit::dropped (void* p, const SString& mimetype, const SString& data)
{
  if (textDialog != 0 && textDialog->isShown()) return false;
  //fprintf (stderr, "SYudit::dropped %*.*s [%*.*s]\n", SSARGS(mimetype), SSARGS(data));
  SStringVector v (data, "\r\n");
  SString file=v[0];
  if (mimetype == "text/uri-list")
  {
    // FIX %XX
    SString ret;
    char* c = 0;
    char buff[3];
    for (unsigned i=0; i<file.size(); i++) 
    {
      if (file[i] == '%' && i+2<file.size()) 
      {
        buff[0] = file[i+1];
        buff[1] = file[i+2];
        buff[2] = 0;
        c = 0;
        ret.append ((char)(unsigned char)strtol (buff, &c, 16));
        i=i+2;
      } else {
        ret.append ((char) file[i]);
      }
    }
    file = ret;
    if (file.size() < strlen ("file:") 
       || strncmp (file.array(), "file:", strlen ("file:"))!=0)
    {
      SString msg = translate ("unsupported text/uri: ");
      msg.append (SS_LB_LRE);
      msg.append ("'");
      msg.append (file);
      msg.append ("'");
      msg.append (SS_LB_PDF);
      messagelabel->setMessage (msg, SMessageLabel::SS_ERR);
      return false;
    }
    file.remove (0, strlen ("file:"));
  }
  if (fileDialog != 0 && fileDialog->isShown())
  {
     fileDialog->center();
     fileDialog->setFileName (file);
     return true;
  }
  SString cm ("open -e ");;
  cm.append (lastencoding);
  cm.append (" ");
  if (toolbar->modified)
  {
    messagelabel->setMessage (translate ("Unsaved changes. Enter discards!"), SMessageLabel::SS_ERR);
    cm.append ("-yes ");
    cm.append (quoteFileName (file));
    command->setText(cm);
    command->setCursorIndex (SCursorIndex(0,10000));
    command->setFocus();
  }
  else
  {
    messagelabel->setMessage (translate ("open -yes -e encoding file"), 
       SMessageLabel::SS_INFO);
    cm.append (quoteFileName (file));
    command->setText(cm);
    command->setCursorIndex (SCursorIndex(0,10000));
    command->putHistory(cm);
    SStringVector cv; cv.smartSplit (cm);
    load (cv, cm);
  }
  return true;
}

void
SYudit::createFileDialog () // If needed
{
  if (fileDialog) return;

  fileDialog = new SFileDialog();
  fileDialog->setModal (this);
  fileDialog->setBackground (originalProperties["yudit.background"]);
  fileDialog->setLabelForeground (originalProperties["yudit.label.foreground"]);
  fileDialog->setTitleForeground (originalProperties["yudit.title.foreground"]);
  fileDialog->setSliderBackground (originalProperties["yudit.slider.background"]);

  SStringVector enc;
  if (originalProperties.get("yudit.editor.filetypes"))
  {
    //SString s = originalProperties["yudit.editor.filetypes"];
    //fprintf (stderr, "Specified filetypes: %*.*s\n", SSARGS (s));
    enc.append (SStringVector (originalProperties["yudit.editor.filetypes"]));
  }
  SBinHashtable<bool> ph;
  unsigned int ic=0;

  for (ic=0; ic<enc.size(); ic++)
  {
    ph.put (enc[ic], true);
  }
  SStringVector benc = SEncoder::builtin();
  
  unsigned int ib=0;
  for (ib=0; ib<benc.size(); ib++)
  {
    /* this is an input method and it is misleading. */
    if (benc[ib] == "unicode") continue;
    ph.put (benc[ib], true);
  }
  SStringVector bext = SEncoder::external();
  
  for (ib=0; ib<bext.size(); ib++)
  {
    /* this is an input method and it is misleading. */
    if (bext[ib].match("iso-8859*"))
    {
      ph.put (bext[ib], true);
    }
    if (bext[ib].match("mik"))
    {
      ph.put (bext[ib], true);
    }
    else if (bext[ib] == "viscii")
    {
      ph.put (bext[ib], true);
    }
    else if (bext[ib] == "wingreek")
    {
      ph.put (bext[ib], true);
    }
    else if (bext[ib] == "tcvn")
    {
      ph.put (bext[ib], true);
    }
    else if (bext[ib].match("koi8-*"))
    {
      ph.put (bext[ib], true);
    }
    else if (bext[ib] == "rovas")
    {
      ph.put (bext[ib], true);
    }
    else if (bext[ib] == "cp-1251")
    {
      ph.put (bext[ib], true);
    }
    else if (bext[ib] == "cp-1250")
    {
      ph.put (bext[ib], true);
    }
  }
 
  SStringVector addEnc;
  for (ic=0; ic<ph.size(); ic++)
  {
    for (ib=0; ib<ph.size(ic); ib++)
    {
       bool vle = ph.get (ic, ib);
       if (vle)
       {
         addEnc.append (ph.key (ic, ib));
       }
    }
  }
  /* set the filetypes */
  SString font = originalProperties["yudit.default.font"];
  SString fontSizeString=originalProperties["yudit.default.fontsize"];

  double fontSize=0;
  fontSizeString.append ((char)0);
  sscanf (fontSizeString.array(), "%lf", &fontSize);
  if (fontSize < 2.0) fontSize = 2.0;
  fileDialog->setFont (font, fontSize);
  fileDialog->setFileTypes (addEnc);

  SIcon* icon = SIconFactory::getIcon("Yudit");
  fileDialog->setApplicationImage (icon->getImage ());
}

void
SYudit::createHighlightDialog () // If needed
{
  if (highlightDialog) return;

  highlightDialog = new SHighlightD();
  highlightDialog->setModal (this);
  highlightDialog->setBackground (originalProperties["yudit.background"]);
  highlightDialog->setLabelForeground (originalProperties["yudit.label.foreground"]);
  highlightDialog->setTitleForeground (originalProperties["yudit.title.foreground"]);
  highlightDialog->setSliderBackground (originalProperties["yudit.slider.background"]);

  /* set the filetypes */
  SString font = originalProperties["yudit.default.font"];
  SString fontSizeString=originalProperties["yudit.default.fontsize"];
  double fontSize=0;
  fontSizeString.append ((char)0);
  sscanf (fontSizeString.array(), "%lf", &fontSize);
  if (fontSize < 2.0) fontSize = 2.0;
  highlightDialog->setFont (font, fontSize);

  SIcon* icon = SIconFactory::getIcon("Yudit");
  highlightDialog->setApplicationImage (icon->getImage ());
}

void
SYudit::createKMapDialog ()
{
  if (kmapDialog) return;
  kmapDialog = new SKMapDialog ();
  kmapDialog->setModal (this);
  kmapDialog->setXInputs (originalProperties["yudit.editor.xinputs"]);
  kmapDialog->setBackground (originalProperties["yudit.background"]);
  kmapDialog->setLabelForeground (originalProperties["yudit.label.foreground"]);
  kmapDialog->setTitleForeground (originalProperties["yudit.title.foreground"]);
  kmapDialog->setSliderBackground (originalProperties["yudit.slider.background"]);
  /* set the filetypes */
  SString font = originalProperties["yudit.default.font"];
  SString fontSizeString=originalProperties["yudit.default.fontsize"];

  double fontSize=0;
  fontSizeString.append ((char)0);
  sscanf (fontSizeString.array(), "%lf", &fontSize);
  if (fontSize < 2.0) fontSize = 2.0;
  kmapDialog->setFont (font, fontSize);
  /* button size bug workaround */
  SDimension dim = kmapDialog->getPreferredSize();
  kmapDialog->resize(SDimension(dim.width+2, dim.height+2));

  SIcon* icon = SIconFactory::getIcon("Yudit");
  kmapDialog->setApplicationImage (icon->getImage ());
}

void
SYudit::createTextDialog ()
{
  if (textDialog) return;

  textDialog = new STextDialog();
  textDialog->setModal (this);

  textDialog->setBackground (originalProperties["yudit.background"]);
  textDialog->setLabelForeground (originalProperties["yudit.label.foreground"]);
  textDialog->setTitleForeground (originalProperties["yudit.title.foreground"]);

  SString font = originalProperties["yudit.default.font"];
  SString fontSizeString=originalProperties["yudit.default.fontsize"];

  double fontSize=0;
  fontSizeString.append ((char)0);
  sscanf (fontSizeString.array(), "%lf", &fontSize);
  if (fontSize < 2.0) fontSize = 2.0;

  textDialog->setFont (font, fontSize);

  SIcon* icon = SIconFactory::getIcon("Yudit");
  textDialog->setApplicationImage (icon->getImage ());
}

/*
 * SFreeHandListener
 * FIXME:
 */
void
SYudit::freeHandTextChanged (void* source, const SString& lookup)
{
  if (lookup.size()==0) return;
  if (editor->isFocused ())
  {
    editor->insertEditorText (lookup);
  }
  else if (command->isFocused())
  {
    command->insertEditorText (lookup);
  }
}

void
printl (const SLayout lo)
{
  SLocation l = lo.getLocation();
  SDimension d = lo.getDimension();
  fprintf (stderr, "l= (%d,%d) d= (%u,%u)          l2 =(%d,%d)\n",
    l.x, l.y, d.width, d.height, l.x + (int) d.width, l.y + d.height);
}


void
SYudit::showFreeHand(bool is)
{
  if (!(is ^ freeHandShown)) return;
  /* need to create */
  bool newhand = (freehand==0);
  freeHandShown = is;
  if (freehand==0)
  {
    freehand = new SFreeHand ();
    freehand->setBackground (originalProperties["yudit.background"]);
    freehand->setLabelForeground (originalProperties["yudit.label.foreground"]);
    freehand->setForeground (originalProperties["yudit.title.foreground"]);
    freehand->setDrawingForeground (SColor("green"), SColor("yellow"));
    freehand->setDrawingBackground (SColor("black"));
    freehand->setConverter (originalProperties["yudit.freehand.converter"]);

    freehand->setSliderBackground (
         originalProperties["yudit.slider.background"]);
    freehand->setFreeHandListener (this);
    SString font = originalProperties["yudit.default.font"];
    SString fontSize=originalProperties["yudit.default.fontsize"];

    double df=0;
    fontSize.append ((char)0);
    sscanf (fontSize.array(), "%lf", &df);
    if (fontSize < 2.0) df = 2.0;
    freehand->setButtonFont (font, df);

    font = toolbar->fonts[toolbar->currentFont];
    fontSize = toolbar->fontsizes[toolbar->currentFontSize];
    fontSize.append ((char)0);

    if (sscanf (fontSize.array(), "%lf", &df)==0 || df < 2.0) df = 2.0;
    freehand->setFont (font, df);
    freeHandHeight = freehand->getPreferredSize().height;
  }

  SLocation l = editorLayout.getLocation ();
  SDimension d = editorLayout.getDimension ();
  SLocation sl = sliderLayout.getLocation(); 
  SDimension sd = sliderLayout.getDimension(); 
  int hp = l.y + (int) d.height -(int)freeHandHeight;

  if (is)
  {
    editor->setLayout (
      SLayout ( 
       SLocation (l.x, l.y),
       SLocation (l.x+(int) d.width, hp),
        SLocation (0, 0),
        SLocation (100, 100)
      )
    );
    slider->setLayout (SLayout (
      SLocation (sl.x, sl.y),
      SLocation (sl.x+(int)sd.width, hp),
      SLocation (100, 0),
      SLocation (100, 100)
    ));

    freehand->setLayout (
      SLayout (
        SLocation (l.x, hp), 
        SLocation (sl.x + (int) sd.width, 
           hp+(int)freeHandHeight),
        SLocation (0, 100),
        SLocation (100, 100)
      )
    );
    SLocation ml = freehand->getLayout().getLocation (layout, getSize());
    SDimension md = freehand->getLayout().getDimension (layout, getSize());
  }
  else 
  {
    editor->setLayout (editorLayout);
    slider->setLayout (sliderLayout);
    /* move it away */
    freehand->setLayout (
      SLayout (
        SLocation (l.x, -(int)hp -(int)freeHandHeight-1), 
        SLocation (sl.x + (int) sd.width, -1),
        SLocation (0, 0),
        SLocation (0, 0)
      )
    );
  }
  if (newhand)
  {
    add (freehand);
    editor->setFocus();
  }
  _resized ();
}

/**
 * Try to prefix executable with mytool -pipecmd.
 * @return false if mytool not found.
 */
bool
SYudit::addMytoolPrefix (SString* executable)
{
  if (executable->size()==0) return false;
#ifdef USE_WINAPI
  SString pr = getPrefix();
  pr.append ("/bin/mytool.exe");
  SFile f (pr);
  if (f.size () <= 0) 
  {
    SString message=translate ("Command not found: ");
    message.append (SS_LB_RLE);
    message.append ("'");
    message.append (pr);
    message.append ("'");
    message.append (SS_LB_PDF);
    messagelabel->setMessage (message, SMessageLabel::SS_ERR);
    return false;
  }
#else
  SString pr = getPrefix();
  pr.append ("/bin/mytool");
  SFile f0 (pr);
  if (f0.size () <= 0) 
  {
    pr = getPrefix();
    SStringVector pv(pr, "/");
    if (pv.size() > 1)
    {
      pv.truncate (pv.size()-2);
    }
    pr = pv.join ("/");
    pr.insert (0, "/");
    pr.append ("/bin/mytool");
    SFile f0 (pr);
    if (f0.size () <= 0) 
    {
      SString message=translate ("Command not found: ");
      message.append (SS_LB_LRE);
      message.append ("'");
      message.append (pr);
      message.append ("'");
      message.append (SS_LB_PDF);
      messagelabel->setMessage (message, SMessageLabel::SS_ERR);
      return false;
    }
  }
#endif
  SStringVector v;
  v.smartSplit (*executable);
  if (!commandExists (v[0]))
  {
    SString message=translate ("Command not found: ");
    message.append (SS_LB_LRE);
    message.append ("'");
    message.append (v[0]);
    message.append ("'");
    message.append (SS_LB_PDF);
    messagelabel->setMessage (message, SMessageLabel::SS_ERR);
    return false;
  }
  pr.insert (0, "\"");
  pr.append ("\"");
  pr.append (" -pipecmd ");
  executable->insert (0, pr);
  return true;
}

/**
 * Set the title to current filename.
 */
void 
SYudit::setFileTitle ()
{
  SDir d;
  /* this will chop off filename */
  d.cd (currentFileName);
  SString dn = d.getName ();
  SString fn;
  SStringVector v(currentFileName, "/");
  if (v.size())
  {
    fn.append (v[v.size()-1]);
  }

  SString t = fn;
  // FIXME: do we need the directory?
  dn = "";
  if (dn.size () > 0)
  {
    t.append (" (");
    t.append (dn);
    t.append (")");
  }
  t.append ("  Yudit-");
  t.append (SD_YUDIT_VERSION);
  setTitle (t);
}

/**
 * Translate our string highlight mode to an integer
 * @return -1 if it can not be translated.
 */
static SString 
translateHighlightMode (const SString& hl)
{
  if (hl == "none" || hl == "simple" || hl == "simple-dark")
  {
    return SString(hl);
  }
  if (SSyntax::isSupported (hl)) return SString(hl);
  return SString("");
}

/**
 * \return negative on fail or the number if it is specified 
 *  as +NUMBER.
 */
static int 
getLineNumber (const SString& s)
{
  if (s.size() < 2 || s.peek(0) != '+') return -1;
  int line = 0;
  for (unsigned int i=1; i<s.size(); i++)
  {
    if (s.peek(i) >= '0' && s.peek(i) <= '9')
    {
      if (line < 100000000) 
      {
        line = line * 10;
        line += (s.peek(i) - '0');
      }
    }
    else
    {
      return -1;
    }
  } 
  return line;
}

static SString getPrintHelp ()
{
   SString pstr = 
     translate ("usage: print [-o file] [-p printer] [-e exec] [-break] [-hsize header-font-size]"); 
  pstr.append (" [-m A4] [-L]");
  return SString (pstr);
}
