/*

    GEO, Postgres GIS (Geografic Information System) frontend

    Copyright (C) 1991  TNO Institute for Perception, The Netherlands
			Written by: Tom Vijlbrief

    This program is free software; 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 1, or (at your option)
    any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.


Note that parts of this distribution are taken from the ET++
distribution. See the et2.2/ subdirectories for details.

===============================================================================
Tom Vijlbrief
TNO Institute for Perception		Phone: +31 34 63 562 11
P.O. Box 23				Fax:   +31 34 63 539 77
3769 ZG  Soesterberg			E-mail: tom@izf.tno.nl
The Netherlands				or: uunet!hp4nl.nluug.nl!tnosoes!tom
===============================================================================

*/

static char *sccs_id= (sccs_id, "@(#)Query.c	3.14 7/29/92");

#ifdef __GNUG__
#pragma implementation "GQuery.h"
#pragma implementation "Query.h"
#endif

#include	"Class.h"
#include	"Geo.h"
#include	"IdDictionary.h"
#include	"ObjList.h"
#include	"Window.h"
#include	"Dialog.h"
#include	"DialogItems.h"
#include	"BorderItems.h"
#include	"RegularExp.h"
#include	"Menu.h"
#include	"Scroller.h"
#include	"Document.h"
#include	"ImageItem.h"
#include	"ByteArray.h"
#include	"Set.h"
#include	"ObjInt.h"
#include	"Alert.h"


#include	"Composer.h"
#include	"QueryDialog.h"
#include	"TableDialog.h"
#include	"Quel.h"
#include	"IconShape.h"

#include	"OverView.h"
#include	"ClassManager.h"
#include	"Query.h"
#include	"QuelInfo.h"
#include	"GQuery.h"


#ifdef ET23
inline void AllWait(bool) {}
#else
#endif

extern char *GEOAP;


MetaImpl(GQuery,(
  T(empty), T(loaded),
  TP(qname), TP(ol), TP(d), TP(q), T(nrtuples), T(force_load),0));


GQuery::GQuery(QuelQueries *owner)
{
  ol= 0;
  empty= TRUE;
  d= 0;
  q= owner;
}

GQuery::~GQuery()
{
  fprintf(stderr, "Freeing layer %s, Please wait... ",
	d->QueryName());
  fflush(stderr);
  if (ol)
    ol->FreeAll();
  fprintf(stderr, "Done.\n");
  SafeDelete(ol);
  SafeDelete(d);
}


void GQuery::DoObserve(int id, int part, void *data, Object *o)
{
  if (id == cQUERYDONE) {
#ifdef ET25
    Dictionary	*dict= q->GetQueries();
    Iter	next(dict);
    Object	*key;
#else
    Iter	next(q->GetQueries());
    Assoc	*a;
#endif
    QueryDialog	*qd= (QueryDialog*) o;
    TextItem	*l;
    char	*news= qd->QueryName();

#ifdef ET25
    qd->SendDown(cIdOk, part, data);
#else
    qd->Dialog::Control(cIdOk, part, data);
#endif

#ifdef ET25
    while (key= next()) {
      if (((GQuery*)dict->AtKey(key))->d == qd) {
#else
    while (a= (Assoc*) next()) {
      if (((GQuery*)a->Value())->d == qd) {
#endif
	if (qd->ChangedDoc()) {
	    // a NOP command so Doc will ask for save:
	    q->GetView()->GetDocument()->PerformCommand(new Command(12345));
	}
#ifdef ET25
	l= (TextItem*) key;
#else
	l= ((TextItem*) a->Key());
#endif
	l->SetString(news[0] ? news: "NoName");
	Send(id, 0, l);
	return;
      }
    }

    // A new query:

    q->GetCreating()->RemovePtr(qd);

    id= cQUERYADD;
    qname= l= new TextItem(news[0] ? news: "NoName");
#ifdef ET25
    dict->PutAtKey(this, l);
#else
    q->GetQueries()->AtKeyPut(l, this);
#endif

    Send(id, 0, l);
  }
}

#ifdef ET25
OStream &GQuery::PrintOn(OStream &o)
#else
ostream &GQuery::PrintOn(ostream &o)
#endif
{
  Object::PrintOn(o);
  o << empty NL << q NL;
  o << qname NL;
  d->Save(o);
  return o;
}

#ifdef ET25
IStream &GQuery::ReadFrom(IStream &i)
#else
istream &GQuery::ReadFrom(istream &i)
#endif
{
  Object::ReadFrom(i);

  i >> Bool(empty) >> q;
  i >> qname;
  d= new QueryDialog((byte*) qname->AsString(), this);
#ifdef ET25
#else
  d->Update();
#endif
  d->Load(i);
  return i;
}

void GQuery::EditQuery(TextItem *t)
{
  if (!d) {
    d= new QueryDialog("NoName", this);
    q->GetCreating()->Add(d);
  }
  d->SetObs();
  d->ShowIt();
}


static int GetDynAttrs(char *attr_list, char **attr, int ai)
{
    const int		MAX_ATTR= 1000;
    static char		attrs[MAX_ATTR];
    register char	*p= attrs, *newp;
    int			ndyn= 0;

    strncpy(attrs, attr_list, MAX_ATTR);
    for (bool done= FALSE; !done; ndyn++) {
      if (newp= index(p, ','))
	*newp= '\0';
      else
	done= TRUE;
      attr[ai++]= p;
      p= newp + 1;
    }
    return ndyn;
}


void GQuery::AddQShapes(class ObjList *ol, class ObjList *lq)
{
  LoadCol(ol, lq);
}


ObjList * GQuery::GetCol(bool area_changed, bool do_load, bool display_refresh)
{
  if (!ol)
    ol= new ObjList;

  if (do_load || area_changed)
    display_refresh= TRUE;
  if (force_load) {
    if (!d->ShowOnMap() || (d->ShowOnMap() && display_refresh)) {
      force_load= FALSE;
      loaded= FALSE;
      ol->FreeAll();
    }
  } else {
    if (!d->ShowOnMap())
      return ol;
  }

  nrtuples= 0;
  if (empty ||
     (loaded && (!do_load || (d->DontRefresh() && !area_changed))))
  	return ol;
  if (area_changed)
    d->UpdateWhere(FALSE);
  if (!d->ShowOnMap() || (!do_load && !display_refresh && !loaded)) {
	TableDialog	*td;

	if ((td= d->GetTD())) {
	  bool is_man= d->IsManual();
	  SetDb(d->DataBaseName());
#if 0
	  QueryIter qi(d->TableName(), 0, is_man ? 0: d->GetWhere());
	  qi.SetFields(TRUE, d->GetAttr(TRUE));

	  if (is_man)
	      qi.SetWhere(d->GetWhere());
#else
	  QueryIter qi(d->TableName(), 0, d->GetWhere());
	  qi.SetFields(TRUE, d->GetAttr(TRUE));
#endif
	  if (d->Epoch())
	      qi.SetTime();
	  for (char **a; a= qi.Next(); nrtuples++)
	      td->AddTuple(a);
	  td->AddTuple(0);
	  loaded= TRUE;
	  // td->UpdateEvent();
	}
	return ol;
  }
  
  fprintf(stderr, "Freeing layer %s, Please wait...\n",
	d->QueryName());
  fflush(stderr);
  ol->FreeAll();
  loaded= TRUE;

  LoadCol(0, 0);
  return ol;
}


void GQuery::GetWhere(char *rw)
{
  char		bbox_where[8192];

  char		*gw= d->GetWhere();
  char		*vbb= ((MapView*) q->GetView())->GetQBbox();
  QuelInfo	*qinfo= GetInfo(TRUE);
  DynInfo	*di= d->GetDyn();

  if (di && di->BBoxAttr())
    sprintf(bbox_where, "(%s.%s && %s)",
      d->TableName(), di->BBoxAttr(), vbb);
  else if (d->HasLoc())
    sprintf(bbox_where, "(%s.geo_loc ---> %s)",
      d->TableName(), vbb);
  else if (qinfo->HasAttr(d->TableName(), "geo_bbox", "box"))
    sprintf(bbox_where, "(%s.geo_bbox && %s)",
      d->TableName(), vbb);
  else
    bbox_where[0]= '\0';

  rw[0]= '\0';
  if (gw)
    strcpy(rw, gw);
  if (bbox_where[0]) {
    if (rw[0])
      strcat(rw, " and ");
    strcat(rw, bbox_where);
  }

  // Add Dynamic Where

  DynWhereInfo	*dwi;
  char		*dyn_where= 0;
  if (dwi= qinfo->GetDynWhereInfo(d->TableName())) {
	  typedef char *(*pcf)(MapView*, char *);
	  pcf pf= (pcf) GetFuncPtr(dwi->File(), dwi->Func());
	  if (pf)
	    dyn_where= (*pf)((MapView*) q->GetView(), d->TableName());
  }

  if (dyn_where) {
    if (rw[0])
      strcat(rw, " and ");
    strcat(rw, dyn_where);
  }
}


static void FixDynAttrs(QuelInfo *qi, QueryIter *qiter, MapView *dv,
  char *relname, char **attrs, int n_attr)
{
  DynAttrInfo	*di;
  char		**na= new char*[n_attr+1];
  int		i;

  for (i= 0; i < n_attr; i++) {
    bool changed= FALSE;
    if (di= qi->GetDynAttrInfo(relname, attrs[i])) {
	  typedef char *(*pcf)(MapView*, char *, char *);
	  pcf pf= (pcf) GetFuncPtr(di->File(), di->Func());
	  if (pf) {
	    char *new_attr= (*pf)(dv, relname, attrs[i]);
	    attrs[i]= new_attr;
	    changed= TRUE;
	  };
    }

    char s[10000];

    if (changed)
      strcpy(s, attrs[i]);
    else
      sprintf(s, "%s.%s", relname, attrs[i]);
    na[i]= strsave(s);
  }
  na[i]= 0;

  qiter->SetFields(FALSE, na);
  for (i= 0; i < n_attr; i++)
    delete na[i];
  delete na;
}


void GQuery::LoadCol(ObjList *new_oids, ObjList *loaded_qs)
{
  AllWait(TRUE);

  char		where_s[8192];

  force_load= FALSE;
  SetDb(d->DataBaseName());

  QuelInfo	*qinfo= GetInfo(TRUE);
  DynInfo	*di= d->GetDyn();

  GetWhere(where_s);

  if (new_oids) {
    bool had_where;
    if (had_where= where_s[0])
      strcat(where_s, " and (");

    {
      char	oid_where[100];
      bool	first= TRUE;
      Iter	onext(new_oids);
      ObjInt	*oi;

      while (oi= (ObjInt*) onext()) {
        sprintf(oid_where, "%s%s.oid=\"%ld\"::oid", first ? "": " or ",
	  d->TableName(), int(*oi));
        strcat(where_s, oid_where);
        first= FALSE;
      }
      if (had_where)
	strcat(where_s, ")");
    }
  }
  
  char		*attr[1000];
  char		**pa= d->GetAttr(), **pb= attr;

  for (; *pa; pa++)
      *pb++= *pa;

  QueryIter	*qi;
  qi= new QueryIter(d->TableName(), 0,
    where_s[0] && !d->IsManual() ? where_s: 0);

  if (d->IsManual() && where_s[0])
    qi->SetWhere(where_s);

  bool		is_bin= di && di->IsBin();
  int		n_attr= is_bin ? 0: d->NrAttr();
  int		ai= n_attr;
  char		**a;
  bool		has_col= d->HasColor();
  bool		has_icon= d->HasIcon();
  bool		has_loc= d->HasLoc();
  bool		has_width= d->HasWidth();
  bool		has_pline= d->HasPline();
  bool		show_attr= d->ShowAttrLabel();
  bool		transp= d->TransLabel();
  ByteArray	*rel;
  int		n_dyn;
  int		col_ind, icon_ind, width_ind;
  int		td_offset= 2;
  int		oid_offset;
  TableDialog	*td;

  if (!(di || has_loc || has_pline)) {
	fprintf(stderr, "%s has none of: dyn_info/loc/pline\n", d->TableName());
	return;
	// abort();
  }

  if (di) {
    rel= new ByteArray(di->Relation());

    n_dyn= GetDynAttrs(di->Attr(), attr, ai);
    ai+= n_dyn;
    td_offset+= n_dyn-1;
    if (is_bin)
	qi->SetBin();
  } else
    attr[ai++]= has_loc ? "geo_loc": "geo_pline";

  oid_offset= ai;
  attr[ai++]= di && di->TheOid() ? di->TheOid(): "oid";
  if (has_col) {
    td_offset++;
    col_ind= ai;
    attr[ai++]= "geo_color";
  }
  if (has_icon) {
    td_offset++;
    icon_ind= ai;
    attr[ai++]= "geo_icon";
  }
  if (has_width) {
    td_offset++;
    width_ind= ai;
    attr[ai++]= "geo_width";
  }
  if (td= d->GetTD()) {
    char **pa= d->GetAttr(TRUE);

    for (; *pa; pa++)
      attr[ai++]= *pa;
  }
  if (d->Epoch())
    qi->SetTime();
  attr[ai++]= 0;

  MapView	*dview= (class MapView *) q->GetView();

  FixDynAttrs(qinfo, qi, dview, d->TableName(), attr, ai-1);
  // qi->SetFields(FALSE, table_attr); // Now done in FixDynAttrs()

  char		**new_attr= new char*[n_attr+1];
  int		i;
  Bitmap	*b;
  pqsfcp	funcp= 0;

  for (i= 0; i < n_attr; i++)
	new_attr[i]= new char[10000];

  if (has_loc && !has_icon)
        b= d->GetIconBitmap();
        
  bool		not_skipping= TRUE;
  QueryShape	*qs;
  while (a= qi->Next()) {
      IconShape		*is;
      PlineShape	*ps;
      float		lng, lat;
      
      if (not_skipping && td)
	not_skipping= td->AddTuple(a+n_attr+td_offset);
      else
        not_skipping= FALSE;

      if (has_loc) {
	if (has_icon)
	  b= qinfo->GetBitmapInfo(
	    atoi(a[icon_ind] ? a[icon_ind]: "0"))->GetBitmap();
        
        qs= is= new IconShape(dview, b);
        lng= atof(a[n_attr]+1);
        lat= atof(index(a[n_attr], ',')+1);
      } else if (has_pline) {
	qs= ps= new PlineShape(dview, a[n_attr]);
	ps->SetWidth(has_width ? atoi(a[width_ind] ? a[width_ind]: "1")
	  : d->GetWidth());
      } else { // Dynamic
	if (!funcp) { // first loop
	  if (!(funcp= qinfo->GetDynFunc(rel, di)))
	    break; // Function could not be loaded.
	}
	if (!(qs= (*funcp)(dview, n_dyn, a + n_attr)))
	  continue; // Shape is not created
      }

      qs->SetColor(has_col ?
	is_bin ?
	  (a[col_ind] ? *(int*) a[col_ind] : 2) :
	  atoi(a[col_ind] ? a[col_ind]: "2")
	: d->GetColor());

      qs->SetOid(a[oid_offset] ?
	(is_bin ? *(long*) a[oid_offset] : atol(a[oid_offset]))
	: -1);

      for (i= 0; i < n_attr; i++) {
	if (show_attr) {
          strcpy(new_attr[i], attr[i]);
          strcat(new_attr[i], ": ");
	} else
	  new_attr[i][0]= '\0';
	if (a[i])
          strcat(new_attr[i], a[i]);
      }
      new_attr[i]= 0;
      qs->SetAttr(new_attr, transp);
      if (has_loc)
	is->Init(lng, lat);
      qs->CompBBox();
      ol->Add(qs);
      if (new_oids)
	loaded_qs->Add(qs);
      else
        nrtuples++;
  }

  if (di)
	delete rel;

  for (i= 0; i < n_attr; i++)
	delete new_attr[i];
  delete new_attr;

  if (td) {
    td->AddTuple(0);
    // td->UpdateEvent(); // Gives trouble with the update of the mapview...
  }

  delete qi;

  AllWait(FALSE);
}



MetaImpl(QuelQueries, (TP(queries),
0));

char *GetGeoDB()
{
  static char *p;
  
  if (!p) {
	extern char *GEODB;

	if (!(p= GEODB))
		p= "geo";
  }
  return p;
}


QuelQueries::QuelQueries(View *v)
{
  AllWait(TRUE);

  static bool	first= TRUE;
  if (first) {
	first= FALSE;
	SetDb(GetGeoDB());
  }

  myview= v;
  queries= new IdDictionary;
  creating= new Set;

  AllWait(FALSE);
}

QuelQueries::~QuelQueries()
{
  queries->FreeAll();
  delete queries;
  creating->FreeAll();
  delete creating;
}


GQuery * QuelQueries::GetGQuery(TextItem *t)
{
    GQuery *g= (GQuery*) queries->AtKey(t);
    if (!g)
      abort();
    return g;
}


ObjList * QuelQueries::GetQueryNames(char *relname)
{
  Iter		next(queries);
#ifdef ET25
  Object	*key;
#else
  Assoc		*a;
#endif
  ObjList	*ol= new ObjList;

#ifdef ET25
  while (key= next()) {
#else
  while (a= (Assoc*) next()) {
#endif
    GQuery *gq;
#ifdef ET25
    if (strcmp(relname, (gq= (GQuery*) queries->AtKey(key))->GetTableName()) == 0) {
      if (gq->GetQD()->ShowOnMap())
	ol->Add(key);
#else
    if (strcmp(relname, (gq= (GQuery*) a->Value())->GetTableName()) == 0) {
      if (gq->GetQD()->ShowOnMap())
	ol->Add(a->Key());
#endif
    }
  }
  if (ol->Size() > 0)
    return ol;
  delete ol;
  return 0;
}


bool QuelQueries::EditShape(TextItem *t, long oid, Object *o)
{
    GQuery	*g;

    g= GetGQuery(t);
    g->SetObs(o);
    return g->EditShape(oid);
}


void QuelQueries::EditQuery(TextItem *t, bool do_delete, Object *o)
{
  GQuery	*g;

  if (t == 0) {
    g= new GQuery(this);
    // g->AddObserver(g->obs= o);
    g->SetObs(o);
    g->EditQuery(t);
  } else if (do_delete) {
#ifdef ET25
    Object *val;
    Object *as= queries->RemoveKey(t, val);
    val->FreeAll();
    delete val;
    as->FreeAll();
    delete as;
#else
    Assoc *as= (Assoc*) queries->RemoveKey(t);
    as->FreeAll();
    delete as;
#endif
  } else {
    g= GetGQuery(t);
    g->SetObs(o); // Needed after load
    g->EditQuery(t);
  }
}


char *QuelQueries::GetTableName(TextItem *layer)
{
  return GetGQuery(layer)->GetTableName();
}


bool QuelQueries::HasPline(TextItem *layer)
{
  return GetGQuery(layer)->GetQD()->HasPline();
}


bool QuelQueries::DontPick(TextItem *layer)
{
  return GetGQuery(layer)->GetQD()->DontPick();
}


ObjList *QuelQueries::GetQueryCol(TextItem *layer, bool area_changed,
		bool do_load, bool display_refresh)
{
  return GetGQuery(layer)->GetCol(area_changed, do_load, display_refresh);
}


class OChange: public Object {
  ByteArray	*relation;
  long		oid;
  long		flag;
public:
  OChange(char *rel, char *the_oid, char *fl) {
    relation= new ByteArray(rel);
    oid= the_oid ? atoi(the_oid): 0;
    flag= atoi(fl);
  }

  ~OChange() {
   delete relation;
  }

  long GetFlag() { return flag; }
  long GetOid() { return oid; }
  ByteArray *GetRel() { return relation; }
};


void QuelQueries::AsyncRefresh()
{
  if (!GEOAP)
    return;

  AllWait(TRUE);

  char ap[1000];
  strcpy(ap, GEOAP);

  char *p= rindex(ap, ',');
  if (p)
    *p= '\0';
  
  ObjList *co= new ObjList;
  QuelExec("begin");
  {
    QueryIter	qi(ap);
    char	**a;

    qi.SetNoTrans();
    int nt= qi.NrTuples();

    while (a= qi.Next()) {
      co->Add(new OChange(a[0], a[1], a[2]));
    }

    if (nt > 0) {
      char ds[1000];
      sprintf(ds, "delete %s", ap);
      QuelExec(ds);
    }
  }
  QuelExec("end");

  Iter		next(co);
  Dictionary	*rel_oids= new Dictionary;
  Set		*append_rels= new Set;
  OChange	*oc;
  int		del_cnt= 0;

  while (oc= (OChange*) next()) {
    int flag= oc->GetFlag();

    ObjList *qnames= GetQueryNames(oc->GetRel()->Str());
    if (!qnames)
      continue; // User is not displaying Query

    if (flag == 4) {
	if (! append_rels->Contains(oc->GetRel()))
	    append_rels->Add(oc->GetRel()); // Clone not needed because
					    // no FreeAll is done.
    } else {
      Iter	qnamenext(qnames);
      TextItem	*qn;

      while (qn= (TextItem*) qnamenext()) {
        Collection	*qc= GetQueryCol(qn, FALSE, FALSE, FALSE);
        QueryShape	*qp;
        long		co_oid= oc->GetOid();

        Iter		qnext(qc);

	// We could use hash tables to speed up the search.
	// For now we don't, because we search the list too often
	// in GEO. We could store a pointer to each QueryShape in an
	// additional Set. This would have to be implemented in
	// GQuery::LoadCol
	//
        while (qp= (QueryShape*) qnext()) {
            if (qp->GetOid() == co_oid)
	      break;
        }

        if (qp) {
            qc->RemovePtr(qp);
	    GetView()->InvalidateRect(qp->GetBBox());
	    delete qp;
	    if (flag == 1)
	      del_cnt++;
	}

	if (flag == 2) {
	    Set *oi_set;
	    oi_set= (Set*) rel_oids->AtKey(oc->GetRel());
	    if (!oi_set)
#ifdef ET25
	      rel_oids->PutAtKey(
		oi_set= new Set,
		new ByteArray(oc->GetRel()->Str())); // Clone prevents double del
#else
	      rel_oids->AtKeyPut(
		new ByteArray(oc->GetRel()->Str()), // Clone prevents double del
		oi_set= new Set);
#endif
	    ObjInt *new_oi= new ObjInt(co_oid);
	    if (!oi_set->Contains(new_oi))
	      oi_set->Add(new_oi);
	}
      }
    }
    delete qnames;
  }
  fprintf(stderr, "%d displayed objects deleted\n", del_cnt);

  const int	MaxChanged= 100;

  bool		ForcedRedraw= FALSE;
  Iter		next_rel(rel_oids);
#ifdef ET25
  Object	*key;
#else
  Assoc		*a;
#endif

#ifdef ET25
  while (key= next_rel()) {
    char    	*relname;
    ObjList	*qnames= GetQueryNames(relname= ((ByteArray*) key)->Str());
    Set		*orgol= (Set*) rel_oids->AtKey(key);
#else
  while (a= (Assoc*) next_rel()) {
    char    	*relname;
    ObjList	*qnames= GetQueryNames(relname= ((ByteArray*) a->Key())->Str());
    Set		*orgol= (Set*) a->Value();
#endif

    fprintf(stderr, "%d tuples have changed in %s\n", orgol->Size(), relname);

    if (orgol->Size() > MaxChanged) {
      Iter	qnamenext(qnames);
      TextItem	*qn;

      while (qn= (TextItem*) qnamenext()) {
	GQuery *gq= GetGQuery(qn);
        gq->ForceLoad();
	gq->GetCol(FALSE, TRUE, TRUE);
      }

      if (!ForcedRedraw) {
        GetView()->ForceRedraw();
        ForcedRedraw= TRUE;
      }
      fprintf(stderr, "Forced redraw for %s\n", relname);

      continue;
    }

    if (!qnames) // User is displaying Query, must be true, is already checked !
	abort();

    Iter	qnamenext(qnames);
    TextItem	*qn;

    while (qn= (TextItem*) qnamenext()) {
	GQuery	*gq= GetGQuery(qn);
        ObjList	*ol= new ObjList;
        ObjList	*lq= new ObjList;

        ol->AddAll(orgol);
        gq->AddQShapes(ol, lq);

	{ // force destruction of olnext
          Iter		lqnext(lq);
          QueryShape	*qs;
          while (qs= (QueryShape*) lqnext())
	    GetView()->InvalidateRect(qs->GetBBox());
	}
	delete lq;
	delete ol;
    }
    delete qnames;
  }

  Iter		next_append(append_rels);
  ByteArray	*ba;

  while (ba= (ByteArray*) next_append()) {

    ObjList *qnames= GetQueryNames(ba->Str());
    if (!qnames) // User is displaying Query, must always be true !
	abort();

    Iter	qnamenext(qnames);
    TextItem	*qn;

    while (qn= (TextItem*) qnamenext()) {

	GQuery		*gq= GetGQuery(qn);
	ObjList		*oidset= new ObjList;

	{ // Block forces destruction of QueryIter
	  char where_s[8192];
	  gq->GetWhere(where_s);

	  QueryIter	qi(ba->Str(), 0, where_s, "oid", 0);

	  char	**a;
	  while (a= qi.Next()) {
	    oidset->Add(new ObjInt(atoi(a[0])));
	  }
	}

	ObjList		*new_oids= new ObjList;
	Collection	*qc= gq->GetCol(FALSE, FALSE, FALSE);
	Set		*qset= new Set;
	ObjInt		*oi;
	Iter		oinext(oidset);

        qset->AddAll(qc);
	QueryShape *test_qs= new QueryShape(0);
        while (oi= (ObjInt*) oinext()) {
	    test_qs->SetOid(int(*oi));
	    if (! qset->Contains(test_qs)) {
	      new_oids->Add(oi);
	    }
	}
	delete test_qs;
	delete qset;

	int n_new;
	fprintf(stderr, "%d tuples are not displayed for %s (%s)\n",
	  n_new= new_oids->Size(), ba->Str(), qn->AsString());

	if (n_new > MaxChanged) {
	  gq->ForceLoad();
	  gq->GetCol(FALSE, TRUE, TRUE);

	  if (!ForcedRedraw) {
	    GetView()->ForceRedraw();
	    ForcedRedraw= TRUE;
	  }
	  fprintf(stderr, "Forced redraw for %s (%s)\n",
	    ba->Str(), qn->AsString());

	  delete new_oids;
	  continue;
	}
	if (n_new == 0) {
	  delete new_oids;
	  continue;
	}

	ObjList *loaded_qs= new ObjList;
	gq->AddQShapes(new_oids, loaded_qs);

        Iter		olnext(loaded_qs);
        QueryShape	*qs;

        while (qs= (QueryShape*) olnext())
	    GetView()->InvalidateRect(qs->GetBBox());

	delete loaded_qs;
	delete new_oids;
	oidset->FreeAll();
	delete oidset;
    }

    delete qnames;
  }

  rel_oids->FreeAll();
  delete rel_oids;
  delete append_rels;
  co->FreeAll();
  delete co;

  AllWait(FALSE);

  GetView()->UpdateEvent();
}


#ifdef ET25
OStream &QuelQueries::PrintOn(OStream &o)
#else
ostream &QuelQueries::PrintOn(ostream &o)
#endif
{
  Object::PrintOn(o);
  return o << queries NL;
}

#ifdef ET25
IStream &QuelQueries::ReadFrom(IStream &i)
#else
istream &QuelQueries::ReadFrom(istream &i)
#endif
{
  Object::ReadFrom(i);
  creating= new Set;
  i >> queries;
  return i;
}
