~ubuntu-branches/ubuntu/wily/gargoyle-free/wily-proposed

« back to all changes in this revision

Viewing changes to tads/tads3/lib/extensions/customBanner.t

  • Committer: Bazaar Package Importer
  • Author(s): Sylvain Beucler
  • Date: 2009-09-11 20:09:43 UTC
  • Revision ID: james.westby@ubuntu.com-20090911200943-idgzoyupq6650zpn
Tags: upstream-2009-08-25
ImportĀ upstreamĀ versionĀ 2009-08-25

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#charset "us-ascii"
 
2
#include <adv3.h>
 
3
#include <en_us.h>
 
4
 
 
5
  /*
 
6
   *    Custom Banner version 1.2
 
7
   *     by Eric Eve
 
8
   *  
 
9
   *     Version date: 13-Sep-06
 
10
   *
 
11
   *     This file implements a CustomBannerWindow class that vastly eases
 
12
   *     the process of setting up banners and displaying material in them.
 
13
   *     e.g. to set up a graphics banner to display pictures, starting with
 
14
   *     pic1.jpg at startup, but not appearing at all on an interpreter that
 
15
   *     can't display JPEGs you could define:
 
16
   *
 
17
   *     pictureWindow: CustomBanner
 
18
   *       canDisplay = (systemInfo(SysInfoJpeg))
 
19
   *       bannerArgs = [nil, BannerAfter,  statuslineBanner, BannerTypeText, 
 
20
   *             BannerAlignTop, 10, BannerSizeAbsolute, BannerStyleBorder]
 
21
   *       currentContents = '<img src="pic1.jpg">'
 
22
   *    ;
 
23
   *
 
24
   *    Then to change the picture dislayed at a later point, call:
 
25
   *
 
26
   *       pictureWindow.updateContents('<img src="pic2.jpg">');
 
27
   *
 
28
   *    And everything else, including getting everything right on RESTART, UNDO
 
29
   *    and RESTORE should be taken care of.
 
30
   */
 
31
 
 
32
  /* ------------------------------------------------------------------------ */
 
33
  /* 
 
34
   *  A CustomBannerWindow, like a BannerWindow, corrsponds to an on-screen
 
35
   *  banner. The purpose of CustomBannerWindow is to eliminate most of the
 
36
   *  busy-work that a game author would otherwise have to take care of in
 
37
   *  displaying and manipulating banners.
 
38
   *
 
39
   *  As with BannerWinnow, merely creating a CustomBannerWindow does not
 
40
   *  display the banner. However, any CustomBannerWindows in existence at
 
41
   *  the start of the game will be added to the screen display, unless the
 
42
   *  condition specified in their shouldDisplay() method prevents initialization.
 
43
   *
 
44
   *  The one property that must be defined on each instance of a CustomBannerWindow
 
45
   *  is bannerArgs, which takes the form:
 
46
   *
 
47
   *     bannerArgs = [parent, where, other, windowType, align, 
 
48
   *                     size, sizeUnits, styleFlags]
 
49
   *
 
50
   *  where each list element has the same meaning at the corresponding argument
 
51
   *  to BannerWindow.showBanner()
 
52
   *
 
53
   *  This merely ensures that the CustomBannerWindow is added to the screen's
 
54
   *  banner window layout. To have the CustomBannerWindow display some content
 
55
   *  when first added to the screen layout, override its current contents property:
 
56
   *
 
57
   *     currentContents = 'My initial contents'
 
58
   *
 
59
   *  To change what's displayed in a CustomBannerWindow from game code, call its
 
60
   *  updateContents() method, e.g.:
 
61
   *
 
62
   *     pictureWindow.updateContents('<img src="pics/troll.jpg">');
 
63
   *
 
64
   *  To redisplay the current contents, call the showCurrentContents() method.
 
65
   *  By default a call to updateContents() or showCurrentContents() clears the
 
66
   *  window before displaying the new content. To have the additional content
 
67
   *  added to the existing content, change the clearBeforeUpdate property to nil.
 
68
   *
 
69
   *  You can control whether the game uses this banner at all by overriding
 
70
   *  the canDisplay property. The main purpose of this property is to easily allow
 
71
   *  a game to run on different types of interpreter. For example, if your banner is
 
72
   *  meant to display pictures in the JPEG format, there's no point having it included
 
73
   *  in the screen layout of an interpreter that can't display JPEGs, and attempts to
 
74
   *  update its contents with a new picture should do nothing. In which case we could
 
75
   *  define:
 
76
   *
 
77
   *     canDisplay = (systemInfo(SysInfoJpeg))
 
78
   *
 
79
   *  Calls to CustomBannerWindow methods like updateContents() and clearWindow()
 
80
   *  should be safe on an interpreter for which shouldDisplay returns nil, since
 
81
   *  by default these will do nothing beyond updating the banner's currentContents
 
82
   *  property. This makes it easier to write game code that is suitable for all 
 
83
   *  classes of interpreter
 
84
   *
 
85
   *  To have a CustomBannerWindow resize to contents each time its contents are
 
86
   *  displayed, set its autoSize property to true.   
 
87
   *
 
88
   *  If you do not want a CustomBannerWindow you have defined not to be dispayed
 
89
   *  at game startup, set its isActive property to nil. Call the activate()
 
90
   *  method to add it to the screen layout and the deactivate() method to remove
 
91
   *  it, or any other CustomBannerWindow, from the screen display.    
 
92
   *    
 
93
   *  Obviously, it is then the game author's responsibility to ensure that no
 
94
   *  other banner window that's meant to persist after pictureWindow is deactivated
 
95
   *  depends on pictureWindow for its existence; i.e. that we're not deactivating
 
96
   *  the parent of some other banner window(s) we want to keep or subsequently
 
97
   *  activate, or the sibling of any banner window that's subsequently going to
 
98
   *  defined in relation to us.    
 
99
   */
 
100
 
 
101
class CustomBannerWindow: BannerWindow
 
102
 
 
103
 /*
 
104
  *  The list of any banner windows that must be set up before me,
 
105
  *  either one of them is my parent, or because I'm going
 
106
  *  to be placed before or after them with BannerBefore or BannerAfter.
 
107
  *
 
108
  *  If bannerArgs has been set up with the list of showBanner arguments, 
 
109
  *  then we can derive this information automatically
 
110
  */
 
111
  initBeforeMe()
 
112
  {
 
113
    /*
 
114
     *  If our bannerArgs property contains a list of the right length, i.e. 8
 
115
     *  elements, then the first and third elements of the list (our parent, and
 
116
     *  the sibling we're to be placed before or after) must be initialized before
 
117
     *  we are. If either of these is nil, no harm is done, since initBannerWindow()
 
118
     *  will simply skip the nil value.     
 
119
     *
 
120
     *  Moreover, if our sibling is in the list, we don't need our parent as well,
 
121
     *  since either our sibling or one of its siblings will initialize our parent.
 
122
     */
 
123
     
 
124
    local lst = [];
 
125
    
 
126
    if (propType(&bannerArgs) == TypeList && bannerArgs.length() == 8) 
 
127
      lst = bannerArgs[3] ? [bannerArgs[3]] : [bannerArgs[1]];
 
128
        
 
129
    initBeforeMe = lst;
 
130
    
 
131
    return lst;    
 
132
  }   
 
133
 
 
134
  
 
135
  /*
 
136
   * A condition to test whether this banner window should actually display.
 
137
   * Normally this would test for the interpreter class if this would
 
138
   * affect whether we wanted this banner to be created. For example, if
 
139
   * we were going to use this banner window to display a JPEG picture, we
 
140
   * might not this window to display at all if the interpreter we're running
 
141
   * on can't display JPEGS, so we might write:
 
142
   *
 
143
   *    canDisplay = (systemInfo(SysInfoJpeg))
 
144
   *
 
145
   *   If your complete system of CustomBanners depends on the same condition
 
146
   *   (e.g. you don't want any CustomBanners if the interpreter we're running
 
147
   *   on can't display JPEGs, then it's probably easiest to modify CustomBanner
 
148
   *   and override scanDisplay on the modified class.
 
149
   *
 
150
   *  By default, we simply check that the interpreter we're running on
 
151
   *  can display banners.
 
152
   */
 
153
  canDisplay = (systemInfo(SysInfoBanners))  
 
154
  
 
155
  shouldDisplay = (canDisplay && isActive)
 
156
   
 
157
   /*
 
158
    *  The standard use of initBannerWindow is first to ensure that any
 
159
    *  banner windows whose existence we presuppose have themselves been
 
160
    *  initialized, and then to set up our own window on screen.
 
161
    *  This function should be used for initializing banner window *layout*,
 
162
    *  not content.
 
163
    */
 
164
   
 
165
   
 
166
  initBannerWindow()
 
167
  {
 
168
     /*
 
169
      *  If we shouldn't display on this class of interpreter, don't
 
170
      *  initialize us.
 
171
      */
 
172
     if(!shouldDisplay)
 
173
       return nil;
 
174
       
 
175
     /*
 
176
      *  If we've already been initialized, there's nothing left to do.
 
177
      */
 
178
        
 
179
     if(inited_)
 
180
       return true;  
 
181
       
 
182
     /*  
 
183
      *  Initialize all the bannner windows on whose existence our own
 
184
      *  depends. If one of them can't be initialized, neither can we,
 
185
      *  in which case return nil to show that our initialization failed. 
 
186
      *  If, however, the parent or sibling banner window we want initialized
 
187
      *  before us is not a CustomBannerWindow, then its initBannerWindow()
 
188
      *  won't have a return value, in which case we ignore the fact that
 
189
      *  it returns nil 
 
190
      */
 
191
       
 
192
     foreach(local ban in initBeforeMe)
 
193
       if(ban && !ban.initBannerWindow() && ban.ofKind(CustomBannerWindow))
 
194
         return nil;
 
195
        
 
196
     /*  
 
197
      * Create my banner window on screen; if this fails return nil
 
198
      * to indicate that the window could not be created
 
199
      */       
 
200
      
 
201
      return (inited_ = initBannerLayout());
 
202
 
 
203
  }
 
204
  
 
205
  /*
 
206
   *  Initialize my onscreen layout, normally through a call to showBanner(),
 
207
   *  whose return value this method should return, e.g.:
 
208
   *
 
209
   *     initBannerLayout()
 
210
   *     {
 
211
   *         return showBanner(nil, BannerAfter,  statuslineBanner,
 
212
   *           BannerTypeText, BannerAlignTop, 1, BannerSizeAbsolute,
 
213
   *           BannerStyleBorder); 
 
214
   *     }
 
215
   *
 
216
   *    By default we simply call initBannerLayout() using our bannerArgs.
 
217
   */
 
218
  
 
219
  initBannerLayout()
 
220
  {
 
221
     return showBanner(bannerArgs...);
 
222
  }
 
223
  
 
224
   
 
225
  
 
226
  /* 
 
227
   * The list of args used to define our screen layout, as they would be passed
 
228
   * to showBanner. This is used both by initBannerLayout and initBeforeMe.
 
229
   *
 
230
   *  The args should be listed in the form
 
231
   *
 
232
   *  bannerArgs = [parent, where, other, windowType, align, size, sizeUnits, styleFlags]
 
233
   *
 
234
   *  e.g.
 
235
   *    bannerArgs = [nil, BannerAfter,  statuslineBanner,
 
236
   *           BannerTypeText, BannerAlignTop, 1, BannerSizeAbsolute,
 
237
   *           BannerStyleBorder]
 
238
   *   
 
239
   */
 
240
    
 
241
  bannerArgs = nil
 
242
  
 
243
  /*
 
244
   *  The current contents to be displayed in this window, which could be 
 
245
   *  a string of text, or the HTML string to display a picture.
 
246
   *
 
247
   *  currentContents can be overridden to hold the initial contents
 
248
   *  we want this banner to display, but it should not otherwise be
 
249
   *  directly written to in game code. To display new contents in the
 
250
   *  banner, use updateContents() instead.  
 
251
   */
 
252
  currentContents = ''
 
253
  
 
254
  /*
 
255
   *  Is this banner currently active? Set to nil if you don't want to this
 
256
   *  CustomBannerWindow to be active at startup; thereafter use the deactivate()
 
257
   *  and activate() methods   
 
258
   */
 
259
   
 
260
  isActive = true
 
261
  
 
262
  /*
 
263
   *  deactivate a currently active banner; this removes it from the screen
 
264
   *  and prevents writing anything further to it. Be careful to respect the
 
265
   *  dependency order of banner windows when activating and deactivating
 
266
   *
 
267
   *  The argument is optional. If it is the constant true then the currentContents
 
268
   *  will be set to an empty string (''). If it is a string, then the currentContents
 
269
   *  will be set to that string (ready to be displayed when the banner is reactivated).
 
270
   */
 
271
   
 
272
  deactivate([args])  
 
273
  {     
 
274
     removeBanner();
 
275
     isActive = nil;
 
276
     
 
277
     if(args.length > 0)
 
278
     {
 
279
        local arg = args[1];
 
280
        switch(dataType(arg))
 
281
        {
 
282
           case TypeTrue:
 
283
             currentContents = '';
 
284
             break;
 
285
           case TypeSString:
 
286
             currentContents = arg;
 
287
             break;
 
288
        }
 
289
     }
 
290
     
 
291
  }
 
292
   
 
293
  /* 
 
294
   *  Activate a currently inactive banner; this restores it to the screen. 
 
295
   *  The argument is optional; if present and true then activate(true)
 
296
   *  displays the current contents of the banner window after activating it.
 
297
   *  If the first argument is a string then the string is displayed in the banner.   
 
298
   */ 
 
299
  activate([args])
 
300
  {
 
301
      if(isActive)
 
302
         return;
 
303
         
 
304
      isActive = true;
 
305
      initBannerWindow();
 
306
      
 
307
      if(args.length() > 0 && args[1] != nil)
 
308
      {
 
309
         if(dataType(args[1]) == TypeSString)
 
310
            updateContents(args...);
 
311
         else  
 
312
            showCurrentContents();
 
313
      }
 
314
  }
 
315
   
 
316
  removeBanner()
 
317
  {
 
318
    /*
 
319
     *  If I'm removed I can't be inited_ any more, and I'll need to be regarded
 
320
     *  as not inited_ in the event of being redisplayed in the future.
 
321
     */
 
322
  
 
323
    inited_ = nil;
 
324
    
 
325
    inherited;
 
326
  } 
 
327
   
 
328
   
 
329
  /*
 
330
   * Set this flag to true to clear the contents of the window before displaying
 
331
   * the new contents, e.g. to display a new picture that replaces the old one.
 
332
   */
 
333
  clearBeforeUpdate =  true
 
334
  
 
335
  /* 
 
336
   *  Set this to true to have this banner size to contents each time its
 
337
   *  contents are displayed. Note that not all interpreters support the size to
 
338
   *  contents so you should still set an appropriate initial size, and, where
 
339
   *  appropriate, call setSize() with the isAdvisory flag set.
 
340
   */
 
341
    
 
342
  autoSize = nil
 
343
  
 
344
  
 
345
  /*
 
346
   *  Update the contents of this banner window. This is the method to
 
347
   *  call to change what a banner displays.   
 
348
   *
 
349
   *  The second argument is optional. If present it overrides the 
 
350
   *  setting of clearBeforeUpdate: updateContents(cont, true) will
 
351
   *  clear the banner before the update, whereas updateContents(cont, nil)
 
352
   *  will not, whatever the value of clearBeforeUpdate. 
 
353
   */
 
354
   
 
355
  updateContents(cont, [args])
 
356
  {
 
357
    /*
 
358
     *  Update our current contents. Note that this takes place even if
 
359
     *  shouldDisplay is nil, so that if, for example, we are updated on
 
360
     *  a text-only interpreter on which this banner is not displayed, 
 
361
     *  and the game is saved there and subsequently restored on a full HTML 
 
362
     *  interpreter in which we are displayed, the HTML interpreter will know 
 
363
     *  what contents it needs to display in us.
 
364
     */
 
365
    currentContents = cont;
 
366
    
 
367
    showCurrentContents(args...);
 
368
  }
 
369
  
 
370
  /* Show the current contents of this banner window */  
 
371
  
 
372
  showCurrentContents([args])
 
373
  { 
 
374
    local clr;
 
375
    if(args.length > 0)
 
376
      clr = (args[1] != nil);
 
377
    else
 
378
      clr = clearBeforeUpdate;
 
379
     
 
380
    if(clr)    
 
381
       clearWindow();
 
382
          
 
383
     writeToBanner(currentContents);      
 
384
     
 
385
     if(autoSize)
 
386
       sizeToContents();
 
387
  }
 
388
  
 
389
  
 
390
   /*  This is called on each CustomBannerWindow after a Restore. */   
 
391
   
 
392
  restoreBannerDisplay()
 
393
  {
 
394
        /* 
 
395
         * It's possible a game was saved in a text-mode terp and
 
396
         * restored in an HTML one. In which case we need to initialize
 
397
         * this banner before attempting to display anything
 
398
         */
 
399
         
 
400
         if(shouldDisplay && handle_ == nil)
 
401
         {
 
402
            if(!initBannerWindow())
 
403
              return;
 
404
         }
 
405
                  
 
406
        
 
407
        /* redisplay my contents after a restore */        
 
408
        showCurrentContents(); 
 
409
  }  
 
410
  
 
411
  /*
 
412
   *  Alternatively we might have been saved in a terp that does
 
413
   *  use this banner and restored in one that doesn't, in which
 
414
   *  case we should remove ourselves. This is called on each BannerWindow
 
415
   *  after a restore, but before bannerTracker.restoreDisplayState().
 
416
   */
 
417
     
 
418
  restoreRemove()
 
419
  {
 
420
     if(!shouldDisplay)        
 
421
        removeBanner();     
 
422
  }
 
423
  
 
424
  
 
425
  /* show my initial contents on startup */
 
426
       
 
427
  initBannerDisplay()  {  showCurrentContents();   }
 
428
  
 
429
  /*
 
430
   *   We provide overrides for all the various banner manipulation methods
 
431
   *   that game code might call, in order to make it safe to call them even
 
432
   *   our shouldDisplay method returns nil and we don't - or shouldn't - exist.
 
433
   *   For each of these xxxYyy methods we provide an altXxxyyy method that is
 
434
   *   called when shouldDisplay is nil (e.g. because we're using a window to
 
435
   *   display graphics on an interpreter that doesn't have graphics capablities).
 
436
   *   By default these altXxxYyy methods do nothing, which in many cases will
 
437
   *   be fine, but if you do want something else to happen you can override
 
438
   *   the appropriate altXxxYyy method accordingly (e.g. to show a message in
 
439
   *   the main game window instead of this banner). This should make it easier
 
440
   *   to structure the rest of your game code without needing to worry about
 
441
   *   what happens on interpreters which don't display your banners.
 
442
   */
 
443
   
 
444
  clearWindow()
 
445
  {
 
446
    if(shouldDisplay)
 
447
      inherited();
 
448
    else
 
449
      altClearWindow();
 
450
  }
 
451
  
 
452
  altClearWindow() { }
 
453
    
 
454
  /* write to me, but only if I should display */ 
 
455
   
 
456
  writeToBanner(txt)
 
457
  {  
 
458
    if(shouldDisplay)
 
459
      inherited(txt);
 
460
    else
 
461
      altWriteToBanner(txt);
 
462
  }
 
463
  
 
464
  /*
 
465
   *  altWriteToBanner(txt) is called when our game code tries to display
 
466
   *  something in this banner, but our shouldDisplay method has ruled out
 
467
   *  displaying this banner. In this case we might want to write something
 
468
   *  to the main display instead. By default we do nothing here, but
 
469
   *  individual instances and/or subclasses can override this method as
 
470
   *  required.
 
471
   */
 
472
  
 
473
  altWriteToBanner(txt) { }
 
474
  
 
475
  
 
476
  /* 
 
477
   *  We don't provide alternative methods for the setSize and sizeToContents
 
478
   *  methods, since there would almost certainly be nothing for them to do.
 
479
   *  We simply do nothing if shouldDisplay is nil.
 
480
   */
 
481
  
 
482
  setSize(size, sizeUnits, isAdvisory)
 
483
  {
 
484
       if(shouldDisplay)
 
485
         inherited(size, sizeUnits, isAdvisory);
 
486
  }
 
487
     
 
488
  sizeToContents()
 
489
  {
 
490
        /* size our system-level window to our contents */
 
491
       if(shouldDisplay)  
 
492
         bannerSizeToContents(handle_);
 
493
  }
 
494
 
 
495
  captureOutput(func)
 
496
  {
 
497
     if(shouldDisplay)
 
498
       inherited(func);
 
499
     else
 
500
       altCaptureOutput(func);  
 
501
  }
 
502
  
 
503
  /* Simply execute the callback without changing the output stream */
 
504
  
 
505
  altCaptureOutput(func) { (func)(); }
 
506
           
 
507
  setOutputStream()
 
508
     {
 
509
        if(shouldDisplay)
 
510
           /* set my stream as the default */
 
511
           return outputManager.setOutputStream(outputStream_);
 
512
        else
 
513
           return altSetOutputStream();          
 
514
     }
 
515
     
 
516
  /* 
 
517
   *  Our caller, or rather our caller's caller, will expect us to return
 
518
   *  the current output stream, which means we must be sure to do this
 
519
   *  whatever else we do.  
 
520
   */
 
521
         
 
522
  altSetOutputStream() { return outputManager.curOutputStream; }         
 
523
  
 
524
  flushBanner() 
 
525
  { 
 
526
    if(shouldDisplay)
 
527
      inherited();
 
528
    else  
 
529
      altFlushBanner();
 
530
  }
 
531
         
 
532
  altFlushBanner() { }       
 
533
           
 
534
  setTextColor(fg, bg)
 
535
  {
 
536
    if(shouldDisplay)
 
537
      inherited(fg, bg);
 
538
    else
 
539
      altSetTextColor(fg, bg);
 
540
  }         
 
541
          
 
542
  altSetTextColor(fg, bg) { }
 
543
  
 
544
  setScreenColor(color)
 
545
  {
 
546
    if(shouldDisplay)
 
547
      inherited(color);
 
548
    else
 
549
      altSetScreenColor(color);
 
550
  }
 
551
  
 
552
  altSetScreenColor(color) { }
 
553
  
 
554
  cursorTo(row, col)
 
555
  {
 
556
    if(shouldDisplay)
 
557
      inherited(row, col);
 
558
    else
 
559
      altCursorTo(row, col);
 
560
  }             
 
561
           
 
562
  /* 
 
563
   *  If this banner isn't displaying we can't do anything directly comparable
 
564
   *  to setting the cursot to a particular column and row in it, but we might
 
565
   *  want to do something else instead, like inserting so many blank lines in 
 
566
   *  the main window.            
 
567
   */       
 
568
  altCursorTo(row, col) { }         
 
569
;
 
570
 
 
571
 
 
572
 
 
573
 /*
 
574
  *  Initialize or reinitialize what all CustomBanners display at startup or
 
575
  *  after an UNDO
 
576
  */
 
577
 
 
578
customBannerInit: InitObject, PostUndoObject
 
579
  execBeforeMe =  [bannerInit]
 
580
  
 
581
  execute()
 
582
  {
 
583
     /* first ensure that all banner windows that need to exist do exist */
 
584
     
 
585
//     forEachInstance(CustomBannerWindow, new function(win) {
 
586
//        if(win.shouldDisplay && win.handle_ == nil)
 
587
//          win.initBannerWindow(); 
 
588
//     } );
 
589
  
 
590
     /* then show the current contents of every active banner */
 
591
  
 
592
     forEachInstance(CustomBannerWindow, {win: win.showCurrentContents() } );
 
593
  }
 
594
;
 
595
 
 
596
  /*
 
597
   *  Reinitialize what all the CustomBanners display on restoring. This requires
 
598
   *  a different procedure since we can't be sure that we're being restored on
 
599
   *  the same class of interpreter as we were saved on.
 
600
   */
 
601
 
 
602
customBannerRestore: PostRestoreObject
 
603
   execBeforeMe = [bannerTracker] 
 
604
 
 
605
   execute()
 
606
   {
 
607
   
 
608
     /*
 
609
      *  If we save in one terp, restore in the second terp, save in the second
 
610
      *  terp, then restore in the first terp, when different rules apply about
 
611
      *  displaying banners in the two terps, then windows removed in the second
 
612
      *  terp could still be marked as inited_ in the restore file that comes
 
613
      *  back to the first terp. To get round this, on restoration we ensure
 
614
      *  that each CustomBanner's inited_ property in fact corresponds to whether
 
615
      *  it has an active handle_, otherwise the attempt to reinitialize missing
 
616
      *  banners might fail.
 
617
      */
 
618
     forEachInstance(CustomBannerWindow, {win: win.inited_ = (win.handle_ != nil) } );
 
619
       
 
620
     forEachInstance(CustomBannerWindow, {win: win.restoreBannerDisplay() } );
 
621
   }
 
622
 
 
623
;
 
624
 
 
625
customBannerRestoreRemove: PostRestoreObject
 
626
  execAfterMe = [bannerTracker]
 
627
 
 
628
  execute()
 
629
  {
 
630
      forEachInstance(CustomBannerWindow, {win: win.restoreRemove() } );
 
631
  }
 
632
;
 
633
 
 
634
 /*
 
635
  *  If we display a menu then we need to remove any active banners from the
 
636
  *  screen before the menu displays and restore them to the screen on exiting
 
637
  *  from the menu
 
638
  */
 
639
modify MenuItem
 
640
  display()
 
641
  {
 
642
    /*
 
643
     *  First we store a list of all the banners that are currently
 
644
     *  displaying
 
645
     */
 
646
    local vec = new Vector(10);
 
647
     
 
648
    forEachInstance(CustomBannerWindow, new function(ban) {
 
649
        if(ban.shouldDisplay)
 
650
          vec.append(ban);  } );    
 
651
          
 
652
    /* deactive all active banners */      
 
653
          
 
654
    foreach(local ban in vec)
 
655
       ban.deactivate();
 
656
        
 
657
    try
 
658
    {
 
659
       /* carry out the inherited menu display */
 
660
       inherited();
 
661
    }  
 
662
      
 
663
   /* 
 
664
    *  Restore all the banners in our list of banners that were previously
 
665
    *  displayed. To ensure that they are activated in the right order
 
666
    *  we make what may be several passes through the list. On each pass
 
667
    *  we activate only those banners that don't depend on any inactive
 
668
    *  banners for their activation. Each time we activate a banner, we
 
669
    *  remove it from the list. On the next pass through the list any
 
670
    *  banners that depended on banners we have just activated may now themselves
 
671
    *  be activated, so we can carry on until every banner has been activated
 
672
    *  and removed from the list.  
 
673
    */  
 
674
   finally
 
675
   {
 
676
     while(vec.length())
 
677
     {
 
678
       local bannerRemoved = nil;
 
679
       
 
680
       foreach(local ban in vec)
 
681
       {
 
682
 
 
683
       
 
684
         if(ban.bannerArgs[1] != nil && ban.bannerArgs[1].handle_ == nil)
 
685
            continue;
 
686
           
 
687
         if(ban.bannerArgs[3] != nil && ban.bannerArgs[3].handle_ == nil)
 
688
            continue; 
 
689
             
 
690
         ban.activate(true);
 
691
         vec.removeElement(ban);
 
692
         bannerRemoved = true;      
 
693
       }  
 
694
       
 
695
       /*
 
696
        *  If we didn't remove any banners on this pass through, we're
 
697
        *  potentially in an infinite loop, so we'd better break out
 
698
        *  of it.
 
699
        */
 
700
       if(!bannerRemoved)
 
701
         break;
 
702
     }
 
703
      
 
704
    }            
 
705
  }
 
706
 
707