home *** CD-ROM | disk | FTP | other *** search
/ Acorn User 2 / AUCD2.iso / program / vista.arc / c / menu < prev    next >
Text File  |  1996-02-01  |  18KB  |  757 lines

  1. // **************************************************************************
  2. //                     Copyright 1996 David Allison
  3. //
  4. //             VV    VV    IIIIII     SSSSS     TTTTTT       AA
  5. //             VV    VV      II      SS           TT       AA  AA
  6. //             VV    VV      II        SSSS       TT      AA    AA
  7. //              VV  VV       II           SS      TT      AAAAAAAA
  8. //                VV       IIIIII     SSSS        TT      AA    AA
  9. //
  10. //                    MULTI-THREADED C++ WIMP CLASS LIBRARY
  11. //                                for RISC OS
  12. // **************************************************************************
  13. //
  14. //             P U B L I C    D O M A I N    L I C E N C E
  15. //             -------------------------------------------
  16. //
  17. //     This library is copyright. You may not sell the library for
  18. //     profit, but you may sell products which use it providing
  19. //     those products are presented as executable code and are not
  20. //     libraries themselves.  The library is supplied without any
  21. //     warranty and the copyright owner cannot be held responsible for
  22. //     damage resulting from failure of any part of this library.
  23. //
  24. //          See the User Manual for details of the licence.
  25. //
  26. // *************************************************************************
  27.  
  28. //
  29. // menu.c
  30. //
  31.  
  32. #include "Vista:icon.h"
  33. #include <stdio.h>
  34. #include <stdlib.h>
  35. #include <string.h>
  36. #include <new.h>
  37. #include <ctype.h>
  38. #include <swis.h>
  39. #include <kernel.h>
  40. #include <stdarg.h>
  41.  
  42. #ifndef __EASY_C
  43.  
  44. // an exception here needs to close the file pointer used for the parser.  This
  45. // trick redefines the throw macro to close the file and then call __throw
  46.  
  47. static FILE *menu_fp ;
  48.  
  49. #undef throw
  50. #define throw menu_fp?fclose(menu_fp):0,__throw
  51.  
  52. #endif
  53.  
  54. MenuItem::MenuItem (char *n, Menu *m, char *text, int length)
  55.    {
  56.    name = n ;
  57.    next = NULL ;
  58.    menu = m ;
  59.    submenu = NULL ;
  60.    index = m->add_item(this) ;           // add a new menu item
  61.    MenuItemData *item = &m->data->items[index] ;
  62.    item->item_flags = 0 ;
  63.    item->window_handle = -1 ;
  64.    item->icon_flags = Icon::ITEXT + Icon::IFILLED + Icon::IVCENTRE + 7*Icon::IFORECOL ;
  65.    print (text) ;
  66.    }
  67.  
  68. MenuItem::MenuItem (char *name, Menu *menu, char *sprite, void *sprite_area)
  69.    {
  70.    }
  71.  
  72. void MenuItem::tick()
  73.    {
  74.    menu->data->items[index].item_flags |= TICKED ;
  75.    }
  76.  
  77. void MenuItem::untick()
  78.    {
  79.    menu->data->items[index].item_flags &= ~TICKED ;
  80.    }
  81.  
  82. void MenuItem::fade()
  83.    {
  84.    menu->data->items[index].icon_flags |= Icon::INOSELECT ;
  85.    }
  86.  
  87. void MenuItem::unfade()
  88.    {
  89.    menu->data->items[index].icon_flags &= ~Icon::INOSELECT ;
  90.    }
  91.  
  92. int MenuItem::flags(int f, int m)
  93.    {
  94.    int old = menu->data->items[index].item_flags ;
  95.    menu->data->items[index].item_flags = (old & ~m) | f ;
  96.    return old ;
  97.    }
  98.  
  99. int MenuItem::is_ticked()
  100.    {
  101.    return menu->data->items[index].item_flags & TICKED ;
  102.    }
  103.  
  104. int MenuItem::is_faded()
  105.    {
  106.    return menu->data->items[index].icon_flags & Icon::INOSELECT ;
  107.    }
  108.  
  109. MenuItem::operator int()
  110.    {
  111.    return index ;
  112.    }
  113.  
  114. MenuItem::operator char *()
  115.    {
  116.    return name ;
  117.    }
  118.  
  119. void MenuItem::make_writeable (char *buffer, int length, char *valid)
  120.    {
  121.    MenuItemData *i = &menu->data->items[index] ;
  122.    i->icon_data.indirecttext.buffer = buffer ;
  123.    i->icon_data.indirecttext.bufflen = length ;
  124.    i->icon_data.indirecttext.validstring = valid ;
  125.    i->icon_flags |= Icon::BWRITABLE * Icon::IBTYPE + Icon::INDIRECT +
  126.                     Icon::IHCENTRE + Icon::IVCENTRE + Icon::ITEXT ;
  127.    i->item_flags |= WRITEABLE ;
  128.    }
  129.  
  130. void MenuItem::set_submenu(Menu *submenu)
  131.    {
  132.    this->submenu = submenu ;
  133.    menu->data->items[index].submenu = submenu->data ;
  134.    }
  135.  
  136. void Menu::init (char *n, char *title)
  137.    {
  138.    name = n ;
  139.    next = NULL ;
  140.    num_items = 0 ;
  141.    max_items = 5 ;
  142.    item_adjust = 0 ;
  143.    if ((data = (MenuData*)malloc (sizeof (MenuData) + sizeof (MenuItemData) * max_items)) == NULL)
  144.       throw ("Out of memory") ;
  145.    items = NULL ;
  146.    max_width = 0 ;
  147.    strncpy (data->title, title, 12) ;
  148.    data->title_fore_colour = 7 ;
  149.    data->title_back_colour = 2 ;
  150.    data->work_fore_colour = 7 ;
  151.    data->work_back_colour = 0 ;
  152.    data->item_width = strlen(title) * 16;
  153.    data->item_height = 44 ;
  154.    data->item_gap = 0 ;
  155.    }
  156.  
  157. Menu::Menu (char *name, char *title)
  158.    {
  159.    init (name, title) ;
  160.    }
  161.  
  162. Menu::Menu (char *name, char *title, MenuItem *item)
  163.    {
  164.    init (name, title) ;
  165.    item->set_submenu (this) ;
  166.    }
  167.  
  168. Menu::~Menu()
  169.    {
  170.    }
  171.  
  172. int Menu::add_item (MenuItem *item)
  173.    {
  174.    if (num_items == max_items)         // out of item space?
  175.       {
  176.       max_items += 5 ;
  177.       if ((data = (MenuData*)realloc (data, sizeof (MenuData) + sizeof (MenuItemData) * max_items)) == NULL)
  178.          throw ("Out of memory") ;
  179.       }
  180.    item->next = items ;          // add to list
  181.    items = item ;
  182.    return num_items++ ;
  183.    }
  184.  
  185. void Menu::finish()
  186.    {
  187.    items->flags(MenuItem::LAST) ;        // set last item
  188.    }
  189.  
  190. void Menu::open (int x, int y)
  191.    {
  192.    _kernel_swi_regs r ;
  193.    _kernel_oserror *e ;
  194.    r.r[1] = (int)data ;
  195.    r.r[2] = x ;
  196.    r.r[3] = y ;
  197.    if ((e = _kernel_swi (Wimp_CreateMenu, &r, &r)) != NULL)
  198.       throw (e) ;
  199.    }
  200.  
  201. void Menu::close()
  202.    {
  203.    _kernel_swi_regs r ;
  204.    _kernel_oserror *e ;
  205.    r.r[1] = -1 ;
  206.    if ((e = _kernel_swi (Wimp_CreateMenu, &r, &r)) != NULL)
  207.       throw (e) ;
  208.    }
  209.  
  210. void Menu::iconbar_adjust (int &x, int &y)
  211.    {
  212.    x -= 16 ;
  213.    y = 96 ;
  214.    y += num_items * (data->item_height + data->item_gap) + item_adjust ;
  215.    }
  216.  
  217. MenuItem *Menu::selection (int *hits)
  218.    {
  219.    int i ;
  220.    MenuItem *item ;
  221.    Menu *m = this ;
  222.    for (i = 0 ; ; i++)
  223.       if (hits[i] == -1)
  224.          break ;
  225.    MenuItem *out_items = (MenuItem*)malloc ((i+1) * sizeof (MenuItem)) ;
  226.    if (out_items == NULL)
  227.       throw ("Out of memory") ;
  228.    for (i = 0 ; hits[i] != -1 ; i++)
  229.       for (item = m->items ; item != NULL ; item = item->next)
  230.          if (item->index == hits[i])
  231.             {
  232.             out_items[i] = *item ;
  233.             if (hits[i+1] != -1)
  234.                m = item->submenu ;
  235.             break ;
  236.             }
  237.    out_items[i].name = NULL ;
  238.    out_items[i].index = -1 ;
  239.    return out_items ;
  240.    }
  241.  
  242. bool Menu::invalid_selection (int *hits)
  243.    {
  244.    int i ;
  245.    MenuItem *item ;
  246.    MenuItem *last_item = NULL ;
  247.    Menu *m = this ;
  248.    for (i = 0 ; hits[i] != -1 ; i++)
  249.       for (item = m->items ; item != NULL ; item = item->next)
  250.          if (item->index == hits[i])
  251.             {
  252.             last_item = item ;
  253.             if (hits[i+1] != -1)
  254.                m = item->submenu ;
  255.             break ;
  256.             }
  257. #ifdef __EASY_C
  258.    return last_item != NULL && (last_item->flags() & MenuItem::MENUWARNING) ;
  259. #else
  260.    return (bool)(last_item != NULL && (last_item->flags() & MenuItem::MENUWARNING)) ;
  261. #endif
  262.    }
  263.  
  264. MenuItem *Menu::item (char *iname)
  265.    {
  266.    for (MenuItem *item = items ; item != NULL ; item = item->next)
  267.       if (strcmp (item->name, iname) == 0)
  268.          return item ;
  269.    return NULL ;
  270.    }
  271.  
  272.  
  273. void Menu::print (char *item_name, char *format ...)
  274.    {
  275.    MenuItem *i = item (item_name) ;
  276.    if (i == NULL)
  277.       throw ("No such menu item") ;
  278.    va_list ap ;
  279.    va_start (ap, format) ;
  280.    i->vprint (format, ap) ;
  281.    }
  282.  
  283. void MenuItem::print (char *format...)
  284.    {
  285.    char str[1024] ;
  286.    va_list arg ;
  287.    va_start (arg,format) ;
  288.    vprint (format, arg) ;
  289.    }
  290.  
  291. void MenuItem::vprint (char *format, va_list ap)
  292.    {
  293.    char str[1024] ;
  294.    vsprintf (str,format,ap) ;
  295.    int length = strlen (str) ;
  296.    MenuItemData *item = &menu->data->items[index] ;
  297.    if (length > menu->max_width)
  298.       {
  299.       menu->max_width = length ;
  300.       int l = (length + 1) * 16 ;
  301.       if (l > menu->data->item_width)
  302.          menu->data->item_width = l ;
  303.       }
  304.    if (length <= 12)
  305.       {
  306.       item->icon_flags &= ~Icon::INDIRECT ;
  307.       strncpy (item->icon_data.text, str, length) ;
  308.       if (length < 12)
  309.          item->icon_data.text[length] = 0 ;
  310.       }
  311.    else
  312.       {
  313.       item->icon_flags |= Icon::INDIRECT ;
  314.       item->icon_data.indirecttext.buffer = new char [length+1] ;
  315.       item->icon_data.indirecttext.validstring = (char *)-1 ;
  316.       item->icon_data.indirecttext.bufflen = length+1 ;
  317.       strcpy (item->icon_data.indirecttext.buffer, str) ;
  318.       }
  319.    }
  320.  
  321. void Menu::read (char *item_name, int &i)
  322.    {
  323.    MenuItem *it = item(item_name) ;
  324.    if (it == NULL)
  325.       throw ("No such menu item") ;
  326.    it->read (i) ;
  327.    }
  328.  
  329. void MenuItem::read (int &i)
  330.    {
  331.    char str[1024] ;
  332.    read (str) ;
  333.    sscanf (str,"%d",&i) ;
  334.    }
  335.  
  336. void Menu::read (char *item_name, char *s)
  337.    {
  338.    MenuItem *i = item(item_name) ;
  339.    if (i == NULL)
  340.       throw ("No such menu item") ;
  341.    i->read (s) ;
  342.    }
  343.  
  344. void MenuItem::read (char *s)
  345.    {
  346.    MenuItemData *item = &menu->data->items[index] ;
  347.    if (item->icon_flags & Icon::INDIRECT)
  348.       strcpy (s, item->icon_data.indirecttext.buffer) ;
  349.    else
  350.       for (int i = 0 ; i < 12 ; i++)
  351.          s[i] = item->icon_data.text[i] ;
  352.    }
  353.  
  354. //
  355. // menu file parser
  356. //
  357.  
  358. // tokens recognised
  359.  
  360. #define T_LBRACK 1
  361. #define T_RBRACK 2
  362. #define T_IDENTIFIER 3
  363. #define T_OTHER 4
  364. #define T_EQUALS 5
  365. #define T_STRING 6
  366. #define T_SEMICOLON 7
  367. #define T_COMMA 8
  368. #define T_COLON 9
  369. #define T_LBRACE 10
  370. #define T_RBRACE 11
  371. #define T_MENU 12
  372. #define T_SEPARATOR 13
  373. #define T_TICKED 14
  374. #define T_WRITEABLE 15
  375. #define T_MESSAGE 16
  376. #define T_OPEN 17
  377. #define T_WINDOW 18
  378. #define T_NUMBER 19
  379.  
  380. static char spelling[256] ;
  381. static char input[1024] ;
  382. static char *ch ;
  383. static int value ;
  384. static FILE *fp ;
  385.  
  386. static int reserved_word() ;
  387.  
  388. static void read_line()
  389.    {
  390.    for (;;)
  391.       {
  392.       if (fgets (input, 1024, fp) == NULL)
  393.          {
  394.          input[0] = '$' ;
  395.          ch = input ;
  396.          break ;
  397.          }
  398.       else
  399.         {
  400.         ch = input ;
  401.         while (isspace (*ch) && *ch != 0)
  402.            ch++ ;
  403.         if (*ch == '#' || *ch == 0)
  404.            continue ;
  405.         else
  406.            break ;
  407.         }
  408.       }
  409.    }
  410.  
  411. static int next_char()
  412.    {
  413.    if (*ch == 0)
  414.       read_line() ;
  415.    return *ch++ ;
  416.    }
  417.  
  418. static int next_token (void)
  419.    {
  420.    int c;
  421.    char str[256] ;
  422.    char *ptr ;
  423.  
  424.    while (isspace (*ch)) next_char() ;
  425.    switch (c = next_char())
  426.       {
  427.       case ':':
  428.          return T_COLON ;
  429.       case ';':
  430.          return T_SEMICOLON ;
  431.       case '=':
  432.          return T_EQUALS ;
  433.       case ',':
  434.          return T_COMMA ;
  435.       case '{':
  436.          return T_LBRACE ;
  437.       case '}':
  438.          return T_RBRACE ;
  439.       case '(':
  440.          return T_LBRACK ;
  441.       case ')':
  442.          return T_RBRACK ;
  443.  
  444.       default:
  445.          if (isdigit(c))
  446.             {
  447.             int i = 0 ;
  448.             do
  449.                {
  450.                spelling[i] = c ;
  451.                c = next_char() ;
  452.                i++ ;
  453.                }
  454.             while (isdigit(c)) ;
  455.             spelling[i] = 0 ;
  456.             ch-- ;
  457.             sscanf (spelling, "%d", &value) ;
  458.             return T_NUMBER ;
  459.             }
  460.          else if (isalpha (c) || c == '_')
  461.             {
  462.             ptr = spelling ;
  463.             do
  464.                {
  465.                *ptr++ = c ;
  466.                c = next_char() ;
  467.                }
  468.             while (isalnum (c) || c == '_') ;
  469.             *ptr = '\0' ;
  470.             ch-- ;
  471.             if ((c = reserved_word()) != 0)
  472.                return c ;
  473.             return T_IDENTIFIER ;
  474.             }
  475.          else if (c == '"')
  476.             {
  477.             ptr = spelling ;
  478.             while ((c = next_char()) != '"')
  479.                {
  480.                if (c == 0)
  481.                   throw ("Bad string") ;
  482.                if (c == '\\')
  483.                   switch (c = next_char())
  484.                      {
  485.                      case 'n':
  486.                         *ptr++ = '\n' ;
  487.                         continue ;
  488.                      case 'r':
  489.                         *ptr++ = '\r' ;
  490.                         continue ;
  491.                      case 't':
  492.                         *ptr++ = '\t' ;
  493.                         continue ;
  494.                      case 'b':
  495.                         *ptr++ = '\b' ;
  496.                         continue ;
  497.                      case 'f':
  498.                         *ptr++ = '\f' ;
  499.                         continue ;
  500.                      case 'a':
  501.                         *ptr++ = '\a' ;
  502.                         continue ;
  503.                      case 'v':
  504.                         *ptr++ = '\v' ;
  505.                         continue ;
  506.                      default:
  507.                         *ptr++ = c ;
  508.                         continue ;
  509.                      }
  510.                   else
  511.                      *ptr++ = c ;
  512.                }
  513.             *ptr = 0 ;
  514.             return T_STRING ;
  515.             }
  516.          else
  517.             return T_OTHER ;
  518.        }
  519.  
  520.    }
  521.  
  522. int this_symbol ;
  523.  
  524. static void next_symbol()
  525.    {
  526.    this_symbol = next_token() ;
  527.    }
  528.  
  529.  
  530. static struct
  531.    {
  532.    char *word ;
  533.    int value ;
  534.    } res_words[] = {
  535.                    {"menu", T_MENU},
  536.                    {"ticked", T_TICKED},
  537.                    {"writeable", T_WRITEABLE},
  538.                    {"message", T_MESSAGE},
  539.                    {"open", T_OPEN},
  540.                    {"separator", T_SEPARATOR},
  541.                    {"window", T_WINDOW},
  542.                    {NULL,  0}
  543.                    } ;
  544.  
  545. static int reserved_word()
  546.    {
  547.    int i ;
  548.    for (i = 0 ; res_words[i].word != NULL ; i++)
  549.       if (strcmp (res_words[i].word, spelling) == 0)
  550.          return res_words[i].value ;
  551.    return 0 ;
  552.    }
  553.  
  554. static void accept (int token, char *error = "Syntax error")
  555.    {
  556.    if (this_symbol == token)
  557.       next_symbol() ;
  558.    else
  559.       throw (error) ;
  560.    }
  561.  
  562.  
  563. static char *stralloc (char *name)
  564.    {
  565.    char *p = new char [strlen(name) + 1] ;
  566.    strcpy (p, name) ;
  567.    return p ;
  568.    }
  569.  
  570. static char *accept_name (int token, char *error = "Expected name")
  571.    {
  572.    char *s ;
  573.    if (this_symbol == token)
  574.       {
  575.       s = stralloc (spelling) ;
  576.       next_symbol() ;
  577.       return s ;
  578.       }
  579.    else
  580.       throw (error) ;
  581.    }
  582.  
  583. void MenuSet::parse_menu()
  584.    {
  585.    char *mname ;
  586.    char *title ;
  587.    Menu *menu ;
  588.    mname = accept_name (T_IDENTIFIER, "Menu name expected") ;
  589.    title = accept_name (T_STRING, "Missing menu title") ;
  590.    accept (T_EQUALS, "Missing =") ;
  591.    menu = new Menu (mname, title) ;
  592.    add_menu (menu) ;
  593.    parse_menu_details (menu) ;             // at end this_symbol == }
  594.    menu->finish() ;
  595.    }
  596.  
  597. void MenuSet::parse_menu_details(Menu *menu)
  598.    {
  599.    accept (T_LBRACE, "Missing {") ;
  600.    while (this_symbol != T_RBRACE)
  601.       parse_item (menu) ;
  602.    }
  603.  
  604. void MenuSet::parse_item (Menu *menu)
  605.    {
  606.    char *name;
  607.    char *text ;
  608.    char *sprite ;
  609.    int has_submenu = 0 ;
  610.    MenuItem *item ;
  611.    name = accept_name (T_IDENTIFIER, "Missing item name") ;
  612.    switch (this_symbol)
  613.       {
  614.       case T_STRING:
  615.          text = stralloc (spelling) ;
  616.          item = new MenuItem (name, menu, text, strlen (text)) ;
  617.          next_symbol() ;
  618.          break ;
  619.       case T_IDENTIFIER:             // sprite
  620.       default:
  621.          throw ("Syntax error") ;
  622.       }
  623.    if (this_symbol == T_COLON)
  624.       {
  625.       next_symbol() ;
  626.       for (;;)
  627.          {
  628.          switch (this_symbol)
  629.             {
  630.             case T_TICKED:
  631.                item->tick() ;
  632.                break ;
  633.             case T_WRITEABLE:
  634.                {
  635.                next_symbol() ;
  636.                accept (T_LBRACK, "Missing (") ;
  637.                accept (T_NUMBER, "Missing writeable buffer size") ;
  638.                accept (T_COMMA, "Missing ,") ;
  639.                char *valid = accept_name (T_STRING, "Missing validation string") ;
  640.                if (this_symbol != T_RBRACK)
  641.                   throw ("Missing )") ;
  642.                char *s = new char [value] ;
  643.                s[0] = 0 ;
  644.                item->make_writeable (s, value, valid) ;
  645.                break;
  646.                }
  647.             case T_SEPARATOR:
  648.                item->flags (MenuItem::SEPARATOR) ;
  649.                menu->item_adjust += 24 ;          // add separator adjust
  650.                break ;
  651.             case T_MESSAGE:
  652.                item->flags (MenuItem::MENUWARNING) ;
  653.                if (has_submenu)
  654.                   throw ("Submenu already exists") ;
  655.                has_submenu = 1 ;
  656.                item->set_submenu ((Menu*)1) ;
  657.                break ;
  658.             case T_OPEN:
  659.                item->flags (MenuItem::OPENANYWAY) ;
  660.                break ;
  661.             case T_MENU:
  662.                if (has_submenu)
  663.                   throw ("Submenu already exists") ;
  664.                has_submenu = 1 ;
  665.                next_symbol() ;
  666.                switch (this_symbol)
  667.                   {
  668.                   case T_IDENTIFIER:
  669.                      {
  670.                      Menu *m = find (spelling) ;
  671.                      if (m == 0)
  672.                         throw ("Unknown menu") ;
  673.                      item->set_submenu (m) ;
  674.                      break ;
  675.                      }
  676.                   case T_STRING:
  677.                      {
  678.                      char *title = stralloc (spelling) ;
  679.                      Menu *submenu = new Menu ("", title, item) ;
  680.                      next_symbol() ;
  681.                      parse_menu_details (submenu) ;
  682.                      submenu->finish() ;
  683.                      break ;
  684.                      }
  685.                   default:
  686.                      throw ("Syntax error") ;
  687.                   }
  688.                break ;
  689.             case T_WINDOW:
  690.                break ;
  691.             }
  692.          next_symbol() ;
  693.          if (this_symbol != T_COMMA)
  694.             break ;
  695.          next_symbol() ;
  696.          }
  697.       }
  698.    accept (T_SEMICOLON, "Missing ;") ;
  699.    }
  700.  
  701. void MenuSet::add_menu(Menu *menu)
  702.    {
  703.    menu->next = menus ;
  704.    menus = menu ;
  705.    }
  706.  
  707. Menu *MenuSet::find (char *name)
  708.    {
  709.    Menu *m ;
  710.    for (m = menus ; m != NULL ; m = m->next)
  711.       if (strcmp (name, m->name) == 0)
  712.          return m ;
  713.    return NULL ;
  714.    }
  715.  
  716. MenuSet::MenuSet(Task *t, char *filename)
  717.    {
  718.    menus = NULL ;                                 // no menu yet
  719.    if ((fp = fopen (filename, "r")) == NULL)
  720. #ifdef __EASY_C
  721.       throw "Cant open Menu file" ;
  722. #else
  723.       __throw ("Cant open Menu file") ;
  724. #endif
  725.  
  726. #ifdef __EASY_C
  727.    try
  728. #endif
  729.       {
  730. #ifndef __EASY_C
  731.       menu_fp = fp ;
  732. #endif
  733.       task = t ;
  734.       read_line() ;
  735.       next_symbol() ;
  736.       while (this_symbol == T_MENU)
  737.          {
  738.          next_symbol() ;
  739.          parse_menu() ;
  740.          next_symbol() ;
  741.          }
  742.       fclose (fp) ;
  743.       }
  744. #ifdef __EASY_C
  745.    catch (char *t)
  746.       {
  747.       fclose (fp) ;
  748.       throw ;            // rethrow exception
  749.       }
  750. #endif
  751.    }
  752.  
  753. MenuSet::~MenuSet()
  754.    {
  755.    }
  756.  
  757.