text: /********************************************************************
   *
   * Copyright (C) 1996 Blue Marsh Softworks -- All Rights Reserved.
   *
   * PROJECT:      COMP 413 Software Engineering Project
   * MODULE:       Document Methods
   * FILE:         document.goc
   *
   * AUTHOR:       Nathan Fiedler
   *
   * REVISION HISTORY:
   *      Name     Date      Description
   *      ----     ----      -----------
   *       NF      03/07/96  Initial version
   *
   * DESCRIPTION:
   *      These are the function definitions and method definitions
   *      for the document object.
   *
   *******************************************************************/
  
  /********************************************************************
   *                 Headers
   *******************************************************************/
      @include <stdapp.goh>
      #include "graph.h"             /* Ken's header file */
      @include "app.goh"             /* Our classes and constants. */
      @include "global.goh"          /* Must be after app.goh. Includes
                                      * globals, objects, prototypes. */
      #include <string.h>            /* For strlen */
      #include <ctype.h>             /* For toupper */
  
  /********************************************************************
   *                 Function Definitions
   *******************************************************************/
  
  /********************************************************************
   *                 Code for GDocumentClass
   *******************************************************************/
  
  /********************************************************************
   *       MSG_GEN_DOCUMENT_INITIALIZE_DOCUMENT_FILE
   ********************************************************************
   * SYNOPSIS:     This creates an empty document.
   * PARAMETERS:   none
   * RETURNS:      Boolean - TRUE if error occurred
   * STRATEGY:     Initialize some variables and instance data,
   *               then initialize the main graph. End by calling
   *               the superclass.
   * ERRORS:       Nothing to check.
   * REVISION HISTORY:
   *      Name     Date      Description
   *      ----     ----      -----------
   *       NF      03/07/96  Initial version
   *******************************************************************/
  @extern method GDocumentClass, MSG_GEN_DOCUMENT_INITIALIZE_DOCUMENT_FILE {
  
       /*
        * Initialize some global variables.
        * Initialize the adjacency matrix.
        */
    fileHan_g = NullHandle;
    winHan_g = NullHandle;
    verticesSelected_g.VNL_count = 0;
    AMInitializeGraph();
  
    return( @callsuper() );
  } /* MSG_GEN_DOCUMENT_INITIALIZE_DOCUMENT_FILE */
  
  /********************************************************************
   *       MSG_GEN_DOCUMENT_ATTACH_UI_TO_DOCUMENT
   ********************************************************************
   * SYNOPSIS:     This message is sent to the GenDocument object
   *               when a particular document is about to be displayed.
   *               It allows the document to attach any UI objects, or
   *               objects that should be displayed, to the GenDocument
   *               so that the first visual update will draw everything.
   * PARAMETERS:   none
   * RETURNS:      nothing
   * STRATEGY:     Call the superclass, then tell primary to update
   *               its scrollers. See what the extension is on the
   *               file name and call the appropriate routine based
   *               on that.
   * ERRORS:       Nothing to check.
   * REVISION HISTORY:
   *      Name     Date      Description
   *      ----     ----      -----------
   *       NF      03/07/96  Initial version
   *******************************************************************/
  @extern method GDocumentClass, MSG_GEN_DOCUMENT_ATTACH_UI_TO_DOCUMENT {
       /* Pointer to visual instance data. */
    VisInstance * vself;
  
       /*
        * Read in the graph from the file by calling
        * one of Ken's functions. Use the extension to
        * know which function to call.
        */
    pself = ObjDerefGen( oself );
    fileHan_g = pself->GDI_fileHandle;
    pself->startingNode = VERTEX_INVALID_NUM;
    pself->endingNode = VERTEX_INVALID_NUM;
    verticesSelected_g.VNL_count = 0;
  
    @callsuper();
  
       /*
        * Set GenView document bounds.
        */
    pself = ObjDerefGen( oself );
    @send pself->GCI_genView::MSG_GEN_VIEW_SET_DOC_BOUNDS( 1000, 1000, 0, 0 );
  
       /*
        * Set the VI_bounds of the document.
        */
    vself = ObjDerefVis( oself );
    vself->VI_bounds.R_left = 0;
    vself->VI_bounds.R_top = 0;
    vself->VI_bounds.R_right = 1000;
    vself->VI_bounds.R_bottom = 1000;
  
       /*
        * Read in the file.
        */
    FilePos( pself->GDI_fileHandle, 0L, FILE_POS_START );
    if ( toupper(
             pself->GDI_fileName[
                 strlen( pself->GDI_fileName )-1] ) == 'M' ) {
      readAdjMatrix( pself->GDI_fileHandle );
    }
    else {
      readAdjList( pself->GDI_fileHandle );
    }
  
       /*
        * Update scrollers for the OmniGo.
        */
    @send GPrimary::MSG_VIS_UPDATE_WIN_GROUP( VUM_DELAYED_VIA_UI_QUEUE );
  } /* MSG_GEN_DOCUMENT_ATTACH_UI_TO_DOCUMENT */
  
  /********************************************************************
   *       MSG_GEN_DOCUMENT_DETACH_UI_FROM_DOCUMENT
   ********************************************************************
   * SYNOPSIS:     This message is sent to the GenDocument object when
   *               the document is about to be closed.  It allows the
   *               GenDocument to detach all the UI objects it attached
   *               earlier before closing.
   * PARAMETERS:   none
   * RETURNS:      nothing
   * STRATEGY:     Clear a couple of variables and then call the
   *               superclass for default behavior.
   * ERRORS:       Nothing to check.
   * REVISION HISTORY:
   *      Name     Date      Description
   *      ----     ----      -----------
   *       NF      03/07/96  Initial version
   *******************************************************************/
  @extern method GDocumentClass, MSG_GEN_DOCUMENT_DETACH_UI_FROM_DOCUMENT {
  
    pself = ObjDerefGen( oself );
    pself->startingNode = VERTEX_INVALID_NUM;
    pself->endingNode = VERTEX_INVALID_NUM;
    verticesSelected_g.VNL_count = 0;
  
    @callsuper();
  } /* MSG_GEN_DOCUMENT_DETACH_UI_FROM_DOCUMENT */
  
  /********************************************************************
   *                 MSG_GEN_DOCUMENT_OPEN
   ********************************************************************
   * SYNOPSIS:     This message is sent to the GenDocument object when
   *               the document is being opened.  It allows the
   *               GenDocument to load the document from disk.
   * PARAMETERS:   ( word *                 fileOrError,
   *                 DocumentCommonParams * params )
   * RETURNS:      Boolean - TRUE if error occurred
   * STRATEGY:     Make sure nothing went wrong with open.
   *******************************************************************/
  @extern method GDocumentClass, MSG_GEN_DOCUMENT_OPEN {
  
       /*
        * Check if error occurred during open.
        * If so, cancel the open. Return error status.
        * (Don't try to do MSG_GEN_DESTROY, that gives an error.)
        */
    if ( @callsuper() ) {
      @send self::MSG_GEN_REMOVE( VUM_NOW, 0 );
      return( TRUE );
    }
    else {
      return( FALSE );
    }
  } /* MSG_GEN_DOCUMENT_OPEN */
  
  /********************************************************************
   *                 MSG_GEN_DOCUMENT_PHYSICAL_REVERT
   ********************************************************************
   * SYNOPSIS:     Return to the last-saved version of the file.
   * PARAMETERS:   word *error
   * RETURNS:      Boolean - TRUE if error occurred
   * STRATEGY:     Call superclass.
   * ERRORS:       Nothing to check.
   * REVISION HISTORY:
   *      Name     Date      Description
   *      ----     ----      -----------
   *       NF      03/07/96  Initial version
   *******************************************************************/
  @extern method GDocumentClass, MSG_GEN_DOCUMENT_PHYSICAL_REVERT {
  
       /*
        * Read in the graph from the file by calling
        * one of Ken's functions. Use the extension to
        * know which function to call.
        */
    pself = ObjDerefGen( oself );
    FilePos( pself->GDI_fileHandle, 0L, FILE_POS_START );
    if ( toupper(
             pself->GDI_fileName[
                 strlen( pself->GDI_fileName )-1] ) == 'M' ) {
      readAdjMatrix( pself->GDI_fileHandle );
    }
    else {
      readAdjList( pself->GDI_fileHandle );
    }
    pself->startingNode = VERTEX_INVALID_NUM;
    pself->endingNode = VERTEX_INVALID_NUM;
    verticesSelected_g.VNL_count = 0;
  
    return( @callsuper() );
  } /* MSG_GEN_DOCUMENT_PHYSICAL_REVERT */
  
  /********************************************************************
   *       MSG_GEN_DOCUMENT_PHYSICAL_SAVE,
   *       MSG_GEN_DOCUMENT_PHYSICAL_UPDATE
   ********************************************************************
   * SYNOPSIS:     Save text to file
   * PARAMETERS:   word * error
   * RETURNS:      Boolean - TRUE if error occurred
   * STRATEGY:     Call superclass.
   * ERRORS:       Nothing to check.
   * REVISION HISTORY:
   *      Name     Date      Description
   *      ----     ----      -----------
   *       NF      03/07/96  Initial version
   *******************************************************************/
  @extern method GDocumentClass, MSG_GEN_DOCUMENT_PHYSICAL_SAVE,
                            MSG_GEN_DOCUMENT_PHYSICAL_UPDATE {
  
       /*
        * Write out the graph to the file by calling
        * one of Ken's functions. Use the extension to
        * know which function to call.
        */
    pself = ObjDerefGen( oself );
    FilePos( pself->GDI_fileHandle, 0L, FILE_POS_START );
    if ( toupper(
             pself->GDI_fileName[
                 strlen( pself->GDI_fileName )-1] ) == 'M' ) {
      writeAdjMatrix( pself->GDI_fileHandle );
    }
    else {
      writeAdjList( pself->GDI_fileHandle );
    }
    FileTruncate( pself->GDI_fileHandle,
                  FilePos( pself->GDI_fileHandle, 0, FILE_POS_RELATIVE ),
                  FALSE );
  
    return( @callsuper() );
  } /* MSG_GEN_DOCUMENT_PHYSICAL_SAVE,
     * MSG_GEN_DOCUMENT_PHYSICAL_UPDATE */
  
  /********************************************************************
   *       MSG_GEN_DOCUMENT_PHYSICAL_SAVE_AS_FILE_HANDLE
   ********************************************************************
   * SYNOPSIS:     Save text to a newly created file.
   * PARAMETERS:   word * error
   *               FileHandle file
   * RETURNS:      Boolean - TRUE if error occurred
   * STRATEGY:     Call superclass.
   * ERRORS:       Nothing to check.
   * REVISION HISTORY:
   *      Name     Date      Description
   *      ----     ----      -----------
   *       NF      03/07/96  Initial version
   *******************************************************************/
  @extern method GDocumentClass,
            MSG_GEN_DOCUMENT_PHYSICAL_SAVE_AS_FILE_HANDLE {
  
       /*
        * Write out the graph to the file by calling
        * one of Ken's functions. Use the extension to
        * know which function to call.
        */
    pself = ObjDerefGen( oself );
    fileHan_g = pself->GDI_fileHandle;
    FilePos( pself->GDI_fileHandle, 0L, FILE_POS_START );
    if ( toupper(
             pself->GDI_fileName[
                 strlen( pself->GDI_fileName )-1] ) == 'M' ) {
      writeAdjMatrix( pself->GDI_fileHandle );
    }
    else {
      writeAdjList( pself->GDI_fileHandle );
    }
    FileTruncate( pself->GDI_fileHandle,
                  FilePos( pself->GDI_fileHandle, 0, FILE_POS_RELATIVE ),
                  FALSE );
  
    return( @callsuper() );
  } /* MSG_GEN_DOCUMENT_PHYSICAL_SAVE_AS_FILE_HANDLE */
  
  /********************************************************************
   *       MSG_META_EXPOSED
   ********************************************************************
   * SYNOPSIS:     Redraw the contents of the view.
   * PARAMETERS:	 WindowHandle win - Window to draw to
   * RETURNS:      nothing
   * STRATEGY:	 This message is sent by the windowing system when a
   *	    	 portion of the GenView has become invalid, either
   *	    	 because a window that was obscuring it has been moved,
   *	    	 or because someone called GrInvalRect.
   *	    	 We redraw the entire document, after telling the
   *		 graphics system we're drawing to the invalid portion
   *		 of the window.
   * ERRORS:       Nothing to check.
   * REVISION HISTORY:
   *      Name     Date      Description
   *      ----     ----      -----------
   *       NF      03/07/96  Initial version
   *******************************************************************/
  @extern method GDocumentClass, MSG_META_EXPOSED {
  
       /*
        * Create the gstate, then save it to a global so the
        * drawLine, drawDirectedLine, and drawNode can use it.
        * Call Ken's graph drawing function.
        * End the gstate update. Destroy the gstate.
        */
    winHan_g = win;
    CreateGState();
    GrBeginUpdate( gstate_g );
    AMDrawGraph();
    GrEndUpdate( gstate_g );
    DestroyGState();
  } /* MSG_META_EXPOSED */
  
  /********************************************************************
   *                 MSG_META_START_SELECT
   ********************************************************************
   * SYNOPSIS:     Select an edge or add a new vertex.
   * PARAMETERS:	 MouseReturnParams * retVal
   *               sword               xPosition
   *               sword               yPosition
   *               word                inputState
   * RETURNS:      nothing
   * STRATEGY:     If not selecting an edge, use the mouse position
   *               to create a new vertex.
   * ERRORS:       Nothing to check.
   * REVISION HISTORY:
   *      Name     Date      Description
   *      ----     ----      -----------
   *       NF      03/07/96  Initial version
   *******************************************************************/
  @extern method GDocumentClass, MSG_META_START_SELECT {
  
    pself = ObjDerefGen( oself );
    pself->startingNode = AMLocateVertex( xPosition, yPosition );
    retVal->flags = MRF_PROCESSED;
  } /* MSG_META_START_SELECT */
  
  /********************************************************************
   *                 MSG_META_END_SELECT
   ********************************************************************
   * SYNOPSIS:     Select a vertex, an edge or add a new vertex, or
   *               add a new edge.
   * PARAMETERS:	 MouseReturnParams * retVal
   *               sword               xPosition
   *               sword               yPosition
   *               word                inputState
   * RETURNS:      nothing
   * STRATEGY:     Check if user clicked on a vertex on the ending
   *               of the selection. If so, check if the user clicked
   *               on a vertex at the start of the selection. If so,
   *               highlight the edge inbetween. Else, toggle the
   *               selected state of the vertex that ended the
   *               selection and redraw it. If the user did not end
   *               the selection on a vertex, see if s/he started it
   *               on a vertex, and if not add a new vertex at the
   *               ending mouse position and draw it.
   * ERRORS:       Nothing to check.
   * REVISION HISTORY:
   *      Name     Date      Description
   *      ----     ----      -----------
   *       NF      03/07/96  Initial version
   *******************************************************************/
  @extern method GDocumentClass, MSG_META_END_SELECT {
       /* Used to add new point to graph. */
    Point_t newPt;
       /* State of the selected vertext. */
    VertexState vstate;
       /* Edge number between startingNode and endingNode. */
    EdgeNumber edge;
       /* State of the edge. */
    EdgeState estate;
  
    pself = ObjDerefGen( oself );
    pself->endingNode = AMLocateVertex( xPosition, yPosition );
    if ( pself->endingNode < VERTEX_INVALID_NUM ) {
      if ( ( pself->startingNode < VERTEX_INVALID_NUM ) &&
           ( pself->endingNode != pself->startingNode ) ) {
           /*
            * Check for an undirected edge from starting to ending.
            * Then if there's still no edge, add one.
            * If there's an edge, toggle its selection state.
            * Redraw edge no matter what.
            */
        edge = AMGetEdgeNumber( pself->startingNode,
                                pself->endingNode );
        if ( edge == EDGE_INVALID_NUM ) {
          /*
           * Add new directed edge.
           * Get its edge number so we can draw it.
           */
          AMAddEdge( pself->startingNode, pself->endingNode, TRUE );
          edge = AMGetEdgeNumber( pself->startingNode,
                                  pself->endingNode );
          @send GDocumentGroup::MSG_GEN_DOCUMENT_GROUP_MARK_DIRTY_BY_FILE(
                    pself->GDI_fileHandle );
        }
        else {
             /*
              * If there exists an edge between startingNode and
              * endingNode, then toggle its selection and redraw it.
              */
          estate = AMGetEdgeNumState( edge );
          if ( estate & ES_SELECTED ) {
            AMSetEdgeNumState( edge, estate & ~ES_SELECTED );
          }
          else {
            AMSetEdgeNumState( edge, estate | ES_SELECTED );
          }
        }
        CreateGState();
        AMDrawEdgeNum( edge, C_BLACK );
        DestroyGState();
      }
      else {
           /*
            * A vertex was clicked on. Toggle its selection state.
            * Save the first and second selected nodes for the
            * search operations. Draw vertex, no matter what happened.
            * Show mouse event was handled.
            */
        vstate = AMGetVertexState( pself->endingNode );
        if ( vstate & VS_SELECTED ) {
             /* Selected - unselect it. */
          AMSetVertexState( pself->endingNode, vstate & ~VS_SELECTED );
          VertexNumListRemoveNumber( &verticesSelected_g, pself->endingNode );
        }
        else {
             /* Not selected - select it. */
          AMSetVertexState( pself->endingNode, vstate | VS_SELECTED );
          VertexNumListAddNumber( &verticesSelected_g, pself->endingNode );
        }
        CreateGState();
        AMDrawVertexNum( pself->endingNode, C_BLACK );
        DestroyGState();
      }
    }
    else {
      if ( pself->startingNode == VERTEX_INVALID_NUM ) {
           /*
            * Vertex was not clicked on.
            * Create point using mouse position.
            * Add new point to graph.
            */
        newPt.P_x = xPosition;
        newPt.P_y = yPosition;
        pself->endingNode = AMAddVertex( newPt );
           /*
            * If there's an error, set vertex to zero so redraw
            * below will not cause any visual problems.
            * Display message about maximum number of vertices.
            */
        if ( pself->endingNode > VERTEX_MAX_COUNT ) {
          pself->endingNode = 0;
          UserStandardDialog( (char*)0, (char*)0, (char*)0, (char*)0,
                              "Cannot add more vertices.",
                              ( ( CDT_WARNING <<
                                  CDBF_DIALOG_TYPE_OFFSET ) |
                                ( GIT_NOTIFICATION <<
                                  CDBF_INTERACTION_TYPE_OFFSET ) ) );
        }
        else {
             /*
              * Draw vertex, no matter what happened.
              * Show mouse event was handled.
              * Mark the document dirty.
              */
          CreateGState();
          AMDrawVertexNum( pself->endingNode, C_BLACK );
          DestroyGState();
          @send GDocumentGroup::MSG_GEN_DOCUMENT_GROUP_MARK_DIRTY_BY_FILE(
                    pself->GDI_fileHandle );
        }
      }
    }
    retVal->flags = MRF_PROCESSED;
  } /* MSG_META_END_SELECT */
  
  /********************************************************************
   *                 MSG_PRINT_START_PRINTING
   ********************************************************************
   * SYNOPSIS:     Print the graph to the current printer.
   * CALLED BY:    GPrintControl
   * PARAMETERS:   optr         printCtrlOD
   *               GStateHandle gstate
   * RETURNS:      nothing
   * STRATEGY:     Get printer margins, then send MSG_VIS_DRAW to
   *               self with the DF_PRINT flag, then signal
   *               printing completed.
   * ERRORS:       Nothing to check.
   * REVISION HISTORY:
   *      Name     Date      Description
   *      ----     ----      -----------
   *       NF      04/23/96  Initial version
   *******************************************************************/
  @extern method GDocumentClass, MSG_PRINT_START_PRINTING {
  
       /*
        * Set the global gstate for drawing functions.
        * Set the document size to reflect graph size.
        * Set number of pages to print.
        * Tell ourself to redraw to the printer.
        * Single end of page.
        * Wrap up the printing (tell PC we're done).
        */
    gstate_g = gstate;
    @call printCtrlOD::MSG_PRINT_CONTROL_SET_DOC_SIZE( 1000, 1000 );
    @call printCtrlOD::MSG_PRINT_CONTROL_SET_TOTAL_PAGE_RANGE( 1, 1 );
    @call self::MSG_VIS_DRAW( DF_PRINT, gstate_g );
    GrNewPage( gstate_g, PEC_FORM_FEED );
    @send printCtrlOD::MSG_PRINT_CONTROL_PRINTING_COMPLETED();
    gstate_g = NullHandle;
  } /* MSG_PRINT_START_PRINTING */
  
  