Changeset 20713

Show
Ignore:
Timestamp:
08/13/07 09:52:30 (1 year ago)
Author:
patrick
Message:

Document the unique and, in my opinion, interesting aspects of VR Juggler
application programming with Cocoa on Mac OS X. This context offers points of
extension not available on any other operating system.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • juggler/trunk/modules/vrjuggler/doc/programmer.guide/programmer.guide.xml

    r19979 r20713  
    545545                  </calloutlist> 
    546546               </programlistingco> 
     547            </section> 
     548 
     549            <section> 
     550               <title>Mac OS X Considerations</title> 
     551 
     552               <indexterm zone="section.app.main.structure"> 
     553                  <primary>applications</primary> 
     554 
     555                  <secondary>main function</secondary> 
     556 
     557                  <tertiary>Mac OS X</tertiary> 
     558               </indexterm> 
     559 
     560               <para>VR Juggler 2.2 introduces support for Cocoa on Mac OS X. 
     561               Cocoa itself is quite different than the X11 or Win32 APIs as 
     562               far as restrictions on threading and the implementation of the 
     563               <function>main()</function> function. The fundamental issue, 
     564               however, is that both the <classname>NSApplication</classname> 
     565               singleton object and the <classname>vrj::Kernel</classname> 
     566               singleton object want to be in charge of application execution. 
     567               A balance has been struck that generally allows VR Juggler 
     568               applications to look and act the same on Mac OS X as on any 
     569               other platform while still taking advantage of unique features 
     570               that Cocoa and Mac OS X have to offer.</para> 
     571 
     572               <section> 
     573                  <title>Application Bundles</title> 
     574 
     575                  <para>First and foremost, when using a Cocoa-aware version 
     576                  of VR Juggler, applications have to be constructed as 
     577                  bundles. The details of OS X application bundles are far 
     578                  beyond the scope of this document. However, VR Juggler 
     579                  provides just about everything that is required to make an 
     580                  application bundle. This makes sense because VR Juggler is 
     581                  an application framework that dictates how applications are 
     582                  written and executed. Thus, it also provides the core 
     583                  information required for proper application construction and 
     584                  execution on Mac OS X.</para> 
     585 
     586                  <para>To get started, it is helpful to understand what the 
     587                  application bundle will look like when we have everything in 
     588                  place. An application bundle is a directory structure with a 
     589                  special name. For example, <filename>MPApp.app</filename> 
     590                  will show up in the <application>Finder</application> as an 
     591                  application named <quote>MPApp</quote> that can be 
     592                  double-clicked. The name can contain spaces if so 
     593                  desired.</para> 
     594 
     595                  <para>Under the base directory is the 
     596                  <filename>Contents</filename> subdirectory. It will contain 
     597                  the files <filename>Info.plist</filename> and 
     598                  <filename>PkgInfo</filename>.</para> 
     599 
     600                  <para>Next, there are two subdirectories 
     601                  <filename>MacOS</filename> and 
     602                  <filename>Resources</filename>. Compiled binaries should 
     603                  normally go into the <filename>MacOS</filename> 
     604                  subdirectory. If nothing else, the bundle executable (the 
     605                  compiled application binary) <emphasis>must</emphasis> go in 
     606                  the <filename>MacOS</filename> directory. Shared libraries 
     607                  can go into the <filename>Resources</filename> directory if 
     608                  desired. Generally, though, the 
     609                  <filename>Resources</filename> directory will contain 
     610                  platform-independent data files.</para> 
     611 
     612                  <para>In the resources directory, there will be another 
     613                  subdirectory <filename>en.lproj</filename><footnote> 
     614                        <para>This is the English language project data. 
     615                        Translations of <filename>MainMenu.nib</filename> 
     616                        would go into the appropriate language-specific 
     617                        subdirectory.</para> 
     618                     </footnote> which in turn contains 
     619                  <filename>MainMenu.nib</filename> from 
     620                  <filename>$VJ_BASE_DIR/share/vrjuggler/data/bundle</filename>. 
     621                  This is a critical part of the application bundle, and it is 
     622                  very important that this NIB behave correctly. The NIB 
     623                  defines the user interface for the VR Juggler application, 
     624                  and what is provided with VR Juggler is set up and ready to 
     625                  go for the most common cases. It is possible to use a 
     626                  different <filename>MainMenu.nib</filename>, but customizing 
     627                  it will require care.</para> 
     628 
     629                  <sidebar> 
     630                     <para>The ability to customize the user interface of the 
     631                     VR Juggler application by using a different 
     632                     <filename>MainMenu.nib</filename> and a different 
     633                     application delegate (see <xref 
     634                     linkend="section.custom.delegate" />) makes VR Juggler on 
     635                     Mac OS X unique with respect to other platforms. Whereas 
     636                     the user interface used with <productname 
     637                     class="registered">Microsoft Windows</productname> and 
     638                     the X Window System is essentially hard coded, the Mac OS 
     639                     X Cocoa interface has been designed with flexibility in 
     640                     mind. Simply put, the Cocoa support in VR Juggler 
     641                     leverages the dynamic nature of Cocoa to provide features 
     642                     that are not so easily available on other 
     643                     platforms.</para> 
     644                  </sidebar> 
     645 
     646                  <section> 
     647                     <title><filename>Info.plist</filename></title> 
     648 
     649                     <para>The files and directories needed for application 
     650                     bundle creation can be found in the directory 
     651                     <filename>$VJ_BASE_DIR/share/vrjuggler/data/bundle</filename>. 
     652                     The first of these is 
     653                     <filename>Contents/Info.plist</filename>, the basic 
     654                     property list for an application bundle. Open it with the 
     655                     Property List Editor application and change the 
     656                     <literal>@APP_NAME@</literal> strings accordingly for the 
     657                     name of the application being constructed. For the 
     658                     <literal>CFBundleExecutable</literal> property, be sure 
     659                     to change <literal>@APP_NAME@</literal> to be the name of 
     660                     the executable that will be in the Contents/MacOS 
     661                     directory. Other properties to change are those that 
     662                     include copyright and version information.</para> 
     663                  </section> 
     664 
     665                  <section> 
     666                     <title><filename>PkgInfo</filename></title> 
     667 
     668                     <para>The contents of 
     669                     <filename>Contents/PkgInfo</filename> define the 
     670                     application bundle as an application bundle. The contents 
     671                     of the file will often be <literal>APPL????</literal>, 
     672                     though other characters are allowed in place of the 
     673                     <literal>????</literal> part.</para> 
     674                  </section> 
     675 
     676                  <section> 
     677                     <title><filename>Resources</filename> Directory</title> 
     678 
     679                     <para>The <filename>Contents/Resources</filename> 
     680                     directory contains data files related to application 
     681                     execution. The file <filename>vrjuggler.plist</filename>, 
     682                     the use of which is highly recommended, must be put in 
     683                     this directory. The application bundle icon file (a file 
     684                     with the extension <filename>.icns</filename>) is also 
     685                     stored in this directory. A default icon file, 
     686                     <filename>vrjuggler.icns</filename>, can be used, or a 
     687                     custom one can be made. The file to use must be named in 
     688                     <filename>Contents/Info.plist</filename>.</para> 
     689                  </section> 
     690 
     691                  <section> 
     692                     <title><filename>vrjuggler.plist</filename></title> 
     693 
     694                     <para>For VR Juggler application bundles, a useful file 
     695                     is <filename>vrjuggler.plist</filename>. The default 
     696                     version of this file from 
     697                     <filename>$VJ_BASE_DIR/share/vrjuggler/data/bundle</filename> 
     698                     disables VR Juggler configuration file loading through 
     699                     <classname>NSApplication</classname> channels and 
     700                     identifies the <classname>NSApplication</classname> 
     701                     delegate class type that will be used (see <xref 
     702                     linkend="section.custom.delegate" />). These are set 
     703                     using the properties <literal>VRJConfigHandling</literal> 
     704                     and <literal>VRJDelegateClass</literal> 
     705                     respectively.</para> 
     706 
     707                     <para>The <quote>configuration file loading through 
     708                     <classname>NSApplication</classname> channels</quote> bit 
     709                     has to do with associating VR Juggler configuration files 
     710                     with application bundles. There are different means by 
     711                     which data can be delivered to applications on Mac OS X. 
     712                     For example, double-clicking on a file in the 
     713                     <application>Finder</application> (or selecting 
     714                     <guimenuitem>Open With</guimenuitem> in the context menu 
     715                     for the file) causes a registered handler application to 
     716                     be opened. The file is given to the application once it 
     717                     has opened through the 
     718                     <classname>NSApplication</classname> event system. By 
     719                     setting the <literal>VRJConfigHandling</literal> property 
     720                     to <literal>false</literal> in 
     721                     <filename>Contents/Resources/vrjuggler.plist</filename>, 
     722                     this capability is disabled for the application bundle in 
     723                     question. That is, the application bundle will ignore the 
     724                     <classname>NSApplication</classname> events pertaining to 
     725                     configuration files to be loaded as a result of 
     726                     double-clicking on the said files. Configuration files 
     727                     can still be opened on the fly using the 
     728                     <guimenu>File</guimenu> menu defined in the default NIB 
     729                     (see below).</para> 
     730                  </section> 
     731 
     732                  <section> 
     733                     <title><filename>en.lproj</filename> Directory</title> 
     734 
     735                     <para>The <filename>en.lproj</filename> subdirectory of 
     736                     <filename>Contents/Resources</filename> contains 
     737                     information translated in the English language. The most 
     738                     important item in this directory is MainMenu.nib 
     739                     (discussed next), but the optional file 
     740                     <filename>$VJ_BASE_DIR/share/vrjuggler/data/bundle/InfoPlist.strings</filename>, 
     741                     or some other version of same, can be copied into this 
     742                     directory. Other language-specific directories can be 
     743                     created as subdirectories of 
     744                     <filename>Contents/Resources</filename> as necessary. 
     745                     They, too, must contain a 
     746                     <filename>MainMenu.nib</filename>.</para> 
     747                  </section> 
     748 
     749                  <section> 
     750                     <title><filename>MainMenu.nib</filename></title> 
     751 
     752                     <para>This is the NIB for the VR juggler application 
     753                     bundle. It has been designed using Interface Builder with 
     754                     a simple user interface that knows how to interact with a 
     755                     VR Juggler application. The interface includes the 
     756                     <guimenu>File</guimenu> menu with the item for opening VR 
     757                     Juggler configuration files on the fly. Other menus and 
     758                     menu items can be added by making a custom MainMenu.nib. 
     759                     Using the version from 
     760                     <filename>$VJ_BASE_DIR/share/vrjuggler/data/bundle</filename> 
     761                     as a starting point is a good idea. Note that 
     762                     <filename>MainMenu.nib</filename> is a directory, and it 
     763                     should be copied recursively to 
     764                     <filename>Contents/Resources/en.lproj</filename>.</para> 
     765                  </section> 
     766               </section> 
     767 
     768               <section id="section.cocoa.app.execution"> 
     769                  <title>Application Execution</title> 
     770 
     771                  <para>The <function>main()</function> function 
     772                  implementation show in <xref 
     773                  linkend="section.app.main.structure" /> will work without 
     774                  any problems on Mac OS X. However, many of the VR Juggler 
     775                  sample and test applications have a slightly more 
     776                  complicated <function>main()</function> function. In 
     777                  particular, these applications usually determine whether the 
     778                  user has passed in any arguments through the command line 
     779                  and exit with an error message explaining how to run the 
     780                  application if none were given. The example below shows how 
     781                  this is commonly done with an <literal>if</literal> 
     782                  statement before anything else:</para> 
     783 
     784                  <programlisting linenumbering="numbered">@include &lt;iostream&gt; 
     785#include &lt;cstdlib&gt; 
     786#include &lt;vrj/Kernel/Kernel.h&gt; 
     787#include &lt;simpleApp.h&gt; 
     788 
     789int main(int argc, char* argv[]) 
     790{ 
     791   if ( argc &lt;= 1 ) 
     792   { 
     793      std::cout &lt;&lt; "Usage: " &lt;&lt; argv[0] 
     794                &lt;&lt; "cfgfile[0] cfgfile[1] ... cfgfile[n]" 
     795                &lt;&lt; std::endl; 
     796      std::exit(EXIT_FAILURE); 
     797   } 
     798 
     799   vrj::Kernel* kernel = vrj::Kernel::instance(); // Get the kernel 
     800   simpleApp* app      = new simpleApp();         // Create the app object 
     801 
     802   kernel-&gt;loadConfigFile(...);             // Configure the kernel 
     803   kernel-&gt;start();                         // Start the kernel thread 
     804   kernel-&gt;setApplication(app);             // Give application to kernel 
     805   kernel-&gt;waitForKernelStop();             // Block until kernel stops 
     806 
     807   return 0; 
     808}</programlisting> 
     809 
     810                  <para>This approach will not necessarily work on Mac OS X 
     811                  because users normally expect to be able to launch an 
     812                  application by double-clicking on its icon in the 
     813                  <application>Finder</application> and then loading data into 
     814                  it through a GUI. If a user launched the above application 
     815                  that way, the application would exit immediately, and the 
     816                  user would have to open the 
     817                  <application>Console</application> application to find out 
     818                  what went wrong. Launching from the command line, while 
     819                  perfectly valid on Mac OS X, is simply not what users 
     820                  expect. At the same time, remote launching of VR juggler 
     821                  applications in a cluster will almost certainly require 
     822                  launching without the use of the 
     823                  <application>Finder</application> and passing in arguments 
     824                  through <varname>argv</varname>. This is definitely the case 
     825                  when using <ulink 
     826                  url="https://realityforge.vrsource.org/trac/maestro/">Maestro</ulink>.</para> 
     827 
     828                  <para>If a VR Juggler application will be run on Mac OS X, 
     829                  the programmer has to decide whether command line processing 
     830                  is critical to the execution of the application. There are 
     831                  three options available to VR Juggler programmers on Mac OS 
     832                  X: rely exclusively on command line arguments to launch, 
     833                  handle no command line arguments, or allow the use of 
     834                  command line arguments but do not require them. Given that 
     835                  VR Juggler applications written on all other platforms are 
     836                  highly likely to rely exclusively on command line arguments 
     837                  to launch, the first choice is expected to be the most 
     838                  commonly chosen approach. Nevertheless, we will explain all 
     839                  three.</para> 
     840 
     841                  <section> 
     842                     <title>Relying Exclusively on Command Line Arguments to 
     843                     Launch</title> 
     844 
     845                     <para>If a VR Juggler application used on Mac OS X will 
     846                     rely solely on command line arguments to launch, it is 
     847                     operating in exactly the same manner as on all other 
     848                     platforms. The <function>main()</function> function can 
     849                     be written as is shown in this document to exit if no 
     850                     command line arguments are supplied. There is still one 
     851                     more thing to do, though. In the 
     852                     <filename>Resources</filename> directory of the 
     853                     application bundle, there must be a property list file in 
     854                     the bundle called 
     855                     <filename>Contents/Resources/vrjuggler.plist</filename>. 
     856                     This property list must have the property 
     857                     <literal>VRJConfigHandling</literal> set to 
     858                     <literal>false</literal>. This is the default setting for 
     859                     this property if the <filename>vrjuggler.plist</filename> 
     860                     file that comes with VR Juggler was used in constructing 
     861                     the application bundle.</para> 
     862 
     863                     <para>To execute the application, run the program in 
     864                     <filename>&lt;appname&gt;.app/Contents/MacOS</filename> 
     865                     from a command prompt and pass in the command line 
     866                     arguments. The appropriate event handling will be set up 
     867                     so that the application will behave just as any other Mac 
     868                     OS X application.</para> 
     869                  </section> 
     870 
     871                  <section> 
     872                     <title>Handling No Command Line Arguments at Launch 
     873                     Time</title> 
     874 
     875                     <para>If no command line arguments are to be handled 
     876                     (i.e., the application is to behave the same as typical 
     877                     Mac OS X applications), then the 
     878                     <function>main()</function> function shown above needs to 
     879                     change. Specifically, it cannot exit with an error 
     880                     message if no command line arguments are passed in. To 
     881                     keep the application portable, a preprocessor conditional 
     882                     can be used, as shown in <xref 
     883                     linkend="example.cocoa.no.cmd.flags" />. Then, in 
     884                     <filename>Contents/Resources/vrjuggler.plist</filename>, 
     885                     set the <literal>VRJConfigHandling</literal> property to 
     886                     <literal>true</literal>. To execute the application, 
     887                     double-click the application bundle icon in the 
     888                     <application>Finder</application> or use the 
     889                     <command>open</command> command from a command 
     890                     prompt.</para> 
     891 
     892                     <example id="example.cocoa.no.cmd.flags"> 
     893                        <title>Ignoring Command Line Arguments on Mac OS 
     894                        X</title> 
     895 
     896                        <programlisting linenumbering="numbered">@include &lt;iostream&gt; 
     897#include &lt;cstdlib&gt; 
     898#include &lt;vrj/Kernel/Kernel.h&gt; 
     899#include &lt;simpleApp.h&gt; 
     900 
     901int main(int argc, char* argv[]) 
     902{ 
     903#if ! defined(VRJ_USE_COCOA) 
     904   if ( argc &lt;= 1 ) 
     905   { 
     906      std::cout &lt;&lt; "Usage: " &lt;&lt; argv[0] 
     907                &lt;&lt; "cfgfile[0] cfgfile[1] ... cfgfile[n]" 
     908                &lt;&lt; std::endl; 
     909      std::exit(EXIT_FAILURE); 
     910   } 
     911#endif 
     912 
     913   vrj::Kernel* kernel = vrj::Kernel::instance(); // Get the kernel 
     914   simpleApp* app      = new simpleApp();         // Create the app object 
     915 
     916#if ! defined(VRJ_USE_COCOA) 
     917   kernel-&gt;loadConfigFile(...);             // Configure the kernel 
     918#endif 
     919   kernel-&gt;start();                         // Start the kernel thread 
     920   kernel-&gt;setApplication(app);             // Give application to kernel 
     921   kernel-&gt;waitForKernelStop();             // Block until kernel stops 
     922 
     923   return 0; 
     924}</programlisting> 
     925                     </example> 
     926                  </section> 
     927 
     928                  <section> 
     929                     <title>Allowing Optional Use of Command Line 
     930                     Arguments</title> 
     931 
     932                     <para>A compromise can be struck between the previous two 
     933                     options by allowing optional use of command line 
     934                     arguments. The compromise is simple: do not require 
     935                     command line arguments but still handle them if they are 
     936                     given. The form of the main() function that allows this 
     937                     is shown in <xref 
     938                     linkend="example.cocoa.optional.cmd.flags" />. Note that 
     939                     the <literal>#if</literal> around the call to 
     940                     <methodname>vrj::Kernel::loadConfigFile()</methodname> 
     941                     has been removed.</para> 
     942 
     943                     <example id="example.cocoa.optional.cmd.flags"> 
     944                        <title>Handling Optional Command Line Arguments on Mac 
     945                        OS X</title> 
     946 
     947                        <programlisting linenumbering="numbered">@include &lt;iostream&gt; 
     948#include &lt;cstdlib&gt; 
     949#include &lt;vrj/Kernel/Kernel.h&gt; 
     950#include &lt;simpleApp.h&gt; 
     951 
     952int main(int argc, char* argv[]) 
     953{ 
     954#if ! defined(VRJ_USE_COCOA) 
     955   if ( argc &lt;= 1 ) 
     956   { 
     957      std::cout &lt;&lt; "Usage: " &lt;&lt; argv[0] 
     958                &lt;&lt; "cfgfile[0] cfgfile[1] ... cfgfile[n]" 
     959                &lt;&lt; std::endl; 
     960      std::exit(EXIT_FAILURE); 
     961   } 
     962#endif 
     963 
     964   vrj::Kernel* kernel = vrj::Kernel::instance(); // Get the kernel 
     965   simpleApp* app      = new simpleApp();         // Create the app object 
     966 
     967   kernel-&gt;loadConfigFile(...);             // Configure the kernel 
     968   kernel-&gt;start();                         // Start the kernel thread 
     969   kernel-&gt;setApplication(app);             // Give application to kernel 
     970   kernel-&gt;waitForKernelStop();             // Block until kernel stops 
     971 
     972   return 0; 
     973}</programlisting> 
     974                     </example> 
     975 
     976                     <para>The only decision left to make is what value to use 
     977                     for the <literal>VRJConfigHandling</literal> property in 
     978                     <filename>Contents/Resources/vrjuggler.plist</filename>. 
     979                     If we set it to <filename>false</filename> (recall that 
     980                     that is the default in the 
     981                     <filename>$VJ_BASE_DIR/share/vrjuggler/data/bundle/vrjuggler.plist</filename> 
     982                     version), the application cannot be used as a handler for 
     983                     VR Juggler configuration files. If we set it to true, we 
     984                     have to be prepared for configuration files to come in 
     985                     other than through the command line or through the use of 
     986                     the <guimenu>File</guimenu> menu. The default 
     987                     <classname>NSApplication</classname> delegate 
     988                     (<classname>VRJBasicDelegate</classname>) can deal with 
     989                     either case. The default behavior for VR Juggler sample 
     990                     applications is to allow configuration files to be 
     991                     specified on the command line through the use of a 
     992                     <function>main()</function> function similar to that 
     993                     shown in <xref 
     994                     linkend="example.cocoa.optional.cmd.flags" /> and to 
     995                     leave the property <literal>VRJConfigHandling</literal> 
     996                     in 
     997                     <filename>Contents/Resources/vrjuggler.plist</filename> 
     998                     set to <literal>false</literal>.</para> 
     999                  </section> 
     1000               </section> 
    5471001            </section> 
    5481002         </section> 
     
    1083211286         </section> 
    1083311287      </chapter> 
     11288 
     11289      <chapter> 
     11290         <title>Advanced Topics</title> 
     11291 
     11292         <para>In this chapter, we address topics that are considered to be 
     11293         for advanced uses of VR Juggler. This is not to say that they are 
     11294         difficult concepts or hard-to-use features. Rather, they are not 
     11295         things that the average VR Juggler application programmer will do or 
     11296         even need to be aware of when writing VR software. These are 
     11297         important, interesting, and worthwhile topics nonetheless.</para> 
     11298 
     11299         <section id="section.custom.delegate"> 
     11300            <title>Making a Custom <classname>NSApplication</classname> 
     11301            Delegate on Mac OS X</title> 
     11302 
     11303            <para>The Cocoa support for VR Juggler is designed to take 
     11304            advantage of the flexibility of Cocoa application design and, more 
     11305            generally, the flexibility of the Objective-C language. 
     11306            Specifically, VR Juggler application programmers targeting Mac OS 
     11307            X have a unique opportunity to customize the behavior of VR 
     11308            Juggler application behavior. Behind the scenes, 
     11309            <classname>vrj::CocoaWrapper</classname> registers a delegate, an 
     11310            instance of the Objective-C class 
     11311            <classname>VRJBasicDelegate</classname>, with the 
     11312            <classname>NSApplication</classname> singleton. Messages from 
     11313            <classname>NSApplication</classname> are received and handled by 
     11314            this object. VR Juggler application programmers can tailor the 
     11315            handling of these messages by creating and using a custom 
     11316            <classname>NSApplication</classname> delegate.</para> 
     11317 
     11318            <para>The full details of delegates and customizing the behavior 
     11319            of <classname>NSApplication</classname> are well beyond the scope 
     11320            of this document. Readers are referred to Apple's documentation 
     11321            for <ulink 
     11322            url="http://developer.apple.com/referencelibrary/GettingStarted/GS_Cocoa/index.html">Cocoa</ulink> 
     11323            and <ulink 
     11324            url="http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/Classes/NSApplication_Class/Reference/Reference.html"><classname>NSApplication</classname> 
     11325            class reference</ulink>. We will proceed from here under the 
     11326            assumption that readers are already familiar with the relevant and 
     11327            necessary topics including, but not limited to, Objective-C, 
     11328            Objective-C++, and <classname>NSApplication</classname>.</para> 
     11329 
     11330            <para>In a typical Cocoa application, the delegate would be given 
     11331            to the <classname>NSApplication</classname> singleton instance by 
     11332            sending that object the<methodname>-setDelegate:</methodname> 
     11333            message. Doing so, however, will interfere with the proper 
     11334            functionality of VR Juggler. Thus, the registration mechanism is 
     11335            done using the <literal>VRJDelegateClass</literal> property in 
     11336            <filename>Contents/Resources/vrjuggler.plist</filename>. The 
     11337            default value of this property is 
     11338            <classname>VRJBasicDelegate</classname>. By changing this to the 
     11339            name of the custom class, <classname>vrj::CocoaWrapper</classname> 
     11340            will instantiate, register, and use the custom class instead. 
     11341            (Being able to do this is a simple and elegant feature of 
     11342            Objective-C.)</para> 
     11343 
     11344            <para>There are two ways to create a custom delegate class. The 
     11345            class can either stand on its own as a subclass of 
     11346            <classname>NSObject</classname>, or it can derive from 
     11347            <classname>VRJBasicDelegate</classname>. The decision about which 
     11348            approach to use depends on whether complete or partial 
     11349            customization is desired. For this discussion, we will focus on 
     11350            complete customization—that is, creating a new delegate type by 
     11351            deriving from <classname>NSObject</classname>.</para> 
     11352 
     11353            <sidebar> 
     11354               <para>It is worth pointing out that 
     11355               <classname>VRJBasicDelegate</classname> is not necessarily 
     11356               designed to be extended. It can be customized, but mainly, this 
     11357               customization is done by overriding methods to 
     11358               <emphasis>replace</emphasis> the behavior of the base class 
     11359               rather than to extend it. Refer to the implementation of 
     11360               <classname>VRJBasicDelegate</classname> to get a better idea of 
     11361               the implications of overriding it for the purposes of 
     11362               customization. The code can be found in 
     11363               <filename>modules/vrjuggler/vrj/Kernel/VRJBasicDelegate.mm</filename> 
     11364               and 
     11365               <filename>modules/vrjuggler/vrj/Kernel/VRJBasicDelegate.h</filename>.</para> 
     11366            </sidebar> 
     11367 
     11368            <para>As noted, the job of the delegate is to respond to messages 
     11369            from <classname>NSApplication</classname>. The messages that are 
     11370            delivered to the delegate are documented in the 
     11371            <classname>NSApplication</classname> reference, and we will not 
     11372            duplicate that information here. Instead, we will concentrate on a 
     11373            small subset of these messages related to document loading. The 
     11374            purpose is twofold. First, this explanation helps in understanding 
     11375            the implementation of <classname>VRJBasicDelegate</classname>, 
     11376            thereby clarifying options for customizing the behavior of that 
     11377            class. Second, document loading is a prominent topic in Mac OS X 
     11378            application development, and it is likely to be of the most 
     11379            interest to VR application programmers, too.</para> 
     11380 
     11381            <para>To get things under way, have a glance at <xref 
     11382            linkend="example.basic.delegate.impl" />. It is a relatively 
     11383            simple class that uses Objective-C++ capabilities to create a 
     11384            simple bridge between <filename>NSApplication</filename> and 
     11385            <classname>vrj::Kernel</classname>. The documents being loaded in 
     11386            this case are VR Juggler configuration files, but it is not much 
     11387            of a leap to imagine the use of any other document type (i.e., 
     11388            data file used as input to the application). We will review each 
     11389            of the methods of this class.</para> 
     11390 
     11391            <example id="example.basic.delegate.impl"> 
     11392               <title><filename>MyDelegate.mm</filename>: Basic Delegate 
     11393               Implementation</title> 
     11394 
     11395               <programlisting linenumbering="numbered">#import &lt;Foundation/NSArray.h&gt; 
     11396#import &lt;Foundation/NSString.h&gt; 
     11397#import &lt;AppKit/NSApplication.h&gt; 
     11398 
     11399#include &lt;vrj/Kernel/Kernel.h&gt; 
     11400 
     11401 
     11402@interface MyDelegate : NSObject 
     11403{ 
     11404   BOOL mLoadConfigs; 
     11405} 
     11406@end 
     11407 
     11408@implementation MyDelegate 
     11409   -(id) init 
     11410   { 
     11411      mLoadConfigs = YES; 
     11412      return [super init]; 
     11413   } 
     11414 
     11415   -(void) setLoadConfigs:(BOOL) load 
     11416   { 
     11417      mLoadConfigs = load; 
     11418   } 
     11419 
     11420   -(BOOL) applicationShouldTerminateAfterLastWindowClosed:(NSApplication*) sender 
     11421   { 
     11422      // We return NO here because we have a different way of shutting down 
     11423      // the application. When vrj::Kernel::stop() is invoked, it will cause 
     11424      // the application run loop to stop by invoking 
     11425      // vrj::CocoaWrapper::stop(). 
     11426      return NO; 
     11427   } 
     11428 
     11429   -(BOOL) application:(NSApplication*) theApplication 
     11430              openFile:(NSString*) file 
     11431   { 
     11432      if ( mLoadConfigs ) 
     11433      { 
     11434         vrj::Kernel::instance()-&gt;loadConfigFile([file UTF8String]); 
     11435      } 
     11436 
     11437      return YES; 
     11438   } 
     11439 
     11440   -(void) application:(NSApplication*) theApplication 
     11441             openFiles:(NSArray*) files 
     11442   { 
     11443      if ( mLoadConfigs ) 
     11444      { 
     11445         const int count = [files count]; 
     11446         for ( int i = 0; i &lt; count; ++i ) 
     11447         { 
     11448            NSString* file = [files objectAtIndex:i]; 
     11449            vrj::Kernel::instance()-&gt;loadConfigFile([file UTF8String]); 
     11450         } 
     11451      } 
     11452   } 
     11453@end</programlisting> 
     11454            </example> 
     11455 
     11456            <section> 
     11457               <title>Data Members</title> 
     11458 
     11459               <para>The class <classname>MyDelegate</classname> has a single 
     11460               data member, <varname>mLoadConfigs</varname>, that determines 
     11461               how instances of this class will respond to certain messages 
     11462               sent by <classname>NSApplication</classname>. The value will be 
     11463               changed if <classname>vrj::CocoaWrapper</classname> sends the 
     11464               <methodname>-setLoadConfigs:</methodname> message as a result 
     11465               of the property <literal>VRJConfigHandling</literal> being set 
     11466               in 
     11467               <filename>Contents/Resources/vrjuggler.plist</filename>.</para> 
     11468            </section> 
     11469 
     11470            <section> 
     11471               <title>Designated Initializer</title> 
     11472 
     11473               <para>Every <classname>NSObject</classname> subclass has a 
     11474               designated initializer. In the case of the VR Juggler 
     11475               <classname>NSApplication</classname> delegate, this is the 
     11476               basic <methodname>-init</methodname> method. The current usage 
     11477               of the application delegate requires that this method be the 
     11478               designated initializer, meaning that it is the only initializer 
     11479               that will be invoked upon creating the delegate instance. This 
     11480               limitation may be fixed in a future version of 
     11481               <classname>vrj::CocoaWrapper</classname>. If the custom 
     11482               delegate does not accept the -init message, then the inherited 
     11483               <classname>NSObject</classname> version will be used.</para> 
     11484 
     11485               <para>In the meantime, this is where all object initialization 
     11486               takes place. What this means exactly is up to the delegate 
     11487               author. In this case, we set the data member 
     11488               <varname>mLoadConfigs</varname> to <constant>YES</constant>, 
     11489               send our base class the <methodname>-init</methodname> message, 
     11490               and return the result. This last step (a widely used 
     11491               convention) is what must be done by any custom delegate 
     11492               implementation of <methodname>-init</methodname>.</para> 
     11493            </section> 
     11494 
     11495            <section> 
     11496               <title><methodname>-setLoadConfigs:</methodname></title> 
     11497 
     11498               <para>This method is a unique aspect of the delegate as it is 
     11499               handled by <classname>vrj::CocoaWrapper</classname>. 
     11500               Specifically, <classname>vrj::CocoaWrapper</classname> will 
     11501               examine <filename>Contents/Resources/vrjuggler.plist</filename> 
     11502               to see if it contains the property 
     11503               <literal>VRJConfigHandling</literal>. If it does, it will send 
     11504               this message to the delegate instance with the value as it is 
     11505               set in the property list. This happens before the delegate 
     11506               object is handed off to the 
     11507               <classname>NSApplication</classname> singleton. The 
     11508               consequences of setting and using the 
     11509               <literal>VRJConfigHandling</literal> property were described in 
     11510               <xref linkend="section.cocoa.app.execution" />.</para> 
     11511 
     11512               <para>A custom delegate does not have to implement this method. 
     11513               If it does not, the Objective-C runtime will print a warning on 
     11514               the console stating that the object does not receive this 
     11515               message. (A future version of 
     11516               <classname>vrj::CocoaWrapper</classname> may examine the 
     11517               delegate interface to determine if it receives the message just 
     11518               to avoid having that message printed.) It is up to the delegate 
     11519               author to determine if implementing this method is necessary 
     11520               and, if so, how it should behave. In our example, we set 
     11521               <varname>mLoadConfigs</varname> to the value of the parameter, 
     11522               just as <classname>VRJBasicDelegate</classname> does.</para> 
     11523            </section> 
     11524 
     11525            <section> 
     11526               <title><methodname>-applicationShouldTerminateAfterLastWindowClosed:</methodname></title> 
     11527 
     11528               <para>This method is used by 
     11529               <classname>NSApplication</classname> to allow customization of 
     11530               application shutdown. In the case of an application delegate 
     11531               instantiated and used by 
     11532               <classname>vrj::CocoaWrapper</classname>, it is critical that 
     11533               this method return the value <constant>NO</constant>. The VR 
     11534               Juggler kernel knows about 
     11535               <classname>vrj::CocoaWrapper</classname> and knows how to shut 
     11536               it down when the time is right. Because of that, we want the 
     11537               kernel to control the termination process.</para> 
     11538