home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / CNRMNU.ZIP / CTXTMENU.C < prev    next >
Text File  |  1993-01-01  |  47KB  |  1,129 lines

  1. /*********************************************************************
  2.  *                                                                   *
  3.  * MODULE NAME :  ctxtmenu.c             AUTHOR:  Rick Fishman       *
  4.  * DATE WRITTEN:  10-24-92                                           *
  5.  *                                                                   *
  6.  * DESCRIPTION:                                                      *
  7.  *                                                                   *
  8.  *  This module is part of CNRMENU.EXE. It contains the functions    *
  9.  *  necessary to implement the container context menu.               *
  10.  *                                                                   *
  11.  * CALLABLE FUNCTIONS:                                               *
  12.  *                                                                   *
  13.  *  VOID CtxtmenuCreate( HWND hwndClient, PCNRITEM pciSelected );    *
  14.  *  VOID CtxtmenuCommand( HWND hwndClient, ULONG idCommand );        *
  15.  *  VOID CtxtmenuSetView( HWND hwndClient, ULONG ulViewType );       *
  16.  *  VOID CtxtmenuEnd( HWND hwndClient );                             *
  17.  *                                                                   *
  18.  * HISTORY:                                                          *
  19.  *                                                                   *
  20.  *  10-24-92 - Program coded.                                        *
  21.  *  11-21-92 - Changed the WinQueryWindowULong/WinSetWindowULong     *
  22.  *               method of setting the MS_CONDITIONALCASCADE bit     *
  23.  *               to WinSetWindowBits per John Webb's idea.           *
  24.  *                                                                   *
  25.  *  Rick Fishman                                                     *
  26.  *  Code Blazers, Inc.                                               *
  27.  *  4113 Apricot                                                     *
  28.  *  Irvine, CA. 92720                                                *
  29.  *  CIS ID: 72251,750                                                *
  30.  *                                                                   *
  31.  *********************************************************************/
  32.  
  33. #pragma strings(readonly)   // used for debug version of memory mgmt routines
  34.  
  35. /*********************************************************************/
  36. /*------- Include relevant sections of the OS/2 header files --------*/
  37. /*********************************************************************/
  38.  
  39. #define  INCL_WINDIALOGS
  40. #define  INCL_WINERRORS
  41. #define  INCL_WINFRAMEMGR
  42. #define  INCL_WINMENUS
  43. #define  INCL_WINPOINTERS
  44. #define  INCL_WINSTDCNR
  45. #define  INCL_WINWINDOWMGR
  46.  
  47. /**********************************************************************/
  48. /*----------------------------- INCLUDES -----------------------------*/
  49. /**********************************************************************/
  50.  
  51. #include <os2.h>
  52. #include <stdarg.h>
  53. #include <stdio.h>
  54. #include <stdlib.h>
  55. #include <string.h>
  56. #include "cnrmenu.h"
  57.  
  58. /*********************************************************************/
  59. /*------------------- APPLICATION DEFINITIONS -----------------------*/
  60. /*********************************************************************/
  61.  
  62. /**********************************************************************/
  63. /*---------------------------- STRUCTURES ----------------------------*/
  64. /**********************************************************************/
  65.  
  66. /**********************************************************************/
  67. /*----------------------- FUNCTION PROTOTYPES ------------------------*/
  68. /**********************************************************************/
  69.  
  70. static VOID   TurnOffSelFlags     ( HWND hwndCnr );
  71. static VOID   NewWindows          ( HWND hwndClient, BOOL fAllSelected );
  72. static VOID   TurnOnSourceEmphasis( HWND hwndClient );
  73. static VOID   TurnOffSourceEmphasis( HWND hwndClient );
  74. static INT    CountSelectedRecs   ( HWND hwndCnr, PCNRITEM pciUnderMouse );
  75. static VOID   NewWin              ( HWND hwndCnr, PSZ szBaseDir, PCNRITEM pci );
  76. static VOID   TailorMenu          ( HWND hwndCnr, HWND hwndMenu,
  77.                                     PCNRITEM pciSelected );
  78. static VOID   SetConditionalCascade( HWND hwndMenu, USHORT idSubMenu,
  79.                                     USHORT idDefaultItem );
  80. static INT    AddOtherWindows     ( HWND hwndCnr, HWND hwndMenu );
  81. static BOOL   AddOtherWinItem     ( HWND hwndClient, HWND hwndOtherFrame,
  82.                                     HWND hwndOtherClient, HWND hwndSubMenu,
  83.                                     INT idMenuItem );
  84. static USHORT GetDefaultId        ( HWND hwndClient, USHORT idSubMenu );
  85.  
  86. /**********************************************************************/
  87. /*------------------------ GLOBAL VARIABLES --------------------------*/
  88. /**********************************************************************/
  89.  
  90. /**********************************************************************/
  91. /*-------------------------- CtxtmenuCreate --------------------------*/
  92. /*                                                                    */
  93. /*  CREATE THE CONTEXT MENU.                                          */
  94. /*                                                                    */
  95. /*  INPUT: client window handle,                                      */
  96. /*         pointer to CNRITEM that mouse pointer is over              */
  97. /*                                                                    */
  98. /*  1.                                                                */
  99. /*                                                                    */
  100. /*  OUTPUT: nothing                                                   */
  101. /*                                                                    */
  102. /*--------------------------------------------------------------------*/
  103. /**********************************************************************/
  104. VOID CtxtmenuCreate( HWND hwndClient, PCNRITEM pciSelected )
  105. {
  106.     POINTL    ptl;
  107.     BOOL      fSuccess = TRUE;
  108.     USHORT    idStart = IDM_VIEW_SUBMENU;
  109.     HWND      hwndCnr = WinWindowFromID( hwndClient, CNR_DIRECTORY );
  110.     HWND      hwndMenu = WinLoadMenu( hwndClient, 0, ID_CONTEXT_MENU );
  111.     PINSTANCE pi = INSTDATA( hwndClient );
  112.  
  113.     if( !pi )
  114.     {
  115.         Msg( "CtxtmenuCreate cant get Inst data. RC(%X)", HWNDERR(hwndClient) );
  116.  
  117.         return;
  118.     }
  119.  
  120.     if( !hwndMenu )
  121.     {
  122.         Msg( "CtxtmenuCreate WinLoadMenu RC(%X)", HWNDERR( hwndClient ) );
  123.  
  124.         return;
  125.     }
  126.  
  127.     // Save the pointer to the CNRITEM that was under the mouse pointer when
  128.     // the context menu was invoked. We will need this value when we get
  129.     // WM_COMMAND values because the WM_COMMAND message doesn't tell you which
  130.     // container record the context menu applies to. If the pointer is over
  131.     // white space when the context menu is invoked, pciSelected will be NULL
  132.  
  133.     pi->pciSelected = pciSelected;
  134.  
  135.     // Selected records are tagged with their CNRITEM.fSelected flag. Initialize
  136.     // all container records to 'not selected'.
  137.  
  138.     TurnOffSelFlags( hwndCnr );
  139.  
  140.     // Turn on source emphasis for applicable records. The function that does
  141.     // this also checks if any of the records represent directories. Init this
  142.     // bool to FALSE. It will be set to TRUE if a directory is found.
  143.  
  144.     pi->fDirSelected = FALSE;
  145.  
  146.     TurnOnSourceEmphasis( hwndClient );
  147.  
  148.     // Get the position of the mouse pointer
  149.  
  150.     fSuccess = WinQueryPointerPos( HWND_DESKTOP, &ptl );
  151.  
  152.     if( fSuccess )
  153.     {
  154.         // Convert the position of the mouse pointer to coordinates with respect
  155.         // to the client window.
  156.  
  157.         fSuccess = WinMapWindowPoints( HWND_DESKTOP, hwndClient, &ptl, 1 );
  158.  
  159.         if( !fSuccess )
  160.             Msg( "CtxtmenuCreate WinMapWindowPoints failed! RC(%X)",
  161.                  HWNDERR( hwndClient ) );
  162.     }
  163.     else
  164.         Msg( "CtxtmenuCreate WinQueryPointerPos failed! RC(%X)",
  165.              HWNDERR( hwndClient ) );
  166.  
  167.     // Add/remove menu items, etc.
  168.  
  169.     TailorMenu( hwndCnr, hwndMenu, pciSelected );
  170.  
  171.     if( !WinPopupMenu( hwndClient, hwndClient, hwndMenu, ptl.x, ptl.y,
  172.                        idStart, PU_HCONSTRAIN | PU_VCONSTRAIN | PU_KEYBOARD |
  173.                        PU_MOUSEBUTTON1 | PU_MOUSEBUTTON2 | PU_NONE ) )
  174.         Msg( "CtxtmenuCreate WinPopupMenu failed! RC(%X)", HWNDERR(hwndClient));
  175.  
  176.     return;
  177. }
  178.  
  179. /**********************************************************************/
  180. /*-------------------------- CtxtmenuCommand -------------------------*/
  181. /*                                                                    */
  182. /*  PROCESS A WM_COMMAND MESSAGE FROM THE CONTEXT MENU.               */
  183. /*                                                                    */
  184. /*  INPUT: client window handle,                                      */
  185. /*         command id,                                                */
  186. /*         command source                                             */
  187. /*                                                                    */
  188. /*  1.                                                                */
  189. /*                                                                    */
  190. /*  OUTPUT: nothing                                                   */
  191. /*                                                                    */
  192. /*--------------------------------------------------------------------*/
  193. /**********************************************************************/
  194. VOID CtxtmenuCommand( HWND hwndClient, ULONG idCommand, ULONG ulCmdSrc )
  195. {
  196.     switch( idCommand )
  197.     {
  198.         case IDM_VIEW_TREE:
  199.  
  200.             CtxtmenuSetView( hwndClient, CV_TREE | CV_ICON );
  201.  
  202.             break;
  203.  
  204.         case IDM_VIEW_NAME:
  205.  
  206.             CtxtmenuSetView( hwndClient, CV_NAME | CV_FLOW );
  207.  
  208.             break;
  209.  
  210.         case IDM_VIEW_TEXT:
  211.  
  212.             CtxtmenuSetView( hwndClient, CV_TEXT | CV_FLOW );
  213.  
  214.             break;
  215.  
  216.         case IDM_VIEW_ICON:
  217.  
  218.             CtxtmenuSetView( hwndClient, CV_ICON );
  219.  
  220.             break;
  221.  
  222.         case IDM_VIEW_DETAILS:
  223.  
  224.             CtxtmenuSetView( hwndClient, CV_DETAIL );
  225.  
  226.             break;
  227.  
  228.         case IDM_CREATE_NEWWIN:
  229.  
  230.             // Only go thru all selected records if this command came from the
  231.             // context menu (this WM_COMMAND message is also sent by the client
  232.             // window on a CN_ENTER).
  233.  
  234.             NewWindows( hwndClient, ulCmdSrc == CMDSRC_MENU ? TRUE : FALSE );
  235.  
  236.             break;
  237.  
  238.         case IDM_ARRANGE:
  239.  
  240.             if( !WinSendDlgItemMsg( hwndClient, CNR_DIRECTORY, CM_ARRANGE,
  241.                                     NULL, NULL ) )
  242.                 Msg( "CtxtmenuCommand CM_ARRANGE RC(%X)", HWNDERR(hwndClient) );
  243.  
  244.             break;
  245.  
  246.         case IDM_SORT_NAME:
  247.         case IDM_SORT_DATETIME:
  248.         case IDM_SORT_DIRORDER:
  249.  
  250.             // In sort.c
  251.  
  252.             SortContainer( hwndClient, idCommand );
  253.  
  254.             break;
  255.  
  256.         case IDM_VIEW_SUBMENU:
  257.         case IDM_SORT_SUBMENU:
  258.         {
  259.             // Find out the default id of the submenu and simulate that menu
  260.             // item being pressed.
  261.  
  262.             USHORT id = GetDefaultId( hwndClient, idCommand );
  263.  
  264.             if( id )
  265.                 WinSendMsg( hwndClient, WM_COMMAND, MPFROM2SHORT( id, 0 ),
  266.                             MPFROM2SHORT( CMDSRC_MENU, 0 ) );
  267.  
  268.             break;
  269.         }
  270.  
  271.         default:
  272.         {
  273.             PINSTANCE pi = INSTDATA( hwndClient );
  274.  
  275.             // hwndFrame was set when the item was put into the menu. It is the
  276.             // frame window handle that contains the directory pointed to by
  277.             // the text in the menuitem.
  278.  
  279.             if( pi && idCommand >= IDM_OTHERWIN_ITEM1 &&
  280.                 idCommand <= IDM_OTHERWIN_LASTITEM )
  281.                 WinSetFocus( HWND_DESKTOP,
  282.                              pi->hwndFrame[ idCommand - IDM_OTHERWIN_ITEM1 ] );
  283.  
  284.             break;
  285.         }
  286.     }
  287.  
  288.     return;
  289. }
  290.  
  291. /**********************************************************************/
  292. /*-------------------------- CtxtmenuSetView -------------------------*/
  293. /*                                                                    */
  294. /*  SET THE TYPE OF VIEW FOR THE CONTAINER                            */
  295. /*                                                                    */
  296. /*  INPUT: client window handle,                                      */
  297. /*         view type to set to                                        */
  298. /*                                                                    */
  299. /*  1.                                                                */
  300. /*                                                                    */
  301. /*  OUTPUT: nothing                                                   */
  302. /*                                                                    */
  303. /*--------------------------------------------------------------------*/
  304. /**********************************************************************/
  305. VOID CtxtmenuSetView( HWND hwndClient, ULONG ulViewType )
  306. {
  307.     PINSTANCE pi = INSTDATA( hwndClient );
  308.     CNRINFO   cnri;
  309.  
  310.     if( !pi )
  311.     {
  312.         Msg( "CtxtmenuSetView cant get Inst data. RC(%X)", HWNDERR(hwndClient));
  313.  
  314.         return;
  315.     }
  316.  
  317.     cnri.cb = sizeof( CNRINFO );
  318.  
  319.     // Set the container window attributes: Set the container view mode. Use a
  320.     // container title. Put a separator between the title and the records
  321.     // beneath it. Make the container title read-only.
  322.  
  323.     cnri.flWindowAttr = ulViewType | CA_CONTAINERTITLE | CA_TITLESEPARATOR |
  324.                         CA_TITLEREADONLY;
  325.  
  326.     switch( ulViewType )
  327.     {
  328.         case CV_TREE:
  329.         case (CV_TREE | CV_ICON):
  330.  
  331.             (void) strcpy( pi->szCnrTitle, "Tree/icon view - " );
  332.  
  333.             // Use default spacing between levels in the tree view. Also use
  334.             // the default width of a line that shows record relationships.
  335.  
  336.             cnri.cxTreeIndent = -1;
  337.             cnri.cxTreeLine   = -1;
  338.  
  339.             cnri.flWindowAttr |=  CA_TREELINE;
  340.  
  341.             break;
  342.  
  343.         case CV_ICON:
  344.  
  345.             (void) strcpy( pi->szCnrTitle, "Icon view - " );
  346.  
  347.             break;
  348.  
  349.         case CV_NAME:
  350.         case (CV_NAME | CV_FLOW):
  351.  
  352.             (void) strcpy( pi->szCnrTitle, "Name/flowed view - " );
  353.  
  354.             break;
  355.  
  356.         case CV_DETAIL:
  357.  
  358.             (void) strcpy( pi->szCnrTitle, "Detail view - " );
  359.  
  360.             // If we are in DETAIL view, tell the container that we will be
  361.             // using column headings.
  362.  
  363.             cnri.flWindowAttr |= CA_DETAILSVIEWTITLES;
  364.  
  365.             break;
  366.  
  367.         case CV_TEXT:
  368.         case (CV_TEXT | CV_FLOW):
  369.  
  370.             (void) strcpy( pi->szCnrTitle, "Text/flowed view - " );
  371.  
  372.             break;
  373.     }
  374.  
  375.     (void) strcat( pi->szCnrTitle, pi->szDirectory );
  376.  
  377.     cnri.pszCnrTitle = pi->szCnrTitle;
  378.  
  379.     // Set the line spacing between rows to be the minimal value so we conserve
  380.     // on container whitespace.
  381.  
  382.     cnri.cyLineSpacing = 0;
  383.  
  384.     // Set the container parameters. Note that mp2 specifies which fields of
  385.     // of the CNRINFO structure to use. The CMA_FLWINDOWATTR says to use
  386.     // flWindowAttr to specify which 'Window Attribute' fields to use.
  387.  
  388.     if( !WinSendDlgItemMsg( hwndClient, CNR_DIRECTORY, CM_SETCNRINFO,
  389.                             MPFROMP( &cnri ),
  390.                             MPFROMLONG( CMA_FLWINDOWATTR | CMA_CNRTITLE |
  391.                                         CMA_LINESPACING ) ) )
  392.         Msg( "CtxtmenuSetView CM_SETCNRINFO RC(%X)", HWNDERR( hwndClient ) );
  393.  
  394.     // The CM_ARRANGE message is applicable only in ICON view. It will arrange
  395.     // the icons according to CUA. Note that this message is unnecessary if
  396.     // the CCS_AUTOPOSITION style is used on the WinCreateWindow call for the
  397.     // container. The problem with using that style is that you have no control
  398.     // over *when* the arranging is done.
  399.  
  400.     if( ulViewType == CV_ICON )
  401.         if( !WinSendDlgItemMsg( hwndClient, CNR_DIRECTORY, CM_ARRANGE, NULL,
  402.                                 NULL ) )
  403.             Msg( "CtxtmenuSetView CM_ARRANGE RC(%X)", HWNDERR( hwndClient ) );
  404.  
  405.     return;
  406. }
  407.  
  408. /**********************************************************************/
  409. /*---------------------------- CtxtmenuEnd ---------------------------*/
  410. /*                                                                    */
  411. /*  PROCESS WM_MENUEND MESSAGE FOR THE CONTEXT MENU.                  */
  412. /*                                                                    */
  413. /*  INPUT: client window handle                                       */
  414. /*                                                                    */
  415. /*  1.                                                                */
  416. /*                                                                    */
  417. /*  OUTPUT: nothing                                                   */
  418. /*                                                                    */
  419. /*--------------------------------------------------------------------*/
  420. /**********************************************************************/
  421. VOID CtxtmenuEnd( HWND hwndClient )
  422. {
  423.     PINSTANCE pi = INSTDATA( hwndClient );
  424.  
  425.     if( !pi )
  426.     {
  427.         Msg( "CtxtmenuEnd cant get Instdata RC(%X)", HWNDERR(hwndClient));
  428.  
  429.         return;
  430.     }
  431.  
  432.     TurnOffSourceEmphasis( hwndClient );
  433.  
  434.     // Reset variable that was only used during context menu processing
  435.  
  436.     pi->fDirSelected = FALSE;
  437.  
  438.     return;
  439. }
  440.  
  441. /**********************************************************************/
  442. /*-------------------------- TurnOffSelFlags -------------------------*/
  443. /*                                                                    */
  444. /*  TURN OFF THE fSelected FLAG FOR ALL CONTAINER RECORDS.            */
  445. /*                                                                    */
  446. /*  INPUT: container window handle                                    */
  447. /*                                                                    */
  448. /*  1.                                                                */
  449. /*                                                                    */
  450. /*  OUTPUT: nothing                                                   */
  451. /*                                                                    */
  452. /*--------------------------------------------------------------------*/
  453. /**********************************************************************/
  454. static VOID TurnOffSelFlags( HWND hwndCnr )
  455. {
  456.     PCNRITEM pci = NULL;
  457.     USHORT usWhatRec = CMA_FIRST;
  458.  
  459.     while( fTrue )
  460.     {
  461.         pci = (PCNRITEM) WinSendMsg( hwndCnr, CM_QUERYRECORD, MPFROMP( pci ),
  462.                                      MPFROM2SHORT( usWhatRec, CMA_ITEMORDER ) );
  463.  
  464.         if( (INT) pci == -1 )
  465.         {
  466.             Msg( "TurnOffSelFlags CM_QUERYRECORD RC(%X)", HWNDERR( hwndCnr ) );
  467.  
  468.             break;
  469.         }
  470.  
  471.         if( !pci )
  472.             break;
  473.  
  474.         pci->fSelected = FALSE;
  475.  
  476.         usWhatRec = CMA_NEXT;
  477.     }
  478.  
  479.     return;
  480. }
  481.  
  482. /**********************************************************************/
  483. /*----------------------------- NewWindows ---------------------------*/
  484. /*                                                                    */
  485. /*  CREATE NEW WINDOWS FOR SELECTED CONTAINER RECORDS.                */
  486. /*                                                                    */
  487. /*  INPUT: client window handle,                                      */
  488. /*         flag indicating if all selected records are to be looked at*/
  489. /*                                                                    */
  490. /*  1.                                                                */
  491. /*                                                                    */
  492. /*  OUTPUT: nothing                                                   */
  493. /*                                                                    */
  494. /*--------------------------------------------------------------------*/
  495. /**********************************************************************/
  496. static VOID NewWindows( HWND hwndClient, BOOL fAllSelected )
  497. {
  498.     PINSTANCE pi = INSTDATA( hwndClient );
  499.     HWND      hwndCnr = WinWindowFromID( hwndClient, CNR_DIRECTORY );
  500.     PCNRITEM  pci = NULL;
  501.     USHORT    usWhatRec = CMA_FIRST;
  502.  
  503.     if( !pi )
  504.     {
  505.         Msg( "NewWindows cant get Inst data RC(%X)", HWNDERR( hwndClient ) );
  506.  
  507.         return;
  508.     }
  509.  
  510.     // Create new window for the currently selected record
  511.  
  512.     NewWin( hwndCnr, pi->szDirectory, pi->pciSelected );
  513.  
  514.     // Create new windows for all records that have source emphasis if the
  515.     // flag passed to this function indicates that we are to do this. We would
  516.     // not want to do this if the user double-clicked on just one record vs.
  517.     // selecting 'CreateNewWindow' from the menu.
  518.  
  519.     for( ; fAllSelected ; )
  520.     {
  521.         pci = (PCNRITEM) WinSendMsg( hwndCnr, CM_QUERYRECORD, MPFROMP( pci ),
  522.                                      MPFROM2SHORT( usWhatRec, CMA_ITEMORDER ) );
  523.  
  524.         if( (INT) pci == -1 )
  525.         {
  526.             Msg( "NewWindows CM_QUERYRECORD RC(%X)", HWNDERR( hwndCnr ) );
  527.  
  528.             break;
  529.         }
  530.  
  531.         if( !pci )
  532.             break;
  533.  
  534.         if( pci->fSelected && pci != pi->pciSelected )
  535.             NewWin( hwndCnr, pi->szDirectory, pci );
  536.  
  537.         usWhatRec = CMA_NEXT;
  538.     }
  539.  
  540.     return;
  541. }
  542.  
  543. /**********************************************************************/
  544. /*------------------------ TurnOnSourceEmphasis ----------------------*/
  545. /*                                                                    */
  546. /*  TURN ON SOURCE EMPHASIS FOR APPLICABLE CONTAINER RECORDS.         */
  547. /*                                                                    */
  548. /*  INPUT: client window handle                                       */
  549. /*                                                                    */
  550. /*  1.                                                                */
  551. /*                                                                    */
  552. /*  OUTPUT: nothing                                                   */
  553. /*                                                                    */
  554. /*--------------------------------------------------------------------*/
  555. /**********************************************************************/
  556. static VOID TurnOnSourceEmphasis( HWND hwndClient )
  557. {
  558.     PINSTANCE pi = INSTDATA( hwndClient );
  559.     HWND      hwndCnr = WinWindowFromID( hwndClient, CNR_DIRECTORY );
  560.     BOOL      fRecsSelected = TRUE;
  561.     INT       iSelCount;
  562.  
  563.     if( !pi )
  564.     {
  565.         Msg( "TurnOn..Emphasis cant get Inst data RC(%X)", HWNDERR(hwndClient));
  566.  
  567.         return;
  568.     }
  569.  
  570.     // NOTE: CRA_SOURCE is defined in *our* header file because CRA_SOURCE
  571.     // appeared in the 10/30/92 Service Pack and is not yet in the toolkit
  572.     // header files at this writing.
  573.  
  574.     // If no record is selected (pciSelected is NULL), we set source emphasis
  575.     // to the container itself because the mouse pointer is over whitespace.
  576.  
  577.     if( !pi->pciSelected )
  578.     {
  579.         if( !WinSendMsg( hwndCnr, CM_SETRECORDEMPHASIS, MPFROMP( NULL ),
  580.                          MPFROM2SHORT( TRUE, CRA_SOURCE ) ) )
  581.             Msg( "CM_SETRECORDEMPHASIS failed! RC(%X)", HWNDERR( hwndClient ) );
  582.  
  583.         return;
  584.     }
  585.  
  586.     // Get the number of records that are currently in the 'selected' state in
  587.     // the container. We only care about *all* selected records if the one that
  588.     // is now under the mouse pointer is also in the selected state. If it
  589.     // is not, CountSelectedRecs will pass us back a -1 to indicate that we only
  590.     // need to process that record even though others are in selected state.
  591.     // Also keep track in pi->fDirSelected if there are any records that
  592.     // represent directories. This will become useful during menu tailoring.
  593.     // Last thing: set the fSelected BOOL in the CNRITEM struct so we know
  594.     // which records we qualified for source emphasis. When we get the
  595.     // WM_MENUEND message we turn off source emphasis. The WM_MENUEND message
  596.     // happens before we get the WM_COMMAND message so it is too late when we
  597.     // get the WM_COMMAND message to just query which records have the source
  598.     // emphasis to figure out which recs to process.
  599.  
  600.     iSelCount = CountSelectedRecs( hwndCnr, pi->pciSelected );
  601.  
  602.     if( iSelCount == -1 )
  603.     {
  604.         fRecsSelected = FALSE;
  605.  
  606.         iSelCount = 1;
  607.     }
  608.  
  609.     if( iSelCount )
  610.     {
  611.         if( fRecsSelected )
  612.         {
  613.             PCNRITEM pci;
  614.             INT      i;
  615.  
  616.             pci = (PCNRITEM) CMA_FIRST;
  617.  
  618.             // For each selected record turn on source emphasis
  619.  
  620.             for( i = 0; i < iSelCount; i++ )
  621.             {
  622.                 pci = (PCNRITEM) WinSendMsg( hwndCnr, CM_QUERYRECORDEMPHASIS,
  623.                                              MPFROMP( pci ),
  624.                                              MPFROMSHORT( CRA_SELECTED ) );
  625.  
  626.                 if( !WinSendMsg( hwndCnr, CM_SETRECORDEMPHASIS, MPFROMP( pci ),
  627.                                  MPFROM2SHORT( TRUE, CRA_SOURCE ) ) )
  628.                     Msg( "CM_SETRECORDEMPHASIS failed! RC(%X)",
  629.                          HWNDERR( hwndClient ) );
  630.  
  631.                 pci->fSelected = TRUE;
  632.  
  633.                 if( (pci->attrFile & FILE_DIRECTORY) &&
  634.                      pci->szFileName[0] != '.' )
  635.                     pi->fDirSelected = TRUE;
  636.             }
  637.         }
  638.         else
  639.         {
  640.             if( !WinSendMsg( hwndCnr, CM_SETRECORDEMPHASIS,
  641.                              MPFROMP( pi->pciSelected ),
  642.                              MPFROM2SHORT( TRUE, CRA_SOURCE ) ) )
  643.                 Msg( "CM_SETRECORDEMPHASIS failed! RC(%X)",HWNDERR(hwndClient));
  644.  
  645.             pi->pciSelected->fSelected = TRUE;
  646.  
  647.             if( (pi->pciSelected->attrFile & FILE_DIRECTORY) &&
  648.                  pi->pciSelected->szFileName[0] != '.' )
  649.                 pi->fDirSelected = TRUE;
  650.         }
  651.     }
  652.  
  653.     return;
  654. }
  655.  
  656. /**********************************************************************/
  657. /*----------------------- TurnOffSourceEmphasis ----------------------*/
  658. /*                                                                    */
  659. /*  TURN OFF SOURCE EMPHASIS FOR THOSE RECORDS THAT HAVE IT.          */
  660. /*                                                                    */
  661. /*  INPUT: client window handle                                       */
  662. /*                                                                    */
  663. /*  1.                                                                */
  664. /*                                                                    */
  665. /*  OUTPUT: nothing                                                   */
  666. /*                                                                    */
  667. /*--------------------------------------------------------------------*/
  668. /**********************************************************************/
  669. static VOID TurnOffSourceEmphasis( HWND hwndClient )
  670. {
  671.     PINSTANCE pi = INSTDATA( hwndClient );
  672.     HWND      hwndCnr = WinWindowFromID( hwndClient, CNR_DIRECTORY );
  673.  
  674.     if( !pi )
  675.     {
  676.         Msg( "TurnOff..Emphasis cant get Instdata RC(%X)", HWNDERR(hwndClient));
  677.  
  678.         return;
  679.     }
  680.  
  681.     // If no record is selected (pciSelected is NULL), we had set source
  682.     // emphasis to the container itself because the mouse pointer was over
  683.     // whitespace. So turn it off for the container. Otherwise, turn it off
  684.     // for the selected record and all others that have source emphasis
  685.  
  686.     if( pi->pciSelected )
  687.     {
  688.         PCNRITEM pci = (PCNRITEM) CMA_FIRST;
  689.  
  690.         if( !WinSendMsg( hwndCnr, CM_SETRECORDEMPHASIS,
  691.                          MPFROMP( pi->pciSelected ),
  692.                          MPFROM2SHORT( FALSE, CRA_SOURCE ) ) )
  693.             Msg( "TurnOff..Emphasis CM_SETRECORDEMPHASIS failed! RC(%X)",
  694.                  HWNDERR( hwndClient ) );
  695.  
  696.         while( fTrue )
  697.         {
  698.             pci = (PCNRITEM) WinSendMsg( hwndCnr, CM_QUERYRECORDEMPHASIS,
  699.                                      MPFROMP( pci ), MPFROMSHORT( CRA_SOURCE) );
  700.  
  701.             if( !pci )
  702.                 break;
  703.  
  704.             if( pci != pi->pciSelected )
  705.                 if( !WinSendMsg( hwndCnr, CM_SETRECORDEMPHASIS, MPFROMP( pci ),
  706.                                  MPFROM2SHORT( FALSE, CRA_SOURCE ) ) )
  707.                     Msg( "TurnOff... CM_SETRECORDEMPHASIS failed! RC(%X)",
  708.                          HWNDERR( hwndClient ) );
  709.         }
  710.     }
  711.     else
  712.         if( !WinSendMsg( hwndCnr, CM_SETRECORDEMPHASIS, MPFROMP( NULL ),
  713.                          MPFROM2SHORT( FALSE, CRA_SOURCE ) ) )
  714.             Msg( "TurnOff...Emphasis CM_SETRECORDEMPHASIS failed! RC(%X)",
  715.                  HWNDERR( hwndClient ) );
  716.  
  717.     return;
  718. }
  719.  
  720. /**********************************************************************/
  721. /*------------------------ CountSelectedRecs -------------------------*/
  722. /*                                                                    */
  723. /*  COUNT THE NUMBER OF RECORDS THAT ARE CURRENTLY SELECTED.          */
  724. /*                                                                    */
  725. /*  INPUT: pointer to the record that was under the pointer.          */
  726. /*                                                                    */
  727. /*  1.                                                                */
  728. /*                                                                    */
  729. /*  OUTPUT: number of selected recs, 0 if none, -1 if the record that */
  730. /*          the mouse pointer was under was not selected.             */
  731. /*                                                                    */
  732. /*--------------------------------------------------------------------*/
  733. /**********************************************************************/
  734. static INT CountSelectedRecs( HWND hwndCnr, PCNRITEM pciUnderMouse )
  735. {
  736.     INT      iCount = 0;
  737.     PCNRITEM pci;
  738.     BOOL     fFound = FALSE;
  739.  
  740.     if( pciUnderMouse )
  741.     {
  742.         pci = (PCNRITEM) CMA_FIRST;
  743.  
  744.         while( pci )
  745.         {
  746.             pci = (PCNRITEM) WinSendMsg( hwndCnr, CM_QUERYRECORDEMPHASIS,
  747.                                          MPFROMP( pci ),
  748.                                          MPFROMSHORT( CRA_SELECTED ) );
  749.             if( pci )
  750.             {
  751.                 if( pci == pciUnderMouse )
  752.                     fFound = TRUE;
  753.  
  754.                 iCount++;
  755.             }
  756.         }
  757.  
  758.         if( !fFound )
  759.             iCount = -1;
  760.     }
  761.  
  762.     return iCount;
  763. }
  764.  
  765. /**********************************************************************/
  766. /*------------------------------ NewWin ------------------------------*/
  767. /*                                                                    */
  768. /*  CREATE A NEW WINDOW FOR A DIRECTORY.                              */
  769. /*                                                                    */
  770. /*  INPUT: container window handle,                                   */
  771. /*         pointer to base directory name for this client window,     */
  772. /*         pointer to CNRITEM record to create window for             */
  773. /*                                                                    */
  774. /*  1.                                                                */
  775. /*                                                                    */
  776. /*  OUTPUT: nothing                                                   */
  777. /*                                                                    */
  778. /*--------------------------------------------------------------------*/
  779. /**********************************************************************/
  780. static VOID NewWin( HWND hwndCnr, PSZ szBaseDir, PCNRITEM pci )
  781. {
  782.     // If the user selected a directory, create another directory window for it
  783.  
  784.     if( pci && (pci->attrFile & FILE_DIRECTORY) && pci->szFileName[0] != '.' )
  785.     {
  786.         CHAR szDirectory[ CCHMAXPATH + 1 ];
  787.  
  788.         (void) strcpy( szDirectory, szBaseDir );
  789.  
  790.         // Recursively go up the tree and add the subdirectory names to the
  791.         // fully qualified directory name.
  792.  
  793.         FullyQualify( szDirectory, hwndCnr, pci );
  794.  
  795.         // CreateDirectoryWin is in CREATE.C. By specifying pci as the third
  796.         // parameter we are saying that we want the new container to share
  797.         // records with this one.
  798.  
  799.         (void) CreateDirectoryWin( szDirectory, hwndCnr, pci );
  800.     }
  801.  
  802.     return;
  803. }
  804.  
  805. /**********************************************************************/
  806. /*--------------------------- TailorMenu -----------------------------*/
  807. /*                                                                    */
  808. /*  TAILOR THE MENU FOR THE SELECTED OBJECT                           */
  809. /*                                                                    */
  810. /*  INPUT: container window handle,                                   */
  811. /*         menu window handle,                                        */
  812. /*         pointer to CNRITEM record that is selected                 */
  813. /*                                                                    */
  814. /*  1.                                                                */
  815. /*                                                                    */
  816. /*  OUTPUT: nothing                                                   */
  817. /*                                                                    */
  818. /*--------------------------------------------------------------------*/
  819. /**********************************************************************/
  820. static VOID TailorMenu( HWND hwndCnr, HWND hwndMenu, PCNRITEM pciSelected )
  821. {
  822.     CNRINFO   cnri;
  823.     PINSTANCE pi = INSTDATA( PARENT( hwndCnr ) );
  824.  
  825.     if( !pi )
  826.     {
  827.         Msg( "TailorMenu cant get Instdata RC(%X)", HWNDERR( hwndCnr ) );
  828.  
  829.         return;
  830.     }
  831.  
  832.     // Set the MS_CONDITIONALCASCADE bit and default menu item for submenus.
  833.  
  834.     SetConditionalCascade( hwndMenu, IDM_VIEW_SUBMENU, IDM_VIEW_ICON );
  835.     SetConditionalCascade( hwndMenu, IDM_SORT_SUBMENU, IDM_SORT_DIRORDER );
  836.  
  837.     // Take the CreateNewWindow menu item off the menu if the mouse pointer is
  838.     // not over a container record or if that record is not a directory or if
  839.     // no selected records are directories. We can only create a new directory
  840.     // window for a directory record.
  841.  
  842.     if( !pciSelected || !(pi->fDirSelected ||
  843.         ((pciSelected->attrFile & FILE_DIRECTORY)
  844.           && pciSelected->szFileName[0] != '.')) )
  845.         if( !WinSendMsg( hwndMenu, MM_DELETEITEM,
  846.                          MPFROM2SHORT( IDM_CREATE_NEWWIN, FALSE ), NULL ) )
  847.             Msg( "TailorMenu MM_DELETEITEM failed for IDM_CREATE_NEWWIN RC(%X)",
  848.                   HWNDERR( hwndMenu ) );
  849.  
  850.     if( WinSendMsg( hwndCnr, CM_QUERYCNRINFO, MPFROMP( &cnri ),
  851.                     MPFROMLONG( sizeof( CNRINFO ) ) ) )
  852.     {
  853.         // The Arrange menu item is only applicable to ICON VIEW. Since the
  854.         // Tree view can be or'ed with CV_ICON, we need to first check if we
  855.         // are in Tree view before we care about CV_ICON by itself
  856.  
  857.         if( (cnri.flWindowAttr & CV_TREE) || !(cnri.flWindowAttr & CV_ICON) )
  858.             if( !WinSendMsg( hwndMenu, MM_DELETEITEM,
  859.                              MPFROM2SHORT( IDM_ARRANGE, FALSE ), NULL ) )
  860.                 Msg( "TailorMenu MM_DELETEITEM failed for IDM_ARRANGE RC(%X)",
  861.                      HWNDERR( hwndMenu ) );
  862.     }
  863.     else
  864.         Msg( "TailorMenu MM_DELETEITEM failed for IDM_CREATE_NEWWIN RC(%X)",
  865.               HWNDERR( hwndCnr ) );
  866.  
  867.     // Add a menu item for each directory window (other than our's) that we
  868.     // find on the desktop. If there aren't any others, delete the OtherWindow
  869.     // Submenu since it wouldn't apply
  870.  
  871.     if( !AddOtherWindows( hwndCnr, hwndMenu ) )
  872.         if( !WinSendMsg( hwndMenu, MM_DELETEITEM,
  873.                          MPFROM2SHORT( IDM_OTHERWIN_SUBMENU, FALSE ), NULL ) )
  874.             Msg( "TailorMenu MM_DELETEITEM failed for IDM_OTHERWIN_SUB RC(%X)",
  875.                  HWNDERR( hwndMenu ) );
  876.  
  877.     return;
  878. }
  879.  
  880. /**********************************************************************/
  881. /*---------------------- SetConditionalCascade -----------------------*/
  882. /*                                                                    */
  883. /*  SET A SUBMENU TO BE A CONDITIONAL CASCADE SUBMENU                 */
  884. /*                                                                    */
  885. /*  INPUT: menu window handle,                                        */
  886. /*         id of submenu to make conditionally cascaded,              */
  887. /*         id of item to make the default item of the cascaded menu   */
  888. /*                                                                    */
  889. /*  1.                                                                */
  890. /*                                                                    */
  891. /*  OUTPUT: nothing                                                   */
  892. /*                                                                    */
  893. /*--------------------------------------------------------------------*/
  894. /**********************************************************************/
  895. static VOID SetConditionalCascade( HWND hwndMenu, USHORT idSubMenu,
  896.                                    USHORT idDefaultItem )
  897. {
  898.     MENUITEM mi;
  899.  
  900.     if( WinSendMsg( hwndMenu, MM_QUERYITEM,
  901.                     MPFROM2SHORT( idSubMenu, TRUE ), &mi ) )
  902.     {
  903.         // Set the MS_CONDITIONALCASCADE bit for the submenu.
  904.  
  905.         if( WinSetWindowBits( mi.hwndSubMenu, QWL_STYLE, MS_CONDITIONALCASCADE,
  906.                               MS_CONDITIONALCASCADE ) )
  907.         {
  908.             // Set cascade menu default
  909.  
  910.             if( !WinSendMsg( mi.hwndSubMenu, MM_SETDEFAULTITEMID,
  911.                              MPFROMSHORT( idDefaultItem ), NULL ) )
  912.                Msg( "MM_SETDEFAULTITEMID failed! RC(%X)", HWNDERR( hwndMenu ) );
  913.         }
  914.         else
  915.             Msg( "Set...Cascade WinSetWindowBits RC(%X)", HWNDERR( hwndMenu ) );
  916.     }
  917.     else
  918.         Msg( "Set...Cascade MM_QUERYITEM failed! RC(%X)", HWNDERR( hwndMenu ) );
  919.  
  920.     return;
  921. }
  922.  
  923. /**********************************************************************/
  924. /*------------------------- AddOtherWindows --------------------------*/
  925. /*                                                                    */
  926. /*  ADD OTHER DIRECTORY WINDOWS AS ITEMS ON THE "OTHER WINDOW" SUBMENU*/
  927. /*                                                                    */
  928. /*  INPUT: container window handle,                                   */
  929. /*         menu window handle                                         */
  930. /*                                                                    */
  931. /*  1.                                                                */
  932. /*                                                                    */
  933. /*  OUTPUT: number of other windows                                   */
  934. /*                                                                    */
  935. /*--------------------------------------------------------------------*/
  936. /**********************************************************************/
  937. static INT AddOtherWindows( HWND hwndCnr, HWND hwndMenu )
  938. {
  939.     INT       iOthers = 0, idMenuItem = IDM_OTHERWIN_ITEM1;
  940.     HWND      hwndEnum;
  941.     MENUITEM  miSubMenu;
  942.  
  943.     if( !WinSendMsg( hwndMenu, MM_QUERYITEM,
  944.                      MPFROM2SHORT( IDM_OTHERWIN_SUBMENU, TRUE ), &miSubMenu ) )
  945.     {
  946.         Msg( "AddOtherWindows MM_QUERYITEM RC(%X)", HWNDERR( hwndMenu ) );
  947.  
  948.         return 0;
  949.     }
  950.  
  951.     hwndEnum = WinBeginEnumWindows( HWND_DESKTOP );
  952.  
  953.     if( hwndEnum )
  954.     {
  955.         HWND hwndFrame, hwndClient;
  956.         CHAR szClass[ 50 ];
  957.  
  958.         while( fTrue )
  959.         {
  960.             hwndFrame = WinGetNextWindow( hwndEnum );
  961.  
  962.             if( !hwndFrame )
  963.                 break;
  964.  
  965.             // If we found a frame window (child of the desktop), check to see
  966.             // if it has a client window because that's the one that would have
  967.             // the class that we're looking for
  968.  
  969.             hwndClient = WinWindowFromID( hwndFrame, FID_CLIENT );
  970.  
  971.             if( hwndClient )
  972.             {
  973.                 // Make sure we are checking NULL-terminated strings
  974.  
  975.                 (void) memset( szClass, 0, sizeof( szClass ) );
  976.  
  977.                 // Get the class of this client window. If it is one of ours
  978.                 // and it isn't the current window, add it to the OtherWindow
  979.                 // submenu.
  980.  
  981.                 if( hwndClient != PARENT( hwndCnr ) &&
  982.                     WinQueryClassName( hwndClient, sizeof(szClass), szClass ) )
  983.                 {
  984.                     if( !strcmp( szClass, DIRECTORY_WINCLASS ) )
  985.                         if( AddOtherWinItem( PARENT( hwndCnr ), hwndFrame,
  986.                                hwndClient, miSubMenu.hwndSubMenu, idMenuItem ) )
  987.                         {
  988.                             idMenuItem++;
  989.  
  990.                             iOthers++;
  991.                         }
  992.                 }
  993.             }
  994.         }
  995.  
  996.         WinEndEnumWindows( hwndEnum );
  997.     }
  998.     else
  999.         Msg( "AddOtherWindows WinBeginEnumWindows RC(%X)", HWNDERR( hwndCnr ) );
  1000.  
  1001.     return iOthers;
  1002. }
  1003.  
  1004. /**********************************************************************/
  1005. /*------------------------- AddOtherWinItem --------------------------*/
  1006. /*                                                                    */
  1007. /*  ADD THE OTHER DIRECTORY WINDOW AS A MENU ITEM.                    */
  1008. /*                                                                    */
  1009. /*  INPUT: client window handle of our window,                        */
  1010. /*         frame window handle of other window,                       */
  1011. /*         client window handle of other window,                      */
  1012. /*         OtherWindow submenu window handle,                         */
  1013. /*         menu item id                                               */
  1014. /*                                                                    */
  1015. /*  1.                                                                */
  1016. /*                                                                    */
  1017. /*  OUTPUT: TRUE or FALSE if successful or not                        */
  1018. /*                                                                    */
  1019. /*--------------------------------------------------------------------*/
  1020. /**********************************************************************/
  1021. static BOOL AddOtherWinItem( HWND hwndClient, HWND hwndOtherFrame,
  1022.                              HWND hwndOtherClient, HWND hwndSubMenu,
  1023.                              INT idMenuItem )
  1024. {
  1025.     PINSTANCE piUs = INSTDATA( hwndClient );
  1026.     PINSTANCE piOther = INSTDATA( hwndOtherClient );
  1027.     BOOL      fSuccess = TRUE;
  1028.  
  1029.     if( piUs && piOther )
  1030.     {
  1031.         MENUITEM  miItem;
  1032.         SHORT     sMenuRC;
  1033.  
  1034.         (void) memset( &miItem, 0, sizeof( MENUITEM ) );
  1035.  
  1036.         miItem.iPosition = MIT_END;
  1037.         miItem.afStyle   = MIS_TEXT;
  1038.         miItem.id        = idMenuItem;
  1039.  
  1040.         // Save the frame window handle so we can easily retrieve it if this
  1041.         // menu item is selected so we can change focus to that frame window.
  1042.  
  1043.         piUs->hwndFrame[ idMenuItem - IDM_OTHERWIN_ITEM1 ] = hwndOtherFrame;
  1044.  
  1045.         sMenuRC = (SHORT) WinSendMsg( hwndSubMenu, MM_INSERTITEM,
  1046.                                       MPFROMP( &miItem ),
  1047.                                       MPFROMP( piOther->szDirectory ) );
  1048.  
  1049.         if( sMenuRC == MIT_MEMERROR || sMenuRC == MIT_ERROR )
  1050.         {
  1051.             fSuccess = FALSE;
  1052.  
  1053.             Msg( "AddOtherWinItem MM_INSERTITEM MenuRC(%d) RC(%X)",
  1054.                  sMenuRC, HWNDERR( hwndClient ) );
  1055.         }
  1056.     }
  1057.     else
  1058.     {
  1059.         fSuccess = FALSE;
  1060.  
  1061.         Msg( "AddOtherWindows cant get Inst data RC(%X)", HWNDERR(hwndClient) );
  1062.     }
  1063.  
  1064.     return fSuccess;
  1065. }
  1066.  
  1067. /**********************************************************************/
  1068. /*-------------------------- GetDefaultId ----------------------------*/
  1069. /*                                                                    */
  1070. /*  GET THE DEFAULT ID FOR A MS_CONDITIONALCASCADE SUBMENU            */
  1071. /*                                                                    */
  1072. /*  INPUT: client window handle,                                      */
  1073. /*         id of submenu                                              */
  1074. /*                                                                    */
  1075. /*  1.                                                                */
  1076. /*                                                                    */
  1077. /*  OUTPUT: id of default item                                        */
  1078. /*                                                                    */
  1079. /*--------------------------------------------------------------------*/
  1080. /**********************************************************************/
  1081. static USHORT GetDefaultId( HWND hwndClient, USHORT idSubMenu )
  1082. {
  1083.     USHORT  id = 0;
  1084.     HWND    hwndMenu = WinWindowFromID( hwndClient, ID_CONTEXT_MENU );
  1085.  
  1086.     if( hwndMenu )
  1087.     {
  1088.         MENUITEM mi;
  1089.  
  1090.         if( WinSendMsg( hwndMenu, MM_QUERYITEM, MPFROM2SHORT( idSubMenu, TRUE ),
  1091.                         &mi ))
  1092.         {
  1093.             id = (USHORT) WinSendMsg( mi.hwndSubMenu,
  1094.                                       MM_QUERYDEFAULTITEMID, NULL, NULL );
  1095.  
  1096.             if( !id )
  1097.                 Msg( "GetDefaultId MM_QUERYDEFAULTITEMID RC(%X)",
  1098.                       HWNDERR( hwndClient ) );
  1099.         }
  1100.         else
  1101.             Msg( "GetDefaultId MM_QUERYITEM RC(%X)", HWNDERR( hwndClient ) );
  1102.     }
  1103.  
  1104. /*****************************************************************************
  1105. BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG
  1106.  
  1107.     else
  1108.         Msg( "GetDefaultId WinWindowFromID RC(%X)", HWNDERR( hwndClient ) );
  1109.  
  1110. For some reason, only in a specific instance, the WinWindowFromID fails but no
  1111. valid error code is returned from WinGetLastError. This happens under the
  1112. following circumstances:
  1113.  
  1114.  - the popup menu is active
  1115.  - you select the Other Window submenu but don't select a menu item
  1116.  - you select a conditionally cascaded submenu but not the cascade button
  1117.  
  1118. I tried to nail this down but could not.
  1119.  
  1120. BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG
  1121. ******************************************************************************/
  1122.  
  1123.     return id;
  1124. }
  1125.  
  1126. /*************************************************************************
  1127.  *                     E N D     O F     S O U R C E                     *
  1128.  *************************************************************************/
  1129.