//<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 <hyperg/Dispatch/dispatcher.h>
#include <hyperg/Dispatch/iocallback.h>
//#include <InterViews/enter-scope.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 <hyperg/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);
  showModifications(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)
{
  if (e.len > 1)
  { // event is not only one character
    edlabel_->insertString(e.keystring,e.len);
  }
  else
  {
    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

void FieldEditor31::showModifications(int onoff)
{
  if (onoff)
    edlabel_->modifyColor(modcolor_);
  else
    edlabel_->modifyColor(nil);
}

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() { }
