home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / mac / UserInterface / CPatternButtonPopup.cp < prev    next >
Encoding:
Text File  |  1998-04-08  |  26.7 KB  |  970 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19.  
  20. /*
  21.     Features (not including the look/feel/behavior from inherited classes):
  22.     
  23.     (1)    LMenu based. This allows flexible use of the class for both
  24.         resource-based menus and menus built dynamically. Additionally,
  25.         the benefits of attaching commands to menu items (using Mcmd and/or
  26.         dynamically) is gained.
  27.     (2) The "current item" can either be marked or not. If marking is specified,
  28.         a mark character can be specified as well.
  29.     (3) Popup or Popdown behavior.
  30.     (4) A delay before popup value is supported. This gives rise to the "quick click"
  31.         feature described below.
  32.     (5) Quick clicks are supported (ala Greg's Browser and others). This allows a
  33.         value or command to be specified such that if the button is released before
  34.         the popup delay value, either the value will change to the specified value
  35.         or the specified command will be sent. Note that if the quick click is
  36.         command-based, the command does not have to coincide with a menu command
  37.         for a menu item (it can be any arbitrary command). Quick click commands are
  38.         supported even when there is no menu (as long as the quick click command
  39.         is an enabled command).
  40.     (6) Use of factory methods for menu management allows subclasses to add menu
  41.         caching, etc.
  42.     (7) Use of template methods (or hooks) for handling "quick clicks" and value
  43.         changes allow subclasses to further customize the behavior. For example,
  44.         a subclass may implement a "smart" button which will perform an action
  45.         directly (instead of relying on broadcaster/listener delegation) by overriding
  46.         HandleNewValue. For example, CGuideMenuPopup (which is a subclass of
  47.         CPatternButtonPopup) can be inserted into a PPob window resource and
  48.         It Will Just Work¬ without additional code to link broadcasters to listeners
  49.         and additional ListenToMessage cases sprinkled about.
  50.     (8) Fully configurable through Constructor PPobs and accessor methods.    
  51.         
  52.         
  53.         
  54.     CAVEAT:    Although we do correctly save and restore the system font, if Mercutio
  55.             MDEF is being used in the app, then it must be used in all menus which
  56.             change the system font in order to not mess up the system font. It is
  57.             probably a wise idea to use Mercutio MDEF in *all* menus throughout the
  58.             app to be safe.
  59. */        
  60.  
  61. #include "CPatternButtonPopup.h"
  62.         
  63. #include <UMemoryMgr.h>        
  64.         
  65. #include "StSetBroadcasting.h"
  66. #include "CDrawingState.h"
  67. #include "CApplicationEventAttachment.h"
  68. #include "CTargetedUpdateMenuRegistry.h"
  69.  
  70. // ---------------------------------------------------------------------------
  71. //        Ñ CPatternButtonPopup
  72. // ---------------------------------------------------------------------------
  73. // Stream-based ctor
  74.  
  75. CPatternButtonPopup::CPatternButtonPopup(
  76.     LStream* inStream)
  77.  
  78.     :    mMenu(nil),
  79.     
  80.         mPopupMenuID(0),
  81.         mPopupTextTraitsID(0),
  82.         mInitialCurrentItem(0),
  83.         mTicksBeforeDisplayingPopup(9),
  84.         mQuickClickValueOrCommand(0),
  85.         mQuickClickIsCommandBased(false),
  86.         mResourceBasedMenu(false),
  87.         mPopdownBehavior(false),
  88.         mMarkCurrentItem(false),
  89.         mMarkCharacter(0),
  90.         mDetachResource(false),
  91.         
  92.         mOwnsMenu(false),
  93.         mPopUpMenuSelectWasCalled(true),    // Start out true to ensure proper behavior
  94.                                             // even if super::HandlePopupMenuSelect()
  95.                                             // is called instead of our own
  96.         
  97.         super(inStream)
  98. {
  99.     // Read in the data from the stream
  100.         
  101.     ThrowIfNil_(inStream);
  102.         
  103.     LStream& theStream = *inStream;
  104.     
  105.     theStream >> mPopupMenuID;
  106.     theStream >> mPopupTextTraitsID;
  107.     theStream >> mInitialCurrentItem;    
  108.     theStream >> mTicksBeforeDisplayingPopup;
  109.     theStream >> mQuickClickValueOrCommand;
  110.     theStream >> mQuickClickIsCommandBased;
  111.     theStream >> mResourceBasedMenu;
  112.     theStream >> mPopdownBehavior;
  113.     theStream >> mMarkCurrentItem;
  114.     theStream >> mMarkCharacter;
  115.     theStream >> mDetachResource;
  116.     
  117.     // Ignore the ticks before displaying popup value specified in the resource.
  118.     // That was a mistake.
  119.     
  120.     mTicksBeforeDisplayingPopup = GetDblTime();
  121. }
  122.  
  123. // ---------------------------------------------------------------------------
  124. //        Ñ ~CPatternButtonPopup
  125. // ---------------------------------------------------------------------------
  126. // dtor
  127.  
  128. CPatternButtonPopup::~CPatternButtonPopup()
  129. {
  130.     CPatternButtonPopup::EliminatePreviousMenu();
  131. }
  132.                         
  133. #pragma mark -
  134.  
  135. // ---------------------------------------------------------------------------
  136. //        Ñ FinishCreateSelf
  137. // ---------------------------------------------------------------------------
  138.  
  139. void
  140. CPatternButtonPopup::FinishCreateSelf()
  141. {
  142.     super::FinishCreateSelf();
  143.     
  144.     MakeNewMenu();
  145. }
  146.  
  147.                         
  148. #pragma mark -
  149.                         
  150. // ---------------------------------------------------------------------------
  151. //        Ñ GetMenu
  152. // ---------------------------------------------------------------------------
  153. //    The menu factory methods (GetMenu, OwnsMenu, SetMenu, AdoptMenu, 
  154. //    MakeNewMenu, and EliminatePreviousMenu) *must* be overriden as a group.
  155. //    The main reason for overriding this group of methods would be to
  156. //    do some form of menu caching.
  157.  
  158. LMenu*
  159. CPatternButtonPopup::GetMenu() const
  160. {
  161.     return mMenu;
  162.     
  163. }    
  164.     
  165. // ---------------------------------------------------------------------------
  166. //        Ñ OwnsMenu
  167. // ---------------------------------------------------------------------------
  168. //    The menu factory methods (GetMenu, OwnsMenu, SetMenu, AdoptMenu, 
  169. //    MakeNewMenu, and EliminatePreviousMenu) *must* be overriden as a group.
  170. //    The main reason for overriding this group of methods would be to
  171. //    do some form of menu caching.
  172.     
  173. Boolean
  174. CPatternButtonPopup::OwnsMenu() const
  175. {
  176.     return mOwnsMenu;
  177.     
  178. }    
  179.                     
  180. // ---------------------------------------------------------------------------
  181. //        Ñ SetMenu
  182. // ---------------------------------------------------------------------------
  183. //    The menu factory methods (GetMenu, OwnsMenu, SetMenu, AdoptMenu, 
  184. //    MakeNewMenu, and EliminatePreviousMenu) *must* be overriden as a group.
  185. //    The main reason for overriding this group of methods would be to
  186. //    do some form of menu caching.
  187.  
  188. void
  189. CPatternButtonPopup::SetMenu(LMenu* inMenu)
  190. {
  191.     EliminatePreviousMenu();
  192.     
  193.     mMenu        = inMenu;
  194.     mOwnsMenu    = false;
  195. }
  196.  
  197. // ---------------------------------------------------------------------------
  198. //        Ñ AdoptMenu
  199. // ---------------------------------------------------------------------------
  200. //    The menu factory methods (GetMenu, OwnsMenu, SetMenu, AdoptMenu, 
  201. //    MakeNewMenu, and EliminatePreviousMenu) *must* be overriden as a group.
  202. //    The main reason for overriding this group of methods would be to
  203. //    do some form of menu caching.
  204.  
  205. void
  206. CPatternButtonPopup::AdoptMenu(
  207.     LMenu* inMenuToAdopt)
  208. {
  209.     EliminatePreviousMenu();
  210.     
  211.     mMenu        = inMenuToAdopt;
  212.     mOwnsMenu    = true;
  213. }
  214.  
  215. // ---------------------------------------------------------------------------
  216. //        Ñ MakeNewMenu
  217. // ---------------------------------------------------------------------------
  218. //    The menu factory methods (GetMenu, OwnsMenu, SetMenu, AdoptMenu, 
  219. //    MakeNewMenu, and EliminatePreviousMenu) *must* be overriden as a group.
  220. //    The main reason for overriding this group of methods would be to
  221. //    do some form of menu caching.
  222.  
  223. void
  224. CPatternButtonPopup::MakeNewMenu()
  225. {
  226.     // Create popup menu if appropriate
  227.     
  228.     if (mResourceBasedMenu)
  229.     {
  230.         if (mPopupMenuID)
  231.         {
  232.             // Resource-based menu (via ::GetMenu)
  233.             
  234.             AdoptMenu(new LMenu(mPopupMenuID));
  235.             mValue = 0;
  236.             SetPopupMinMaxValues();
  237.             
  238.             // Detach the resource as specified
  239.             
  240.             if (mDetachResource)
  241.             {
  242.                 ::DetachResource((Handle) GetMenu()->GetMacMenuH());
  243.             }
  244.             
  245.             // For resource-based menus, set the initial current item
  246.             
  247.             StSetBroadcasting setBroadcasting(this, false);
  248.             
  249.             try
  250.             {
  251.                 SetValue(mInitialCurrentItem);
  252.             }
  253.             catch (const CValueRangeException&) { }
  254.             catch (const CAttemptToSetDisabledValueException&) { }
  255.         }
  256.     }
  257.     else
  258.     {
  259.         if (mPopupMenuID)
  260.         {
  261.             // Dynamic menu (via ::NewMenu)
  262.             
  263.             AdoptMenu(new LMenu(mPopupMenuID, "\p"));
  264.             mValue = 0;
  265.             SetPopupMinMaxValues();
  266.         }
  267.     }
  268. }                        
  269.  
  270. // ---------------------------------------------------------------------------
  271. //        Ñ EliminatePreviousMenu
  272. // ---------------------------------------------------------------------------
  273. //    The menu factory methods (GetMenu, OwnsMenu, SetMenu, AdoptMenu, 
  274. //    MakeNewMenu, and EliminatePreviousMenu) *must* be overriden as a group.
  275. //    The main reason for overriding this group of methods would be to
  276. //    do some form of menu caching.
  277.  
  278. void
  279. CPatternButtonPopup::EliminatePreviousMenu()
  280. {
  281.     if (mMenu)
  282.     {
  283.         if (mOwnsMenu)
  284.         {
  285.             delete mMenu;
  286.         }
  287.         
  288.         mMenu = nil;
  289.     }
  290. }                        
  291.  
  292. #pragma mark -
  293.  
  294. // ---------------------------------------------------------------------------
  295. //        Ñ TrackHotSpot
  296. // ---------------------------------------------------------------------------
  297.  
  298. Boolean
  299. CPatternButtonPopup::TrackHotSpot(
  300.     Int16        inHotSpot,
  301.     Point        inPoint,
  302.     Int16        inModifiers)
  303. {
  304.     Boolean pointInHotSpot;
  305.                                         // The value we set mPopUpMenuSelectWasCalled
  306.                                         // is not important. We just want to restore
  307.                                         // the value when this function returns.
  308.     StValueChanger<Boolean> theValueChanger(mPopUpMenuSelectWasCalled, true);
  309.     
  310.                                         // If no menu, then allow normal tracking and
  311.                                         // perform a quick click if button is released
  312.                                         // in the hot spot.
  313.     if (!GetMenu() || !GetMenu()->GetMacMenuH())
  314.     {
  315.         pointInHotSpot = super::TrackHotSpot(inHotSpot, inPoint, inModifiers);
  316.  
  317.         if (pointInHotSpot)
  318.         {
  319.             if (mQuickClickIsCommandBased)
  320.             {
  321.                 HandleQuickClick();
  322.             }
  323.         }
  324.         
  325.         return pointInHotSpot;
  326.     }
  327.     
  328.     Point    currPt        = inPoint;
  329.     UInt32    endTicks    = sWhenLastMouseDown + mTicksBeforeDisplayingPopup;
  330.     
  331.                                         // For the initial mouse down, the
  332.                                         // mouse is currently inside the HotSpot
  333.                                         // when it was previously outside
  334.     Boolean        currInside = true;
  335.     Boolean        prevInside = false;
  336.     HotSpotAction(inHotSpot, currInside, prevInside);
  337.     
  338.     // If the controlKey is down then we bypass quick click tracking in favor of
  339.     // the popup menu. Additionally, we don't even bother tracking unless
  340.     // mQuickClickValueOrCommand is non-zero
  341.  
  342.     if (!CApplicationEventAttachment::CurrentEventHasModifiers(controlKey) && mQuickClickValueOrCommand)
  343.     {        
  344.                                         // Track the mouse while it is down
  345.         Point    currPt = inPoint;
  346.         while (::StillDown() && ::TickCount() < endTicks)
  347.         {
  348.             ::GetMouse(&currPt);        // Must keep track if mouse moves from
  349.             prevInside = currInside;    // In-to-Out or Out-To-In
  350.             currInside = PointInHotSpot(currPt, inHotSpot);
  351.             
  352.             if (!currInside)
  353.             {
  354.                                         // Reset endTicks if mouse moves outside                                        
  355.                 endTicks = ::TickCount() + mTicksBeforeDisplayingPopup;
  356.             }
  357.             else
  358.             {
  359.                                         // Quick Click tracking
  360.                 if (!::StillDown())
  361.                 {
  362.                                         // Set button in "up" state
  363.                     HotSpotAction(inHotSpot, false, true);
  364.                     
  365.                                         // Check if MouseUp occurred in HotSpot
  366.                     ::GetMouse(&currPt);
  367.                     pointInHotSpot = PointInHotSpot(currPt, inHotSpot);
  368.  
  369.                     if (pointInHotSpot)
  370.                     {
  371.                         HandleQuickClick();
  372.                     }
  373.                     
  374.                     return pointInHotSpot;
  375.                 }
  376.             }
  377.             
  378.             HotSpotAction(inHotSpot, currInside, prevInside);
  379.         }
  380.     }
  381.  
  382.     if (!::StillDown())
  383.     {
  384.                             // Set button in "up" state
  385.         HotSpotAction(inHotSpot, false, true);
  386.         
  387.                             // Check if MouseUp occurred in HotSpot
  388.         ::GetMouse(&currPt);
  389.         pointInHotSpot = PointInHotSpot(currPt, inHotSpot);
  390.  
  391.         if (pointInHotSpot)
  392.         {
  393.             HandleQuickClick();
  394.         }
  395.         
  396.         return pointInHotSpot;
  397.     }
  398.     
  399.                                         // We tracked inside the button longer than
  400.                                         // mTicksBeforeDisplayingPopup and the mouse is
  401.                                         // still down, so we now skip normal tracking
  402.                                         // to display the popup menu
  403.     Int16 menuID    = 0;
  404.     Int16 menuItem    = GetValue();
  405.     Point popLocation;
  406.     
  407.     GetPopupMenuPosition(popLocation);
  408.     
  409.                                         // Handle user interaction with the menu
  410.     HandlePopupMenuSelect(popLocation, mPopdownBehavior ? 1 : menuItem, menuID, menuItem);
  411.  
  412.     if (!mPopUpMenuSelectWasCalled && mQuickClickValueOrCommand)
  413.     {
  414.                                         // If the popup menu was not displayed,
  415.                                         // then we will check once more if MouseUp
  416.                                         // occurred in HotSpot so we can do
  417.                                         // the quick click
  418.         ::GetMouse(&currPt);
  419.         pointInHotSpot = PointInHotSpot(currPt, inHotSpot);
  420.  
  421.         if (pointInHotSpot)
  422.         {
  423.             HandleQuickClick();
  424.         }
  425.     }
  426.     
  427.     Rect    localFrame;
  428.     Point    mousePt;
  429.     
  430.     FocusDraw();        
  431.     CalcLocalFrameRect(localFrame);
  432.     ::GetMouse(&mousePt);
  433.     
  434.     mMouseInFrame = ::PtInRect(mousePt, &localFrame);
  435.  
  436.                                         // Set the new item, if specified. NOTE: We set the
  437.                                         // value here so that this control acts the same as
  438.                                         // LStdControl and LGAPopup
  439.     if (menuItem > 0)
  440.     {
  441.         try
  442.         {
  443.             SetValue(menuItem);
  444.         }
  445.         catch (const CValueRangeException&) { }
  446.         catch (const CAttemptToSetDisabledValueException&) { }
  447.     }
  448.     else
  449.     {
  450.                                         // Set button in "up" state
  451.         HotSpotAction(inHotSpot, false, true);
  452.     }
  453.     
  454.     return (menuItem > 0);
  455. }
  456.  
  457. // ---------------------------------------------------------------------------
  458. //        Ñ HandlePopupMenuSelect
  459. // ---------------------------------------------------------------------------
  460. //    Handle user interaction with the popup menu
  461.  
  462. void
  463. CPatternButtonPopup::HandlePopupMenuSelect(
  464.     Point        inPopupLoc,
  465.     Int16        inCurrentItem,
  466.     Int16&        outMenuID,
  467.     Int16&        outMenuItem)
  468. {        
  469.     ThrowIfNil_(GetMenu());
  470.     ThrowIfNil_(GetMenu()->GetMacMenuH());
  471.  
  472.     mPopUpMenuSelectWasCalled = false;
  473.     
  474.     // Handle the actual insertion into the hierarchical menubar.
  475.     //
  476.     // From Apple Sample Code (PopupMenuWithCurFont.c):
  477.     //        "Believe it or not, it's important to insert
  478.     //         the menu before diddling the font characteristics."
  479.     //
  480.     // Note that this is also what LGAPopup.cp does. Unfortunately,
  481.     // neither this nor the StMercutioMDEFTextState seems to help
  482.     // with the font metrics problems we see with Mercurtio MDEF.
  483.         
  484.     ::InsertMenu(GetMenu()->GetMacMenuH(), hierMenu);
  485.  
  486.     Int16 saveFont = ::LMGetSysFontFam();
  487.     Int16 saveSize = ::LMGetSysFontSize();
  488.     
  489.     StMercutioMDEFTextState theMercutioMDEFTextState;
  490.     
  491.     try
  492.     {
  493.         // Reconfigure the system font so that the menu will be drawn in our desired 
  494.         // font and size.
  495.         
  496.         if (mPopupTextTraitsID)
  497.         {                
  498.             FocusDraw();
  499.             
  500.             TextTraitsH traitsH = UTextTraits::LoadTextTraits(mPopupTextTraitsID);
  501.             
  502.             if (traitsH)
  503.             {
  504.                 // Bug #64133 kellys
  505.                 // If setting to application font, get the application font for current script
  506.                 if((**traitsH).fontNumber == 1)
  507.                     ::LMSetSysFontFam ( ::GetScriptVariable(::FontToScript(1), smScriptAppFond) );
  508.                 else
  509.                     ::LMSetSysFontFam ( (**traitsH).fontNumber );
  510.                 ::LMSetSysFontSize((**traitsH).size);
  511.                 ::LMSetLastSPExtra(-1L);
  512.             }
  513.         }
  514.         
  515.         // Adjust the contents of the menu
  516.         
  517.         AdjustMenuContents();
  518.         
  519.         // Setup the currently selected menu item
  520.         
  521.         SetupCurrentMenuItem(GetMenu()->GetMacMenuH(), GetValue());
  522.  
  523.         // Call PopupMenuSelect and wait for it to return
  524.  
  525.         Int32 result = 0;
  526.         
  527.         if (::StillDown())
  528.         {
  529.             mPopUpMenuSelectWasCalled = true;
  530.             
  531.             result = ::PopUpMenuSelect(
  532.                                             GetMenu()->GetMacMenuH(),
  533.                                             inPopupLoc.v,
  534.                                             inPopupLoc.h,
  535.                                             inCurrentItem);
  536.         }                                            
  537.         
  538.         // Extract the values from the returned result these are then passed 
  539.         // back out to the caller
  540.         
  541.         outMenuID    = HiWord(result);
  542.         outMenuItem    = LoWord(result);
  543.     }
  544.     catch (...)
  545.     {
  546.         // Ignore errors
  547.     }
  548.  
  549.     // Restore the system font
  550.     
  551.     ::LMSetSysFontFam(saveFont);
  552.     ::LMSetSysFontSize(saveSize);
  553.     ::LMSetLastSPExtra(-1L);
  554.  
  555.     // Finally get the menu removed
  556.     
  557.     ::DeleteMenu(mPopupMenuID);
  558. }
  559.  
  560. // ---------------------------------------------------------------------------
  561. //        Ñ HotSpotResult
  562. // ---------------------------------------------------------------------------
  563. //    Note that actual hot spot result processing occurs in TrackHotSpot. This
  564. //    is because the information necessary to perform the correct action is
  565. //    only available while tracking.
  566.  
  567. void
  568. CPatternButtonPopup::HotSpotResult(Int16 inHotSpot)
  569. {
  570.     if (!IsBehaviourToggle())
  571.     {
  572.         // Make sure that non-toggle buttons return to the "up" state
  573.         
  574.         HotSpotAction(inHotSpot, false, true);
  575.     }
  576. }
  577.  
  578. #pragma mark -
  579.  
  580. // ---------------------------------------------------------------------------
  581. //        Ñ AdjustMenuContents
  582. // ---------------------------------------------------------------------------
  583. //    Do stuff to the menu (add or remove menu items). The default implementation
  584. //    is intentionally a noop. Override to add interesting behavior.
  585.  
  586. void
  587. CPatternButtonPopup::AdjustMenuContents()
  588. {
  589. }
  590.  
  591. // ---------------------------------------------------------------------------
  592. //        Ñ SetupCurrentMenuItem
  593. // ---------------------------------------------------------------------------
  594. //    Mark new item and unmark old item
  595.  
  596. void
  597. CPatternButtonPopup::SetupCurrentMenuItem(MenuHandle inMenuH, Int16 inCurrentItem)
  598. {
  599.     ThrowIfNil_(inMenuH);
  600.         
  601.     if (mMarkCurrentItem)
  602.     {
  603.         if (GetValue() != inCurrentItem)
  604.         {
  605.             Int16 oldItem = GetValue();
  606.             
  607.             if (oldItem > 0)
  608.             {
  609.                 ::SetItemMark(inMenuH, oldItem, noMark);
  610.             }
  611.         }
  612.         
  613.         if (mMarkCharacter && (inCurrentItem > 0))
  614.         {
  615.             ::SetItemMark(inMenuH, inCurrentItem, mMarkCharacter);
  616.         }
  617.         else 
  618.         {
  619.             ::SetItemMark(inMenuH, inCurrentItem, noMark);
  620.         }
  621.     }
  622. }
  623.  
  624. // ---------------------------------------------------------------------------
  625. //        Ñ GetPopupMenuPosition
  626. // ---------------------------------------------------------------------------
  627. //    Get the position for displaying the popup menu in global coordinates
  628.  
  629. void
  630. CPatternButtonPopup::GetPopupMenuPosition(Point &outPopupLoc)
  631. {
  632.     Rect popupRect;
  633.  
  634.     CalcLocalFrameRect(popupRect);
  635.     
  636.     if (mPopdownBehavior)
  637.     {
  638.         outPopupLoc.v = popupRect.bottom + 1;
  639.         outPopupLoc.h = popupRect.left   + 2;
  640.     }
  641.     else
  642.     {
  643.         outPopupLoc.v = popupRect.top;
  644.         outPopupLoc.h = popupRect.left;
  645.     }
  646.         
  647.     LocalToPortPoint(outPopupLoc);
  648.     PortToGlobalPoint(outPopupLoc);
  649. }
  650.  
  651. #pragma mark -
  652.  
  653. // ---------------------------------------------------------------------------
  654. //        Ñ SetValue
  655. // ---------------------------------------------------------------------------
  656. //    This is overridden to adjust the mark on the previous/new menu items and
  657. //    to ensure that the value is always broadcast (even if it hasn't really
  658. //    changed) and to call a "hook" for derived classes to perform some action
  659. //    on value changes.
  660. //
  661. //    Note that this implementation should be considered "final" (i.e. no further
  662. //    overrides allowed) in order to preserve the semantics of this class.
  663. //
  664. //    Exceptions thrown: CValueRangeException, CAttemptToSetDisabledValueException
  665.  
  666. void
  667. CPatternButtonPopup::SetValue(Int32 inValue)
  668. {
  669.     ThrowIfNil_(GetMenu());
  670.     ThrowIfNil_(GetMenu()->GetMacMenuH());
  671.     
  672.     if (inValue < 0 || inValue > ::CountMItems(GetMenu()->GetMacMenuH()))
  673.     {
  674.         throw CValueRangeException();
  675.     }
  676.     
  677.     if (!GetMenu()->ItemIsEnabled(inValue))
  678.     {
  679.         throw CAttemptToSetDisabledValueException();
  680.     }
  681.         
  682.         // If mMarkCurrentItem is false, it is assumed that we want to
  683.         // be able to select the same command several times in a row.
  684.         // By hacking mValue to 0, we make sure that the menu command
  685.         // will always be broadcast.
  686.         
  687.     if (!mMarkCurrentItem)
  688.     {
  689.         mValue = 0;
  690.     }
  691.  
  692.     if (mValue != inValue)
  693.     {
  694.         // Setup the menu item
  695.         
  696.         SetupCurrentMenuItem(GetMenu()->GetMacMenuH(), inValue);
  697.  
  698.         // Set the new value. If the HandleNewValue hook indicates that
  699.         // the value was handled, then we make sure that the call to
  700.         // the base class SetValue does not broadcast the new value.
  701.             
  702.         if (HandleNewValue(inValue))
  703.         {
  704.             StSetBroadcasting setBroadcasting(this, false);
  705.  
  706.             super::SetValue(inValue);
  707.         }
  708.         else
  709.         {
  710.             super::SetValue(inValue);
  711.         }
  712.     }
  713. }
  714.  
  715.  
  716.  
  717. // ---------------------------------------------------------------------------
  718. //        Ñ SetPopupMinMaxValues
  719. // ---------------------------------------------------------------------------
  720. //    Setup the minimum and maximum values for the control based on the
  721. //    current menu state.
  722. //
  723. //    NOTE: It is important for clients to call this method after inserting/removing
  724. //    menu items dynamically.
  725.  
  726. void
  727. CPatternButtonPopup::SetPopupMinMaxValues()
  728. {
  729.     if (GetMenu() && GetMenu()->GetMacMenuH())
  730.     {
  731.         mMinValue = 0;
  732.         mMaxValue = ::CountMItems(GetMenu()->GetMacMenuH());
  733.     }
  734.     else
  735.     {
  736.         mMaxValue = mMinValue = 0;
  737.     }
  738. }
  739.  
  740. #pragma mark -
  741.  
  742. // ---------------------------------------------------------------------------
  743. //        Ñ OKToSendCommand
  744. // ---------------------------------------------------------------------------
  745.  
  746. Boolean
  747. CPatternButtonPopup::OKToSendCommand(
  748.     Int32 inCommand)
  749. {
  750.     LCommander* theTarget        = LCommander::GetTarget();
  751.     Boolean     enabled            = false;
  752.     Boolean        usesMark        = false;
  753.     Str255        outName;
  754.     Char16        outMark;
  755.             
  756.     if (inCommand && theTarget)
  757.     {
  758.         theTarget->ProcessCommandStatus(inCommand, enabled, usesMark, outMark, outName);
  759.     }
  760.  
  761.     return enabled;
  762. }
  763.  
  764. // ---------------------------------------------------------------------------
  765. //        Ñ BroadcastValueMessage
  766. // ---------------------------------------------------------------------------
  767. //    Broadcast the menu command if there is one, otherwise broadcast the value
  768.  
  769. void CPatternButtonPopup::BroadcastValueMessage()
  770. {
  771.     ThrowIfNil_(GetMenu());
  772.     ThrowIfNil_(GetMenu()->GetMacMenuH());
  773.     
  774.     if (mValueMessage != msg_Nothing)
  775.     {
  776.         CommandT theCommand    = GetMenu()->CommandFromIndex(GetValue());
  777.         
  778.         if (theCommand != cmd_Nothing)
  779.         {
  780.             if (OKToSendCommand(theCommand))
  781.             {
  782.                 BroadcastMessage(theCommand, nil);
  783.             }
  784.         }
  785.         else
  786.         {
  787.             Int32 value = mValue;
  788.             
  789.             BroadcastMessage(mValueMessage, &value);
  790.         }
  791.     }
  792. }
  793.  
  794. #pragma mark -
  795.  
  796. // ---------------------------------------------------------------------------
  797. //        Ñ HandleQuickClick
  798. // ---------------------------------------------------------------------------
  799. //    Hook for handling quick clicks.
  800.  
  801. void
  802. CPatternButtonPopup::HandleQuickClick()
  803. {
  804.     if (mQuickClickValueOrCommand)
  805.     {
  806.         if (mQuickClickIsCommandBased)
  807.         {
  808.             if (OKToSendCommand(mQuickClickValueOrCommand))
  809.             {
  810.                 BroadcastMessage(mQuickClickValueOrCommand, nil);
  811.             }
  812.         }
  813.         else
  814.         {
  815.             try
  816.             {
  817.                 SetValue(mQuickClickValueOrCommand);
  818.             }
  819.             catch (const CValueRangeException&) { }
  820.             catch (const CAttemptToSetDisabledValueException&) { }
  821.         }
  822.     }
  823. }
  824.  
  825. // ---------------------------------------------------------------------------
  826. //        Ñ HandleNewValue
  827. // ---------------------------------------------------------------------------
  828. //    Hook for handling value changes. Called by SetValue.
  829. //
  830. //    Some CPatternButtonPopups may override this to perform an action rather
  831. //    than using broadcaster/listener. Returns true if HandleNewValue handled the new
  832. //    value (this will cause the new value to not be broadcast).
  833. // 
  834. //    Note that the setting of the new value is done by CPatternButtonPopup::SetValue.
  835. //  Therefore, GetValue() will still return the old value here, so the old value is
  836. //    still available in this method.
  837.  
  838. Boolean
  839. CPatternButtonPopup::HandleNewValue(Int32 inNewValue)
  840. {
  841. #pragma unused(inNewValue)
  842.  
  843.     return false;
  844. }
  845.  
  846. #pragma mark -
  847.  
  848. // ---------------------------------------------------------------------------
  849. //        Ñ HandleEnablingPolicy
  850. // ---------------------------------------------------------------------------
  851. //    This is our required implementation of the MPaneEnablerPolicy interface.
  852. //
  853. //    The enabling policy is as follows:
  854. //
  855. //    - If all of the menu items associated with the menu items are disabled
  856. //      then disable the button. Note that a menu item without a command
  857. //      is automatically enabled and a menu item with a command is enabled
  858. //      only if the command associated with it is enabled.
  859. //    - If there is (no menu or no menu items in the menu) and
  860. //      mQuickClickIsCommandBased is true and it is *not OK* to send the
  861. //      mQuickClickValueOrCommand command then disable the button.
  862. //    - Otherwise enable the buton.
  863.  
  864. void
  865. CPatternButtonPopup::HandleEnablingPolicy()
  866. {
  867.     LCommander* theTarget        = LCommander::GetTarget();
  868.     MenuHandle    macMenuH        = nil;
  869.     MessageT    buttonCommand    = GetValueMessage();
  870.     Boolean     enabled            = false;
  871.     Boolean        usesMark        = false;
  872.     Str255        outName;
  873.     Char16        outMark;
  874.     
  875.     if (!CTargetedUpdateMenuRegistry::UseRegistryToUpdateMenus() ||
  876.             CTargetedUpdateMenuRegistry::CommandInRegistry(buttonCommand))
  877.     {
  878.         if (!IsActive() || !IsVisible())
  879.             return;
  880.             
  881.         if (!theTarget)
  882.             return;
  883.         
  884.         if (GetMenu() && GetMenu()->GetMacMenuH() && ::CountMItems(GetMenu()->GetMacMenuH()))
  885.         {
  886.             macMenuH = GetMenu()->GetMacMenuH();
  887.         }
  888.         
  889.         if (macMenuH)
  890.         {
  891.             // We have a menu
  892.             
  893.             CommandT    theCommand;
  894.             Boolean        allDisabled    = true;
  895.             Int16        menuItem    = 0;
  896.             
  897.             while (GetMenu()->FindNextCommand(menuItem, theCommand))
  898.             {
  899.                 if (theCommand)
  900.                 {
  901.                     outName[0] = 0;
  902.         
  903.                     // Call ProcessCommandStatus() to determine whether to enable the menu items.
  904.                     // If they are all disabled then disable the button itself.
  905.  
  906.                     theTarget->ProcessCommandStatus(theCommand, enabled, usesMark, outMark, outName);
  907.  
  908.                     if (outName[0] != 0)
  909.                     {
  910.                         ::SetMenuItemText(macMenuH, menuItem, outName);
  911.                     }
  912.  
  913.                     if (enabled)
  914.                     {
  915.                         allDisabled = false;
  916.                         ::EnableItem(macMenuH, menuItem);
  917.                     }
  918.                     else
  919.                     {
  920.                         ::DisableItem(macMenuH, menuItem);
  921.                     }
  922.  
  923.                     if (usesMark)
  924.                     {
  925.                         if ( outMark && mMarkCharacter )
  926.                             outMark = mMarkCharacter;
  927.                         ::SetItemMark(macMenuH, menuItem, outMark);
  928.                     }
  929.                 }
  930.                 else
  931.                 {
  932.                     allDisabled = false;
  933.                     ::EnableItem(macMenuH, menuItem);
  934.                 }
  935.             }
  936.  
  937.             if (menuItem == 0)
  938.             {                
  939.                     // The menu does not have any commands, so we check the command attached to
  940.                     // the button itself.
  941.                 
  942.                 theTarget->ProcessCommandStatus(buttonCommand, enabled, usesMark, outMark, outName);
  943.                 allDisabled = (!enabled);
  944.             }
  945.  
  946.             if (allDisabled)
  947.             {
  948.                 Disable();
  949.             }
  950.             else
  951.             {
  952.                 Enable();
  953.             }
  954.         }
  955.         else
  956.         {
  957.             // We don't have a menu
  958.             
  959.             if (mQuickClickIsCommandBased && OKToSendCommand(mQuickClickValueOrCommand))
  960.             {
  961.                 Enable();            
  962.             }
  963.             else
  964.             {
  965.                 Disable();
  966.             }
  967.         }
  968.     }
  969. }
  970.