/* ==================================================== ======== ======= *
 *
 *  uuappli.cc
 *  Ubit Project  [Elc][beta1][2001]
 *  Author: Eric Lecolinet
 *
 *  Part of the Ubit Toolkit: A Brick Construction Game Model for Creating GUIs
 *
 *  (C) 1999-2001 Eric Lecolinet @ ENST Paris
 *  WWW: http://www.enst.fr/~elc/ubit   Email: elc@enst.fr (subject: ubit)
 *
 * ***********************************************************************
 * COPYRIGHT NOTICE : 
 * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY AND WITHOUT EVEN THE 
 * IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 
 * YOU CAN REDISTRIBUTE IT AND/OR MODIFY IT UNDER THE TERMS OF THE GNU 
 * GENERAL PUBLIC LICENSE AS PUBLISHED BY THE FREE SOFTWARE FOUNDATION; 
 * EITHER VERSION 2 OF THE LICENSE, OR (AT YOUR OPTION) ANY LATER VERSION.
 * SEE FILES 'COPYRIGHT' AND 'COPYING' FOR MORE DETAILS.
 * ***********************************************************************
 *
 * ==================================================== [Elc:01] ======= *
 * ==================================================== ======== ======= */

//pragma ident	"@(#)uuappli.cc	ubit:b1.11.2"
#include <locale.h>
#include <search.h>
#include <udefs.hh>
#include <ubrick.hh>
#include <uconfig.hh>
#include <ubox.hh>
#include <uwin.hh>
#include <umenu.hh>
#include <umenuImpl.hh>
#include <uview.hh>
#include <uappli.hh>
#include <utimer.hh>
#include <ugraph.hh>
#include <unat.hh>
#include <uborder.hh>
#include <usymbol.hh>
#include <ustr.hh>
#include <ucolor.hh>
#include <upix.hh>
#include <ustyle.hh>

/* ==================================================== ======== ======= */

UAppli *UAppli::defaultAppli;
int UAppli::mclickRadius = 3;    ///! -> UConfig PARAMETRER
int UAppli::mclickDelay  = 500;  ///! -> UConfig

/* ==================================================== ======== ======= */

const int UDisp::pseudoColor = PseudoColor;
const int UDisp::trueColor   = TrueColor;

/* ==================================================== ======== ======= */

const char* UError::NO_MEMORY = 
"Not enough memory";

const char* UError::UNREALIZED_WINDOW = 
"Unrealized Window:\n - check if this window has a parent and if this parent is realized";

const char* UError::CANT_REALIZE_WINDOW =
"Can't realize Window:\n - check if this window has a parent and if this parent is realized\n - (valid parents may be any UBox or UWin objects or the UAppli)";

const char* UError::CANT_REALIZE_SOFTWIN =
"Internal error: wrong type: can not realize softwins";

const char* UError::CANT_CREATE_X_WINDOW = 
"Could not create X Window";

const char* UError::CANT_OPEN_X_CONNECTION = 
"!Fatal Error: Could not open connection to the X Server";

const char* UError::INVALID_X_SCREEN_NUMBER = 
"Invalid Screen Number";


/* ==================================================== [Elc:01] ======= */
/* ==================================================== ======== ======= */

UDisp::UDisp(class UAppli *a, u_bool init) {
  appli = a;
  natdisp = init ? new UNatDisp(this) : null;
}

void UDisp::realize() {
  natdisp->realize();
}

u_bool UDisp::isRealized() {
  return natdisp->isRealized();
}

int UDisp::getDepth() const {
  return natdisp->getDepth();
}
int UDisp::getScreenWidth() const {
  return natdisp->getScreenWidth();
}
int UDisp::getScreenHeight() const {
  return natdisp->getScreenHeight();
}
int UDisp::getScreenDefaultDepth() const {
  return natdisp->getScreenDefaultDepth();
}

int UDisp::matchVisualProps(int visual_class, int depth_hint, int screen_no) {
  return natdisp->matchVisualProps(visual_class, depth_hint, screen_no);
}

int UDisp::setVisualProps(int visual_class, int depth_hint, int screen_no) {
  return natdisp->setVisualProps(visual_class, depth_hint, screen_no);
}

u_bool UDisp::realizeFont(const UFont *f) {
  return natdisp->realizeFont(f);
}

u_bool UDisp::realizeColor(UColor *c) {
  // la valeur de ix est automatiquement initialisee si elle est == 0
  // (et si l'allocation reussit)
  return natdisp->realizeColor(c);
}

u_bool UDisp::realizeCursor(UCursor *c) {
  // la valeur de ix est automatiquement initialisee si elle est == 0
  // (et si l'allocation reussit)
  return natdisp->realizeCursor(c);
}

void UDisp::addHardwin(class UWin* win) {
  //ne pas ajouter 2 fois
  if (!hardwins.search(win)) {
    //printf("-add hardwin %p \n", win);
    hardwins.add(win->makeLink());
  }
}

void UDisp::removeHardwin(class UWin* win) {
  ULink *l = hardwins.search(win);
  if (l) {
    //printf("-remove hardwin %p \n", win);
    hardwins.remove(l);
  }
}

/* ==================================================== [Elc:01] ======= */
/* ==================================================== ======== ======= */

struct UAPP_VAR {
  const char *name;
  char *value;
};

extern "C" {
static int varCompar(const void *n1, const void *n2) {
  return strcmp( ((UAPP_VAR*)n1)->name, ((UAPP_VAR*)n2)->name);
}
}

const char* UAppli::getVar(const char *name, u_bool get_shell_env) {
  UAPP_VAR key;
  key.name = name;
  void *p = tfind(&key, &varDB, varCompar);

  if (p) {  //found in local appli environment
    UAPP_VAR *item = *(UAPP_VAR**)p;
    if (item) return item->value;  //note that value can be NULL
  }

  else if (get_shell_env) { //search in SHELL env (and add to local env)
    const char *value = ::getenv(name);
    //if value not found, a NULL value is stored so that we won't
    //search the SHELL env again and again
    this->setVar(name, value);
    if (value) return value; //found in shell env
  }

  return null; //definitively not found (all default cases)
}

const char *UAppli::setVar(const char *name, const char *value) {
  UAPP_VAR key;
  key.name = name;
  void *p = tsearch(&key, &varDB, varCompar);
  if (!p) return null;   //not enough mem

  UAPP_VAR *item = *(UAPP_VAR**)p;
  if (!item) return null;   //hum, should not happen

  if (item == &key) { //a new node was added
    // => create the new item in heap memory and make the node point to it
    item = (UAPP_VAR*)malloc(sizeof(UAPP_VAR));
    *(UAPP_VAR**)p = item;
    item->name  = u_strdup(name);
    item->value = value ? u_strdup(value) : null;
  }
  else { //existing item was retrieved ==> just update val
    if (item->value) free(item->value);
    item->value = value ? u_strdup(value) : null;
  }

  return item->value;
} 

const char* UAppli::getImaPath() {
  return getVar("UIMA_PATH");
}
const char* UAppli::setImaPath(const char* value) {
  return setVar("UIMA_PATH", value);
}
// obsolete synomym
const char* UAppli::setPixmapPaths(const char* value) {
  return setVar("UIMA_PATH", value);
}

char *UAppli::makeImaPath(const char* name) {
  if (!name || name[0] == '\0')
    return null;

  else if (name[0] == '.' || name[0] == '/')
    return u_strdup(name);

  else if (name[0] == '$') {  //variable
    const char *val = null;
    const char *sep = strchr(name, '/');

    if (!sep)
      val = getenv(name+1);   //just a varname
    else {
      int varlen = sep - (name+1);   //skip the $
      char *varname = (char*)malloc((varlen+1) * sizeof(char));
      strncpy(varname, name+1, varlen);
      varname[varlen] = 0;
      val = appli->getVar(varname);
      free(varname);
    }

    if (val) {
      if (sep) return u_strdupcat(val, sep); // 'sep' includes the slash
      else return u_strdup(val);  // just the varname's value
    }
    else return u_strdup(name);  //val not found => just return name 'as it'
  }

  else {   // no prefix ==> add default pixmap path
    // penser a liste de repertoires !!!!

    const char* ima_path = appli->getImaPath();
    if (ima_path)
      return u_strdupcat(ima_path, '/', name);
    else 
      return u_strdup(name);
  }
}

/* ==================================================== [Elc:01] ======= */
/* ==================================================== ======== ======= */

//top of file: UAppli *UAppli::defaultAppli;

/*** obs:

  // special constructor for customizing X parameters
  UAppli(const char *app_name,     	   // X Application Name
	 const char *app_class = null,     // X Application Class
	 const char *app_display = null);  // X Display String

UAppli::UAppli(const char *app_name, 
	       const char *app_class, const char *app_display)
  : UDisp(this, false),
    lastEnteredEvent(0,null,null), 
    lastPressedEvent(0,null,null)
{
  initAppli(app_name, app_class, app_display);
  // IMPORTANT: natDisp == natAppli et fait APRES initAppli()
  natdisp = new UNatAppli(this);
}
*/

// standard constructor for creating a new application

UAppli::UAppli(int *argc, char *argv[],
	       const char *app_class, const char *app_display)
  : UDisp(this, false),
    lastEnteredEvent(0,null,null), 
    lastPressedEvent(0,null,null)
{
  defaultAppli = this;
  varDB = null;

  appli_name   = (*argc >=1) ? strdup(argv[0]) : null;
  style_name    = app_class ? strdup(app_class) : null;
  display_name  = app_display ? strdup(app_display) : null;

  mainFrame     = null;
  lastEnteredView= null;
  lastEnteredBox= null;
  lastPressed   = null;
  lastArmed     = null;
  dragSource    = dropTarget = null;
  lastCursor    = null;
  modalDialog   = null;
  browseGroup   = null;
  menuCtrl      = new UMenuCtrl(this);
  textsel       = new UTextsel();
  server_selection_str = null;
  server_selection_pos = 0;
  pixmapPath    = null;
  bitmapPath    = null;
  mclickTime    = 0;
  mclickCount   = 0;
  mclickX = mclickY = -5;

  // window group
  winlist = new UGroup();

  //!!LOCALE!!
  setlocale(LC_CTYPE, "iso_8859_1"); //fr

  // initialize the default style
  defaultStyle = new UStyle(null);

  // IMPORTANT: natDisp == natAppli et fait APRES initAppli()
  natdisp = new UNatAppli(this, argc, argv);
}

UAppli::~UAppli() {}

void UAppli::realize() {
  getNatAppli()->realize();
}

void UAppli::realize(int *argc, char *argv[]) {
  getNatAppli()->realize(argc, argv);
}

u_bool UAppli::isRealized() {
  return getNatAppli()->isRealized();
}

class UFrame *UAppli::getMainFrame() const {return mainFrame;}

/* ==================================================== [Elc:00] ======= */
/* ==================================================== ======== ======= */
// En arg:  n'importe quelle Window; mais seul le MainFrame (= le 1er Frame 
// ajoute a UAppli) est automatiquement affichee par defaut

void UAppli::add(UWin &win) {

  // deplace: autrefois dans UAppli::construct
  if (!isRealized()) realize();

  // add to the application's window list
  winlist->add(win);

  // par construction; 
  // !!ATT: marche pas si meme win ajoutee plusieurs fois !!
  //UWinLink *link = (UWinLink*)win.parents.first()->link();
  UBoxLink *link = (UBoxLink*)win.parents.first()->link();

  win.init(link, null, null, this);

  // makes the first UFrame be the 'Main Frame'
  //if (!mainFrame && win.getClass() == &UFrame::uclass) {
  //  mainFrame = (UFrame*)&win;
  if (!mainFrame 
      && (mainFrame = dynamic_cast<UFrame*>(&win))) {
    mainFrame->is_main_frame = true;
    // this window will be automatically opened
    mainFrame->setCmodes(UMode::CAN_SHOW, true);
    //mainFrame->must_update = true;
  }
  if (win.isShowable()) win.show(true);
}

void UAppli::add(UWin *win) {add(*win);}

UWin* UAppli::remove(UWin &win, u_bool delete_win) {
  if (&win == mainFrame) mainFrame = null;
  win.show(false);
  return (UWin*)winlist->remove(win, delete_win);
}

UWin* UAppli::remove(UWin *win, u_bool delete_win) {
  return remove(*win, delete_win);
}

void UAppli::add(UTimer &timer) {timer.realize(this);}
void UAppli::add(UTimer *timer) {timer->realize(this);}

/* ==================================================== [Elc:01] ======= */
/* ==================================================== ======== ======= */

static void updateAll2(UGroup *grp, UUpdate mode) {
  // NB: du fait des parents multipls, il est possible de remettre
  // a jour plusieurs fois la meme window
  // ==> il faudrait tenir compte du flag must-update

  if (grp->winCast()) grp->winCast()->update(mode);

  for (ULink *l = grp->getChildLinks(); l != null; l = l->next()) {
    UGroup *chg = l->brick()->groupCast();
    if (chg) {
      //printf("updateAll2\n");
      updateAll2(chg, mode);    // in any case
    }
  }
}

//  updates all visible windows   !! A REVOIR !!
void UAppli::updateAll(UUpdate mode) {

  for (ULink *l = winlist->getChildLinks(); l != null; l = l->next()) {
    UGroup *chg = l->brick()->groupCast();
    if (chg) {
      //printf("updateAll\n");
      updateAll2(chg, mode);
    //EX: if (ww && ww->isShowable() && (/*ww->mustUpdate() ||*/ force_update))
    }
  }
}

void UAppli::updateAll() {
  updateAll(UUpdate::layout);
}

// updates all visible windows
/* le pbm c'est que ce n'est pas la liste de TOUTES les windos
   mais seulement de celles qui sont filles de UAppli

void UAppli::updateAll(UUpdate mode) {
  for (ULink *l = winlist->getChildLinks(); l != null; l = l->next()) {
    printf("update : obj %d %s\n", l->brick(), l->brick()->cname());
    UWin *win = dynamic_cast<UWin*>(l->brick());
    if (win) {
      printf("update win %d\n", win);
      win->update(mode);
    }
  }
}
*/

int UAppli::mainLoop() {
  return getNatAppli()->mainLoop();
}
void UAppli::quit(int status) {
  getNatAppli()->quit(status);
}

// sert a quoi ??? utitle() devrait suffire
void UAppli::setTitle(const UStr &title) {
  if (mainFrame) {
    UUpdate upd(UUpdate::TITLE);
    upd.paintTitle(&title);
    mainFrame->update(upd);
  }
}

/* ==================================================== [Elc:01] ======= */
/* ==================================================== ======== ======= */
// notifies the application that a View has been deleted
// PROBLEME -- les UGroups ne sont pas geres !!!

void UAppli::deleteNotify(UView* deleted_view) {
  if (deleted_view == lastEnteredView) {
    //NB: lasteEnteredBox always corresponds to lastEnteredView
    if (lastEnteredBox == lastArmed)   lastArmed  = null;
    if (lastEnteredBox == lastPressed) lastPressed = null;
    if (lastEnteredBox == dragSource)  dragSource = null;
    if (lastEnteredBox == dropTarget)  dropTarget = null;
    if (lastEnteredBox == textsel->inObj) textsel->inObj = null;
    lastEnteredView = null;
    lastEnteredBox  = null;
  }
}

void UAppli::winDestroy(UWin *win, UEvent& e) {
  win->setCmodes(UMode::CAN_SHOW, false);
  if (win == modalDialog)  modalDialog = null;
  if (menuCtrl->currentGrab())  menuCtrl->closeAllGrabbedMenus(true);
  udelete(win);
}

void UAppli::winLeave(UWin *, UEvent& e) {
  UBox *box = null;
  // le XGrab genere des LeaveWindow qunad on ouvre le menu associe
  // a un bouton et qunad on bouge la souris sur ce bouton une fois
  // que le menu est ouvert. ne me demandez pas pourquoi, mais en tout
  // cas, ce qui est sur, c'est qu'il ne faut pas en tenir compte
  // sinon le bouton ouvrant va osciller entre Enter et Leave qunad
  // on deplace la souris sur le menubar
  if (menuCtrl->currentGrab()) return;
  if (lastEnteredView && (box = lastEnteredView->getBox())) boxLeave(e);
}

/* ==================================================== [Elc:01] ======= */
/* ==================================================== ======== ======= */

void UAppli::winMousePress(UWin *win, UEvent& e) {
  if (modalDialog) {
    if (win == modalDialog)	// box inside modalDialog
      boxMousePress(e);
    else modalDialog->highlight(true); // highlight ++ RAISE!
  }

  else if (menuCtrl->currentGrab()) {
    // mouse outside GUI ==> close menus
    if (!e.sourceView) 
      menuCtrl->closeAllGrabbedMenus(true);
    // mouse inside the grabbedMenu GROUP (including the menubar if any)
    // ==> process mouse events (and don't close menus)
    else if (e.getParentContext() && browseGroup
	     && e.getParentContext()->browseGroup == browseGroup) {
      boxMousePress(e);
    }
    else { // mouse inside GUI but outside menu ==> close menus
      menuCtrl->closeAllGrabbedMenus(true);
      
      // ex: boxMousePress(e); pour eviter boxRelease() orphelin
      // mais effet nefaste: si je clique sur un menu opener et que
      // son menu est deja ouvert, je le ferme d'abord par 
      // closeAllGrabbedMenus() puis je le rouvre par boxMousePress()
      // [or on veut qu'un premier click sur un opener ouvre le menu 
      // [mais que le click suivant le ferme et ainsi de suite]
    }
  }

  else boxMousePress(e);	// cas standard
}


void UAppli::winKeyPress(UWin *win, UEvent& e) {
  if (modalDialog) {
    if (win == modalDialog)       // box inside modalDialog
      boxKeyPress(e);
    else modalDialog->highlight(true); // highlight ++ RAISE!
  }

  else if (menuCtrl->currentGrab()) {
    // mouse outside GUI ==> do nothing
    if (!e.sourceView)
      ;
    // mouse inside the grabbedMenu GROUP (including the menubar if any)
    // ==> process arentmouse events (and don't close menus)
    else if (e.getParentContext() && browseGroup
	     && e.getParentContext()->browseGroup == browseGroup) {
      //printf("kpress: mouse in BG \n");
      boxKeyPress(e);
    }
    else { // mouse inside GUI but outside menu ==> do nothing
    }
  }

  else boxKeyPress(e);	// cas standard
}

/* ==================================================== [Elc:01] ======= */
/* ==================================================== ======== ======= */

void UAppli::winMouseMove(UWin *win, UEvent& e) {
  // meme topo que precedemment
  boxMouseMove(e);
}

void UAppli::winMouseRelease(UWin *win, UEvent& e) {
  if (modalDialog) {
    // box inside modalDialog
    if (win == modalDialog) boxMouseRelease(e);
    else modalDialog->highlight(false); // unhighlight
    return;
  }

  // mouse outside GUI ==> close all menus
  if (menuCtrl->currentGrab() && !e.sourceView) {
    menuCtrl->closeAllGrabbedMenus(true);
    return;
  }

  // tous les autres cas (y compris si menuCtrl->currentGrab())
  // sinon risque de perdre des Release importants (typiquement s'il y a
  // un mdrag a partir d'un browse group (par ex un menu)
  // ==> il y aura des Release orphelins dans certains cas 
  boxMouseRelease(e);
}

void UAppli::winKeyRelease(UWin *win, UEvent& e) {
  if (modalDialog) {
    if (win == modalDialog)       // box inside modalDialog
      boxKeyRelease(e);
    else modalDialog->highlight(false); // unhighlight
  }

  else if (menuCtrl->currentGrab()) {
    // mouse outside GUI ==> do nothing
    if (!e.sourceView)
      ;
    else if (e.getParentContext() && browseGroup
	&& e.getParentContext()->browseGroup == browseGroup) {
      boxKeyRelease(e);
    }
    else { // mouse inside GUI but outside menu ==> do nothing
    }
  }

  else boxKeyRelease(e);	// cas standard
}

/* ==================================================== [Elc:00] ======= */
/* ==================================================== ======== ======= */
#if OBSOLETE
void UAppli::winMouseRelease(UWin *win, UEvent *e) {
  if (modalDialog) {
    if (win == modalDialog)	// box inside modalDialog
      boxMouseRelease(e);
    else modalDialog->highlight(false); // unhighlight
  }

  else if (menuCtrl->currentGrab()) {
    // mouse outside GUI ==> close menus
    if (!e.sourceView) {
      menuCtrl->closeAllGrabbedMenus(true);
    }
    // mouse inside the grabbedMenu GROUP (including the menubar if any)
    // ==> process mouse events (and don't close menus)

    // we send boxRelease only to the objects inside the grabbed menus
    // so that the menu opener won't get mrelease, and thus, won't close
    // the menu when released (ie, the menu stays open once its opener
    // has been clicked. it is closed by clicking somewehre else)

    else if (e.getParentContext() && browseGroup
	&& e.getParentContext()->browseGroup == browseGroup) {
      boxMouseRelease(e);
    }
    else {
      // popmenus will become spring menus if the following line
      // if this the next line is uncommented (ie. popmenus won't stay)
      //menuCtrl->closeAllGrabbedMenus(true);

      //NEW: ajout 7oct01: sinon releases perdus qunad ya des grabs ds menus
      boxMouseRelease(e);
    }
  }
  else {
    boxMouseRelease(e);	// cas standard
}

void UAppli::winMouseMove(UWin *win, UEvent *e) {
  /* ca deconne: bloque les mmove et les mdrag : */
  if (modalDialog) {
    if (win == modalDialog)	// box inside modalDialog
      boxMouseMove(e);
  }
  // mouse inside the grabbedMenu GROUP (including the menubar if any)
  // ==> process mouse events (and don't close menus)
  else if (menuCtrl->currentGrab()) {
    if (e.getContext() && browseGroup
	&& e.getContext()->browseGroup == browseGroup) {
      boxMouseMove(e);
    }
  }
  else   boxMouseMove(e);	// cas standard
}
#endif

/* ==================================================== [Elc:01] ======= */
/* ==================================================== ======== ======= */
/* ==================================================== ======== ======= */

void UAppli::boxEnter(UEvent &e, UBox *box, int bstyle) {
  lastEnteredEvent.copy(e);
  lastEnteredEvent.eid = UEvent::enter;
  lastEnteredView = e.sourceView;
  lastEnteredBox  = box;
  box->enterBehavior(this, lastEnteredEvent, bstyle);
}

void UAppli::boxLeave(UEvent &e) {
  lastEnteredEvent.actualize(UEvent::leave, e.xmouse, e.ymouse, e.xev);
  lastEnteredView->getBox()->leaveBehavior(this, lastEnteredEvent);
  lastEnteredView = null;
  lastEnteredBox  = null;
}

/* ==================================================== [Elc:01] ======= */
/* ==================================================== ======== ======= */

void UAppli::boxKeyPress(UEvent& e) {
  // *** securite ***
  // normalement aurait du etre desactive par boxMotion mais le XEvent
  // correspondant peut eventuellement avoir ete manque
  if (lastEnteredView && lastEnteredView != e.sourceView)
    boxLeave(e);

  if (e.sourceView) {
    UBox *box = e.sourceView->getBox();
    // *** securite ***
    // normalement aurait du etre active par boxMotion mais le XEvent
    // correspondant peut eventuellement avoir ete manque
    if (e.sourceView != lastEnteredView) 
      boxEnter(e, box, UCtrl::STANDARD);

    box->keyPressBehavior(this, e);
  }
}

void UAppli::boxKeyRelease(UEvent& e) {
  if (e.sourceView) {
    UBox *box = e.sourceView->getBox();
    box->keyReleaseBehavior(this, e);
  }
}

/* ==================================================== [Elc:00] ======= */
/* ==================================================== ======== ======= */

void UAppli::boxMousePress(UEvent& e) {
  // *** securite ***
  // normalement aurait du etre desactive par boxMotion mais le XEvent
  // correspondant peut eventuellement avoir ete manque
  if (lastEnteredView && lastEnteredView != e.sourceView)
    boxLeave(e);

  if (e.sourceView) {
    // *** securite *** pour le cas ou le "release" aurait ete loupe
    if (lastPressed
	/*&& lastPressed->isDef(UMode::PRESS_CB, 0)*/) {
      lastPressedEvent.actualize(UEvent::mrelease, e.xmouse,e.ymouse,e.xev);
      lastPressed->fire(lastPressedEvent, UOn::mrelease);
    }

    UBox *box = e.sourceView->getBox();
    // RECOPIE tout le contenu AINSI que celui des champs pointes
    lastPressedEvent.copy(e);
    //NEW 7oct01 : screen coords. of the View that got the Press event.
    //=> see UEvent.getX(), UEvent.getY() for details
    lastPressedEvent.xdrag_ref = e.getXscreen()- e.getX();
    lastPressedEvent.ydrag_ref = e.getYscreen()- e.getY();
    lastPressed = box;

    // *** securite ***
    // normalement aurait du etre active par boxMotion mais le XEvent
    // correspondant peut eventuellement avoir ete manque
    if (e.sourceView != lastEnteredView) boxEnter(e, box, UCtrl::STANDARD);

    // NB: ordre des callbacks
    // -- fire(UOn::press) est remis AVANT armBehavior car c'est l'ordre standard
    // que l'on trouve dans Swing et ailleurs
    // -- si l'on veut dessiner sur l'objet apres le ARM il faut utiliser UOn::arm

    /*if (box->isDef(UMode::PRESS_CB, 0))*/
    box->fire(e, UOn::mpress);

    box->armBehavior(this, e, UCtrl::STANDARD);  // means NOT browsing
                                                            // !!$$PARAMETRER$$!! 
    if (box->isDef(0,UMode::CAN_SELECT_TEXT) && e.getButtonNumber() == 1)
      textsel->start(this, e);
  }
}

/* ==================================================== [Elc:00] ======= */
/* ==================================================== ======== ======= */
// !NOTE IMPORTANTE:
// C'EST LA Box INITIALE (= celle qui a fait le Press) QUI RECUPERE 
// LES Motions et le Release final

void UAppli::boxMouseRelease(UEvent& e) {
  // NB1: ordre des callbacks
  // -- fire(UOn::release) est remis AVANT armBehavior car c'est l'ordre standard
  // que l'on trouve dans Swing et ailleurs
  // -- si l'on veut dessiner sur l'objet apres le DISARM il faut utiliser
  // UOn::disarm
  
  // NB2: l'evenement Release doit etre relatif a l'objet qui a fait Press
  // en dehors du if (e.sourceView) pour detecter les 'release'
  // en dehors de la fenetre
  // NB: ordre d'appel des callbacks: voir note ci-dessus a propos de UOn::press
  
  if (lastPressed
      /*&& lastPressed->isDef(UMode::PRESS_CB, 0)*/) {
    lastPressedEvent.actualize(UEvent::mrelease, 
			       e.xmouse, e.ymouse, e.xev);
    lastPressed->fire(lastPressedEvent, UOn::mrelease);
  }
  lastPressed = null; 

  if (textsel->beingSelected) textsel->complete(this, e);

  if (e.sourceView) {
    UBox *box = e.sourceView->getBox();

    // -- STANDARD means NOT browsing UCtrl::STANDARD
    // -- returned value: false la vue a ete detruite
    //    ==> sortir alors au plus vite!
    if (box->disarmBehavior(this, e, UCtrl::STANDARD)) {
      //textsel->complete(this, e);

      // mrelax is received by the object where the mouse was actually
      // released (otherwise it is similar to mrelease)
      box->fire(e, UOn::mrelax);
    }
  }
}

/* ==================================================== [Elc:00] ======= */
/* ==================================================== ======== ======= */

void UAppli::boxMouseMove(UEvent& e) {
  // detection normale des "leave" (si l'evenement n'est pas manque
  // sinon controle de securite dans boxPress)
  if (lastEnteredView && lastEnteredView != e.sourceView)
    boxLeave(e);

  if (e.sourceView) {
    UBox *box = e.sourceView->getBox();
    u_bool mbtn_pressed = (e.getButtons() != 0);

    // detection normale des "enter" (si l'evenement n'est pas manque
    // sinon controle de securite dans boxPress)
    if (e.sourceView != lastEnteredView)
      boxEnter(e, box, (mbtn_pressed ? UCtrl::BROWSING : UCtrl::STANDARD));

    // dans tous les cas : Move
    if (box->isDef(UMode::MOUSE_MOVE_CB,0)) {
      box->fire(e, UOn::mmove);
    }

    // dans le cas Drag: 
    // -- NB faire le Press apres le reaffichage provoque par le Enter eventuel
    //    l'evenement doit etre relatif a l'objet qui a fait Press
 
    if (mbtn_pressed && lastPressed) {  // Drag

      if (textsel->beingSelected && box->isDef(0,UMode::CAN_SELECT_TEXT))
	textsel->extend(this, e);
      
      if (lastPressed->isDef(UMode::MOUSE_DRAG_CB,0)) {
	lastPressedEvent.actualize(UEvent::mdrag, e.xmouse, e.ymouse, e.xev);
	lastPressed->fire(lastPressedEvent, UOn::mdrag);
      }
    }
  }
}

/* ==================================================== [TheEnd] ======= */
/* ==================================================== [Elc:01] ======= */
