(* Ulm's Oberon Library
   Copyright (C) 1989-2004 by University of Ulm, SAI, D-89069 Ulm, Germany
   ----------------------------------------------------------------------------
   Ulm's Oberon Library is free software; you can redistribute it
   and/or modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either version
   2 of the License, or (at your option) any later version.

   Ulm's Oberon Library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   ----------------------------------------------------------------------------
   E-mail contact: oberon@mathematik.uni-ulm.de
   ----------------------------------------------------------------------------
   $Id: Plotters.om,v 1.1 2004/04/08 12:30:29 borchert Exp borchert $
   ----------------------------------------------------------------------------
   $Log: Plotters.om,v $
   Revision 1.1  2004/04/08 12:30:29  borchert
   Initial revision

   ----------------------------------------------------------------------------
*)

MODULE ulmPlotters;

   IMPORT Events := ulmEvents, Objects := ulmObjects, Resources := ulmResources, Services := ulmServices, SYS := ulmSYSTEM;

   TYPE
      Plotter* = POINTER TO PlotterRec;

   CONST
      solid* = 0;
      dotted* = 1;
      dotdashed* = 2;
      shortdashed* = 3;
      longdashed* = 4;
      lineModes* = 5;
   TYPE
      LineMode* = SHORTINT; (* solid ... *)

   CONST
      setspace* = 0;
      erase* = 1;
      string* = 2;
      linemodes* = 3;
      linewidth* = 4;
   TYPE
      CapabilitySet* = SET; (* OF setspace, erase ... *)

   TYPE
      Description* = POINTER TO DescriptionRec;
      DescriptionRec* =
	 RECORD
	    (Objects.ObjectRec)
	    xmin*, ymin, xmax, ymax: INTEGER; (* maximal supported range *)
	 END;

   TYPE
      GetSpaceProc* = PROCEDURE (
	 plotter: Plotter;
	 VAR xmin, ymin, xmax, ymax: INTEGER);
      SetSpaceProc* = PROCEDURE (
	 plotter: Plotter;
	 xmin, ymin, xmax, ymax: INTEGER);
      EraseProc* = PROCEDURE (plotter: Plotter);
      MoveProc* = PROCEDURE (plotter: Plotter; xto, yto: INTEGER);
      LineProc* = PROCEDURE (plotter: Plotter; xfrom, yfrom, xto, yto: INTEGER);
      ArcProc* = PROCEDURE (
	 plotter: Plotter;
	 xcenter, ycenter, xstart, ystart, xend, yend: INTEGER);
      CircleProc* = PROCEDURE (
         plotter: Plotter; xcenter, ycenter, radius: INTEGER);
      StringProc* = PROCEDURE (plotter: Plotter; str: ARRAY OF CHAR);
      SetLineModeProc* = PROCEDURE (plotter: Plotter; mode: LineMode);
      SetLineWidthProc* = PROCEDURE (plotter: Plotter; width: INTEGER);
      CloseProc* = PROCEDURE (plotter: Plotter);
   TYPE
      Interface* = POINTER TO InterfaceRec;
      InterfaceRec* =
	 RECORD
	    (Objects.ObjectRec)
	    setSpace*: SetSpaceProc;
	    erase*: EraseProc;
	    move*: MoveProc;
	    cont*: MoveProc;
	    point*: MoveProc;
	    line*: LineProc;
	    arc*: ArcProc;
	    circle*: CircleProc;
	    string*: StringProc;
	    setLineMode*: SetLineModeProc;
	    setLineWidth*: SetLineWidthProc;
	    close*: CloseProc;
	 END;

   TYPE
      PlotterRec* =
	 RECORD
	    (Services.ObjectRec)
	    if: Interface;
	    caps: CapabilitySet;
	    desc: Description;
	    xmin, ymin, xmax, ymax: INTEGER; (* current range *)
	    terminated: BOOLEAN;
	 END;
   VAR
      plotterType: Services.Type;

   PROCEDURE InitModule;
   BEGIN
      Services.CreateType(plotterType, "Plotters.Plotter", "");
   END InitModule;

   PROCEDURE ^ Close*(plotter: Plotter);

   PROCEDURE TerminationHandler(event: Events.Event);
      VAR
	 plotter: Plotter;
   BEGIN
      WITH event: Resources.Event DO
	 IF event.change IN {Resources.terminated, Resources.unreferenced} THEN
	    Close(event.resource(Plotter));
	 END;
      END;
   END TerminationHandler;

   PROCEDURE Init*(plotter: Plotter; if: Interface;
		  caps: CapabilitySet; desc: Description);
      VAR
	 eventType: Events.EventType;
   BEGIN
      ASSERT((if # NIL) & (if.move # NIL) & (if.cont # NIL) &
	 (if.point # NIL) & (if.line # NIL) & (if.arc # NIL) &
	 (if.circle # NIL));
      ASSERT(~(setspace IN caps) OR (if.setSpace # NIL));
      ASSERT(~(erase IN caps) OR (if.erase # NIL));
      ASSERT(~(string IN caps) OR (if.string # NIL));
      ASSERT(~(linemodes IN caps) OR (if.setLineMode # NIL));
      ASSERT(~(linewidth IN caps) OR (if.setLineWidth # NIL));
      ASSERT((desc.xmin < desc.xmax) & (desc.ymin < desc.ymax));
      plotter.if := if;
      plotter.caps := caps;
      plotter.desc := desc;
      plotter.xmin := desc.xmin;
      plotter.xmax := desc.xmax;
      plotter.ymin := desc.ymin;
      plotter.ymax := desc.ymax;
      plotter.terminated := FALSE;
      Resources.TakeInterest(plotter, eventType);
      Events.Handler(eventType, TerminationHandler);
   END Init;

   PROCEDURE GetCapabilities*(plotter: Plotter) : CapabilitySet;
   BEGIN
      RETURN plotter.caps
   END GetCapabilities;

   PROCEDURE GetSpace*(plotter: Plotter;
                      VAR xmin, ymin,
		      xmax, ymax: INTEGER);
   BEGIN
      xmin := plotter.xmin;
      xmax := plotter.xmax;
      ymin := plotter.ymin;
      ymax := plotter.ymax;
   END GetSpace;

   PROCEDURE GetMaxSpace*(plotter: Plotter;
                         VAR xmin, ymin,
		         xmax, ymax: INTEGER);
   BEGIN
      xmin := plotter.desc.xmin;
      xmax := plotter.desc.xmax;
      ymin := plotter.desc.ymin;
      ymax := plotter.desc.ymax;
   END GetMaxSpace;

   PROCEDURE SetSpace*(plotter: Plotter;
                      xmin, ymin,
		      xmax, ymax: INTEGER);
   BEGIN
      ASSERT((xmin < xmax) & (ymin < ymax));
      ASSERT((xmin >= plotter.desc.xmin) &
             (xmax <= plotter.desc.xmax) &
	     (ymin >= plotter.desc.ymin) &
	     (ymax <= plotter.desc.ymax));
      ASSERT(setspace IN plotter.caps);
      plotter.if.setSpace(plotter, xmin, ymin, xmax, ymax);
      plotter.xmin := xmin;
      plotter.ymin := ymin;
      plotter.xmax := xmax;
      plotter.ymax := ymax;
   END SetSpace;
		     
   PROCEDURE Erase*(plotter: Plotter);
   BEGIN
      ASSERT(erase IN plotter.caps);
      plotter.if.erase(plotter);
   END Erase;

   PROCEDURE Move*(plotter: Plotter; xto, yto: INTEGER);
   BEGIN
      plotter.if.move(plotter, xto, yto);
   END Move;

   PROCEDURE Cont*(plotter: Plotter; xto, yto: INTEGER);
   BEGIN
      plotter.if.cont(plotter, xto, yto);
   END Cont;

   PROCEDURE Point*(plotter: Plotter; xpoint, ypoint: INTEGER);
   BEGIN
      plotter.if.point(plotter, xpoint, ypoint);
   END Point;

   PROCEDURE Line*(plotter: Plotter; xfrom, yfrom, xto, yto: INTEGER);
   BEGIN
      plotter.if.line(plotter, xfrom, yfrom, xto, yto);
   END Line;

   PROCEDURE Arc*(plotter: Plotter;
                 xcenter, ycenter, xstart, ystart, xend, yend: INTEGER);
   BEGIN
      plotter.if.arc(plotter, xcenter, ycenter, xstart, ystart, xend, yend);
   END Arc;

   PROCEDURE Circle*(plotter: Plotter; xcenter, ycenter, radius: INTEGER);
   BEGIN
      plotter.if.circle(plotter, xcenter, ycenter, radius);
   END Circle;

   PROCEDURE String*(plotter: Plotter; str: ARRAY OF CHAR);
   BEGIN
      ASSERT(string IN plotter.caps);
      plotter.if.string(plotter, str);
   END String;

   PROCEDURE SetLineMode*(plotter: Plotter; mode: LineMode);
   BEGIN
      ASSERT((linemodes IN plotter.caps) & (mode >= 0) & (mode < lineModes));
      plotter.if.setLineMode(plotter, mode);
   END SetLineMode;

   PROCEDURE SetLineWidth*(plotter: Plotter; width: INTEGER);
   BEGIN
      ASSERT((linewidth IN plotter.caps) & (width > 0));
      plotter.if.setLineWidth(plotter, width);
   END SetLineWidth;

   PROCEDURE Close*(plotter: Plotter);
   BEGIN
      IF ~SYS.TAS(plotter.terminated) THEN
	 IF plotter.if.close # NIL THEN
	    plotter.if.close(plotter);
	 END;
	 Resources.Notify(plotter, Resources.terminated);
	 plotter.if := NIL; plotter.desc := NIL;
      END;
   END Close;

BEGIN
   InitModule;
END ulmPlotters.
