//<copyright>
//
// Copyright (c) 1994,95
// Institute for Information Processing and Computer Supported New Media (IICM),
// Graz University of Technology, Austria.
// original version:
// Copyright (c) 1987, 1988, 1989, 1990, 1991 Stanford University
// Copyright (c) 1991 Silicon Graphics, Inc.
//
//</copyright>
/*
 * Permission to use, copy, modify, distribute, and sell this software and 
 * its documentation for any purpose is hereby granted without fee, provided
 * that (i) the above copyright notices and this permission notice appear in
 * all copies of the software and related documentation, and (ii) the names of
 * Stanford and Silicon Graphics may not be used in any advertising or
 * publicity relating to the software without the specific, prior written
 * permission of Stanford and Silicon Graphics.
 * 
 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
 *
 * IN NO EVENT SHALL STANFORD OR SILICON GRAPHICS BE LIABLE FOR
 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
 * OF THIS SOFTWARE.
 */

/*
 * FieldEditor -- simple editor for text fields
 */


//<file>
//
// File:        field.C - portation of field editor to InterViews 3.1
//
// Created:     10 Mar 94   Michael Pichler
//
// Changed:     26 Jan 95   Michael Pichler
//
//
//</file>


#include "field.h"
#include "editlabel.h"
#include "glyphutil.h"

#include <Dispatch/dispatcher.h>
#include <Dispatch/iocallback.h>
#include <IV-look/kit.h>
#include <InterViews/background.h>
#include <InterViews/canvas.h>
#include <InterViews/color.h>
#include <InterViews/display.h>
#include <InterViews/font.h>
#include <InterViews/event.h>
#include <InterViews/hit.h>
#include <InterViews/layout.h>
#include <InterViews/patch.h>
#include <InterViews/selection.h>
#include <InterViews/session.h>
#include <InterViews/style.h>
#include <InterViews/window.h>

#include <OS/string.h>

#include <X11/keysym.h>

#include <string.h>
#include <ctype.h>
#include <iostream.h>


/*** control keys ***/
// alternative to Home, Delete etc.

static const char FE_Left   = (char) 2;  // ^b
static const char FE_Right  = (char) 6;  // ^f
static const char FE_Home   = (char) 1;  // ^a
static const char FE_End    = (char) 5;  // ^e
static const char FE_Delete = (char) 4;  // ^d
static const char FE_DelEOL = (char) 11; // ^k
static const char FE_Backsp = (char) 8;  // ^h
static const char FE_Copy   = (char) 3;  // ^c
static const char FE_Paste  = (char) 22; // ^v


declareSelectionCallback(FieldBrowser)



/*** FieldEditor31 ***/


FieldEditor31::FieldEditor31 (
  WidgetKit* kit, Style* s,
  const char* sample,
  FieldEditorAction31* action,
  const char* special, const char* accept, const char* cancel,
  int hide, char hidechar
)
: FieldBrowser (kit, s, sample, hide, hidechar, "FieldEditor")
{
  action_ = action;
  Resource::ref (action);
  special_ = strcpy_new (special);
  accept_ = strcpy_new (accept);
  cancel_ = strcpy_new (cancel);
  edlabel_->cursorPosition (edlabel_->strlen (), 0);
  showmod_ = 0;

  // alternate key bindings (may be worth to be extended by keycode function settings)
  kit->begin_style ("FieldEditor", "EditLabel");
  delbackward_ = kit->style ()->value_is_on ("deleteBackward");
  kit->end_style ();
}


FieldEditor31::~FieldEditor31 ()
{
  Resource::unref (action_);
  delete special_;
  delete accept_;
  delete cancel_;
}


void FieldEditor31::pasteSelection (const Event& e)
{
  SelectionManager* s = e.display ()->primary_selection ();
  // request the selection
  s->retrieve ("string", new SelectionCallback(FieldBrowser) (this, &FieldBrowser::paste));
  modifying ();
}


// FieldEditor31::undraw (): stop blinking


// press (): not overridden


// drag (): should do paste with middle mouse button - not supported by IV


// release (): not overridden


// double_click (): not overridden


// keystroke
// a character can be in more than one group of special, accept, cancel
// the order of function execution is:
//   (1) special, (2) accept, (3) cancel
// both accept and cancel seems not useful - but this is
// in responsibility of caller

void FieldEditor31::keystroke (const Event& e)
{
  unsigned long keysym = e.keysym ();
  char key = '\0';
  e.mapkey (&key, 1);

  int sel = e.shift_is_down ();
  int ctrl = e.control_is_down ();

  if (key == '\t')  // pass focus
  {
    if (sel)
      prev_focus ();
    else
      next_focus ();
  }

  // call action on special keys
  // (special_, accept_, cancel_ are non-nil)
  if (action_ && key)
  {
    if (strchr (special_, key))
    { action_->special (this, key);
      return;
    }
    if (strchr (accept_, key))
    { action_->accept (this, key);
      return;
    }
    if (strchr (cancel_, key))
    { action_->cancel (this, key);
      return;
    }
  }

  switch (keysym)  // cursor keys
  {
    case XK_Up:
      prev_focus ();
    break;
    case XK_Down:
      next_focus ();
    break;
    case XK_Left:
      if (ctrl)
        edlabel_->cursorWordLeft (sel);
      else
        edlabel_->cursorLeft (sel);
    break;
    case XK_Right:
      if (ctrl)
        edlabel_->cursorWordRight (sel);
      else
        edlabel_->cursorRight (sel);
    break;
    case XK_Home:
      edlabel_->cursorHome (sel);
    break;
    case XK_End:
      edlabel_->cursorEnd (sel);
    break;
    case XK_Insert:
      if (ctrl)
        copySelection (e);
      else if (sel)
        pasteSelection (e);
      else
        edlabel_->toggleInsert ();
    break;
    case XK_Delete:
      if (ctrl)
        edlabel_->deleteToEndOfLine ();
      else if (delbackward_)
        edlabel_->deleteBackward ();
      else
        edlabel_->deleteChar ();
      modifying ();
    break;
    case XK_BackSpace:
      edlabel_->deleteBackward ();
      modifying ();
    break;

    default:
      if (iscntrl (key))   // control keys
        switch (key)
        {
          case FE_Left:
            edlabel_->cursorLeft (sel);
          break;
          case FE_Right:
            edlabel_->cursorRight (sel);
          break;
          case FE_Home:
            edlabel_->cursorHome (sel);
          break;
          case FE_End:
            edlabel_->cursorEnd (sel);
          break;
          case FE_Delete:
            edlabel_->deleteChar ();
            modifying ();
          break;
          case FE_DelEOL:
            edlabel_->deleteToEndOfLine ();
            modifying ();
          break;
          case FE_Backsp:
            edlabel_->deleteBackward ();
            modifying ();
          case FE_Copy:
            copySelection (e);
          break;
          case FE_Paste:
            pasteSelection (e);
          break;
        }
      else
      { edlabel_->insertChar (key);
        modifying ();
      }
    break;
  } // switch keysym

  edpatch_->redraw ();

} // keystroke


InputHandler* FieldEditor31::focus_in ()
{
// initiate blinking
//cerr << "focus in" << endl;
  edlabel_->showCursor (1);
  edpatch_->redraw ();
  return InputHandler::focus_in ();
}


void FieldEditor31::focus_out ()
{
// stop blinking
//cerr << "focus out" << endl;
  edlabel_->showCursor (0);
  edpatch_->redraw ();
  InputHandler::focus_out ();
}


void FieldEditor31::field (const char* str)
{
  edlabel_->setString (str);
  edlabel_->cursorEnd (0);
  edpatch_->redraw ();
}


// string (): same as for FieldBrowser


int FieldEditor31::modified () const
{
  return edlabel_->modified ();
}


void FieldEditor31::clearModification ()
{
  edlabel_->modify (0);
  edpatch_->redraw ();
}


void FieldEditor31::modifying ()
{
  if (showmod_)
    edlabel_->modify (1);
  // redraw done by caller
}


/*** FieldEditorAction31 ***/


FieldEditorAction31::FieldEditorAction31() { }
FieldEditorAction31::~FieldEditorAction31() { }
