summaryrefslogtreecommitdiffstats
path: root/private/oleutest/ole1/srvrdemo/doc.c
blob: 8849d5f69b41da356d7ffe2024853d1e46cb1537 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
/*
  OLE SERVER DEMO           
  Doc.c             
                                                                     
  This file contains document methods and various document-related support 
  functions.
                                                                     
  (c) Copyright Microsoft Corp. 1990 - 1992 All Rights Reserved   
*/                                                                     
 
/* 
   Important Note:

   No method should ever dispatch a DDE message or allow a DDE message to
   be dispatched.
   Therefore, no method should ever enter a message dispatch loop.
   Also, a method should not show a dialog or message box, because the 
   processing of the dialog box messages will allow DDE messages to be
   dispatched.
*/



#define SERVERONLY
#include <windows.h>
#include <ole.h>

#include "srvrdemo.h"

/* AssociateClient
 * ---------------
 *
 * Add a client to the list of clients associated with an object.
 *
 * This function is necessary only because ServerDemo does not create object
 * structures as they are requested, but rather has a fixed set of objects.
 * When DocGetObject is called with a NULL object name, the entire 
 * document is requested, but ServerDemo does not currently support making
 * the entire document an object, so DocGetObject returns one object.
 * That object now goes by two names: NULL and its real name.  Therefore
 * we need to keep track of both lpoleclient's that were passed to 
 * DocGetObject.  Ideally, DocGetObject should always create a new OBJ 
 * structure containing a pointer (or some reference) to the object's native
 * data and also containing one lpoleclient.
 *
 * LPOLECLIENT lpoleclient - the client to be associated with the object.
 * LPOBJ lpobj             - the object 
 *
 * RETURNS: TRUE if successful
 *          FALSE if out of memory
 *
 * CUSTOMIZATION: Server Demo specific
 *
 */
static BOOL AssociateClient (LPOLECLIENT lpoleclient, LPOBJ lpobj)
{
   INT i;
   for (i=0; i < clpoleclient; i++)
   {
      if (lpobj->lpoleclient[i]==lpoleclient)
      {
         return TRUE;
      }
      if (lpobj->lpoleclient[i]==NULL)
      {
         lpobj->lpoleclient[i]=lpoleclient;
         return TRUE;
      }
   }
   return FALSE;
}



/* CreateNewDoc
 * ------------
 *
 * If lhdoc == NULL then we must register the new document by calling
 * OleRegisterServerDoc, which will return a new handle which will be stored
 * in docMain.lhdoc.
 * Also if lhdoc==NULL then this document is being created at the request of
 * the user, not of the client library.
 *
 * LONG lhdoc      - Document handle
 * LPSTR lpszDoc   - Title of the new document
 * DOCTYPE doctype - What type of document is being created
 * 
 * RETURNS: TRUE if successful, FALSE otherwise.
 *
 * CUSTOMIZATION: Re-implement
 *
 */
BOOL CreateNewDoc (LONG lhdoc, LPSTR lpszDoc, DOCTYPE doctype)
{
   INT i;

   // Fill in the fields of the document structure.

   docMain.doctype      = doctype;
   docMain.oledoc.lpvtbl= &docvtbl;

   if (lhdoc == 0)
   {
      if (OLE_OK != OleRegisterServerDoc 
                     (srvrMain.lhsrvr, 
                      lpszDoc,
                      (LPOLESERVERDOC) &docMain, 
                      (LHSERVERDOC FAR *) &docMain.lhdoc))
         return FALSE;
   }
   else
      docMain.lhdoc = lhdoc;

   // Reset all the flags because no object numbers have been used.
   for (i=1; i <= cfObjNums; i++)
      docMain.rgfObjNums[i] = FALSE;

   fDocChanged = FALSE;

   SetTitle (lpszDoc, doctype == doctypeEmbedded);
   return TRUE;
}



/* DestroyDoc
 * ----------
 *
 * Free all memory that had been allocated for a document.
 *
 *
 * CUSTOMIZATION: Re-implement.  Your application will probably use some
 *                other method for enumerating all the objects in a document.
 *                ServerDemo enumerates the child windows, but if each object 
 *                does not have its own window, this will not work.
 *
 */
VOID DestroyDoc (VOID)
{
   HWND hwnd;
   HWND hwndNext;

   // Delete all object windows.  
   hwnd = SelectedObjectWindow();
   while (hwnd) 
   {
      hwndNext = GetWindow (hwnd, GW_HWNDNEXT);
      // Each object window frees its own memory upon receiving WM_DESTROY.
      DestroyWindow (hwnd);
      hwnd = hwndNext;
   } 

   if (docMain.aName)
   {
      GlobalDeleteAtom (docMain.aName);
      docMain.aName = '\0';
   }

   if (docMain.hpal)
      DeleteObject (docMain.hpal);
}



/* DocClose                DOCUMENT "Close" METHOD
 * --------
 *
 * The library calls this method to unconditionally close the document.
 *
 * LPOLESERVERDOC lpoledoc - The server document to close
 * 
 * RETURNS: Return value from RevokeDoc.
 *
 * CUSTOMIZATION: None
 *
 */
OLESTATUS  APIENTRY DocClose (LPOLESERVERDOC lpoledoc)
{
   return RevokeDoc();
}



/* DocExecute                DOCUMENT "Execute" METHOD
 * ----------
 *
 * This application does not support the execution of DDE execution commands.
 * 
 * LPOLESERVERDOC lpoledoc - The server document
 * HANDLE hCommands        - DDE execute commands
 * 
 * RETURNS: OLE_ERROR_COMMAND
 *
 * CUSTOMIZATION: Re-implement if your application supports the execution of
 *                DDE commands.
 *
 */
OLESTATUS  APIENTRY DocExecute (LPOLESERVERDOC lpoledoc, HANDLE hCommands)
{
   return OLE_ERROR_COMMAND;
}



/* DocGetObject                DOCUMENT "GetObject" METHOD
 * ------------
 *
 * The library uses this method to get an object's structure for the
 * client.  Memory needs to be allocated and initialized here for this.
 * A NULL string indicates that the client has an embedded object
 * which was started from Create, CreateFromTemplate, or Edit, but not Open.
 *
 * First see if the object name is NULL.  If so, you would ordinarily
 * return the entire document, but Server Demo returns the selected object.
 * If the object name is not NULL, then go through the list of objects, 
 * searching for one with that name.  Return an error if there is not one.
 *
 * LPOLESERVERDOC lpoledoc        - The server document
 * OLE_LPCSTR lpszObjectName           - The name of the object to get data for
 * LPOLEOBJECT FAR *lplpoleobject - The object's data is put here
 * LPOLECLIENT lpoleclient        - The client structure
 * 
 * RETURNS:        OLE_OK
 *                 OLE_ERROR_NAME if object not found
 *                 OLE_ERROR_MEMORY if no more memory to store lpoleclient
 *
 * CUSTOMIZATION: Re-implement.
 *                lpszObjectName == "" indicates that the whole document 
 *                should be the object returned.
 *
 */
OLESTATUS  APIENTRY DocGetObject
   (LPOLESERVERDOC lpoledoc, OLE_LPCSTR lpszObjectName, 
    LPOLEOBJECT FAR *lplpoleobject, LPOLECLIENT lpoleclient)
{
    HWND  hwnd;
    ATOM  aName;
    LPOBJ lpobj;


    if (lpszObjectName == NULL || lpszObjectName[0] == '\0')
    {   
        // Return a new object or the selected object.
        hwnd = SelectedObjectWindow();
        lpobj = hwnd ? HwndToLpobj (hwnd) : CreateNewObj (FALSE);
        *lplpoleobject = (LPOLEOBJECT) lpobj;
        // Associate client with object.
        if (!AssociateClient (lpoleclient, lpobj))
            return OLE_ERROR_MEMORY;
        return OLE_OK;
    }

    if (!(aName = GlobalFindAtom (lpszObjectName)))
        return OLE_ERROR_NAME;

    hwnd = SelectedObjectWindow();

    // Go through all the child windows and find the window whose name
    // matches the given object name.

    while (hwnd)
    {
         lpobj = HwndToLpobj (hwnd);

         if (aName == lpobj->aName)
         {
            // Return the object with the matching name.
            *lplpoleobject = (LPOLEOBJECT) lpobj;
            // Associate client with the object.
            if (!AssociateClient (lpoleclient, lpobj))
               return OLE_ERROR_MEMORY;
            return OLE_OK;
         }
         hwnd = GetWindow (hwnd, GW_HWNDNEXT);
    }

   if (((DOCPTR)lpoledoc)->doctype ==  doctypeEmbedded)
   {
      lpobj = CreateNewObj (FALSE);
      *lplpoleobject = (LPOLEOBJECT) lpobj;
      
      // Associate client with object.
      if (!AssociateClient (lpoleclient, lpobj))
         return OLE_ERROR_MEMORY;
      return OLE_OK;
    }

    // Object with name lpszObjName was not found.
    return OLE_ERROR_NAME;
}

/* DocRelease                DOCUMENT "Release" METHOD
 * ----------
 *
 * The library uses this method to notify the server that a revoked
 * document has finally finished all conversations, and can be 
 * destroyed.
 * It sets fWaitingForDocRelease to FALSE so a new document can be created
 * and the user can continue working.
 *
 * LPOLESERVERDOC lpoledoc        - The server document
 * 
 * RETURNS: OLE_OK
 *
 * CUSTOMIZATION: None
 *
 */
OLESTATUS  APIENTRY DocRelease (LPOLESERVERDOC lpoledoc)
{
   fWaitingForDocRelease = FALSE;
   // Free all memory that has been allocated for the document.
   DestroyDoc();

   return OLE_OK;
}



/* DocSave                DOCUMENT "Save" METHOD
 * -------
 *
 * Save document to a file.
 *
 * LPOLESERVERDOC lpoledoc - The document to save
 * 
 * RETURNS: OLE_OK
 *
 * CUSTOMIZATION: None
 *
 */
OLESTATUS  APIENTRY DocSave (LPOLESERVERDOC lpoledoc)
{
    if (docMain.doctype == doctypeFromFile)
    {
         // No "File Save As" dialog box will be brought up because the
         // file name is already known.
         return SaveDoc() ? OLE_OK : OLE_ERROR_GENERIC;
    }
    else
      return OLE_ERROR_GENERIC;
}



/* DocSetDocDimensions        DOCUMENT "SetDocDimensions" METHOD
 * -------------------
 *
 * The library calls this method to tell the server the bounds on
 * the target device for rendering the document.
 * A call to this method is ignored for linked objects because the size of
 * a linked document depends only on the source file.
 *
 * LPOLESERVERDOC lpoledoc - The server document
 * CONST LPRECT         lprect   - The target size in MM_HIMETRIC units
 * 
 * RETURNS: OLE_OK
 *
 * CUSTOMIZATION: Re-implement
 *                How an object is sized is application-specific. (Server Demo
 *                uses MoveWindow.)
 *                     
 */
OLESTATUS  APIENTRY DocSetDocDimensions 
   (LPOLESERVERDOC lpoledoc, OLE_CONST RECT FAR * lprect)
{
   if (docMain.doctype == doctypeEmbedded)
   {
      RECT rect = *lprect;
      
      // the units are in HIMETRIC
      rect.right   = rect.right - rect.left;
		// the following was bottom - top
		rect.bottom  = rect.top -  rect.bottom;
		
      HiMetricToDevice ( (LPPOINT) &rect.right );
      MoveWindow (SelectedObjectWindow(), 0, 0, 
                  rect.right + 2 * GetSystemMetrics(SM_CXFRAME), 
                  rect.bottom + 2 * GetSystemMetrics(SM_CYFRAME), 
                  TRUE);
      /* If for some reason your application needs to notify the client that
         the data has changed because DocSetDocDimensions has been called,
         then notify the client here.
         SendDocMsg (OLE_CHANGED);
      */
   }
   return OLE_OK;
}



/* DocSetHostNames        DOCUMENT "SetHostNames" METHOD
 * ---------------
 *
 * The library uses this method to set the name of the document
 * window.
 * All this function does is change the title bar text, although it could
 * do more if necesary. 
 * This function is only called for embedded objects; linked objects
 * use their filenames for the title bar text.
 *
 * LPOLESERVERDOC lpoledoc    - The server document
 * OLE_LPCSTR lpszClient           - The name of the client
 * OLE_LPCSTR lpszDoc              - The client's name for the document
 * 
 * RETURNS:        OLE_OK
 * 
 * CUSTOMIZATION: None
 *
 */
OLESTATUS  APIENTRY DocSetHostNames 
   (LPOLESERVERDOC lpoledoc, OLE_LPCSTR lpszClient, OLE_LPCSTR lpszDoc)
{
   SetTitle ((LPSTR)lpszDoc, TRUE);
   lstrcpy ((LPSTR) szClient, lpszClient);
   lstrcpy ((LPSTR) szClientDoc, Abbrev((LPSTR)lpszDoc));
   UpdateFileMenu (IDM_UPDATE);   
   return OLE_OK;
}



/* DocSetColorScheme                DOCUMENT "SetColorScheme" METHOD
 * -----------------
 *
 * The client calls this method to suggest a color scheme (palette) for
 * the server to use.
 * In Server Demo the document's palette is never actually used because each 
 * object has its own palette.  See ObjSetColorScheme.
 *
 * LPOLESERVERDOC lpoledoc - The server document
 * CONST LOGPALETTE FAR * lppal    - Suggested palette
 *
 * RETURNS: OLE_ERROR_PALETTE if CreatePalette fails, 
 *          OLE_OK otherwise
 *
 * 
 * CUSTOMIZATION: If your application supports color schemes, then this 
 *                function is a good example of how to create and store
 *                a palette.
 */
OLESTATUS  APIENTRY DocSetColorScheme 
   (LPOLESERVERDOC lpoledoc, OLE_CONST LOGPALETTE FAR * lppal)
{
   HPALETTE hpal = CreatePalette (lppal);

   if (hpal==NULL)
      return OLE_ERROR_PALETTE;

   if (docMain.hpal) 
   {
      // Delete old palette
      DeleteObject (docMain.hpal);
   }
   // Store handle to new palette
   docMain.hpal = hpal;
   return OLE_OK;
}



/* RevokeDoc
 * ---------
 *
 * Call OleRevokeServerDoc.
 * If the return value is OLE_WAIT_FOR_BUSY, then set fWaitingForDocRelease
 * and enter a message-dispatch loop until fWaitingForDocRelease is reset.
 * As long as fWaitingForDocRelease is set, the user interface will be 
 * disabled so that the user will not be able to manipulate the document.
 * When the DocRelease method is called, it will reset fWaitingForDocRelease,
 * allowing RevokeDoc to free the document's memory and return.
 *
 * This is essentially a way to make an asynchronous operation synchronous.
 * We need to wait until the old document is revoked before we can delete
 * its data and create a new one.
 *
 * Note that we cannot call RevokeDoc from a method because it is illegal to
 * enter a message-dispatch loop within a method.
 *
 * RETURNS: The return value of OleRevokeServerDoc.
 *
 * CUSTOMIZATION: lhdoc may need to be passed in as a parameter if your 
 *                application does not have a global variable corresponding 
 *                to docMain.
 * 
 */
OLESTATUS RevokeDoc (VOID)
{
   OLESTATUS olestatus;

   if ((olestatus = OleRevokeServerDoc(docMain.lhdoc)) > OLE_WAIT_FOR_RELEASE)
      DestroyDoc();

   docMain.lhdoc = 0; // A NULL handle indicates that the document 
                         // has been revoked or is being revoked.
   return olestatus;

}



/* SaveChangesOption
 * -----------------
 *
 * Give the user the opportunity to save changes to the current document
 * before continuing.
 *
 * BOOL *pfUpdateLater - Will be set to TRUE if the client does not accept
 *                       the update and needs to be updated when the document
 *                       is closed.  In that case, OLE_CLOSED will be sent.
 *
 * RETURNS: IDYES, IDNO, or IDCANCEL
 *
 * CUSTOMIZATION: None
 *
 */
INT SaveChangesOption (BOOL *pfUpdateLater)
{
   INT  nReply;
   CHAR szBuf[cchFilenameMax];
   
   *pfUpdateLater = FALSE;
   
   if (fDocChanged)
   {
       CHAR szTmp[cchFilenameMax];
       
       if (docMain.aName) 
           GlobalGetAtomName (docMain.aName, szTmp, cchFilenameMax);
       else 
           szTmp[0] = '\0';

       if (docMain.doctype == doctypeEmbedded)
           wsprintf (szBuf, "The object has been changed.\n\nUpdate %s before closing the object?", Abbrev (szTmp));        
       else
           lstrcpy (szBuf, (LPSTR) "Save changes?");         
     
       nReply = MessageBox (hwndMain, szBuf, szAppName, 
                      MB_ICONEXCLAMATION | MB_YESNOCANCEL);
                  
       switch (nReply)
       {
          case IDYES:
              if (docMain.doctype != doctypeEmbedded)
                  SaveDoc();
              else
                  switch (OleSavedServerDoc (docMain.lhdoc))
                  {
                      case OLE_ERROR_CANT_UPDATE_CLIENT:
                          *pfUpdateLater = TRUE;
                          break;
                      case OLE_OK:
                          break;
                      default:
                          ErrorBox ("Fatal Error: Cannot update.");
                  }                                      
              return IDYES;
          case IDNO:
              return IDNO;
         case IDCANCEL:
              return IDCANCEL;
       }
   }
   return TRUE;
}



/* SendDocMsg
 * ----------
 *
 * This function sends messages to all the objects in a document when
 * the document has changed.
 *
 * WORD wMessage - The message to send
 * 
 * CUSTOMIZATION: The means of enumerating all the objects in a document
 *                is application specific.
 */
VOID SendDocMsg (WORD wMessage)
{
    HWND    hwnd;

    // Get handle to first object window.
    hwnd = SelectedObjectWindow();

    // Send message to all object windows.
    while (hwnd)
    {
        SendObjMsg (HwndToLpobj(hwnd), wMessage);
        hwnd = GetWindow (hwnd, GW_HWNDNEXT);
    }
}