//<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.
 */

/*
 * FieldBrowser -- scrolled display of text fields
 */


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


#include "fieldb.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>



/*** FieldBrowser ***/

declareSelectionCallback(FieldBrowser)
implementSelectionCallback(FieldBrowser)


FieldBrowser::FieldBrowser (
  WidgetKit* kit, Style* s,
  const char* sample,
  int hide, char hidechar,
  const char* stylename
)
: InputHandler (nil, s)
{
  dblclickindex_ = -1;

  const LayoutKit& layout = *LayoutKit::instance ();

  copy_ = lose_ = nil;

  kit->begin_style (stylename ? stylename : "FieldBrowser", "EditLabel");
  Style* style = kit->style ();

  Display* dis = Session::instance ()->default_display ();

  const Color* csrcol;  // "cursorColor"
  if (!(csrcol = lookupColor (style, dis, "cursorColour", "cursorColor", "cursorcolour", "cursorcolor")))
    csrcol = kit->foreground ();

  const Color* selcol;  // "selectionColor"
  if (!(selcol = lookupColor (style, dis, "selectionColour", "selectionColor", "selectioncolour", "selectioncolor")))
    selcol = kit->foreground ();

  const Color* invcol;  // "inverseColor"
  if (!(invcol = lookupColor (style, dis, "inverseColour", "inverseColor", "inversecolour", "inversecolor")))
    invcol = kit->background ();

  const Color* modcol;  // "modifiedColor"
  if (!(modcol = lookupColor (style, dis, "modifiedColour", "modifiedColor", "modifiedcolour", "modifiedcolor")))
    modcol = kit->background ();

  edlabel_ = new EditLabel (
    sample, kit->font (),
    kit->foreground (),
    csrcol,
    selcol,
    invcol,
    modcol,
    hide, hidechar
  );
  edpatch_ = new Patch (edlabel_);

  body (kit->inset_frame (edpatch_));

  kit->end_style ();  // "FieldEditor"/"EditLabel"
}


FieldBrowser::~FieldBrowser ()
{
  // inhibit selection callbacks on this field editor
  if (copy_)
  { copy_->detach ();
    copy_->unref ();
  }
  if (lose_)
  { lose_->detach ();
    lose_->unref ();
  }
}

// FieldBrowser uses middle mouse button for paste (click) and scrolling (drag)

void FieldBrowser::press (const Event& e)
{
  Hit hit (&e);
  repick (0, hit);  // pick (canvas (), allocation (), 0, hit);

  button_ = e.pointer_button ();
  dragx_ = e.pointer_x ();
  dragged_ = 0;

  if (button_ == Event::middle)  // initiate scrolling
  {
  }
  else if (hit.any ())
  {
    int index = (int) hit.index (0);
    switch (button_)
    {
      case Event::left:  // set text cursor
        edlabel_->cursorPosition (index, 0);
        if (index != dblclickindex_)
          dblclickindex_ = -1;
      break;

      case Event::right:  // expand selection (at any endpoint)
        // when index is nearer to cursor than mark then swap mark and cursor
        edlabel_->attractMark (index);  // change at side nearer to index position
        edlabel_->cursorPosition (index, 1);  // move mark
        dragged_ = 1;  // to copy the selection on release
      break;
    }

    edpatch_->redraw ();
  }

} // press


void FieldBrowser::drag (const Event& e)
{
  if (button_ == Event::middle)
  {
    if (!dragged_)
    {
      // does not affect text cursor position
      // change mouse cursor to grabbing hand
      Window* win = edpatch_->canvas ()->window ();
      win->push_cursor ();
      win->cursor (WidgetKit::instance ()->hand_cursor ());
    }

    edlabel_->scroll (e.pointer_x () - dragx_);
    edpatch_->redraw ();
    dragx_ = e.pointer_x ();
  }
  else
  {
    Hit hit (&e);
    int index = edlabel_->hitIndex (hit.left () - allocation ().x ());
    int oldmark = edlabel_->markPosition ();

    edlabel_->cursorPosition (index, 1);
    edlabel_->scrollToMark ();
    if (edlabel_->markPosition () != oldmark)
      edpatch_->redraw ();

    if (index != dblclickindex_)
      dblclickindex_ = -1;
  }

  dragged_ = 1;

} // drag


void FieldBrowser::release (const Event& e)
{
  if (button_ == Event::middle)
  {
    if (dragged_)
    {
      edpatch_->canvas ()->window ()->pop_cursor ();  // stop scrolling
    }
    else  // paste at mouse position
    {
      Hit hit (&e);
      repick (0, hit);
      if (hit.any ())
      {
        edlabel_->cursorPosition ((int) hit.index (0), 0);
        // tiny bug: does not allow paste within one FieldEditor
        pasteSelection (e);
        edpatch_->redraw ();  // show new cursor when no paste was possible
      }
    } // paste
  }
  else  // copy selected text
  {
    if (dragged_)
      copySelection (e);
  }
}


void FieldBrowser::double_click (const Event& e)
{
  Hit hit (&e);
  repick (0, hit);

  if (hit.any () && button_ == Event::left)
  {
    int index = (int) hit.index (0);
  
    if (index == dblclickindex_)  // triple click (press removed selection)
      edlabel_->selectAll ();
    else  // double click
      edlabel_->selectWord (index);
    edpatch_->redraw ();
  
    dblclickindex_ = index;
    copySelection (e);  // no longer done in press
  }
} // double_click


// FieldBrowser not sensitive for keystrokes


void FieldBrowser::copySelection (const Event& e)
{
  SelectionManager* s = e.display ()->primary_selection ();
  Resource::unref (copy_);
  if (lose_)
  { lose_->detach ();  // otherwise I'd lose my new selection
    lose_->unref ();
  }
  // offer a copy
  copy_ = new SelectionCallback(FieldBrowser) (this, &FieldBrowser::copy);
  Resource::ref (copy_);
  lose_ = new SelectionCallback(FieldBrowser) (this, &FieldBrowser::lose);
  Resource::ref (lose_);
  s->own (copy_, lose_);
}


void FieldBrowser::copy (SelectionManager* s)  // do copy operation
{
  if (edlabel_->hidden ())  // do not copy hidden fields
    return;

  int mark = edlabel_->markPosition ();
  int cursor = edlabel_->cursorPosition ();
  const char* text = edlabel_->string ();

  if (cursor < mark)
    s->put_value (text + cursor, mark - cursor);
  else  // cursor >= mark
    s->put_value (text + mark, cursor - mark);
}



void FieldBrowser::paste (SelectionManager* s)  // do paste operation
{
//cerr << "FieldBrowser::paste" << endl;
  String* type;
  void* data;
  int nbyte, format;

  s->get_value (type, data, nbyte, format);

  if (nbyte <= 0)
    return;

  // care for invalid characters
  char* str = (char*) data;
  for (int n = nbyte;  n--;  str++)
  { if (!*str || *str == '\n')
      *str = ' ';
  }

  edlabel_->insertString ((const char*) data, nbyte);
  edpatch_->redraw ();

//cerr << "I got to insert: <";  cerr.write ((const char*) data, nbyte);  cerr << ">." << endl;
}


void FieldBrowser::lose (SelectionManager*)  // losing selection
{
//cerr << "FieldBrowser::lose" << endl;
  edlabel_->stopSelecting ();
  edpatch_->redraw ();
}


// FieldBrowser has no focus management


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


void FieldBrowser::scrollToEnd ()
{
  edlabel_->cursorEnd (0);
  edpatch_->redraw ();
}


const char* FieldBrowser::field () const
{
  return edlabel_->string ();
}
