/*

    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, "@(#)OverView.c	3.13 7/28/92");

#ifdef __GNUG__
#pragma implementation "GeoPos.h"
#pragma implementation "OverView.h"
#endif


//$OverView$
#include "Class.h"
#include "OverView.h"
#include "ObjList.h"
#include "Document.h"
#include "Window.h"
#include "Alert.h"
#include "Dialog.h"
#include "DialogItems.h"
#include "Scroller.h"
#include "CollView.h"
#include "Dictionary.h"
#include "Filler.h"
#include "ImageItem.h"
#include "Set.h"
#include "WindowPort.h"
#include "RegularExp.h"

#include "PolyShape.h"
#include "CellShape.h"
#include "IconShape.h"
#include "Geo.h"
#include "Query.h"
#include "DrawDocument.h"
#include "DrawView.h"
#include "Application.h"

#include <sys/file.h>
extern "C" {
#include <sys/mman.h>
}

#ifdef ET25
extern "C" char *index();
#include "math.h"
#endif
#ifdef ET23
inline void AllWait(bool) {};
#else
#endif


static bool	has_cs;

class ZoomDialog: public Dialog {
  OverView	*ov;
public:
  MetaDef(ZoomDialog);
  
  ZoomDialog(OverView *m);
  VObject *DoCreateDialog();
  void Control(int id, int p, void *v);
};

static const int cIdZoomIn	= cIdFirstUser + 0,
		 cIdZoomOut	= cIdFirstUser + 1,
		 cIdNewCenter	= cIdFirstUser + 2;

MetaImpl0(ZoomDialog);

#ifdef ET25
ZoomDialog::ZoomDialog(OverView *m): ("Overview Zoom Operations"),
#else
ZoomDialog::ZoomDialog(OverView *m): ("Overview Zoom Operations", eBWinBlock),
#endif
	ov(m)
{
}

VObject *ZoomDialog::DoCreateDialog()
{
  return new Cluster(cIdNone, eVObjVCenter, 5,
    new ActionButton(cIdZoomIn, "Zoom In"),
    new ActionButton(cIdZoomOut, "Zoom Out"),
    new ActionButton(cIdNewCenter, "New Center"),
    new ActionButton(cIdCancel, "Cancel"),
  0);
}

void ZoomDialog::Control(int id, int p, void *v)
{
  bool doc_changed= FALSE;

  switch(id) {
  case cIdZoomIn:
    ((DrawView*) ov->GetMapView())->SetMessage(
	"Zoom with middle mouse button in the Overview Map.");
    ov->DoZoomIn();
    doc_changed= TRUE;
    break;
  case cIdZoomOut:
    ov->DoZoomOut();
    doc_changed= TRUE;
    break;
  case cIdNewCenter:
    ((DrawView*) ov->GetMapView())->SetMessage(
	"Choose with middle mouse button a new center in the Overview Map.");
    ov->DoNewCenter();
    doc_changed= TRUE;
    break;
  }
  if (doc_changed)
	// a NOP command so Doc will ask for save:
	ov->GetDocument()->PerformCommand(new Command(12345));

  id= cIdCancel;
  Dialog::Control(id, p, v);
}

extern Clipper *gClipper;


class LayerDialog: public Dialog {
    Scroller 		*layer_scroller,
	     		*choice_scroller;
    CollectionView	*layer_view,
			*choice_view;
    OrdCollection	*layer_col,
			*choice_col;
#ifdef ET25
    RegExpField		*new_text;
#else
    RestrTextItem	*new_text;
#endif
    int			layer_sel,
			choice_sel;
    bool		order_changed;
    bool		moved_from_sel;
    byte		*my_title;
protected:
    MapDialogView	*mdv;
    virtual VObject	*NewItem(byte *)
	{ AbstractMethod("LayerDialog::NewItem"); return 0; };
    void NewOrder();
public:
    MetaDef(LayerDialog);

    LayerDialog(MapDialogView *, byte *title);
    ~LayerDialog();

    bool PrintOnWhenObserved(Object*) { return FALSE; }

    virtual VObject *DoEdit(VObject *, bool b= FALSE) { return 0; };
    virtual bool HasEdit() { return FALSE; }

    VObject *DoCreateDialog();
    void Control(int id, int p, void *v);
    void DoSetup();
    void Add(VObject *l);
    void AddLayer(VObject *l);
    void EditLayer(int);
    OrdCollection *GetLayerCol() { return layer_col; }
    bool OrderChanged() { return order_changed; }
    bool MovedFromSel() { return moved_from_sel; }
    void MakeEmpty()
      { if (IsOpen()) Close(); layer_col->Empty(); choice_col->Empty(); }

    void DoObserve(int, int, void *, Object *);

#ifdef ET25
    OStream& Save(OStream&);    
    IStream& Load(IStream&);
#else
    ostream& Save(ostream&);    
    istream& Load(istream&);
#endif
};

MetaImpl(LayerDialog,(TP(layer_view),TP(choice_view),0));

static const int cIdLayer	= cIdFirstUser + 0,
		 cIdChoice	= cIdFirstUser + 1,
		 cIdFirst	= cIdFirstUser + 2,
		 cIdLast	= cIdFirstUser + 3,
		 cIdAdd		= cIdFirstUser + 4,
		 cIdDelete	= cIdFirstUser + 5,
		 cIdLayerUp	= cIdFirstUser + 6,
		 cIdLayerDown	= cIdFirstUser + 7,
		 cIdNewText	= cIdFirstUser + 8,
		 cIdAddText	= cIdFirstUser + 9,
		 cIdEditSelection = cIdFirstUser + 10,
		 cIdRemoveSelection = cIdFirstUser + 11,
		 cIdNewItem	= cIdFirstUser + 12,
		 cIdRefresh	= cIdFirstUser + 13,
		 cIdGeoInfo	= cIdFirstUser + 14,
		 cIdBlinkQuery	= cIdFirstUser + 15;

static const int cItemMinWidth =   200;

LayerDialog::LayerDialog(MapDialogView *m, byte *title)
#ifdef ET25
	: ((char *)title, eWinFixed)
#else
	: ((char *)title)
#endif
{
  my_title= title;
  mdv= m;

  layer_col= new OrdCollection;
  choice_col= new OrdCollection;

  layer_view= new CollectionView(this, layer_col);
  layer_view->SetMinExtent(Point(cItemMinWidth, 0));
  layer_view->SetId(cIdLayer);
#ifdef ET25
#else
  layer_view->SetContainer(this);
#endif

  choice_view= new CollectionView(this, choice_col);
  choice_view->SetMinExtent(Point(cItemMinWidth, 0));
  choice_view->SetId(cIdChoice);
#ifdef ET25
#else
  choice_view->SetContainer(this);
#endif
}

LayerDialog::~LayerDialog()
{
  if (IsOpen())
	Close();
  // Contents of layer_col and choice_col already deleted in
  // ~geo and ~quelqueries. We do an Empty() because otherwise 
  // SafeDelete(layer_view) will do a FreeAll() resulting in 
  // double deletion!
  layer_col->Empty();
  choice_col->Empty();
  
  SafeDelete(layer_view);
  SafeDelete(choice_view);
}


#ifdef ET25
// VObject *LayerDialog::DoMakeContent()
VObject *LayerDialog::DoCreateDialog()
#else
VObject *LayerDialog::DoCreateDialog()
#endif
{
#ifdef EXP
  return new ActionButton(cIdNewItem, "Start Experiment...");
#else
  layer_scroller= new Scroller(layer_view, Point(cItemMinWidth, 16*14), cIdLayer);
  choice_scroller= new Scroller(choice_view, Point(cItemMinWidth, 16*14), cIdChoice);
  //layer_scroller= new Scroller(layer_view, Point(cItemMinWidth, 16*8), cIdLayer);
  //choice_scroller= new Scroller(choice_view, Point(cItemMinWidth, 16*8), cIdChoice);
  
  VObject *v=
    new Cluster(cIdNone, eVObjHLeft, 10,
     new Cluster(cIdNone, eVObjHCenter, 10,
#ifdef ET25
#else
      new BorderItem(new TextItem(my_title)),
#endif
      GEOBROWSE ?
       layer_scroller:
       new Cluster(cIdNone, eVObjVCenter, 5,
	new BorderItem("Draw Order",
	  new Cluster(cIdNone, eVObjVCenter, 5,
	    new Cluster(cIdNone, eVObjHLeft, 10,
		    new ActionButton(cIdLayerUp, "Up"),
		    new ActionButton(cIdLayerDown, "Down"),
		    new ActionButton(cIdFirst, "Make First"),
		    new ActionButton(cIdLast, "Make Last"),
		    0),
	    layer_scroller,
	  0)
	),
	new Cluster(cIdNone, eVObjHLeft, 10,
		new ActionButton(cIdDelete, "==>"),
		0),
	new BorderItem("Not Drawn",
	  choice_scroller),
       0),
      0),
      new BorderItem("Manipulate item",
	  !HasEdit() ?
	    (new Cluster(cIdNone, eVObjVCenter, 5,
	      new ActionButton(cIdAddText, "Add"),
#ifdef ET25
	      new_text = new RegExpField(cIdNewText,
		new RegularExp("[a-z.0-9_/]*"), 30, "Text:"),
#else
	      new TextItem("Text: "),
	      new_text = new RestrTextItem(cIdNewText,
		new RegularExp("[a-z.0-9_/]*"), "", 300),
#endif
	      new ActionButton(cIdEditSelection, "Edit/Remove Selected Item"),
	    0))
	  :
	    (new Cluster(cIdNone, eVObjVCenter, 5,
	      new ActionButton(cIdNewItem, "New Item..."),
	      new ActionButton(cIdEditSelection, "Edit Item..."),
	      new ActionButton(cIdRemoveSelection, "Remove Item"),
	    0))
	  ),
      new Cluster(cIdNone, eVObjVCenter, 5,
		new ActionButton(cIdOk, GEOBROWSE ? "Exit": "Done", TRUE),
		GEOBROWSE ?
		  0:
		  new ActionButton(cIdRefresh, "Refresh Display"),
		HasEdit() ?
		  new ActionButton(cIdBlinkQuery, "Blink"):
		  0,
		0),
      0);

  return v;
#endif
}

void LayerDialog::AddLayer(VObject *l)
{
  layer_col->Add(l);
  layer_view->Modified();
}

void LayerDialog::Add(VObject *l)
{
  choice_col->Add(l);
  choice_view->Modified();
}

void LayerDialog::DoSetup()
{
    // DisableItem(cIdAdd);
    DisableItem(cIdDelete);
    DisableItem(cIdBlinkQuery);
    DisableItem(cIdFirst);
    DisableItem(cIdLast);
    DisableItem(cIdLayerUp);
    DisableItem(cIdLayerDown);
    DisableItem(cIdEditSelection);
    if (!HasEdit())
      DisableItem(cIdAddText);
    else
      DisableItem(cIdRemoveSelection);
    choice_view->Modified();
    layer_view->Modified();
#ifdef ET25
    choice_view->ClearSelection();
    layer_view->ClearSelection();
#else
    choice_view->SetNoSelection();
    layer_view->SetNoSelection();
#endif
}

#if 0
void LayerDialog::DoSave()
{
  old_layer_col->Empty();
  old_choice_col->Empty();
  old_layer_col->AddAll(layer_col);
  old_choice_col->AddAll(choice_col);
  // moved_from_sel= order_changed= FALSE;
}

void LayerDialog::DoRestore()
{
  layer_col->Empty();
  choice_col->Empty();
  layer_col->AddAll(old_layer_col);
  choice_col->AddAll(old_choice_col);
  moved_from_sel= order_changed= FALSE;
}
#endif

void LayerDialog::NewOrder()
{
  mdv->GetMapView()->GetDocument()->PerformCommand(new Command(12345));
  order_changed= TRUE;
}

void LayerDialog::Control(int id, int p, void *v)
{
  switch (id) {
  case cIdOk:
	if (GEOBROWSE)
	  exit(0);
  case cIdCancel:
  case cIdRefresh:
  	mdv->LayersChanged(this);
	moved_from_sel= order_changed= FALSE;
  	if (id != cIdRefresh)
		Dialog::Control(id, p, v);
	return;
  case cIdLayer:
  case cIdChoice:
	switch (p) {
	case cPartCollSelect:
	case cPartCollDoubleSelect:
		if (id == cIdLayer) {
		    layer_sel= (int) v;
		    if (p == cPartCollDoubleSelect) {
			Control(cIdEditSelection, cPartAction, 0);
			return;
		    }
		    EnableItem(cIdDelete);
		    EnableItem(cIdFirst);
		    EnableItem(cIdBlinkQuery);
		    EnableItem(cIdLast);
		    EnableItem(cIdLayerUp);
		    EnableItem(cIdLayerDown);
		    EnableItem(cIdEditSelection);
		    EnableItem(cIdRemoveSelection);
		} else {
		    choice_sel= (int) v;
		    NewOrder();
		    moved_from_sel= TRUE;
		    layer_col->Add(choice_col->RemovePtr(
			choice_col->At(choice_sel)));
		    DoSetup();
		}
	}
	return;
  case cIdDelete:
	NewOrder();
#ifdef ET25
	choice_col->AddAt(layer_col->RemovePtr(layer_col->At(layer_sel)),0);
#else
	choice_col->AddAt(0,layer_col->RemovePtr(layer_col->At(layer_sel)));
#endif
	DoSetup();
#ifdef ET25
	if (!HasEdit() && strlen(new_text->AsString()))
#else
	if (!HasEdit() && strlen(new_text->GetText()->AsString()))
#endif
		EnableItem(cIdAddText);
	return;
  case cIdBlinkQuery:
	order_changed= TRUE;
	mdv->GetMapView()->BlinkQuery((TextItem*) layer_col->At(layer_sel));
	return;
  case cIdFirst:
	NewOrder();
#ifdef ET25
	layer_col->AddAt(layer_col->RemovePtr(layer_col->At(layer_sel)),0);
#else
	layer_col->AddAt(0,layer_col->RemovePtr(layer_col->At(layer_sel)));
#endif
	layer_view->SetSelection(Rectangle(0, layer_sel= 0, 1, 1));
	return;
  case cIdLast:
	NewOrder();
	layer_col->Add(layer_col->RemovePtr(layer_col->At(layer_sel)));
	layer_view->SetSelection(
		Rectangle(0, layer_sel= layer_col->Size()-1, 1, 1));
	return;
  case cIdLayerDown:
	if (layer_sel != layer_col->Size()-1) {
	  NewOrder();
#ifdef ET25
	  layer_col->AddAt(layer_col->RemovePtr(layer_col->At(layer_sel)),
		layer_sel+1);
#else
	  layer_col->AddAt(layer_sel+1,
		layer_col->RemovePtr(layer_col->At(layer_sel)));
#endif
	  layer_view->SetSelection(Rectangle(0, ++layer_sel, 1, 1));
	}
	return;
  case cIdLayerUp:
	if (layer_sel != 0) {
	  NewOrder();
#ifdef ET25
	  layer_col->AddAt(layer_col->RemovePtr(layer_col->At(layer_sel)),
		layer_sel-1);
#else
	  layer_col->AddAt(layer_sel-1,
		layer_col->RemovePtr(layer_col->At(layer_sel)));
#endif
	  layer_view->SetSelection(Rectangle(0, --layer_sel, 1, 1));
	}
	return;
  case cIdNewText:
#ifdef ET25
	if (strlen(new_text->AsString()))
#else
	if (strlen(new_text->GetText()->AsString()))
#endif
		EnableItem(cIdAddText);
	else
		DisableItem(cIdAddText);
	return;
  case cIdEditSelection:
	NewOrder();
	if (!HasEdit()) {
	  new_text->SetString((byte*) layer_col->RemoveAt(layer_sel)
	    ->AsString());
	  DoSetup();
	  EnableItem(cIdAddText);
	} else {
	  EditLayer(layer_sel);
	}
	return;
  case cIdNewItem:
	DoEdit((VObject*) 0, FALSE);
#ifdef EXP
	Close();
#endif
	return;
  case cIdRemoveSelection:
	TextItem *ti= (TextItem *) layer_col->At(layer_sel);
	layer_col->RemovePtr(ti);
	NewOrder();
	DoEdit(ti, TRUE);
	DoSetup();
	return;
  case cIdAddText:
	NewOrder();
#ifdef ET25
	layer_col->Add(NewItem((byte*) new_text->AsString()));
#else
	layer_col->Add(NewItem((byte*) new_text->GetText()->AsString()));
#endif
	return;
  }
}

void LayerDialog::DoObserve(int id, int part, void *data, Object *o)
{
  switch (id) {
    case cQUERYADD:
      layer_col->Add((VObject*) data);
    case cQUERYDONE:
      layer_view->Modified();
#ifdef ET25
#else
      UpdateEvent();
#endif
      NewOrder();
      Control(cIdRefresh, 0, 0);
      return;
  }
}

void LayerDialog::EditLayer(int layer_sel)
{
    DoEdit((VObject*) layer_col->At(layer_sel));
}


#ifdef ET25
OStream& LayerDialog::Save(OStream& to)
#else
ostream& LayerDialog::Save(ostream& to)
#endif
{
  to << layer_col << choice_col;
  return to;
}

#ifdef ET25
IStream& LayerDialog::Load(IStream& from)
#else
istream& LayerDialog::Load(istream& from)
#endif
{
  OrdCollection *l= 0, *c= 0;

  from >> l >> c;
  layer_col->Empty();
  choice_col->Empty();
  layer_col->AddAll(l);
  choice_col->AddAll(c);
  choice_view->Modified();
  layer_view->Modified();
  delete l;
  delete c;
  // DoSave();
  return from;
}


MetaImpl0(MapDialogView);

static MapView	*curmapView;

static void ChangeQuery(VObject *v, bool do_delete, LayerDialog *ld)
{
  curmapView->GetQueries()->EditQuery((Layer*)v, do_delete, ld);
}


class MapLayerDialog: public LayerDialog {
protected:
    virtual VObject	*NewItem(byte *s) { return new Layer((char*)s); };
};

class QueryLayerDialog: public MapLayerDialog {
protected:
    virtual VObject	*NewItem(byte *s) { return new Layer((char*)s); };
public:
    bool HasEdit() { return TRUE; }
    VObject * DoEdit(VObject *v, bool d) {
	curmapView= mdv->GetMapView();
	ChangeQuery(v, d, this);
    }
};



MapDialogView::MapDialogView(MapView *m, byte *title, bool overlay_flag)
{
  mapView= m;
  layerdialog= new MapLayerDialog(this, title);
  if (overlays= overlay_flag) {
    overlaydialog= new MapLayerDialog(this, (byte*) "Map Overlay Select");
  } else {
    zd= new ZoomDialog((OverView*) m);
  }
  querydialog= new QueryLayerDialog(this,
    (byte*) (overlay_flag ? "Query Select": "OverView Query Select"));
};

MapDialogView::~MapDialogView()
{
  SafeDelete(layerdialog);
  if (overlays) {
    SafeDelete(overlaydialog);
  } else
    SafeDelete(zd);
  SafeDelete(querydialog);
}


static const int cIdSetLayers	= cIdFirstUser + 0,
		 cIdSetOverlays	= cIdFirstUser + 1,
		 cIdBlink	= cIdFirstUser + 2,
		 cIdSetQueries	= cIdFirstUser + 3,
		 cIdEditQuery	= cIdFirstUser + 4,
		 cIdRefreshQueries = cIdFirstUser + 5,
		 cIdShowGrid	= cIdFirstUser + 6,
		 cIdShowLabels	= cIdFirstUser + 7,
		 cIdZoomOverview= cIdFirstUser + 8,
		 cIdAsync= cIdFirstUser + 9;

VObject *MapDialogView::DoCreateDialog()
{
  extern bool	GEONOOVER,
		GEONORTREE;
  extern short	IZFLogo[];
  VObject	*lb;

  top_query= new Layer("None                   ");
  
  Cluster *cl= new Cluster(cIdNone, VObjAlign(eVObjHCenter|eVObjHExpand), 10,
    new BorderItem(
	new Cluster(cIdNone, eVObjHLeft, gPoint2,
	    Coordsys= new TextItem(""),
#ifdef ET25
	    Lat= new TextItem("               ", gFixedFont),
	    Long= new TextItem("", gFixedFont),
#else
	    Lat= new TextItem("               ", new_Font(eFontHelvetica, 10, eFaceBold)),
	    Long= new TextItem("", new_Font(eFontHelvetica, 10, eFaceBold)),
#endif
	    GEONORTREE ?
	      new Filler(gPoint0):
	      new ActionButton(cIdSetLayers, "Set Layers..."),
#ifdef ET25
	    show_grid= new ToggleButton(cIdShowGrid, "Show Grid"),
	    show_labels= new ToggleButton(cIdShowLabels, "Show Labels"),
#else
	    new LabeledButton(cIdShowGrid,
	      show_grid= new ToggleButton,
	      new TextItem("Show Grid")),
	    lb= new LabeledButton(cIdShowLabels,
	      show_labels= new ToggleButton,
	      new TextItem("Show Labels")),
#endif
	    overlays ?
	      GEONOOVER ?
		new ActionButton(cIdGeoInfo, new ImageItem(IZFLogo, Point(24)))
	      :
	        new ActionButton(cIdSetOverlays, "Set Overlays...")
	    :
	      new ActionButton(cIdZoomOverview, "Zoom Overview..."),
	    new ActionButton(cIdSetQueries, "Set Queries..."),
	    overlays ?
	      new BorderItem("Top Overlay:", top_overlay=
		new Layer("None                   "))
	    :
	      0,
	    new BorderItem("Top Query:", top_query),
	    new Cluster(cIdNone, eVObjVCenter, gPoint2,
	      new ActionButton(cIdRefreshQueries, "Refresh"),
	      GEOAP ?
	        new ActionButton(cIdAsync, "Async") :
		0,
	      0),
#ifdef ET25
	0)),
#else
	0),
    10, 0),
#endif
  0);
#ifdef ET25
#else
  lb->Enable(FALSE, TRUE);
#endif
  return cl;
}


void MapDialogView::SetTopOverlay()
{
#ifdef ET25
    SeqCollection *col= overlaydialog->GetLayerCol();
#else
    Collection *col= overlaydialog->GetLayerCol();
#endif
    int		s;
    if (s= col->Size()) {

      top_overlay->SetString(col->At(s-1)->AsString());
    } else
      top_overlay->SetString("None");
    ((DrawView*)mapView)->ChangeShapes();
}

void MapDialogView::SetTopQuery()
{
    if (GEOBROWSE)
      return;

#ifdef ET25
    SeqCollection *col= querydialog->GetLayerCol();
#else
    Collection *col= querydialog->GetLayerCol();
#endif
    int		s;

    if (s= col->Size()) {
      top_query->SetString(col->At(s-1)->AsString());
    } else
      top_query->SetString("None");
}


bool MapDialogView::ShowLabels()
{
  return show_labels->GetValue();
}

bool MapDialogView::ShowGrid()
{
  return show_grid->GetValue();
}

void MapDialogView::LayersChanged(LayerDialog *l)
{
  if (GEOBROWSE)
	return;

  if (l == layerdialog) {
	if (layerdialog->OrderChanged()) {
	    mapView->NewContent();
	    // mapView->GetWindow()->ForceRedraw();
	    mapView->ForceRedraw();
	    mapView->UpdateEvent();
	}  	
  } else if (l == overlaydialog) {
	if (overlaydialog->OrderChanged()) {
	    // mapView->FetchLayers();
	    SetTopOverlay();
#ifdef ET25
#else
	    UpdateEvent();
#endif
	    // mapView->GetWindow()->ForceRedraw();
	    mapView->ForceRedraw();
	    mapView->UpdateEvent();
	}
  } else {
	if (querydialog->OrderChanged()) {
	    if (querydialog->MovedFromSel())
	      mapView->DoQueryRefresh();  // Only needed when Queries are moved
	    				  // from not drawn to drawn
	    SetTopQuery();
#ifdef ET25
#else
	    UpdateEvent();
#endif
	    mapView->QDisplayRefresh();
	    // mapView->GetWindow()->ForceRedraw();
	    mapView->ForceRedraw();
	    mapView->UpdateEvent();
	}
  }
}

void MapDialogView::Control(int id, int p, void *v)
{
  if (mapView->IsGrabbing()
  && id != cIdZoomOverview && id != cIdShowGrid && id != cIdShowLabels) {
    ShowAlert(eAlertNote, 
	"You are creating/editing on the map.\nFinish that operation first !");
    return;
  }

  switch(id) {
  case cIdGeoInfo:
	gApplication->About();
	return;
  case cIdSetLayers:
	if (!layerdialog->IsOpen())
	  layerdialog->Show();
	else
	  layerdialog->GetWindow()->Top();
	return;
  case cIdSetOverlays:
	((DrawDocument*)mapView->GetDocument())->DoMakeInfo();
	if (!overlaydialog->IsOpen())
	  overlaydialog->Show();
	else
	  overlaydialog->GetWindow()->Top();
	return;
  case cIdRefreshQueries:
	mapView->DoQueryRefresh();
	return;
  case cIdAsync:
	mapView->AsyncRefresh();
	return;
  case cIdZoomOverview:
  	zd->ShowOnWindow(GetWindow());
  	return;
  case cIdShowGrid:
	if (p != cPartToggle)
	  return;
        EnableItem(cIdShowLabels, show_grid->GetValue());
  case cIdShowLabels:
	if (p != cPartToggle)
	  return;
  	mapView->ForceRedraw();
  	mapView->UpdateEvent();
  	break;
  case cIdSetQueries:
	if (!querydialog->IsOpen())
	  querydialog->Show();
	else
	  querydialog->GetWindow()->Top();
	return;
#if 0
  case cIdBlink:
	mapView->blink_count= 10;
	mapView->blink_col= 3;
	return;
#endif
  }
}


#ifdef ET25
OStream& MapDialogView::Save(OStream& to)    
#else
ostream& MapDialogView::Save(ostream& to)    
#endif
{
    layerdialog->Save(to);

    to << show_labels->Enabled() NL;
    to << show_labels->GetValue() NL;
    to << show_grid->GetValue() NL;

    to << overlays NL;
    if (overlays) {
	overlaydialog->Save(to);
	mapView->GetGeo()->SaveOverlays();
    }
    querydialog->Save(to);
    to << mapView->GetQueries();

    return(to);
}


#ifdef ET25
IStream& MapDialogView::Load(IStream& from)
#else
istream& MapDialogView::Load(istream& from)
#endif
{
    layerdialog->Load(from);
    
    bool b;
    from >> Bool(b); show_labels->Enable(b);
#ifdef ET25
    from >> Bool(b); show_labels->SetValue(b);
    from >> Bool(b); show_grid->SetValue(b);
#else
    from >> Bool(b); show_labels->SetState(b);
    from >> Bool(b); show_grid->SetState(b);
#endif

    from >> overlays;
    if (overlays) {
	overlaydialog->Load(from);
	SetTopOverlay();
	if (overlaydialog->GetLayerCol()->Size())
	  ((DrawDocument*)mapView->GetDocument())->DoMakeInfo();
    }
    querydialog->MakeEmpty();
    if (geo_version > 102 || overlays) {
	querydialog->Load(from);
	mapView->LoadQueries(from);
    } else {
	mapView->NewQuel();
    }
    ForceRedraw();
    SetTopQuery();

    return from;
}


void MapView::NewQuel()
{
  SafeDelete(quel);
  quel= new QuelQueries(this);
}


OrdCollection * MapView::GetQueryCol()
{
  MapLayerDialog *d= dialogView->GetQueryDialog();
  return d ? d->GetLayerCol(): 0;
}

#ifdef ET25
SeqCollection * MapView::GetMapOverlayCol()
#else
Collection * MapView::GetMapOverlayCol()
#endif
{
  MapLayerDialog *d= dialogView->GetOverlayDialog();
  return d ? d->GetLayerCol(): 0;
}

#ifdef ET25
IStream &MapView::LoadQueries(IStream &i)
#else
istream &MapView::LoadQueries(istream &i)
#endif
{
  SafeDelete(quel);
  i >> quel;
  quel->SetView(this);
  return i;
}

void MapView::AsyncRefresh()
{
    quel->AsyncRefresh();
}


void MapView::DoQueryRefresh()
{
    do_query_refresh= TRUE;
    ForceRedraw();
#ifdef ET25
#else
    UpdateEvent();
#endif
}


MetaImpl0(AreaSelector);

AreaSelector::AreaSelector(MapView *mv, MapView *tv): (mv)
{
  targetview= tv;
}

void AreaSelector::TrackFeedback(Point anchorPoint, Point nextpoint, bool)
{
    GrSetPenSize(2);
    GrStrokeRect(NormRect(anchorPoint, nextpoint));
}

void AreaSelector::TrackConstrain(Point ap, Point pp, Point *np)
{
  // if (mapview->has_cs) {
  if (has_cs) {
    typedef int (*pif)(MapView *, MapView*, int, int, int, int, int *, int *);
    int new_x, new_y;

    (*((pif)mapview->cs_constrain))(mapview, targetview, 
	ap.x,ap.y,np->x,np->y,&new_x,&new_y);
    np->x= new_x;
    np->y= new_y;
  } else {
    float ax= mapview->XToLong(ap.x), ay= mapview->YToLat(ap.y);
    float lngdiff= fabs(mapview->XToLong(np->x) - ax);
    float newlat;
    float latdiff= fabs((newlat= mapview->YToLat(np->y)) - ay);
    float latavg= (newlat + ay) / 2;
    float scale= cos(deg2rad(latavg));
    float maxextent= lngdiff > latdiff ? lngdiff: latdiff;
    bool own_aspect= targetview->IsKindOf(OverView) || mapview->ZoomingIn();
    float aspect= (own_aspect ? mapview: targetview)->AspectRatio();
    Point extent= *np - ap;
    *np= mapview->GeoToPoint(ax+(extent.x > 0 ? maxextent: -maxextent)*aspect,
	ay + scale*(extent.y > 0 ? -maxextent: maxextent));
  }
}

static void Check_Poles(float &Lat_max, float &Lat_min)
{
    bool adjusted= FALSE;

    if (Lat_max > 85) {
      Lat_max= 85;
      adjusted= TRUE;
    }
    if (Lat_min < -85) {
      Lat_min= -85;
      adjusted= TRUE;
    }
    if (adjusted) {
      ShowAlert(eAlertNote, 
	"@BArea Selector@P: "
	"The aspect ratio will be distorted\n"
	"because the area would extent over the poles.\n"
	"Zoomin to obtain a decent aspect ratio.");
    }
}

Command *AreaSelector::TrackMouse(TrackPhase tp, Point ap, Point pp, Point np)
{
    MapCommand::TrackMouse(tp, ap, pp, np);

    mapview->ShowPos(np);
    switch (tp) {
    case eTrackPress:
        GrSetCursor(eCrsHand);
        break;
    case eTrackRelease:
	float Long_min;
	float Lat_min;
	float Long_max;
	float Lat_max;
	bool in_over= !targetview->IsKindOf(OverView);
	bool new_center= FALSE;

	Rectangle nr(NormRect(ap, np));

	((DrawView*) (in_over ? targetview: mapview))->SetMessage("");
	if (!moved || (in_over && mapview->NewCenter())) {
	    float Long= mapview->XToLong(ap.x);
	    float Lat= mapview->YToLat(ap.y);
 
    	    MapView *center_view;
    	    if (in_over) {
    	      if (mapview->NewCenter()) {
    	        new_center= TRUE;
    	        center_view= mapview;
    	      } else {
    	        center_view= targetview;
    	        nr= gRect0;
    	      }
    	    } else
    	      center_view= mapview;

	    Long_min= Long-center_view->GetLongSpan()/2;
	    Long_max= Long+center_view->GetLongSpan()/2;
	    Lat_min= Lat-center_view->GetLatSpan()/2;
	    Lat_max= Lat+center_view->GetLatSpan()/2;
	    
	} else {
	    Long_min= mapview->XToLong(nr.Left());
	    Lat_min= mapview->YToLat(nr.Top()+nr.Height());
	    Long_max= mapview->XToLong(nr.Left()+nr.Width());
	    Lat_max= mapview->YToLat(nr.Top());
	}

	//if (!mapview->has_cs)
	if (!has_cs)
	  Check_Poles(Lat_max, Lat_min);

	if (new_center) {
	    mapview->NewCenterDone();
	    mapview->SetArea(Long_min, Long_max, Lat_min, Lat_max);
	    mapview->ForceRedraw();
	    return gNoChanges;
	}
	    
	const int Min_Select_Size= 2;
	if (moved && 
	(nr.Width() < Min_Select_Size || nr.Height() < Min_Select_Size)) {
	    ShowAlert(eAlertNote, 
	      "@BArea Selector@P: Area is not changed. (Too small)");
	    return gNoChanges;
	}

	const float Min_Size= 0.01;
	if (!has_cs &&
	   (Long_max-Long_min < Min_Size || Lat_max-Lat_min < Min_Size)) {
	    ShowAlert(eAlertNote, "@BArea Selector@P: Area is not changed.\
(Smaller than '@B%f@P' degrees)", Min_Size);
	    return gNoChanges;
	}

	OverView	*over;
	MapView		*map;
	if (!in_over) {
	    nr= gRect0;
	    over= (OverView*)targetview;
	    map= mapview;
	} else {
	    if (mapview->ZoomingIn())
	      map= mapview;
	    else {
	      over= (OverView*)mapview;
	      map= targetview;
	    }
	}
	if (mapview->ZoomingIn())
	  mapview->ZoomInDone();
	else {
	  over->ShowArea(nr, Long_min, Long_max, Lat_min, Lat_max);
	  over->UpdateEvent();
	}
	map->SetArea(Long_min, Long_max, Lat_min, Lat_max);
	map->ForceRedraw();
	map->UpdateEvent();
        return gNoChanges;
    }  
    return this;
}


//---- MapView ----------------------------------------------------------------

MetaImpl(MapView, (T(MinLong), T(MaxLong),
	T(MinLat), T(MaxLat), T(extent),
	T(LatSpan), T(LongSpan),
	T(MercMinLat), T(MercMaxLat), T(MercLatSpan),
	0));

static Dictionary	*rtree_share;
extern bool		GEORTREESHARE;

MapView::MapView(Document *dp, Point extent, ObjList *sl,
	float Long_Min, float Long_Max,
	float Lat_Min, float Lat_Max,
	byte *title, bool overlay_flag) : (dp, extent)
{
    blink_count= cur_blink_col= blink_col= 0;

    if (GEORTREESHARE) {
      if (!rtree_share)
	rtree_share= new Dictionary;
      rtreelayers= rtree_share;
    } else
      rtreelayers= new Dictionary;

    quel= new QuelQueries(this);

    SetArea(Long_Min, Long_Max, Lat_Min, Lat_Max, TRUE);
    dialogView= new MapDialogView(this, title, overlay_flag);
}

MapView::~MapView()
{
    if (!GEORTREESHARE) {
      rtreelayers->FreeAll();
      delete rtreelayers;
    }

    SafeDelete(quel); // Must be done before delete dialogView (Observers!)
    SafeDelete(dialogView);
    SafeDelete(failed_rtree);
}

//---- initializing ------------------------------------------------------------

Window *MapView::GetBrowseWin()
{
  LayerDialog *ld= GetDialogView()->GetQueryDialog();
  ld->Show();
  return (Window*) ld->GetWindow();
}


void MapView::SetArea(float Long_Min, float Long_Max,
	float Lat_Min, float Lat_Max, bool init)
{
  MinLong= Long_Min;
  MaxLong= Long_Max;
  MinLat= Lat_Min;
  MaxLat= Lat_Max;
  LatSpan= MaxLat-MinLat;
  LongSpan= MaxLong-MinLong;
  if (!has_cs) {
    MercMinLat= LatToMerc(MinLat);
    MercMaxLat= LatToMerc(MaxLat);
    MercLatSpan= MercMaxLat-MercMinLat;
  }
  NewContent();

#if 0
  if (!init) {
    ForceRedraw();
    UpdateEvent();
  }
#endif
}


void MapView::HourOn()
{
    AllWait(TRUE);
}

void MapView::HourOff()
{
    AllWait(FALSE);
}


#if 1
Command *MapView::DoIdleCommand()
{
  // ((WindowPort*) GetWindow()->GetPortDesc())->MoveMousePos(gPoint0);
  if (blink_count) {
    cur_blink_col= blink_count == 1 ? 0
	: (blink_count & 1) ? blink_col: 30;
    fprintf(stderr, "Blink_count: %d\n", blink_count);
    TopDamaged();
    // ForceRedraw();
#ifdef ET25
#else
    UpdateEvent();
#endif
    blink_count--;
  }
  fprintf(stderr, "Idle\n");
  return View::DoIdleCommand();
}
#endif

//---- drawing -----------------------------------------------------------------


static const float POINT_OVERFLOW= 31000;
static const float POINT_HIGH= 10000;

Point BADPOINT(POINT_OVERFLOW,POINT_OVERFLOW);

const float MaxMercLat=	89.0;

float MapView::CompLatToMerc(float Lat)
{
  return log(tan(deg2rad(Lat)/2 + Pi/4));
}


static float	geo_values[int(MaxMercLat*10 + 1)];
static bool	did_geo_init;


float MapView::LatToMerc(float Lat)
{
  if (!did_geo_init)
    CompGeo();

  int	sign= Lat < 0 ? -1: 1;
  float f10= 10 * Lat * sign;
  int	i= int(f10);
  float	mod= f10 - i;

  float f1= geo_values[i];
  float f2= geo_values[i+1];

  return sign * (f1 + ((f2-f1)*mod));
}
  
void MapView::CompGeo()
{
  if (!did_geo_init) {
    did_geo_init= TRUE;

    double f= 0;
    int	   i= 0;
    for (; f <= MaxMercLat; f+= 0.1, i++)
      geo_values[i]= CompLatToMerc(f);
  }

  xfac= extent.x / LongSpan;
  yfac= extent.y / MercLatSpan;
}


Point MapView::GeoToPoint(float Long, float Lat)
{
  if (has_cs) {
    typedef int (*pif)(double, double, int *, int *,
      double, double, double, double, int, int);
    int rx;
    int ry;

    if (! (*((pif)cs_ts))(Long,Lat,&rx,&ry,
      MinLong,LongSpan,MinLat,LatSpan,extent.x,extent.y))
        return BADPOINT;
    else
        return Point(rx,ry);

  } else {

    if (MinLong > 90 && Long < -90)
	Long+= 360;
    else if (MaxLong < -90 && Long > 90)
	Long-= 360;
    if (Long < MinLong - 10 || Long > MaxLong + 10)
	return BADPOINT;

    float x= (Long-MinLong) * xfac;
    float y;

    if (Lat >= MaxMercLat || Lat <= -MaxMercLat) {
      return BADPOINT;
    } else
      y= extent.y- (LatToMerc(Lat)-MercMinLat) * yfac;

    if (x > POINT_HIGH || x < -POINT_HIGH
     || y > POINT_HIGH || y < -POINT_HIGH)
	return BADPOINT;
  
    return Point(x, y);
  }
}

Point MapView::GeoToPoint(GeoPos &g)
{
  return GeoToPoint(g.Long, g.Lat);
}


float MapView::XToLong(int x)
{
  if (has_cs) {
    typedef int (*pif)(int, int, double *, double *,
      double, double, double, double, int, int);
    double rx;

    (*((pif)cs_fs))(x,0,&rx,0,MinLong,LongSpan,MinLat,LatSpan,
	extent.x,extent.y);
    return rx;

  } else
    return float(x) / extent.x * LongSpan + MinLong;
}

float MapView::YToLat(int yi)
{
  if (has_cs) {
    typedef int (*pif)(int, int, double *, double *,
      double, double, double, double, int, int);
    double ry;

    (*((pif)cs_fs))(0,yi,0,&ry,MinLong,LongSpan,MinLat,LatSpan,
	extent.x,extent.y);
    return ry;

  } else {
    float y;
    y= extent.y-yi;
    y= y / extent.y * MercLatSpan;
    y+= MercMinLat;
    return rad2deg(Pi/2 - 2 * atan(exp(-y)));
  }
}

void MapView::PToGeo(Point p, GeoPos &g)
{
  g.Long= XToLong(p.x);
  g.Lat= YToLat(p.y);
}



#if 0
bool MapView::FetchLayers()
{
  HourOn();

  OrdCollection *layers= GetDialogView()->GetLayerDialog()->GetLayerCol();
  Iter next(layers);
  TextItem *layer;
  bool ret_val= TRUE;
  char *error_msg;

  while (layer= (TextItem *)next()) {
    if (!geo->GetLayerCol(layer, &error_msg)) {
	ret_val= FALSE;
	break;
    }
  }

  HourOff();

  if (!ret_val)
    ShowAlert(eAlertNote, "@B%s@P: %s", layer->AsString(), error_msg);

  return ret_val;
}
#endif


extern "C" double exp10(double);
extern "C" double log10(double);

inline float MapView::GridStep(float r)
{
  extern char *GEONGRID;

  // Max number of visible grid lines:
  const float NrLines= GEONGRID ? atoi(GEONGRID): 7;

  float step= r / NrLines;

  step= exp10(ceil(log10(step)));
  while (r / step < NrLines/2)
  	step/= 2;
  return step;
}

void MapView::DrawLabel(Point pos, char *l)
{
    GrSetTextNormal();
    GrTextMoveto(pos);
    GrSetInk(ePatBlack);
    GrDrawString(l);
}

int NrDecPlaces(float f)
{
  int nrp= 2 - int(log10(f));
  return nrp < 0 ? 0: nrp;
}

void MapView::DrawGrid(Rectangle r)
{
  float latstep= GridStep(LatSpan);
  float lngstep= GridStep(LongSpan);

  float latstart= floor(MinLat/latstep)*latstep;
  float lngstart= floor(MinLong/lngstep)*lngstep;

  GrSetPenNormal();
  char	labelstr[100];
  Point pos;
  
#if 0
  for (; latstart < MaxLat; latstart+= latstep) {
    GrLine(pos= GeoToPoint(MinLong, latstart),
	GeoToPoint(MaxLong, latstart));
    if (GetDialogView()->ShowLabels()) {
      sprintf(labelstr, "%-.*f", NrDecPlaces(LatSpan), latstart);
      DrawLabel(pos-= Point(0,1), labelstr);
    }
  }
  for (; lngstart < MaxLong; lngstart+= lngstep) {
    GrLine(pos= GeoToPoint(lngstart, MinLat),
	GeoToPoint(lngstart, MaxLat));
    if (GetDialogView()->ShowLabels()) {
      sprintf(labelstr, "%-.*f", NrDecPlaces(LongSpan), lngstart);
      DrawLabel(pos+= Point(2,-1), labelstr);
    }
  }
#else
  float lng, lat;

  for (lat= latstart; lat < MaxLat; lat+= latstep) {
    if (GetDialogView()->ShowLabels()) {
      sprintf(labelstr, "%-.*f", NrDecPlaces(LatSpan), lat);
      pos= GeoToPoint(MinLong, lat);
      pos.y-= 1;
      pos.x= 0;
      DrawLabel(pos, labelstr);
    }
    for (lng= lngstart; lng < MaxLong; lng+= lngstep) {
	GrLine(GeoToPoint(lng, lat), GeoToPoint(lng+lngstep, lat));
    }
  }

  for (lng= lngstart; lng < MaxLong; lng+= lngstep) {
    if (GetDialogView()->ShowLabels()) {
      sprintf(labelstr, "%-.*f", NrDecPlaces(LongSpan), lng);
      pos= GeoToPoint(lng, MinLat);
      pos.x+= 2;
      pos.y+= -1;
      DrawLabel(pos, labelstr);
    }
    for (lat= latstart; lat < MaxLat; lat+= latstep) {
	GrLine(GeoToPoint(lng, lat), GeoToPoint(lng, lat+latstep));
    }
  }
#endif
}

void MapView::DisplayQuery(Rectangle r, Collection *cp)
{
    Iter next(cp);
    QueryShape *s;
	    
    while (s= (QueryShape*) next()) {
	  if (s->GetBBox().Intersects(r))
	    s->Draw();
    }
}


bool MapView::FindPoint(Point sp, Point *pt, GeoPos *gp)
{
    extern int	GEOMARGIN;

    Collection	*c= GetQueryCol();
    Iter	next(c->MakeIterator());
    TextItem	*layer;
    Point	margin(GEOMARGIN ? GEOMARGIN: 5);

    while (layer= (TextItem *)next()) {

	if (! quel->HasPline(layer))
	  continue;

	ObjList	*qshapes= quel->GetQueryCol(layer, FALSE, FALSE, FALSE);
	
	if (!qshapes || !qshapes->Size())
	  continue;

	PlineShape *ps;
	Iter nexts(qshapes);

	while (ps= (PlineShape*) nexts()) {
	  if (! ps->NrPoints())
		continue;
	  if (Abs(sp - ps->GetPoint(0)) <= margin) {
	    *pt= ps->GetPoint(0);
	    float lng, lat;
	    ps->GetStart(&lng, &lat);
	    *gp= GeoPos(0, lng, lat);
	    return TRUE;
	  } else if (Abs(sp - ps->GetPoint(ps->NrPoints()-1)) <= margin) {
	    *pt= ps->GetPoint(ps->NrPoints()-1);
	    float lng, lat;
	    ps->GetEnd(&lng, &lat);
	    *gp= GeoPos(0, lng, lat);
	    return TRUE;
	  }
	}
    }

    return FALSE;
}


void MapView::DrawQueries(Rectangle r)
{
    Collection *c= GetQueryCol();
    Iter next(c->MakeIterator());
    TextItem *layer;

    while (layer= (TextItem *)next()) {
	ObjList	*qshapes= quel->GetQueryCol(layer,
		AreaIsChanged(), do_query_refresh || AreaIsChanged(),
		do_display_refresh);
	
	if (qshapes && qshapes != showcol)
	  DisplayQuery(r, qshapes);
    }
    // DrawAllLabels();
    do_display_refresh= do_query_refresh= FALSE;
}

void MapView::BlinkQuery(TextItem *layer)
{
#if 0
  q_blink_layer= layer;
  q_blink_col= 1;
  ForceRedraw();
  UpdateEvent();
  q_blink_col= 2;
  ForceRedraw();
  UpdateEvent();
  q_blink_col= 0;
  q_blink_layer= 0;
  ForceRedraw();
  UpdateEvent();
#else
  WindowPort *wp;
  Point offset;
  Rectangle oldcliprect;

  GrSetPort(wp= (WindowPort*) GetWindow()->GetPortDesc());
  offset= wp->GetOrigin();
  oldcliprect= wp->GetCliprect();

  q_blink_layer= layer;
  q_blink_col= 1;
#ifdef ET25
  GrSetClip(ContentRect()+GetContainer()->ContainerPoint(offset));
#else
  GrSetClip(ContentRect()+GetContainer()->ContainerPoint(offset), GetContainer()->ContainerPoint(offset));
#endif
  Draw(extent);
  q_blink_col= NrInks()-1;
#ifdef ET25
  GrSetClip(ContentRect()+GetContainer()->ContainerPoint(offset));
#else
  GrSetClip(ContentRect()+GetContainer()->ContainerPoint(offset), GetContainer()->ContainerPoint(offset));
#endif
  Draw(extent);
#ifdef ET25
  GrSetClip(oldcliprect);
#else
  GrSetClip(oldcliprect, offset);
#endif
  q_blink_col= 0;
  q_blink_layer= 0;
#endif
}

void MapView::DrawBlinkQuery(Rectangle r, TextItem *layer, int color)
{
  ObjList	*qshapes= quel->GetQueryCol(layer, FALSE, FALSE, FALSE);
  Iter		next(qshapes);
  QueryShape	*s;

  while (s= (QueryShape*) next())
      s->DrawCol(color);
}


void MapView::Draw(Rectangle r)
{
    if (Loading())
	return;

    if (q_blink_layer) {
      HourOn();
      DrawBlinkQuery(r, q_blink_layer, q_blink_col);
      DrawAllLabels();
      HourOff();
      return;
    }

    Point	extent(GetContainer()->GetExtent());
    bool	extent_changed= FALSE;
    extern bool GEOCACHE;

    if (extent != this->extent || this->extent == gPoint0) {
	this->extent= extent;	// Also initialise for first call
	new_content= extent_changed= TRUE;
	// SetMapExtent(extent);
	SetExtent(extent); // ??? Why is this needed
	SetShowCol(0);
	CompGeo();
    } else
	new_content= FALSE;

    HourOn();
    
    if (extent_changed && GEOCACHE)
	cache.Invalidate();

    if (!GEOCACHE || cache.Open(r)) {
	OrdCollection *layers= GetDialogView()->GetLayerDialog()->GetLayerCol();
	Iter next(layers);
	TextItem *layer;

	while (layer= (TextItem *)next()) {
	    char	*s= layer->AsString();
	    char	*p;

	    if ((p= index(s, '.')) && strncmp(p+1, "rtree", 5) == 0) {
		DrawRtree(layer, r, extent_changed);
	    } else
		fprintf(stderr, "Unknown layer extension in: %s\n", s);
		// DrawMemMap(layer, r, extent_changed);
	}

	if (GEOCACHE)
	  cache.Close();
    }
    
#if 1
    if (blink_count)
	fprintf(stderr, "Blinking\n");
#endif

    DrawQueries(r);
    if (showcol)
	DisplayQuery(r, showcol);	
    DrawAllLabels();

    if (GetDialogView()->ShowGrid())
      DrawGrid(r);

    HourOff();
}



#include "rtree.h"

extern "C" { int munmap(char *mp, long datasize); }

class RTreeFile: public Object {
public:
	MetaDef(RTreeFile);

	RTreeFile(char *p, int f, long l, r_tree *tp)
		{ mp= p; datasize= l; t= tp; fd= f; }
	~RTreeFile();

	long	datasize;
	char	*mp;
	r_tree	*t;
	int	fd;
};

MetaImpl0(RTreeFile);

RTreeFile::~RTreeFile()
{
#if 0
fprintf(stderr, "Deleting RTreeFile\n");
#endif
  if (munmap(mp, datasize) == -1)
	perror("munmap");
  if (close(fd) == -1)
	perror("close");
  delete t;
}


char *MapView::GetQBbox()
{
  static char s[100];
  extern char *GEOBBOX;

  sprintf(s, "\"(%f,%f,%f,%f)\"::%s",
    GetLongMin(),
    GetLatMin(),
    GetLongMax(),
    GetLatMax(),
    GEOBBOX ? GEOBBOX: "box");

  return s;
}


void MapView::DrawRtree(TextItem *layer, Rectangle r, bool extent_changed)
{
  char		*mp;
  RTreeFile 	*rf;
  long		datasize;
  char		fn[1000];
  r_tree	*t;
  char		ls[1000];
  char		*lp;

  strcpy(ls, layer->AsString());
  if (lp= index(ls, '.'))
    *lp= '\0';
  TextItem *the_layer= (TextItem*) layer->DeepClone();
  the_layer->SetString(ls);

  if (!(rf= (RTreeFile *) rtreelayers->AtKey(the_layer))) {
#if 0
fprintf(stderr, "Adding %s as rtree\n", layer->AsString());
#endif
	if (!failed_rtree)
	    failed_rtree= new Set;
	else if (failed_rtree->Contains(the_layer)) {
	  delete the_layer;
	  return;
	}

	int	fdata;

	strcpy(fn, layer->AsString());
	strcpy(index(fn, '.')+1, "bin");
        if ((fdata = open(fn, O_RDONLY)) == -1) {
#if 0
	  ShowAlert(eAlertNote, 
	      "@BDraw RTree@P: Map data file: '@B%s@P' can't be opened.", fn);
#else
	  perror(fn);
	  fprintf(stderr, "Draw RTree: Map data file:\n");
#endif
	  failed_rtree->Add(the_layer);
	  return;
	}

#if 0
fprintf(stderr, "Fileno: %d\n", fdata);
#endif

	datasize=lseek(fdata,0L,L_XTND);
	if ((mp = mmap(0,datasize, PROT_READ, MAP_SHARED, fdata, 0))
           == (char *)-1) {
	    fprintf(stderr, "Map data file can't be mapped.\n");
	    abort();
	}

	strcpy(fn, layer->AsString());
	index(fn, '.')[0]= '\0';
	if (!(t= new r_tree(fn))) {
	    cerr << "Out of memory!\n";
	    abort();
	}

	rf= new RTreeFile(mp,fdata,datasize,t);
#ifdef ET25
	rtreelayers->PutAtKey(rf, the_layer);
#else
	rtreelayers->AtKeyPut(the_layer, rf);
#endif
  } else {
#if 0
fprintf(stderr, "Found %s as rtree\n", layer->AsString());
#endif
	mp= rf->mp;
	datasize= rf->datasize;
	t= rf->t;
	delete the_layer;
  }

  const int	MaxPoints= 10000;
  bool		polyline= FALSE;
  Ink		*col= ePatBlack;
  int		pen_w= 1;
  bool		fl= *(short*)mp != -9999;

  char		pts_data[MaxPoints * sizeof(Point)];
  Point		*pts= (Point*) pts_data;
  point		vis_area[2];
  char		*cp= index(index(layer->AsString(), '.')+1, '.');

  if (cp) {
	char *width_p= index(cp+1, '.');

	if (width_p)
	  pen_w= atoi(width_p+1);
	polyline= cp[1] == 'l';
	col= GetInk(atoi(cp+2));
  }


#if 1
  vis_area[0][0]= XToLong(r.origin.x);
  vis_area[0][1]= YToLat(r.origin.y+r.extent.y);
  vis_area[1][0]= XToLong(r.origin.x+r.extent.x);
  vis_area[1][1]= YToLat(r.origin.y);
#else
  vis_area[0][0]= GetLongMin();
  vis_area[0][1]= GetLatMin();
  vis_area[1][0]= GetLongMax();
  vis_area[1][1]= GetLatMax();
#endif

  GrSetPenNormal();
  GrSetPenSize(pen_w);

  for (int offset= t->First(vis_area); offset != NULL_ID; offset= t->Next()) {
    short	*p= (short*) (mp + offset);

    p++;
    if (fl)
	p++;

    int		npoints= fl ? *(long*) p: *p;

    if (npoints >= MaxPoints) {
	cerr << "Too many points!";
	return;
    }
    p++;
    if (fl)
	p++;

    Point	lastpoint, newpoint;
    int		np= 0;
    for (int i= 0; i < npoints; i++) {
	float	Long, Lat;

	if (fl) {
	  Lat= (*(float*)p); p++; p++;
	  Long= (*(float*)p); p++; p++;
	} else {
	  Lat= (*p++)/float(100);
	  Long= (*p++)/float(100);
	}
	newpoint= GeoToPoint(Long, Lat);
	if (newpoint == BADPOINT) {
	  np= 1;
	  break;
	}

	if (i == 0 || newpoint != lastpoint) {
		lastpoint= pts[np]= newpoint;
		np++;
	}
    }
    if (np == 1)
	continue;

    if (!polyline) {
	pts[np]= pts[0];
    }

    if (polyline) {
      GrSetPenInk(col);
      GrStrokePolygon(0, pts, np, int(ePolyDefault));
    } else {
      GrSetInk(col);
      GrFillPolygon(0, pts, np+1, ePolyDefault);
    }
  }
}


//---- shape list management ---------------------------------------------------
    
void MapView::Add(TextItem *l)
{
  GetDialogView()->GetLayerDialog()->Add(l);
}


#if 0
void MapView::Add(PolyShape *p, int polyline, int col, char *name, bool newl)
{
abort();
}
#endif


void MapView::LoadDone()
{
  GetDialogView()->GetLayerDialog()->Show();
  if (GetDialogView()->GetLayerDialog()->OrderChanged()) {
    NewContent();
    GetWindow()->ForceRedraw();
#ifdef ET25
#else
    UpdateEvent();
#endif
  }
}


void MapView::SetCoordSys(char *s, pvf ts, pvf fs, pvf constrain, pvf t)
{
  dialogView->Update(); // Create textitems
  dialogView->SetCoordSys(s ? s: "Merc. Lat/Long");
  has_cs= s != 0;
  cs_ts= ts;
  cs_fs= fs;
  cs_constrain= constrain;
  cs_t= t;
}

void MapView::ShowPos(Point p)
{
  if (has_cs) {
    typedef void (*pif)(double, double, char *, char *);
    char strx[1000], stry[1000];
    
    (*((pif)cs_t))(XToLong(p.x), YToLat(p.y), strx, stry);
    // We have to reverse x and y because Long is displayed
    // above Lat and we want X above Y.
    dialogView->SetLong(stry);
    dialogView->SetLat(strx);

  } else {

    dialogView->SetLong(form("Long: %8.4f", XToLong(p.x)));
    dialogView->SetLat(form("Lat:  %8.4f", YToLat(p.y)));
  }
}

Command *MapView::DoOtherEventCommand(Point p, Token t)
{
#if 0
    if (t.Code == eEvtIdle)
	fprintf(stderr, "Idle OE\n");
#endif
    if (t.Code == eEvtLocMove || t.Code == eEvtIdle)
	ShowPos(p);
    // fprintf(stderr, "OE");
    return gNoChanges;
}

void MapView::DoZoomOut()
{
    float newlatmin = GetLatMin()-GetLatSpan()/2;
    float newlatmax= GetLatMax()+GetLatSpan()/2;

    if (!has_cs)
      Check_Poles(newlatmax, newlatmin);

    SetArea(GetLongMin()-GetLongSpan()/2, GetLongMax()+GetLongSpan()/2,
      newlatmin, newlatmax);
    NewContent();
    ForceRedraw();
#ifdef ET25
#else
    UpdateEvent();
#endif
}

#if 1
Command *MapView::DoMiddleButtonDownCommand(Point p, Token t, int)
{
    return gNoChanges;
}
#endif

MetaImpl0(OverView);

OverView::OverView(Document *dp, Point extent, ObjList *sl, MapView *m,
	float Long_Min, float Long_Max,
	float Lat_Min, float Lat_Max, byte *title)
		: (dp, extent, sl, Long_Min, Long_Max, Lat_Min, Lat_Max,
		   title, FALSE)
{
  mapview= m;
}

Rectangle OverView::GeoToRect(float area_long_min, float area_long_max,
  float area_lat_min, float area_lat_max)
{
  return NormRect(GeoToPoint(area_long_min, area_lat_max),
  	GeoToPoint(area_long_max, area_lat_min));
}

void OverView::Draw(Rectangle r)
{
  if (Loading())
	return;
  
  if (q_blink_layer) {
    MapView::Draw(r);
    return;
  }

  HourOn();

  MapView::Draw(r);
  if (area_long_min != area_long_max) {
      Rectangle r= GeoToRect(area_long_min, area_long_max,
	area_lat_min, area_lat_max);
      GrSetPenNormal();
      GrSetPenInk(GetInk(2));
      GrStrokeRect(r);
      r.origin-= gPoint2;
      r.extent+= gPoint4;
      GrSetPenInk(GetInk(1));
      GrStrokeRect(r);
  }

  HourOff();
}


bool MapView::IsGrabbing()
{
  extern WindowPort *map_grab_port;

  return map_grab_port != 0;
}


static Rectangle ext_rect(Rectangle r)
{
  r.origin-= gPoint3;
  r.extent+= Point(6,6);
  return r;
}


void OverView::ShowArea(Rectangle newr,
	float Long_Min, float Long_Max,
	float Lat_Min, float Lat_Max)
{
#if 1
  InvalidateRect(ext_rect(
      GeoToRect(area_long_min, area_long_max, area_lat_min, area_lat_max)));

  if (newr == gRect0)
	newr= GeoToRect(Long_Min, Long_Max, Lat_Min, Lat_Max);

  InvalidateRect(ext_rect(newr));
#else
  InvalidateRect(ext_rect(
      GeoToRect(area_long_min, area_long_max, area_lat_min, area_lat_min)));
  InvalidateRect(ext_rect(
      GeoToRect(area_long_min, area_long_max, area_lat_max, area_lat_max)));
  InvalidateRect(ext_rect(
      GeoToRect(area_long_min, area_long_min, area_lat_min, area_lat_max)));
  InvalidateRect(ext_rect(
      GeoToRect(area_long_max, area_long_max, area_lat_min, area_lat_max)));

  if (newr == gRect0)
	newr= GeoToRect(Long_Min, Long_Max, Lat_Min, Lat_Max);

  InvalidateRect(ext_rect(newr));
#endif
  area_long_min= Long_Min;
  area_long_max= Long_Max;
  area_lat_min= Lat_Min;
  area_lat_max= Lat_Max;
}


Command *OverView::DoMiddleButtonDownCommand(Point p, Token t, int)
{
    // a NOP command so Doc will ask for save:
    GetDocument()->PerformCommand(new Command(12345));
    return new AreaSelector(this, mapview);
}


#ifdef ET25
OStream& MapView::Save(OStream& to)
#else
ostream& MapView::Save(ostream& to)
#endif
{
    to  << "\n" << MinLong SP << MaxLong SP << MinLat SP << MaxLat SP
    << MercMinLat SP << MercMaxLat SP << MercLatSpan SP << "\n";
    GetDialogView()->Save(to);
    return(to);
}

#ifdef ET25
IStream& MapView::Load(IStream& from)
#else
istream& MapView::Load(istream& from)
#endif
{
    from >> MinLong >> MaxLong >> MinLat >> MaxLat
      >> MercMinLat >> MercMaxLat >> MercLatSpan;
    LatSpan= MaxLat-MinLat;
    LongSpan= MaxLong-MinLong;
    GetDialogView()->Load(from);
    if (!GEORTREESHARE) {
      rtreelayers->FreeAll();
    }
    return from;
}

#ifdef ET25
OStream& OverView::Save(OStream& to)
#else
ostream& OverView::Save(ostream& to)
#endif
{
    MapView::Save(to);
    to  << "\n" << area_long_min SP << area_long_max SP
       << area_lat_min SP << area_lat_max << "\n";
    return(to);
}

#ifdef ET25
IStream& OverView::Load(IStream& from)
#else
istream& OverView::Load(istream& from)
#endif
{
    MapView::Load(from);
    from >> area_long_min >> area_long_max >> area_lat_min >> area_lat_max;
    return from;
}



GeoPos::GeoPos()
{}

GeoPos::GeoPos(Shape *s)
{ withshape= s; }

GeoPos::GeoPos(Shape *s, Point p)
{
  withshape =s;
  MapView *mv= (MapView*) withshape->GetView();
  if (!mv)
	return;
  Long= mv->XToLong(p.x);
  Lat= mv->YToLat(p.y);
}

GeoPos::GeoPos(Shape *s, float lng, float lat)
{
  withshape =s;
  Long= lng;
  Lat= lat;
}

GeoPos::operator Point()
{ return ((MapView*)withshape->GetView())->GeoToPoint(*this); }

GeoPos &GeoPos::operator= (Point p)
{
  MapView *mv= (MapView*) withshape->GetView();
  Long= mv->XToLong(p.x);
  Lat= mv->YToLat(p.y);
  return *this;
}

void GeoPos::moveto(Point p)
{
  MapView *mv= (MapView*) withshape->GetView();
  Long= mv->XToLong(p.x);
  Lat= mv->YToLat(p.y);
}

#ifdef ET25
OStream &GeoPos::Save(OStream &s)
#else
ostream &GeoPos::Save(ostream &s)
#endif
{
  return s << withshape << Long SP << Lat << "\n";
}

#ifdef ET25
IStream &GeoPos::Load(IStream &s)
#else
istream &GeoPos::Load(istream &s)
#endif
{
  return s >> withshape >> Long >> Lat;
}
