home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / C++ / Snippets / New Venus / src / Dialog.cc next >
Encoding:
C/C++ Source or Header  |  1995-11-05  |  11.4 KB  |  298 lines  |  [TEXT/CWIE]

  1. /*
  2.  ***********************************************************************
  3.  *
  4.  *
  5.  *                            Generic Dialog Class
  6.  *                    Implementing a few very basic functions
  7.  *                        messing with items, etc
  8.  *        
  9.  *
  10.  ***********************************************************************
  11.  */
  12.  
  13. #include "Dialog.h"
  14. #include "mymenv.h"
  15.  
  16.  
  17. /*
  18.  *-------------------------------------------------------------------------------------
  19.  *                            Dialog construction/destruction
  20.  *
  21.  * Dialog constructors create a (modeless) dialog (from a DLOG resource) and set
  22.  * up the initial state of the controls and user items.
  23.  * Note that the dialog is ought to be specified as initially hidden
  24.  * in the resource template. So that constructors of the derived class could get a chance
  25.  * to set up user item drawing routines, or initialize/move around custom controls.
  26.  * Call show() when ready (from an upper-level constructor)
  27.  */
  28.  
  29.                             // Create a color dialog from a resource template
  30. ModelessDialog::ModelessDialog(const short resource_id)
  31. {
  32.   assert( this_window = (WindowPtr)GetNewDialog(resource_id, nil, (WindowPtr)-1) );
  33.   SetWRefCon(this_window,(long)this);        // Store the ptr to our class in RefCon
  34. }
  35.  
  36.  
  37.                             // A kludge for a virtual destructor
  38. void ModelessDialog::destroy_it(void)
  39. {
  40.   assert( this_window != 0 );
  41.   DisposeDialog((DialogPtr)this_window);
  42.   this_window = 0;
  43. }
  44.  
  45. /*
  46.  *-------------------------------------------------------------------------------------
  47.  *                                    Event Handling
  48.  */
  49.  
  50. Boolean ModelessDialog::handle_event(const EventRecord& the_event)
  51. {
  52.   if( !IsDialogEvent(&the_event) )
  53.     return ScreenWindow::handle_event(the_event);    // Not a dialog event, let ScreenWindow
  54.                                                     // handle that. Note that, say, dragging
  55.                                                     // of a window is reported as NOT a dialog
  56.                                                     // event
  57.     
  58.   if( the_event.what == activateEvt && (WindowPtr)(the_event.message) == our_window() )
  59.     handle_activate(the_event.modifiers & activeFlag);
  60.  
  61.   DialogPtr dialog_hit;
  62.   short item_hit;
  63.   if( !DialogSelect(&the_event, &dialog_hit, &item_hit) )
  64.     return TRUE;                            // assume that DialogSelect has handled the Event
  65.   if( dialog_hit != this_window )
  66.     return TRUE;                            // Not our dialog event, wait for the other event
  67.  
  68.                                             // The following stmt means that if a generic_item_hit
  69.                                             // handle didn't handle the click completely, a more
  70.                                             // specific handle would be given a chance
  71.   return generic_item_hit(item_hit) || handle_item_hit(item_hit);
  72. }
  73.  
  74. Boolean ModelessDialog::handle_null_event(const long event_time)
  75. {
  76.   return TRUE;
  77. }
  78.  
  79.                                             // Handle suspend/resume events
  80. Boolean ModelessDialog::handle_activate(Boolean onoff)
  81. {
  82.   return TRUE;
  83. }
  84.  
  85.                                 // Figure out if our item was hit and give an object
  86.                                 // click handler a chance to handle the click.
  87.                                 // Return TRUE if the click is _completely_
  88.                                   // handled, return FALSE if additional attention
  89.                                   // needed (say, a control's value was changed)
  90. Boolean ModelessDialog::generic_item_hit(const int item_no)
  91. {
  92.   Handle        item_handle;
  93.   short            item_type;
  94.   Rect            item_rect;
  95.   GetDItem((DialogPtr)this_window, item_no, &item_type, &item_handle, &item_rect);
  96.   if( (item_type & ctrlItem) != ctrlItem || item_handle == nil )
  97.     return FALSE;
  98.  
  99.   BasicControl * our_control_ptr = (BasicControl *)GetControlReference((ControlHandle)item_handle);
  100.   
  101.   return our_control_ptr != nil && our_control_ptr->is_our_control() && 
  102.            our_control_ptr->handle_click();
  103. }
  104.  
  105. /*
  106.  *-------------------------------------------------------------------------------------
  107.  *                            Utilities to handle dialog items
  108.  */
  109.  
  110.  
  111.  
  112.                                 // Take a "ref" of a dialog item
  113. ModelessDialog::Item::Item(const ModelessDialog& the_dialog, const int item_no)
  114. {
  115.   GetDItem((DialogPtr)the_dialog.our_window(), item_no, &item_type, &item_handle, &item_rect);
  116.   assert( item_handle != nil );
  117. }
  118.  
  119.  
  120.                                 // Take a "ref" of a dialog _control_ item, and
  121.                                 // make sure that it's a control indeed
  122. ModelessDialog::ControlItem::ControlItem(const ModelessDialog& the_dialog, const int item_no)
  123.     : ModelessDialog::Item(the_dialog,item_no)
  124. {
  125.   assert( (item_type & ctrlItem) == ctrlItem );
  126. }
  127.  
  128.                                 // Take a "ref" of a dialog _text_ item, and
  129.                                 // make sure that it's text indeed
  130. ModelessDialog::TextItem::TextItem(const ModelessDialog& the_dialog, const int item_no)
  131.     : ModelessDialog::Item(the_dialog,item_no)
  132. {
  133.   assert( (item_type & statText) == statText );
  134. }
  135.  
  136.                                             // Draw a new text in an item
  137. void ModelessDialog::TextItem::draw(const Str255 str)
  138. {
  139.   SetDialogItemText(item_handle,str);
  140. }
  141.  
  142.  
  143.                                     // Set a new value of a control, clipped to
  144.                                       // its [min,max] range
  145. void BasicControl::set_value(const int new_value)
  146. {
  147.   ControlHandle ctl_handle = our_control();
  148.   const int max_val = GetControlMaximum(ctl_handle);
  149.   SetControlValue(ctl_handle, new_value > max_val ? max_val :
  150.                                 new_value < GetControlMinimum(ctl_handle) ?
  151.                                 GetControlMinimum(ctl_handle) : new_value );
  152. }
  153.  
  154. /*
  155.  *-------------------------------------------------------------------------------------
  156.  *                                    Patching the CDEF
  157.  *
  158.  * In handling a scrollbar, we need to tell the Control Manager how to change the control
  159.  * value when PgUp/PgDn/ArrowUp/ArrowDn areas of a scrollbar got clicked. Normally it's
  160.  * accomplished by a track Action procedure, which is called by TrackControl(). In our
  161.  * case, TrackControl() is called by DialogSelect. We could supply our Action procedure
  162.  * by putting a pointer to it in a corresponding field of the ControlRecord. But it
  163.  * doesn't work! The reason is that if an action procedure is specified this way, it's
  164.  * called under two different circumstances: mouse click in PgUp/PgDn/ArrowUp/ArrowDn areas,
  165.  * and a mouse click in the thumb (indicator). In the former case, the action procedure is
  166.  * passed two parameters, in the latter case, no parameters. Since the action procedure
  167.  * is a 'pascal' one, passing it zero parameters when it expects two, or vice versa
  168.  * messes up the stack on return (with all the disastorous consequences). As IM itself
  169.  * admits, the only way to specify a "generic action procedure" is to modify a CDEF,
  170.  * which we're going to do.
  171.  * Note, the usual way of handling this situation is calling FindControl(), TrackControl()
  172.  * etc before DialogSelect(). In a sense, the usual hack is to duplicate DialogSelect.
  173.  * IMHO, patching CDEF seems more logical.
  174.  *
  175.  * Note, in patching a CDEF, we assume that BasicControl is a scrollbar-type thing,
  176.  * that's why real_defproc_handle, patch_defproc_handle etc are declared static.
  177.  * Although it's damn easy to lift this limitation.
  178.  *
  179.  * Note, that in installing our patch, we can't just barge into CDEF handle and change
  180.  * the pointer. ResourceManager, who created that CDEF handle, wouldn't like that.
  181.  * So we need to allocate our own handle, stash a pointer (universal pointer) of our patch
  182.  * into it, and put the new handle into CDEF. DisposeControl would dispose of that handle
  183.  * later.
  184.  */
  185.                                     // Late constructor for a basic control
  186.                                     // A regular constructor won't cut it, since it would
  187.                                     // be called before the container (the dialog)
  188.                                     // gets constructed
  189. void BasicControl::bind(const ModelessDialog::ControlItem& item)
  190. {
  191.   this_control = (ControlHandle)item;
  192.   SetControlReference(this_control,(long)this);
  193.   // SetControlAction(this_control,track_action_relay_upp);
  194.   // SetControlAction(this_control,(ControlActionUPP)(-1));
  195.   if( real_defproc_handle == nil )        // save the old CDEF handle and initialize a new one
  196.   {
  197.     real_defproc_handle = (ControlDefProcPtr *)(*this_control)->contrlDefProc;
  198.     patch_defproc_handle = (ControlDefProcPtr *)NewHandle(sizeof(ControlDefUPP));
  199.     HLock((Handle)patch_defproc_handle);
  200.     *patch_defproc_handle = (ControlDefProcPtr)defproc_patch_upp;
  201.   }                                        // Install our CDEF patch
  202.   assert( real_defproc_handle == (ControlDefProcPtr *)(*this_control)->contrlDefProc );
  203.   (*this_control)->contrlDefProc = (Handle)patch_defproc_handle;
  204.   
  205. }
  206.  
  207.  
  208.                         // This is an on-the-fly patch to a scroll bar control definition
  209.                         // function.
  210.                         // The patch itself calls the standard CDEF, and, if it finds out
  211.                         // that thumb had been moved and/or gray areas/arrows were clicked,
  212.                         // calls the object's track action procedure
  213.                         // We assume that the pointer to the BasicControl Object is stored
  214.                         // in the refcon
  215. pascal SInt32 BasicControl::defproc_patch
  216.     (SInt16 var_code, ControlRef the_control, ControlDefProcMessage message, SInt32 param)
  217. {
  218.   
  219.   SInt32 def_result = CallControlDefProc(*real_defproc_handle,var_code,the_control,message,param);
  220.   if( (message == drawCntl && (short)param == 129)     // indicator to be redrawn
  221.      || (message == testCntl && def_result != 0) )    // a scrollbar area might've been clicked
  222.   if( !StillDown() )                    // mouse was released, that is, the click is completed
  223.   {
  224.     BasicControl * this_ptr = (BasicControl *)((*the_control)->contrlRfCon);
  225.     assert( this_ptr != nil && this_ptr->is_our_control() );
  226.     this_ptr->track_action(def_result);
  227.   }
  228.   return def_result;
  229. }
  230.  
  231. ControlDefProcPtr * BasicControl::real_defproc_handle = 0; 
  232. ControlDefProcPtr * BasicControl::patch_defproc_handle = 0; 
  233. ControlDefUPP BasicControl::defproc_patch_upp 
  234.     = NewControlDefProc(defproc_patch);    // Universal pointer to BasicControl::defproc_patch
  235.  
  236. #if 0
  237.                         // This is a relay between the Control Manager and real action
  238.                         // tracking procedure
  239.                         // We assume that the pointer to the BasicControl Object is stored
  240.                         // in the refcon
  241. pascal void BasicControl::track_action_relay(ControlHandle the_control, unsigned short part_no)
  242. {
  243.   DebugStr("\pIn track action relay");
  244.   if( part_no >= inThumb )                    // Note when the mouse is clicked in the indicator,
  245.     return;                                    // parameters are probably invalid
  246.   BasicControl * this_ptr = (BasicControl *)GetControlReference(the_control);
  247.   assert( this_ptr != nil && this_ptr->is_our_control() );
  248.   this_ptr->track_action(part_no);
  249. }
  250.  
  251. ControlActionUPP BasicControl::track_action_relay_upp 
  252.     = NewControlActionProc(track_action_relay);    // Universal pointer to the user_item_univ_handler
  253.  
  254. #endif
  255.  
  256. /*
  257.  *-------------------------------------------------------------------------------------
  258.  *                                    Handling User items
  259.  */
  260.  
  261.                         // This is a relay between the Dialog Manager and a real user item
  262.                         // drawing procedure
  263.                         // We assume that the pointer to the Dialog Object is stored
  264.                         // in the refcon
  265. pascal void UserItem::user_item_relay_handler(WindowPtr the_dialog, short the_item)
  266. {
  267.   ModelessDialog * this_ptr = (ModelessDialog *)GetWRefCon(the_dialog);
  268.   assert( this_ptr != nil );
  269.   this_ptr->draw_user_item(the_item);
  270. }
  271.  
  272. UserItemUPP UserItem::user_item_relay_handler_upp 
  273.     = NewUserItemProc(user_item_relay_handler);    // Universal pointer to the user_item_univ_handler
  274.  
  275.  
  276.                         // Binding and activating a user item
  277.                         // It sets up a relay function as an item handle
  278. void UserItem::bind(const ModelessDialog& the_dialog, const int item_no)
  279. {
  280.   Handle        item_handle;
  281.   short            item_type;
  282.   GetDItem((DialogPtr)the_dialog.our_window(), item_no, &item_type, &item_handle, &rect);
  283.   assert( item_handle == nil );                    // make sure it hasn't been set up yet
  284.   SetDItem((DialogPtr)the_dialog.our_window(), item_no, item_type, (Handle)user_item_relay_handler_upp, &rect);
  285. }
  286.  
  287. #if 0
  288.                                 // Standard way to draw a border around the default button.
  289. void ModelessDialog::draw_default_item_border(const int item_no)
  290. {
  291.   const Item item(*this,item_no);
  292.     
  293.   PenSize(3,3);
  294.   InsetRect((Rect *)(const Rect *)item, -4, -4);
  295.   FrameRoundRect((const Rect *)item, 16,16);
  296. }
  297. #endif
  298.