// ObjectWindows - (C) Copyright 1992 by Borland International

/* implementation of class TOLEDocument */

#include <owl.h>

#define SERVERONLY
#include <ole.h>

#include "olesrvr.h"
#include <fstream.h>
#include <CommDlg.h>
#include <string.h>

#define UNNAMED_DOC   "(Untitled)"
OLESERVERDOCVTBL      TOLEDocument::_vtbl;

//
// overloaded stream operators defined in "object.cpp"
//
ostream  &operator<< (ostream &, TOLEObject &);
istream  &operator>> (istream &, TOLEObject &);

/*
    constructor
    -----------

    if "szPath" is NULL then we create an untitled document and default object
    
    the type is "doctypeNew" if "lhDoc" is NULL and "doctypeEmbedded" if
    "lhDoc" is non NULL
    
    if "szPath" is non NULL we create a document of type "doctypeFromFile"
    and initialize it from file "szPath"
    
    if "lhDoc" is NULL then we call OleRegisterServerDoc() otherwise we
    just use "lhDoc" as our registration handle
*/
TOLEDocument::TOLEDocument (TOLEServer &server,
                            LHSERVERDOC lhDoc,
                            LPSTR       szPath,
                            BOOL        dirty)
{
  szName = 0;
  lpvtbl = &_vtbl;
  fRelease = FALSE;
  fDirty = dirty;

  server.pDocument = this;

  //
  // since we only have one object we can create it now...
  //
  pObject = new TOLEObject;

  if (szPath)
    LoadFromFile (szPath);

  else {
    SetDocumentName (UNNAMED_DOC);

    type = lhDoc ? doctypeEmbedded : doctypeNew;
  }

  if (lhDoc != 0)
    this->lhDoc = lhDoc;  // use registration handle we were given

  else
  	OleRegisterServerDoc (server.lhServer, szName, this, (LHSERVERDOC FAR *) &this->lhDoc);
}


/*
    SetDocumentName
    ---------------

    changes instance variable "szName" and changes the window caption
*/
void
TOLEDocument::SetDocumentName (LPSTR newName, BOOL changeCaption)
{
  int   len = lstrlen (newName);

  delete szName;
  szName = new char[len + 1];
  lstrcpy (szName, newName);

  if (changeCaption) {
    char  buf[MAXPATHLENGTH];

    wsprintf (buf, "%s - %s", (LPSTR) szAppName, newName);
    GetApplicationObject()->MainWindow->SetCaption (buf);
  }
}


/*
    LoadFromFile
    ------------

    returns TRUE if successful and FALSE otherwise

    SIDE EFFECTS: if successful sets type to "objtypeFromFile" and sets "szName"
                  to "szPath"
*/
BOOL
TOLEDocument::LoadFromFile (LPSTR szPath)
{
  char      buf[MAXPATHLENGTH];
  TOLEApp  *pApp = (TOLEApp *) GetApplicationObject();

  //
  // in small model if I want to use C++ streams I need to have a near
  // pointer...
  //
  lstrcpy (buf, szPath);

  ifstream  in (buf);

  if (in.fail()) {
      wsprintf (buf, "Cannot open file %s!", szPath);
      MessageBeep (0);
      MessageBox (pApp->MainWindow->HWindow,
                  buf,
                  szAppName, 
                  MB_OK | MB_ICONEXCLAMATION);
    return FALSE;

  } else {
    //
    // read in the signature
    //
    in.read (buf, sizeof (szClassKey) - 1);

    if (strncmp (buf, szClassKey, sizeof (szClassKey) - 1) != 0) {
      wsprintf (buf,
                "File %s is not an \"%s\" file!",
                szPath,
                (LPSTR) szAppName);
      MessageBeep (0);
      MessageBox (pApp->MainWindow->HWindow,
                  buf,
                  szAppName, 
                  MB_OK | MB_ICONEXCLAMATION);

      return FALSE;

    } else {
      in >> *pObject;
      type = doctypeFromFile;
      SetDocumentName (szPath);
    }

    return TRUE;
  }
}


/*
    Reset
    -----

    the only reason that we need this routine is that we re-use the document
    object. if your app doesn't then you would delete the old object and create
    a new one...

    SIDE EFFECTS: sets "fDirty" flag to FALSE and "fRelease" to FALSE

    if "lhDoc" is NULL then call OleRegisterServerDoc()
*/
void
TOLEDocument::Reset (LPSTR szPath)
{
  if (!szPath || !LoadFromFile (szPath)) {
    ((TWindowServer *) GetApplicationObject()->MainWindow)->ShapeChange (objEllipse);

    pObject->native.type = objEllipse;
    pObject->native.version = 1;

    type = doctypeNew;
    SetDocumentName (UNNAMED_DOC);
  }

  if (lhDoc == 0) {
    TOLEApp  *pApp = (TOLEApp *) GetApplicationObject();

  	OleRegisterServerDoc (pApp->pServer->lhServer, szName, this, (LHSERVERDOC FAR *) &lhDoc);
  }

  fDirty = fRelease = FALSE;
}


static
void
Setup (OPENFILENAME *fnStruct)
{
  TOLEApp  *pApp = (TOLEApp *) GetApplicationObject();

  //
  // we want the fully qualified pathname that is returned in "lpstrFile"
  // and not the simple name/extension
  //
  static char simpleName[13]; 

  fnStruct->lStructSize = sizeof (OPENFILENAME);
  fnStruct->nMaxFile = MAXPATHLENGTH;
  fnStruct->Flags = 0;
  fnStruct->hInstance = pApp->hInstance;
  fnStruct->hwndOwner = pApp->MainWindow->HWindow;
  fnStruct->lCustData = 0;
  fnStruct->lpfnHook = 0;
  fnStruct->lpstrCustomFilter = 0;
  fnStruct->lpstrDefExt = szFileExt;
  fnStruct->lpstrFilter = "OWL OLE Server (*." szFileExt ")\0*." szFileExt "\0" ;
  fnStruct->lpstrFileTitle = simpleName;
  fnStruct->lpstrInitialDir = 0;
  fnStruct->lpstrTitle = 0;  // use default
  fnStruct->lpTemplateName = 0;
  fnStruct->nFilterIndex = 1;
  fnStruct->nFileOffset = 0;
  fnStruct->nFileExtension = 0;
  fnStruct->nMaxCustFilter = 0;
}


/*
    PromptForOpenFileName
    ---------------------
*/
BOOL
TOLEDocument::PromptForOpenFileName (char *path)
{
  OPENFILENAME  fnStruct;

  Setup (&fnStruct);

  fnStruct.Flags |= OFN_HIDEREADONLY | OFN_PATHMUSTEXIST;
  wsprintf (path, "*.%s", (LPSTR) szFileExt);
  fnStruct.lpstrFile = path;

  return GetOpenFileName (&fnStruct);
}


/*
    SaveAs
    ------

    calls the common Windows dialog function to prompt the user for the
    filename to use
*/
void
TOLEDocument::SaveAs ()
{
  char          path[MAXPATHLENGTH];  // result of GetSaveFileName()
  OPENFILENAME  fnStruct;

  Setup (&fnStruct);
  fnStruct.Flags |= OFN_HIDEREADONLY | OFN_PATHMUSTEXIST;
  wsprintf (path, "*.%s", (LPSTR) szFileExt);
  fnStruct.lpstrFile = path;

  if (GetSaveFileName (&fnStruct)) {
    type = doctypeFromFile;
    SetDocumentName (path);  // we must do this BEFORE we call SaveDoc()
    SaveDoc();

    //
    // now inform the server library that we have renamed the document
    //
    OleRenameServerDoc (lhDoc, szName);
  }
}


/*
    SaveDoc
    -------

    save the document to file "szName" and mark the document as not "dirty"
*/
void
TOLEDocument::SaveDoc ()
{
  if (type == doctypeNew)
    SaveAs();

  else {
    ofstream  out (szName);

    out << szClassKey << " ";  // signature
    out << *pObject;
    fDirty = FALSE;
  }
}


/*
    callback Close
    -------- -----

    we have been requested to close the document because the client that
    contains a link (embedding or linking) to that document has shut down

    this method is always called *before* the document's "Release" method
    is called

    WHAT TO DO:
      - call OleRevokeServerDoc() and *don't* free any resources until
        "Release" is called
      - return the value of OleRevokeServerDoc()

    NOTE: since TOLEDocument is a subclass of OLESERVERDOC we can just cast
          a LPOLESERVERDOC to a (TOLEDocument FAR *)
*/
OLESTATUS FAR PASCAL _export
TOLEDocument::Close (LPOLESERVERDOC lpOleDoc)
{
  return OleRevokeServerDoc (((TOLEDocument FAR *) lpOleDoc)->lhDoc);
}


/*
    callback GetObjet
    -------- --------

    server library calls this method whenever a client application creates
    an object using a function like OleCreate()

    if "lpszObjName" is NULL, that means we are being called for an embedded
    object after the server was sent "Create", "Edit", or "CreateFromTemplate"
    and the server library requests the entire document

    if "lpszObjName" isn't NULL then the server has already received a "Open"
    message to activate the linked object

    WHAT TO DO:
      - allocate a TOLEObject if "lpszObjName" is NULL, or look up "lpszObjName"
        in the list of objects if it isn't NULL
      - store the pointer to the TOLEObject in "lplpOleObject"
      - store "lpOleClient" so we can send notifications to the client
        (used for linked objects)
      - return OLE_OK if successful, OLE_ERROR_NAME if "lpszObjName" isn't
        recognized, or OLE_ERROR_MEMORY if the object could not be allocated

    NOTE:
      - we only have one object and it is created when the document is
        created. Therefore, we don't actually create anything here...

      - "lpOleClient" resides in the rever library and is used on behalf of
        a client application
*/
OLESTATUS FAR PASCAL _export
TOLEDocument::GetObject (LPOLESERVERDOC lpOleDoc,
	                       LPSTR          lpszObjName,
	                       LPOLEOBJECT    FAR *lplpOleObj,
	                       LPOLECLIENT    lpOleClient)
{
  TOLEDocument  *pDoc = (TOLEDocument *) lpOleDoc;

  //
  // in either case (whether "lpszObjName" is null or not) we just return
  // the object associated with the document
  //
  *lplpOleObj = pDoc->pObject;

  //
  // if "lpszObjName" isn't NULL then we associate client "lpOleClient" with
  // it...
  //
  // NOTE: we only have one object. if you have multiple objects then you would
  //       have to search your objects to find the one that matched "lpszObjName"
  //
  if (lpszObjName)
    pDoc->pObject->AddClientLink (lpOleClient);

	return OLE_OK;
}


/*
    callback Execute
    -------- -------

    if your app supports DDE execution commands then you would handle this
    event

    since we don't we return OLE_ERROR_COMMAND
*/
OLESTATUS FAR PASCAL _export
TOLEDocument::Execute (LPOLESERVERDOC /* lpOleDoc */,
	                     HANDLE         /* hCommands */)
{
	return OLE_ERROR_COMMAND;
}


/*
    callback Release
    -------- -------

    the server library calls this routine when all conversations to the
    object have been closed. at this point the server has called either
    OleRevokeServerDoc() or OleRevokeServer()

    there will be no more calls to the document's methods. it is okay to
    free the document's objects, but *not* the TOLEDocument yet...

    WHAT TO DO:
      - free the document's objects and resources (e.g. atoms) but *not* the
        document itself
      - set a flag to indicate that "Release" has been called
      - return OLE_OK if successful, OLE_ERROR_GENERIC otherwise

    NOTE: since we only have one document and one object within the
          documeny we don't NOT delete the object here

          however, you might want to...
*/
OLESTATUS FAR PASCAL _export
TOLEDocument::Release (LPOLESERVERDOC lpOleDoc)
{
  ((TOLEDocument *) lpOleDoc)->fRelease = TRUE;
	return OLE_OK;
}


/*
    callback Save
    -------- ----

    this method is only used when the server is editing a linked object:

    the client application is closing and the user has requested saving the
    client document which contains a linked object

    WHAT TO DO:
      - save the document to the filename which was passed in when the document
        was opened for linking
      - return OLE_OK if successful, OLE_ERROR_GENERIC otherwise
*/
OLESTATUS FAR PASCAL _export
TOLEDocument::Save (LPOLESERVERDOC lpOleDoc)
{
  TOLEDocument  *doc = (TOLEDocument *) lpOleDoc;

  if (doc->type != doctypeFromFile)
    return OLE_ERROR_GENERIC;

  else {
    doc->SaveDoc();
    return OLE_OK;
  }
}


/*
    callback SetColorScheme
    -------- --------------
*/
OLESTATUS FAR PASCAL _export
TOLEDocument::SetColorScheme (LPOLESERVERDOC /* lpOleDoc */,
	                            LPLOGPALETTE   /* lpPal */)
{
	return OLE_ERROR_GENERIC;
}


/*
    callback DocSetDimensions
    -------- ----------------

    the client is informing  us how big the object should be

    "lpRect" is in MM_HIMETRIC units (all OLE libraries express the size of
     every object in MM_HIMETRIC)
*/
OLESTATUS FAR PASCAL _export
TOLEDocument::SetDocDimensions (LPOLESERVERDOC /* lpOleDoc */,
	                              LPRECT         /* lpRect */)
{
	return OLE_OK;
}


/*
    callback SetHostNames
    -------- ------------

    the server library is calling to provide the server with the name of the
    client's document and the name of the object in the client application

    these names should be used to make the necessary window title bar and
    menu changes

    this method is only called for embedded objects because linked objects
    display their filename in the title bar

    WHAT IT DOES:
      - change the title bar and File menu
      - store the object and client names for later use
      - return OLE_OK is successful, OLE_ERROR_GENERIC otherwise

    PARAMETERS:
      - "lpClient" is the name of the client application document
      - "lpDoc" is the name of the object in the client application
*/
OLESTATUS FAR PASCAL _export
TOLEDocument::SetHostNames (LPOLESERVERDOC lpOleDoc,
	                          LPSTR          lpClient,
	                          LPSTR          lpDoc)
{
  ((TWindowServer *) GetApplicationObject()->MainWindow)->UpdateFileMenu (lpDoc);

  //
  // store the document name, but don't update the title bar we will do that
  // below
  //
  ((TOLEDocument *) lpOleDoc)->SetDocumentName (lpDoc, FALSE);

  //
  // set the caption to be <App Name> - <Object Name> in <Client App Document>
  //
  char  buf[128];

  wsprintf (buf, "%s - %s in %s", (LPSTR) szAppName, lpDoc, lpClient);
  GetApplicationObject()->MainWindow->SetCaption (buf);
	return OLE_OK;
}



typedef OLESTATUS FAR PASCAL _export (FAR *LPDOCCLOSE)(LPOLESERVERDOC);
typedef OLESTATUS FAR PASCAL _export (FAR *LPDOCGETOBJECT)(LPOLESERVERDOC,const char far *,LPOLEOBJECT FAR *,LPOLECLIENT);
typedef OLESTATUS FAR PASCAL _export (FAR *LPDOCEXEC)(LPOLESERVERDOC,HANDLE);
typedef OLESTATUS FAR PASCAL _export (FAR *LPDOCRELEASE)(LPOLESERVERDOC);
typedef OLESTATUS FAR PASCAL _export (FAR *LPDOCSAVE)(LPOLESERVERDOC);
typedef OLESTATUS FAR PASCAL _export (FAR *LPDOCSETCOLORSCHM)(LPOLESERVERDOC,const LOGPALETTE far *);
typedef OLESTATUS FAR PASCAL _export (FAR *LPDOCSETDIMNSN)(LPOLESERVERDOC,const RECT far *);
typedef OLESTATUS FAR PASCAL _export (FAR *LPDOCSETHOSTNAMES)(LPOLESERVERDOC,const char far *,const char far *);


/*
    InitVTBL
    --------

    create thunks for OLESERVERDOC method callback tables
*/
BOOL
TOLEDocument::InitVTBL (HINSTANCE hInstance)
{
	_vtbl.Close = (LPDOCCLOSE) MakeProcInstance ((FARPROC) Close, hInstance);
	_vtbl.GetObject = (LPDOCGETOBJECT) MakeProcInstance ((FARPROC) GetObject, hInstance);
	_vtbl.Execute = (LPDOCEXEC) MakeProcInstance ((FARPROC) Execute, hInstance);
	_vtbl.Release = (LPDOCRELEASE) MakeProcInstance ((FARPROC) Release, hInstance);
	_vtbl.Save = (LPDOCSAVE) MakeProcInstance ((FARPROC) Save, hInstance);
	_vtbl.SetColorScheme = (LPDOCSETCOLORSCHM) MakeProcInstance ((FARPROC) SetColorScheme, hInstance);
	_vtbl.SetDocDimensions = (LPDOCSETDIMNSN) MakeProcInstance ((FARPROC) SetDocDimensions, hInstance);
	_vtbl.SetHostNames = (LPDOCSETHOSTNAMES) MakeProcInstance ((FARPROC) SetHostNames, hInstance);

  return _vtbl.Close != NULL &&
         _vtbl.GetObject != NULL &&
         _vtbl.Execute != NULL &&
         _vtbl.Release != NULL &&
         _vtbl.Save != NULL &&
         _vtbl.SetColorScheme != NULL &&
         _vtbl.SetDocDimensions != NULL &&
         _vtbl.SetHostNames != NULL;
}

