/*
 * Copyright (c) 1992 Stanford University
 * Copyright (c) 1992 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.
 */

#include "wtable.h"
#include <InterViews/display.h>
#include <InterViews/patch.h>
#include <InterViews/window.h>
#include <IV-X11/Xlib.h>
#include <IV-X11/xdisplay.h>
#include <IV-X11/xselection.h>
#include <IV-X11/xwindow.h>
#include <OS/string.h>
#include <OS/table.h>
#include <X11/Xatom.h>
#include <iostream.h>  /* debug output */
#include <stdio.h>  /* debug output */

/*
 * SelectionManager -- inter client communications mechanism
 */

SelectionManager::SelectionManager(Display* d, const char* name) {
    rep_ = new SelectionManagerRep(d, String(name));
}

SelectionManager::SelectionManager(Display* d, const String& name) {
    rep_ = new SelectionManagerRep(d, name);
}

SelectionManager::~SelectionManager() {
    delete rep_;
}

void SelectionManager::own(
    SelectionHandler* convert, SelectionHandler* lose, SelectionHandler* done
) {
    SelectionManagerRep& s = *rep();
    Atom a = XInternAtom(s.xdisplay_, s.name_->string(), true);
    XSetSelectionOwner(s.xdisplay_, a, s.owner_->rep()->xwindow_, CurrentTime);
    if (s.lose_ && s.lose_ != lose)  // mpichler, 19950413: when another selection is currently active
      s.lose_->handle(this);         // it loses the selection
    Resource::ref(convert);
    Resource::unref(s.convert_);
    s.convert_ = convert;
    Resource::ref(lose);
    Resource::unref(s.lose_);
    s.lose_ = lose;
    Resource::ref(done);
    Resource::unref(s.done_);
    s.done_ = done;
}

void SelectionManager::put_value(const void* data, int length, int format) {
    SelectionManagerRep& s = *rep();
    XChangeProperty(
        s.xdisplay_, s.x_req_.requestor, s.x_req_.property,
	/* type */ XA_STRING, format, PropModeReplace,
	(const unsigned char*)data, length
    );
    XEvent xe;
    XSelectionEvent& xs = xe.xselection;
    xs.type = SelectionNotify;
    xs.requestor = s.x_req_.requestor;
    xs.selection = s.x_req_.selection;
    xs.target = s.x_req_.target;
    xs.property = s.x_req_.property;
    xs.time = s.x_req_.time;
    XSendEvent(s.xdisplay_, xs.requestor, False, 0, &xe);
}

/* mpichler, 19940110 */

void SelectionManager::retrieve (const String& type, SelectionHandler* ok, SelectionHandler* fail)
{
  SelectionManagerRep& s = *rep();
  Atom a = XInternAtom (s.xdisplay_, s.name_->string (), true);  /* e.g. "PRIMARY" */
  Atom p = XInternAtom (s.xdisplay_, type.string (), false);

  XConvertSelection (s.xdisplay_, a, XA_STRING, p, s.owner_->rep()->xwindow_, CurrentTime);
  Resource::ref (ok);
  Resource::unref (s.ok_);
  s.ok_ = ok;
  Resource::ref (fail);
  Resource::unref (s.fail_);
  s.fail_ = fail;
} // retrieve

/* mpichler/mgais, 19940111 */

void SelectionManager::get_value (String*& type, void*& data, int& nitems, int& format)
{
  SelectionManagerRep& s = *rep();

  Atom actual_type;
  int actual_format;
  unsigned long n_items;
  unsigned long bytes_after;
  unsigned char* data_prop;

  XSelectionEvent& xs = s.x_sel_;
  XGetWindowProperty (
    s.xdisplay_, s.owner_->rep ()->xwindow_, // owner_ is indeed the requestor
    xs.property, 0L, 1024L,  // max. buffer size (in longs) (4 KB)
    True, AnyPropertyType,  // delete, type
    &actual_type, &actual_format,
    &n_items, &bytes_after, &data_prop
  );

  type = nil;  // to be extracted from actual_type
  data = data_prop;  // data array is allocated and freed by X
  nitems = (int) n_items;  // number of data items
  format = actual_format;  // 8 = byte, 16 = short, 32 = long
  // in case max. buffer size was too small, bytes_after are waiting to be read...

//   cerr << "got " << n_items << " items; data: ";
//   fprintf (stderr, "%*s\n", n_items, data_prop);

} // get_value

SelectionManagerRep* SelectionManager::rep() const { return rep_; }

/* class SelectionManagerRep */

SelectionManagerRep::SelectionManagerRep(Display* d, const String& name) {
    DisplayRep& dr = *d->rep();
    xdisplay_ = dr.display_;
    name_ = new CopyString(name);
    owner_ = new PopupWindow(new Patch(nil));
    WindowRep& wr = *owner_->rep();
    wr.display_ = d;
    wr.xwindow_ = XCreateSimpleWindow(
        xdisplay_, dr.root_, 0, 0, 1, 1, 0, 0, 0
    );
    dr.wtable_->insert(wr.xwindow_, owner_);
    wr.xtoplevel_ = wr.xwindow_;
    convert_ = nil;
    lose_ = nil;
    done_ = nil;
    ok_ = nil;
    fail_ = nil;
}

SelectionManagerRep::~SelectionManagerRep() {
    delete name_;
    delete owner_;
    Resource::unref(convert_);
    Resource::unref(lose_);
    Resource::unref(done_);
    Resource::unref(ok_);
    Resource::unref(fail_);
}

void SelectionManagerRep::request (SelectionManager* s, const XSelectionRequestEvent& x)
{
  if (convert_)
  { x_req_ = x;
    convert_->handle (s);  // the selection handler should call put_value to put the actual data
  }
}

// mpichler, 19950110

void SelectionManagerRep::notify (SelectionManager* s, const XSelectionEvent& x)
{
  x_sel_ = x;
  if (x.property == None)
  { if (fail_)
      fail_->handle (s);
  }
  else
  { if (ok_)
      ok_->handle (s);  // the selection handler should call get_value to get the actual data
  }
}

// mpichler, 19950413

void SelectionManagerRep::clear (SelectionManager* s, const XSelectionClearEvent& x)
{
  x_clr_ = x;  // currently unused information
  if (lose_)
    lose_->handle (s);  // the selection handler should unhighlight the selection
}

/* class SelectionHandler */

SelectionHandler::SelectionHandler(){}

SelectionHandler::~SelectionHandler(){}
