/*
 * Copyright (c) 1991 Stanford University
 * Copyright (c) 1991 Silicon Graphics, Inc.
 *
 * 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.
 */

/*
 * Browser -- select an item
 */

//
// $Id: browser.C,v 1.10 1996/10/21 09:06:57 bmarsch Exp $
//
// $Log: browser.C,v $
// Revision 1.10  1996/10/21 09:06:57  bmarsch
// Bugfix: removed memory leak
//
// Revision 1.9  1996/07/31 14:20:27  bmarsch
// Bugfix in unmark()
//
// Revision 1.8  1996/06/05 13:22:14  bmarsch
// Bugfix in mark()/unmark()
//
// Revision 1.7  1996/06/04 07:27:48  bmarsch
// Added function unmark()
//
// Revision 1.6  1996/02/15 17:23:47  bmarsch
// Includes from OS/ were moved to hyperg/OS/
//
// Revision 1.5  1996/01/09 14:49:37  bmarsch
// Added member selectable_count()
//

#include <IV-look/browser.h>
#include <IV-look/telltale.h>
#include <InterViews/action.h>
#include <InterViews/event.h>
#include <InterViews/hit.h>
#include <InterViews/style.h>
#include <hyperg/OS/list.h>


declarePtrList(BrowserList,TelltaleState)
implementPtrList(BrowserList,TelltaleState)


Browser::Browser(
    Glyph* g, Style* s, Action* accept, Action* cancel, boolean multiselect
) : InputHandler(g, s) {
    Resource::ref(accept);
    accept_ = accept;
    Resource::ref(cancel);
    cancel_ = cancel;
    items_ = new BrowserList;
    item_ = -1;

    multiselect_ = multiselect;
    numMarked_ = 0;
    marking_ = false;
    lastdragged_ = -1;
}

Browser::~Browser() {
    Resource::unref(accept_);
    Resource::unref(cancel_);
    while (items_->count()) {
      Resource::unref(items_->item(0));
      items_->remove(0);
    }
    delete items_;
}

void Browser::append_selectable(TelltaleState* t) {
    Resource::ref(t);
    items_->append(t);
}

void Browser::replace_selectable(GlyphIndex i, TelltaleState* t) {
    Resource::ref(t);
    Resource::unref(items_->item(i));
    items_->remove(i);
    items_->insert(i, t);
}

void Browser::remove_selectable(GlyphIndex i) {
    if (items_->item(i)->test(TelltaleState::is_active))
      numMarked_--;

    Resource::unref(items_->item(i));
    items_->remove(i);
    // bmarsch 951005 select "next" item if selection is removed
    if (item_ == i) {
      GlyphIndex save = item_;
      item_ = -1;
      select(save);
    }
}

TelltaleState* Browser::state(GlyphIndex i) const {
    return items_->item(i);
}

void Browser::select(GlyphIndex i) {
    if (item_ != i || !marking_) {
        if (!marking_)
          removeAllMarked();
// 	if (item_ != -1)
// 	    active(item_, false);
	if (i >= -1 && i < items_->count()) {
	    item_ = i;
	    if (i >= 0) {
              if (!state(i)->test(TelltaleState::is_active))
                numMarked_++;
              active(item_, true);
	    }
	}
    }
}

void Browser::active(GlyphIndex i, boolean b) {
    TelltaleState* t = items_->item(i);
    t->attach(this);
    t->set(TelltaleState::is_active, b);
    t->detach(this);
}

GlyphIndex Browser::selected() const {
    return item_;
}

void Browser::choose(GlyphIndex i) const {
    if (i != -1 && accept_) {
	accept_->execute();
    }
}

void Browser::cancel() {
    if (cancel_)
	cancel_->execute();
}

void Browser::press(const Event& e) {
    Hit h(&e);
    repick(0, h);
    if (h.any()) {
        if (multiselect_ && e.shift_is_down()) {
          GlyphIndex hit = h.index(0);
          marking_ = true;
          toggleSelect(hit);
          marking_ = false;
          lastdragged_ = hit;
        }
        else {
          select(h.index(0));
        }
    }
}

void Browser::drag(const Event& e) {
    if (inside(e)) {
	Hit h(&e);
	repick(0, h);
	if (h.any()) {
          if (multiselect_ && e.shift_is_down()) {
            GlyphIndex hit = h.index(0);
            if (hit != lastdragged_) {  // toggle only once
              marking_ = true;
              toggleSelect(h.index(0));
              marking_ = false;
            }
            lastdragged_ = hit;
          }
          else
            select(h.index(0));
          return;
	}
    }
    lastdragged_ = -1;
//    select(-1);
}

void Browser::release(const Event&) {
    if (style()->value_is_on("singleClick")) {
	choose(item_);
    }
    lastdragged_ = -1;
}

void Browser::double_click(const Event&) {
    choose(item_);
}

void Browser::update(Observable*) {
    redraw();
}

void Browser::mark_all()
{
  if (multiselect_) {
    GlyphIndex i = items_->count();
    numMarked_ = i;
    while (i--) {
      TelltaleState* t = items_->item(i);
      t->attach(this);
      t->set(TelltaleState::is_active, true);
      t->detach(this);
    }
  }
}

void Browser::clear_all()
{
  GlyphIndex i = items_->count();
  while (i--) {
    TelltaleState* t = items_->item(i);
    t->attach(this);
    t->set(TelltaleState::is_active, false);
    t->detach(this);
  }
  numMarked_ = 0;
}

void Browser::mark(GlyphIndex gi)
{
  if (multiselect_) {
    marking_ = true;
    select(gi);
    marking_ = false;
  }
  else
    select(gi);
}

void Browser::unmark(GlyphIndex gi)
{
//  if (multiselect_) {
    marking_ = true;
    if (marked(gi))
      toggleSelect(gi);
    marking_ = false;
//   }
//   else
//     select(-1);
}

int Browser::num_marked() const
{
  return numMarked_;
}

boolean Browser::marked(GlyphIndex gi) const
{
  if (gi < 0 || gi >= items_->count())
    return false;

  return state(gi)->test(TelltaleState::is_active);
}

void Browser::toggleSelect(GlyphIndex gi)
{
  boolean was_choosen = items_->item(gi)->test(TelltaleState::is_active);
  active(gi, !was_choosen);
  if (was_choosen) {
    numMarked_--;
    select(find_first());
  }
  else {
    numMarked_++;
    select(gi);
  }
}

void Browser::removeAllMarked()
{
  numMarked_ = 0;
  GlyphIndex i = items_->count();
  while (i--) {
    TelltaleState* t = items_->item(i);
    t->attach(this);
    t->set(TelltaleState::is_active, false);
    t->detach(this);
  }
}

GlyphIndex Browser::find_first() const
{
  GlyphIndex count = items_->count();
  for (int i = 0; i < count; i++) {
    if (state(i)->test(TelltaleState::is_active))
      return i;
  }

  return -1;
}

int Browser::selectable_count() const
{
  return items_->count();
}
