Changeset 20714

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

MFT r20713: 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/branches/2.2/modules/vrjuggler/doc/programmer.guide/programmer.guide.xml

    r19951 r20714  
    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> 
     
    1049510949         </section> 
    1049610950      </chapter> 
     10951 
     10952      <chapter> 
     10953         <title>Advanced Topics</title> 
     10954 
     10955         <para>In this chapter, we address topics that are considered to be 
     10956         for advanced uses of VR Juggler. This is not to say that they are 
     10957         difficult concepts or hard-to-use features. Rather, they are not 
     10958         things that the average VR Juggler application programmer will do or 
     10959         even need to be aware of when writing VR software. These are 
     10960         important, interesting, and worthwhile topics nonetheless.</para> 
     10961 
     10962         <section id="section.custom.delegate"> 
     10963            <title>Making a Custom <classname>NSApplication</classname> 
     10964            Delegate on Mac OS X</title> 
     10965 
     10966            <para>The Cocoa support for VR Juggler is designed to take 
     10967            advantage of the flexibility of Cocoa application design and, more 
     10968            generally, the flexibility of the Objective-C language. 
     10969            Specifically, VR Juggler application programmers targeting Mac OS 
     10970            X have a unique opportunity to customize the behavior of VR 
     10971            Juggler application behavior. Behind the scenes, 
     10972            <classname>vrj::CocoaWrapper</classname> registers a delegate, an 
     10973            instance of the Objective-C class 
     10974            <classname>VRJBasicDelegate</classname>, with the 
     10975            <classname>NSApplication</classname> singleton. Messages from 
     10976            <classname>NSApplication</classname> are received and handled by 
     10977            this object. VR Juggler application programmers can tailor the 
     10978            handling of these messages by creating and using a custom 
     10979            <classname>NSApplication</classname> delegate.</para> 
     10980 
     10981            <para>The full details of delegates and customizing the behavior 
     10982            of <classname>NSApplication</classname> are well beyond the scope 
     10983            of this document. Readers are referred to Apple's documentation 
     10984            for <ulink 
     10985            url="http://developer.apple.com/referencelibrary/GettingStarted/GS_Cocoa/index.html">Cocoa</ulink> 
     10986            and <ulink 
     10987            url="http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/Classes/NSApplication_Class/Reference/Reference.html"><classname>NSApplication</classname> 
     10988            class reference</ulink>. We will proceed from here under the 
     10989            assumption that readers are already familiar with the relevant and 
     10990            necessary topics including, but not limited to, Objective-C, 
     10991            Objective-C++, and <classname>NSApplication</classname>.</para> 
     10992 
     10993            <para>In a typical Cocoa application, the delegate would be given 
     10994            to the <classname>NSApplication</classname> singleton instance by 
     10995            sending that object the<methodname>-setDelegate:</methodname> 
     10996            message. Doing so, however, will interfere with the proper 
     10997            functionality of VR Juggler. Thus, the registration mechanism is 
     10998            done using the <literal>VRJDelegateClass</literal> property in 
     10999            <filename>Contents/Resources/vrjuggler.plist</filename>. The 
     11000            default value of this property is 
     11001            <classname>VRJBasicDelegate</classname>. By changing this to the 
     11002            name of the custom class, <classname>vrj::CocoaWrapper</classname> 
     11003            will instantiate, register, and use the custom class instead. 
     11004            (Being able to do this is a simple and elegant feature of 
     11005            Objective-C.)</para> 
     11006 
     11007            <para>There are two ways to create a custom delegate class. The 
     11008            class can either stand on its own as a subclass of 
     11009            <classname>NSObject</classname>, or it can derive from 
     11010            <classname>VRJBasicDelegate</classname>. The decision about which 
     11011            approach to use depends on whether complete or partial 
     11012            customization is desired. For this discussion, we will focus on 
     11013            complete customization—that is, creating a new delegate type by 
     11014            deriving from <classname>NSObject</classname>.</para> 
     11015 
     11016            <sidebar> 
     11017               <para>It is worth pointing out that 
     11018               <classname>VRJBasicDelegate</classname> is not necessarily 
     11019               designed to be extended. It can be customized, but mainly, this 
     11020               customization is done by overriding methods to 
     11021               <emphasis>replace</emphasis> the behavior of the base class 
     11022               rather than to extend it. Refer to the implementation of 
     11023               <classname>VRJBasicDelegate</classname> to get a better idea of 
     11024               the implications of overriding it for the purposes of 
     11025               customization. The code can be found in 
     11026               <filename>modules/vrjuggler/vrj/Kernel/VRJBasicDelegate.mm</filename> 
     11027               and 
     11028               <filename>modules/vrjuggler/vrj/Kernel/VRJBasicDelegate.h</filename>.</para> 
     11029            </sidebar> 
     11030 
     11031            <para>As noted, the job of the delegate is to respond to messages 
     11032            from <classname>NSApplication</classname>. The messages that are 
     11033            delivered to the delegate are documented in the 
     11034            <classname>NSApplication</classname> reference, and we will not 
     11035            duplicate that information here. Instead, we will concentrate on a 
     11036            small subset of these messages related to document loading. The 
     11037            purpose is twofold. First, this explanation helps in understanding 
     11038            the implementation of <classname>VRJBasicDelegate</classname>, 
     11039            thereby clarifying options for customizing the behavior of that 
     11040            class. Second, document loading is a prominent topic in Mac OS X 
     11041            application development, and it is likely to be of the most 
     11042            interest to VR application programmers, too.</para> 
     11043 
     11044            <para>To get things under way, have a glance at <xref 
     11045            linkend="example.basic.delegate.impl" />. It is a relatively 
     11046            simple class that uses Objective-C++ capabilities to create a 
     11047            simple bridge between <filename>NSApplication</filename> and 
     11048            <classname>vrj::Kernel</classname>. The documents being loaded in 
     11049            this case are VR Juggler configuration files, but it is not much 
     11050            of a leap to imagine the use of any other document type (i.e., 
     11051            data file used as input to the application). We will review each 
     11052            of the methods of this class.</para> 
     11053 
     11054            <example id="example.basic.delegate.impl"> 
     11055               <title><filename>MyDelegate.mm</filename>: Basic Delegate 
     11056               Implementation</title> 
     11057 
     11058               <programlisting linenumbering="numbered">#import &lt;Foundation/NSArray.h&gt; 
     11059#import &lt;Foundation/NSString.h&gt; 
     11060#import &lt;AppKit/NSApplication.h&gt; 
     11061 
     11062#include &lt;vrj/Kernel/Kernel.h&gt; 
     11063 
     11064 
     11065@interface MyDelegate : NSObject 
     11066{ 
     11067   BOOL mLoadConfigs; 
     11068} 
     11069@end 
     11070 
     11071@implementation MyDelegate 
     11072   -(id) init 
     11073   { 
     11074      mLoadConfigs = YES; 
     11075      return [super init]; 
     11076   } 
     11077 
     11078   -(void) setLoadConfigs:(BOOL) load 
     11079   { 
     11080      mLoadConfigs = load; 
     11081   } 
     11082 
     11083   -(BOOL) applicationShouldTerminateAfterLastWindowClosed:(NSApplication*) sender 
     11084   { 
     11085      // We return NO here because we have a different way of shutting down 
     11086      // the application. When vrj::Kernel::stop() is invoked, it will cause 
     11087      // the application run loop to stop by invoking 
     11088      // vrj::CocoaWrapper::stop(). 
     11089      return NO; 
     11090   } 
     11091 
     11092   -(BOOL) application:(NSApplication*) theApplication 
     11093              openFile:(NSString*) file 
     11094   { 
     11095      if ( mLoadConfigs ) 
     11096      { 
     11097         vrj::Kernel::instance()-&gt;loadConfigFile([file UTF8String]); 
     11098      } 
     11099 
     11100      return YES; 
     11101   } 
     11102 
     11103   -(void) application:(NSApplication*) theApplication 
     11104             openFiles:(NSArray*) files 
     11105   { 
     11106      if ( mLoadConfigs ) 
     11107      { 
     11108         const int count = [files count]; 
     11109         for ( int i = 0; i &lt; count; ++i ) 
     11110         { 
     11111            NSString* file = [files objectAtIndex:i]; 
     11112            vrj::Kernel::instance()-&gt;loadConfigFile([file UTF8String]); 
     11113         } 
     11114      } 
     11115   } 
     11116@end</programlisting> 
     11117            </example> 
     11118 
     11119            <section> 
     11120               <title>Data Members</title> 
     11121 
     11122               <para>The class <classname>MyDelegate</classname> has a single 
     11123               data member, <varname>mLoadConfigs</varname>, that determines 
     11124               how instances of this class will respond to certain messages 
     11125               sent by <classname>NSApplication</classname>. The value will be 
     11126               changed if <classname>vrj::CocoaWrapper</classname> sends the 
     11127               <methodname>-setLoadConfigs:</methodname> message as a result 
     11128               of the property <literal>VRJConfigHandling</literal> being set 
     11129               in 
     11130               <filename>Contents/Resources/vrjuggler.plist</filename>.</para> 
     11131            </section> 
     11132 
     11133            <section> 
     11134               <title>Designated Initializer</title> 
     11135 
     11136               <para>Every <classname>NSObject</classname> subclass has a 
     11137               designated initializer. In the case of the VR Juggler 
     11138               <classname>NSApplication</classname> delegate, this is the 
     11139               basic <methodname>-init</methodname> method. The current usage 
     11140               of the application delegate requires that this method be the 
     11141               designated initializer, meaning that it is the only initializer 
     11142               that will be invoked upon creating the delegate instance. This 
     11143               limitation may be fixed in a future version of 
     11144               <classname>vrj::CocoaWrapper</classname>. If the custom 
     11145               delegate does not accept the -init message, then the inherited 
     11146               <classname>NSObject</classname> version will be used.</para> 
     11147 
     11148               <para>In the meantime, this is where all object initialization 
     11149               takes place. What this means exactly is up to the delegate 
     11150               author. In this case, we set the data member 
     11151               <varname>mLoadConfigs</varname> to <constant>YES</constant>, 
     11152               send our base class the <methodname>-init</methodname> message, 
     11153               and return the result. This last step (a widely used 
     11154               convention) is what must be done by any custom delegate 
     11155               implementation of <methodname>-init</methodname>.</para> 
     11156            </section> 
     11157 
     11158            <section> 
     11159               <title><methodname>-setLoadConfigs:</methodname></title> 
     11160 
     11161               <para>This method is a unique aspect of the delegate as it is 
     11162               handled by <classname>vrj::CocoaWrapper</classname>. 
     11163               Specifically, <classname>vrj::CocoaWrapper</classname> will 
     11164               examine <filename>Contents/Resources/vrjuggler.plist</filename> 
     11165               to see if it contains the property 
     11166               <literal>VRJConfigHandling</literal>. If it does, it will send 
     11167               this message to the delegate instance with the value as it is 
     11168               set in the property list. This happens before the delegate 
     11169               object is handed off to the 
     11170               <classname>NSApplication</classname> singleton. The 
     11171               consequences of setting and using the 
     11172               <literal>VRJConfigHandling</literal> property were described in 
     11173               <xref linkend="section.cocoa.app.execution" />.</para> 
     11174 
     11175               <para>A custom delegate does not have to implement this method. 
     11176               If it does not, the Objective-C runtime will print a warning on 
     11177               the console stating that the object does not receive this 
     11178               message. (A future version of 
     11179               <classname>vrj::CocoaWrapper</classname> may examine the 
     11180               delegate interface to determine if it receives the message just 
     11181               to avoid having that message printed.) It is up to the delegate 
     11182               author to determine if implementing this method is necessary 
     11183               and, if so, how it should behave. In our example, we set 
     11184               <varname>mLoadConfigs</varname> to the value of the parameter, 
     11185               just as <classname>VRJBasicDelegate</classname> does.</para> 
     11186            </section> 
     11187 
     11188            <section> 
     11189               <title><methodname>-applicationShouldTerminateAfterLastWindowClosed:</methodname></title> 
     11190 
     11191               <para>This method is used by 
     11192               <classname>NSApplication</classname> to allow customization of 
     11193               application shutdown. In the case of an application delegate 
     11194               instantiated and used by 
     11195               <classname>vrj::CocoaWrapper</classname>, it is critical that 
     11196               this method return the value <constant>NO</constant>. The VR 
     11197               Juggler kernel knows about 
     11198               <classname>vrj::CocoaWrapper</classname> and knows how to shut 
     11199               it down when the time is right. Because of that, we wan