//<copyright>
//
// Copyright (c) 1995
// Institute for Information Processing and Computer Supported New Media (IICM),
// Graz University of Technology, Austria.
//
//</copyright>



//<file>
//
//
// Name:        $RCSfile: numbereditor.C,v $
//
// Purpose:     Implementation of class NumberEditor
//              (based on FieldSlider source from bmarsch)
//
// Created:     19 May 95   Bernhard Marschall
// Modified:    19 Sept 96  Thomas Starlinger
// Last:        $Date: 1996/10/08 08:24:36 $
//
// $Id: numbereditor.C,v 1.3 1996/10/08 08:24:36 bmarsch Exp $
//
// Description: See .h file for a, more or less, detailed descr.
//
// $Log: numbereditor.C,v $
// Revision 1.3  1996/10/08 08:24:36  bmarsch
// Renamed FieldObserver to FieldEditorObserver (there's already a
// FieldObserver in fslider.c)
//
// Revision 1.2  1996/09/20 13:45:37  bmarsch
// Corrected includes
//
// Revision 1.1  1996/09/20 13:29:57  bmarsch
// Initial revision
//
// Revision 1.3  1996/09/20 13:09:58  tstar
// Totally rewritten. To much to be documented here.
//
//
//</file>


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <InterViews/style.h>
#include <InterViews/font.h>
#include <InterViews/layout.h>
#include <InterViews/observe.h>
#include "adjvalue.h"
#include "field.h"
#include <IV-look/kit.h>
#include <IV-look/slider.h>
#include <IV-look/bevel.h>

#include "numbereditor.h"

//---------- FieldEditorObserver --------------------------------------

class FieldEditorObserver : public Observer {
public:
  FieldEditorObserver(NumberEditor*);
  ~FieldEditorObserver() {}

  virtual void update(Observable*);

private:
  NumberEditor* fslider_;
};


FieldEditorObserver::FieldEditorObserver(NumberEditor* fslider)
{
  fslider_ = fslider;
}


void FieldEditorObserver::update(Observable*)
{
  fslider_->setField();
}

//---------- Callbacks ------------------------------------------------

declareFieldEditorCallback31(NumberEditor)
implementFieldEditorCallback31(NumberEditor)

//---------- NumberEditor ---------------------------------------------

NumberEditor::NumberEditor(float lower, float upper, float initial, int pos, 
			   Action* focus_in, Action* changed,
			   int precision, const char* pattern,
			   float small, float large)
: InputHandler(nil, WidgetKit::instance()->style()),
  adjInside_(true)
{
  int i;
  float s=small;
  float l=large;
  
  // Save actions to call...
  chAction_=changed;
  fiAction_=focus_in;

  // If no value specified for small stepper movement -> 1/10 unit.
  if (s<0) {
    s=1;
    for(i=0; i<precision; i++) s/=10;
  }

  // If no value specified for small stepper movement -> 1 unit.
  if (l<0) {
    l=10;
    for(i=0; i<precision; i++) l/=10;
  }

  // Widget- and LayoutKit instances.
  kit_ = WidgetKit::instance();
  layout_ = LayoutKit::instance();

  // Create new adjustable.
  adj_ = new AdjValue(lower, upper, initial, s, l);

  slPos_=pos;

  init(precision, pattern);
}

NumberEditor::NumberEditor(AdjValue* adj, int pos, Action* focus_in, Action* changed,
			   int precision, const char* pattern)
: InputHandler(nil, WidgetKit::instance()->style()),
  adjInside_(false)
{
  // Save actions to call...
  chAction_=changed;
  fiAction_=focus_in;

  // Widget- and LayoutKit instances.
  kit_ = WidgetKit::instance();
  layout_ = LayoutKit::instance();

  // Use adjustable adj.
  adj_ = adj;

  slPos_=pos;

  init(precision, pattern);
}

void NumberEditor::init(int precision, const char* pattern)
{
  // store precision and make format string for (s)printf
  precision_ = precision;
  sprintf(format_, "%%.%df", precision_);

  // scrollbar

  // field editor
  kit_->begin_style("FieldEditor", "EditLabel");
  field_ = new FieldEditor31(
    kit_, kit_->style(), "     ",
    new FieldEditorCallback31(NumberEditor)(
      this, &NumberEditor::accept, &NumberEditor::accept, nil
    ),
    nil,
    "\r",
    "\t"
  );   // ref'd by hbox
  field_->showModifications(0);
  setField();
  append_input_handler(field_);

  // calculate horizontal size of field editor
  const Font* font = kit_->font();
  float hsize;
  if (pattern) {
    // use pattern as template
    hsize = font->width(pattern, strlen(pattern));
  }
  else {
    // get width from range and precision
    char buf[32];
    sprintf(buf, format_, adj_->lower(Dimension_X));
    float size = font->width(buf, strlen(buf));
    sprintf(buf, format_, adj_->upper(Dimension_X));
    float size2 = font->width(buf, strlen(buf));
    hsize = (size < size2) ? size2 : size;
    size = font->width('8');
    hsize += precision_ * size;
  }
  hsize += 10;   // some margin
  kit_->end_style();

  Requisition reqTmp;
  field_->request(reqTmp);
  Coord sl_size=reqTmp.y_requirement().natural();

  fobs_ = new FieldEditorObserver(this);
  adj_->attach(Dimension_X, fobs_);

  // Assemble the parts.
  if (slPos_ & NONE) {
    body(
      layout_->hbox(
	layout_->vcenter(layout_->hfixed(field_, hsize))
	)
      );
    return;
  } 
  
  if (slPos_ & LEFT) {
    body(
      layout_->hbox(
	layout_->vcenter(slider(Dimension_Y,adj_,sl_size)),
        layout_->vcenter(layout_->hfixed(field_, hsize))
	)
      );
  }
  
  if (slPos_ & RIGHT) {
    body(
      layout_->hbox(
        layout_->vcenter(layout_->hfixed(field_, hsize)),
	layout_->vcenter(slider(Dimension_Y,adj_,sl_size))
	)
      );
  }

  if (slPos_ & ABOVE) {
    body(
      layout_->vbox(
	layout_->vcenter(slider(Dimension_X,adj_,hsize)),
        layout_->vcenter(layout_->hfixed(field_, hsize))
	)
      );
  }

  if (slPos_ & UNDER) {
    body(
      layout_->vbox(
        layout_->vcenter(layout_->hfixed(field_, hsize)),
	layout_->vcenter(slider(Dimension_X,adj_,hsize))
	)
      );
  }
}

Glyph* NumberEditor::slider(DimensionName d, AdjValue* adj, Coord length) {
  int w=1;

  // Calculate sizes.
  Requisition reqTmp;
  field_->request(reqTmp);
  Coord height=reqTmp.y_requirement().natural();

  Coord xszStepper=0;
  Coord yszStepper=0;
  Coord xszSlider=0;
  Coord yszSlider=0;

  // Calculate dimensions for the steppers and sliders.
  if (d==Dimension_X) {
    yszSlider=yszStepper=height*0.7;
    if (length>(2.4*height)) {
      xszStepper=yszStepper;
      xszSlider=length-2.0*xszStepper;
    } else {
      w=0;
      if (length<(height*1.2))
	xszStepper=height*1.2;
      else
	xszStepper=length/2.0;
    }
  } else {
    xszSlider=xszStepper=height;
    if (length>(3.5*height)) {
      yszStepper=xszStepper;
      yszSlider=length-2.0*yszStepper;
    } else if (length>(2.4*height)) {
      yszStepper=xszStepper*0.7;
      yszSlider=length-2.0*yszStepper;
    } else {
      w=0;
      if (length<(height*1.2))
	yszStepper=height*0.70;
      else
	yszStepper=length/2.0;
    }
  }

  // Set special styles for NumberEditor slider.
  kit_->begin_style("NumberEditor");

  // Smaller thumb size and no ridges.
  kit_->style()->attribute("minimumThumbSize", "10.0");
  kit_->style()->attribute("thumbRidges", "0");

  // Arrow up, slider and arrow down.
  Glyph *thumb=nil, *start=nil, *end=nil;
  if (d==Dimension_X) {
    start = layout_->fixed(kit_->left_mover(adj),xszStepper,yszStepper);
    if (w) thumb = layout_->fixed(kit_->hslider(adj),xszSlider,yszSlider);
    end = layout_->fixed(kit_->right_mover(adj),xszStepper,yszStepper);
  } else {
    start = layout_->fixed(kit_->up_mover(adj),xszStepper,yszStepper);
    if (w) thumb = layout_->fixed(kit_->vslider(adj),xszSlider,yszSlider);
    end = layout_->fixed(kit_->down_mover(adj),xszStepper,yszStepper);
  }

  kit_->end_style();

  if (d==Dimension_X) {
    if (w)
      return layout_->hbox(start,thumb,end);
    else
      return layout_->hbox(start,end);
  } else if (d==Dimension_Y) {
    if (w)
      return layout_->vbox(start,thumb,end);
    else
      return layout_->vbox(start,end);
  } else
    return nil;
}

//-----------------------------------------------------------------------

NumberEditor::~NumberEditor()
{
  adj_->detach(Dimension_X, fobs_);
  delete fobs_;
  if (adjInside_) delete adj_;
}

//-----------------------------------------------------------------------

void NumberEditor::setField()
{
  char buf[32];
  sprintf(buf, format_, getValue());
  field_->field(buf);

  if (chAction_)
    chAction_->execute();
}

void NumberEditor::accept(FieldEditor31*, char)
{
  // read & set value
  float val = atof(field_->field());
  setValue(val);
}

InputHandler* NumberEditor::focus_in() { 
  if (fiAction_)
    fiAction_->execute();

  return InputHandler::focus_in();
};

void NumberEditor::setValue(float value) {
  adj_->scroll_to(Dimension_X, value);
  adj_->notify_all();
}

void NumberEditor::setUpper(float value) {
  adj_->setUpper(value);
  adj_->notify_all();
}

void NumberEditor::setLower(float value) {
  adj_->setLower(value);
  adj_->notify_all();
}
