!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!       AltMenu                 An Inform 6 Library extension to create 
!  Version 3.6                  object-oriented menus.  By L. Ross Raszewski
!                               (rraszews@skipjack.bluecrab.org)
!
! NEW in 3.6   minor (re: One byte) bug fix.
! NEW in 3.5   Code cleaning, one bug fix
! NEW in 3.0:
!      *Multiple page menus are now supported.  Altmenu automatically
!       paginates these menus.  The menu has been given two new properties, 
!       .page, and .pages, which hold the current and total number of pages
!       (note that these values are only correct while the menu is running)
!      *Multi-lingual support.  Modify the constants in the language block.
!      *Support for menu-null items.   Objects of the class "separator" will
!       appear in the menu list, but cannot be selected. (DoMenu v.2 feature)
!      *A new property, sup_bar, which provides the second line of a menu title
!       (unlike title_bar which replaces the first line.)
!      *Expanded functionality for the .label property.  Objects other than 
!       switchoptions which provide this will be listed on the menu by their name, 
!       but at the top of the screen by their label.
!      *New rigorous comments. 
! 
!  Requires DoMenu.h, Center.h, and pmove.h
!  *Note that Pmove is now _required_ (whereas it weas only reccomended before)
! First of all, let me say that this is inspired heavily by Graham Nelson's
! own alternative menu system.  While the code is all my own, I'm not the 
! sort of person who would shamelessly copy someone else's idea without at 
! least mentioning them.
! Graham's implemenatation is really a lot more impressive code-wise.  Mine 
! is really just a shell for DoMenu.  However, my library uses the extended
! functions of the DoMenu Library I've written.
! Usage:
! To activate a menu, send the select(); message to the appropriate 
! Menu-Class object.  
! Menu objectname "Title of the Menu"
!     with description "If given, this text will be displayed above the menu
!                       choices",
!          number (number of lines in the description),
!          title_bar "If given, this will be displayed on the top line and the
!                       object name will appear on the second line.";
!  The children of a menu object are the options on the menu.  All children of 
! a menu MUST be of class Option, or of an option-derrived class. (actually,
! just have to provide a select property, but that's all Class
! Option does)
! Option -> "Title of the option"
!    with description [; string or rouine to be printed when Option is selected
!                        returns 1 to prompt a waitforkey, 2 to return instanly
!                        to the menu, or 3 to return and quit the menu];
! option.select(); is called when an option is selected, so you could change 
! that if you wanted.
!
!
!  This is part of a series actually, these are inter-dependant libraries.
!  For this to work, you have to include:
!  center.h   -> Centers a line of text (or a routine to print one) in 
!  the upper or lower window.
!  DoMenu       -> A modified DoMenu routine to allow long headers and 2-line
!                       title bars.
! In that order, and after the parser.
! For maximum enjoyment, add on (not required)
!  Hints.H (UPDATED)  -> A system for making object-oriented hints which appear
!                      formatted in the same way as Infocom's InvisiClues
!


!!!!!!!!!!!!!!!!!!
!!Language Block!!
!!!!!!!!!!!!!!!!!!
#IFNDEF nextp__TX;
Constant nextp__TX "[Next Page]";
#ENDIF;
#IFNDEF prevp__TX;
Constant prevp__TX "[Previous Page]";
#ENDIF;
!!!!!!!!!!!!!!!!!!!!!!!!!
!!End of Language Block!!
!!!!!!!!!!!!!!!!!!!!!!!!!


!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
! Option Class (Menu options)
!        select             (calls self.description to perform menu
!                                       function.)
!                               returns: self.description();
!       Usage:
! Option "Option One"
!       with description "Some text here.";
!       Note - description returns 1 to run and wait for a keypress,
!               2 to run and return to the menu with no keypress
!               3 to run and quit the current menu.
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Class Option
        with    select [; return self.description();];

!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
! Separator Class (Null Menu Item)
!      Usage:
! Separator "Game Commands";
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Class separator
        class option;

!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
! SwitchOption Class (Toggle options)
!       with sup_bar (this is only a stub property.  Switchoptions will never 
!                        use a sup_bar.)
!           short_name (prints self.label, if provided.)    
!           select (runs self.toggle();)    
!           title_bar (this is also a stub.)
!
!       Usage:
!  SwitchOption sound
!        with label [; print "Sound     ";
!                       if (self has on) style reverse; print "ON";
!                       style roman; print "   ";
!                       if (self hasnt on) style reverse; print "OFF";
!                       style roman;],
!               toggle [; if (self has on) give self ~on; else give self on;];
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
Class SwitchOption              
        with    
                sup_bar NULL,
                short_name [; if (self provides label) 
                                { self.label(); return 1;}; ],
                select [; self.toggle(); return 2;],
                title_bar NULL;


!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
! Menu class (This is the big one.)
!       class option (A menu can also be an option of another menu.)
!       with select (The main menu loop. Processes multi-page menus, then
!                    throws the results back to Domenu.)
!            page (The current page of the menu.)   
!            pages (The total munber of pages.  These are purely for the use 
!                        of the game writer.  Altmenu sets and updates these, 
!                       but does not use them in any other way.  Please note 
!                       that changing them directly WILL NOT affect the menu.)
!            num_lines_avail (Used for internal calculation of free space)
!            sw (Screen height, adjusted for the banner)
!            mp_mode (set if the menu is in multiple-page mode)
!            label (Stub for the option label property.)
!            toggle (Stub for the switchoption toggle property)  
!            emblazon (prints the menu text, self.description, and the 
!               list of children.  This is the first argument sent to Domenu.)
!            titles (generates the option names sent to Domenu.)
!            execute (runs the selected option.  Sent as the third argument 
!                       to Domenu)  
!            selection_name (generates the name needed by self.titles)
!            doname (prints the name of the menu.  A separate property, 
!               because of the form of argument required by Domenu.)
!
!       Usage:
! Menu "Main Menu"
!       with description "This is the main Menu",
!            number 1, ! not strictly required, because in this case it is 1,
!                      !but provided for clarity
!            title_bar "The big game",  !will be printed on the first line 
!                               ! of the menu bar
!            sup_bar "A menu"; !will be printed on the second line.  Use one,
!                               ! or the other, not both.
!
!      Note:
!       self.put_option(x) will put x into the current menu.  This is the 
!                safest way to add options to a menu while it's running, 
!                however, it's not infalable.  
!               WHile the menu is not running, Pmove will function adequately.
!
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Class Menu
        class Option
        with select [ j k; 
                        self.pages=1;
                        self.mp_mode=0;
                        self.sw=0->32;
                        if (self.sw==0 or 255) self.sw==25;
                        self.sw--;
                        self.num_lines_avail=5;
                        if (self provides number) 
                                self.num_lines_avail=self.num_lines_avail+self.number();
                                else self.num_lines_avail++;
                        
                        j=self.num_lines_avail+children(self);
                        
                        if (j>self.sw) { self.mp_mode=1;
                                self.pages=children(self)/(self.sw-self.num_lines_avail);
                                if (children(self)%(self.sw-self.num_lines_avail)~=0) self.pages++;
                                while (child(self)~=nothing) Pmove(child(self),a_queue);
                                for(k=self.num_lines_avail:k<self.sw:k++)
                                        Pmove(child(a_queue),self);
                                Pmove(nextp,self);
                                }
                        self.page=1;
                       DoMenu(self.emblazon,self.titles,self.execute);
                        if (self.mp_mode==1) {
                                remove nextp; remove prevp;
                                while (child(self)~=nothing) Pmove(child(self),b_queue);
                                while (child(a_queue)~=nothing) Pmove(child(a_queue),b_queue);
                                while (child(b_queue)~=nothing) Pmove(child(b_queue),self);
                                }
                        return 2;],
             pages 1,
             page 1,
             num_lines_avail 0,
             sw 0,
             mp_mode 0,
             label NULL,
             toggle NULL,
             emblazon [ o; if (self provides description) self.description();
                                else new_line;
                         objectloop (o in self) {
                                spaces(5);
                                print (name) o;
                                new_line;
                                }
                         ],
             titles [ i o;
                          if (menu_item==-5) {
                                o=child(self);
                                for (i=1:o~=nothing:i++)
                                  { if (SkipItem==i && o ofclass separator) return SKIP;
                                 o=sibling(o);}}
                          
                          
                          if (menu_item==-1) {
                                if (self provides sup_bar)
                                        item_name=self.sup_bar;
                                if (self provides title_bar) 
                                        item_name=self.doname;
                                if (self provides number)
                                        return self.number();
                                else return 1;
                                }
                        if (menu_item==0) {
                                if (self provides title_bar)
                                        item_name=self.title_bar;
                                else item_name=self.doname;
                                return children(self);
                                }
                        o=child(self);
                        for (i=1:o~=nothing:i++)
                                { if (menu_item==i) { item_width=o;
                                                item_name=self.selection_name;};
                                 o=sibling(o);}
                        ],
             execute [ i o j k; 
                        o=child(self);
                        k=0; j=2;
                        for (i=1:o~=nothing:i++)
                                {if (menu_item==i) {
                                        if (self.mp_mode==1 && o ofclass menu) {
                                                remove nextp; remove prevp;k=1;
                                                while (child(self)~=nothing) Pmove(child(self),b_queue);
                                                while (child(a_queue)~=nothing) Pmove(child(a_queue),b_queue);
                                                while (child(b_queue)~=nothing) Pmove(child(b_queue),self);
                                               }
                                        j=o.select();}
                                 o=sibling(o);}
                                if (self.mp_mode==1 && k==1) {
                                while (child(self)~=nothing) Pmove(child(self),a_queue);
                                for(k=self.num_lines_avail:k<self.sw:k++)
                                        Pmove(child(a_queue),self);
                                Pmove(nextp,self);    }
                        
                        return j;
                        ],
             selection_name [;  if (item_width provides label) {
                                        if (item_width.label~=NULL)
                                                print (string) item_width.label;
                                        }
                                else
                                print (name) item_width;],
             put_Option [ j k; 
                        if (self.mp_mode==1) {
                                remove nextp; remove prevp;
                                while (child(self)~=nothing) Pmove(child(self),b_queue);
                                while (child(a_queue)~=nothing) Pmove(child(a_queue),b_queue);
                                while (child(b_queue)~=nothing) Pmove(child(b_queue),self);
                                }
                        Pmove(j,self);                             
                        j=self.num_lines_avail+children(self);
                        if (j>self.sw) { self.mp_mode=1;
                                self.pages=children(self)/(self.sw-self.num_lines_avail);
                                if (children(self)%(self.sw-self.num_lines_avail)~=0) self.pages++;
                                while (child(self)~=nothing) Pmove(child(self),a_queue);
                                for(k=self.num_lines_avail:k<self.sw:k++)
                                        Pmove(child(a_queue),self);
                                Pmove(nextp,self);
                                }
                        self.page=1;
                        
                             ],
             doname [; print (name) self;];


!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
! a_queue and b_queue   (multi-page caches)
! a_queue holds the "options ahead" of the ones being viewed, and b_queue, 
!       those behind.
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
object a_queue;
object b_queue;
        
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
! nextp and prevp
!       options for switching to the next and previous pages.  Inserted 
!       automatically as needed.
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

option nextp 
        with short_name [; print (string) nextp__TX; rtrue; ],
             select [ j k; j=parent(self);
                        j.page++;
                        remove self; remove prevp;
                        while (child(j)~=nothing) Pmove(child(j),b_queue);
                                Pmove(prevp,j);
                                for(k=j.num_lines_avail+1:(k<j.sw && child(a_queue)~=nothing):k++)
                                                Pmove(child(a_queue),j);
                                if (k==j.sw)
                                Pmove(nextp,j);
                                return 2;];

option prevp
        with short_name [; print (string) prevp__TX; rtrue;],
             select [ j k; j=parent(self);
                        j.page--;
                        remove self; remove nextp;
                        while (child(j)~=nothing) move youngest(j) to a_queue;
                        move nextp to j;
                        for(k=j.num_lines_avail+1:(k<j.sw && child(b_queue)~=nothing):k++)
                                                move youngest(b_queue) to j;
                                if (k==j.sw)
                                move prevp to j;
                                return 2;];

