home *** CD-ROM | disk | FTP | other *** search
- /*
- ***********************************************************************
- *
- *
- * Generic Dialog Class
- * Implementing a few very basic functions
- * messing with items, etc
- *
- *
- ***********************************************************************
- */
-
- #include "Dialog.h"
- #include "mymenv.h"
-
-
- /*
- *-------------------------------------------------------------------------------------
- * Dialog construction/destruction
- *
- * Dialog constructors create a (modeless) dialog (from a DLOG resource) and set
- * up the initial state of the controls and user items.
- * Note that the dialog is ought to be specified as initially hidden
- * in the resource template. So that constructors of the derived class could get a chance
- * to set up user item drawing routines, or initialize/move around custom controls.
- * Call show() when ready (from an upper-level constructor)
- */
-
- // Create a color dialog from a resource template
- ModelessDialog::ModelessDialog(const short resource_id)
- {
- assert( this_window = (WindowPtr)GetNewDialog(resource_id, nil, (WindowPtr)-1) );
- SetWRefCon(this_window,(long)this); // Store the ptr to our class in RefCon
- }
-
-
- // A kludge for a virtual destructor
- void ModelessDialog::destroy_it(void)
- {
- assert( this_window != 0 );
- DisposeDialog((DialogPtr)this_window);
- this_window = 0;
- }
-
- /*
- *-------------------------------------------------------------------------------------
- * Event Handling
- */
-
- Boolean ModelessDialog::handle_event(const EventRecord& the_event)
- {
- if( !IsDialogEvent(&the_event) )
- return ScreenWindow::handle_event(the_event); // Not a dialog event, let ScreenWindow
- // handle that. Note that, say, dragging
- // of a window is reported as NOT a dialog
- // event
-
- if( the_event.what == activateEvt && (WindowPtr)(the_event.message) == our_window() )
- handle_activate(the_event.modifiers & activeFlag);
-
- DialogPtr dialog_hit;
- short item_hit;
- if( !DialogSelect(&the_event, &dialog_hit, &item_hit) )
- return TRUE; // assume that DialogSelect has handled the Event
- if( dialog_hit != this_window )
- return TRUE; // Not our dialog event, wait for the other event
-
- // The following stmt means that if a generic_item_hit
- // handle didn't handle the click completely, a more
- // specific handle would be given a chance
- return generic_item_hit(item_hit) || handle_item_hit(item_hit);
- }
-
- Boolean ModelessDialog::handle_null_event(const long event_time)
- {
- return TRUE;
- }
-
- // Handle suspend/resume events
- Boolean ModelessDialog::handle_activate(Boolean onoff)
- {
- return TRUE;
- }
-
- // Figure out if our item was hit and give an object
- // click handler a chance to handle the click.
- // Return TRUE if the click is _completely_
- // handled, return FALSE if additional attention
- // needed (say, a control's value was changed)
- Boolean ModelessDialog::generic_item_hit(const int item_no)
- {
- Handle item_handle;
- short item_type;
- Rect item_rect;
- GetDItem((DialogPtr)this_window, item_no, &item_type, &item_handle, &item_rect);
- if( (item_type & ctrlItem) != ctrlItem || item_handle == nil )
- return FALSE;
-
- BasicControl * our_control_ptr = (BasicControl *)GetControlReference((ControlHandle)item_handle);
-
- return our_control_ptr != nil && our_control_ptr->is_our_control() &&
- our_control_ptr->handle_click();
- }
-
- /*
- *-------------------------------------------------------------------------------------
- * Utilities to handle dialog items
- */
-
-
-
- // Take a "ref" of a dialog item
- ModelessDialog::Item::Item(const ModelessDialog& the_dialog, const int item_no)
- {
- GetDItem((DialogPtr)the_dialog.our_window(), item_no, &item_type, &item_handle, &item_rect);
- assert( item_handle != nil );
- }
-
-
- // Take a "ref" of a dialog _control_ item, and
- // make sure that it's a control indeed
- ModelessDialog::ControlItem::ControlItem(const ModelessDialog& the_dialog, const int item_no)
- : ModelessDialog::Item(the_dialog,item_no)
- {
- assert( (item_type & ctrlItem) == ctrlItem );
- }
-
- // Take a "ref" of a dialog _text_ item, and
- // make sure that it's text indeed
- ModelessDialog::TextItem::TextItem(const ModelessDialog& the_dialog, const int item_no)
- : ModelessDialog::Item(the_dialog,item_no)
- {
- assert( (item_type & statText) == statText );
- }
-
- // Draw a new text in an item
- void ModelessDialog::TextItem::draw(const Str255 str)
- {
- SetDialogItemText(item_handle,str);
- }
-
-
- // Set a new value of a control, clipped to
- // its [min,max] range
- void BasicControl::set_value(const int new_value)
- {
- ControlHandle ctl_handle = our_control();
- const int max_val = GetControlMaximum(ctl_handle);
- SetControlValue(ctl_handle, new_value > max_val ? max_val :
- new_value < GetControlMinimum(ctl_handle) ?
- GetControlMinimum(ctl_handle) : new_value );
- }
-
- /*
- *-------------------------------------------------------------------------------------
- * Patching the CDEF
- *
- * In handling a scrollbar, we need to tell the Control Manager how to change the control
- * value when PgUp/PgDn/ArrowUp/ArrowDn areas of a scrollbar got clicked. Normally it's
- * accomplished by a track Action procedure, which is called by TrackControl(). In our
- * case, TrackControl() is called by DialogSelect. We could supply our Action procedure
- * by putting a pointer to it in a corresponding field of the ControlRecord. But it
- * doesn't work! The reason is that if an action procedure is specified this way, it's
- * called under two different circumstances: mouse click in PgUp/PgDn/ArrowUp/ArrowDn areas,
- * and a mouse click in the thumb (indicator). In the former case, the action procedure is
- * passed two parameters, in the latter case, no parameters. Since the action procedure
- * is a 'pascal' one, passing it zero parameters when it expects two, or vice versa
- * messes up the stack on return (with all the disastorous consequences). As IM itself
- * admits, the only way to specify a "generic action procedure" is to modify a CDEF,
- * which we're going to do.
- * Note, the usual way of handling this situation is calling FindControl(), TrackControl()
- * etc before DialogSelect(). In a sense, the usual hack is to duplicate DialogSelect.
- * IMHO, patching CDEF seems more logical.
- *
- * Note, in patching a CDEF, we assume that BasicControl is a scrollbar-type thing,
- * that's why real_defproc_handle, patch_defproc_handle etc are declared static.
- * Although it's damn easy to lift this limitation.
- *
- * Note, that in installing our patch, we can't just barge into CDEF handle and change
- * the pointer. ResourceManager, who created that CDEF handle, wouldn't like that.
- * So we need to allocate our own handle, stash a pointer (universal pointer) of our patch
- * into it, and put the new handle into CDEF. DisposeControl would dispose of that handle
- * later.
- */
- // Late constructor for a basic control
- // A regular constructor won't cut it, since it would
- // be called before the container (the dialog)
- // gets constructed
- void BasicControl::bind(const ModelessDialog::ControlItem& item)
- {
- this_control = (ControlHandle)item;
- SetControlReference(this_control,(long)this);
- // SetControlAction(this_control,track_action_relay_upp);
- // SetControlAction(this_control,(ControlActionUPP)(-1));
- if( real_defproc_handle == nil ) // save the old CDEF handle and initialize a new one
- {
- real_defproc_handle = (ControlDefProcPtr *)(*this_control)->contrlDefProc;
- patch_defproc_handle = (ControlDefProcPtr *)NewHandle(sizeof(ControlDefUPP));
- HLock((Handle)patch_defproc_handle);
- *patch_defproc_handle = (ControlDefProcPtr)defproc_patch_upp;
- } // Install our CDEF patch
- assert( real_defproc_handle == (ControlDefProcPtr *)(*this_control)->contrlDefProc );
- (*this_control)->contrlDefProc = (Handle)patch_defproc_handle;
-
- }
-
-
- // This is an on-the-fly patch to a scroll bar control definition
- // function.
- // The patch itself calls the standard CDEF, and, if it finds out
- // that thumb had been moved and/or gray areas/arrows were clicked,
- // calls the object's track action procedure
- // We assume that the pointer to the BasicControl Object is stored
- // in the refcon
- pascal SInt32 BasicControl::defproc_patch
- (SInt16 var_code, ControlRef the_control, ControlDefProcMessage message, SInt32 param)
- {
-
- SInt32 def_result = CallControlDefProc(*real_defproc_handle,var_code,the_control,message,param);
- if( (message == drawCntl && (short)param == 129) // indicator to be redrawn
- || (message == testCntl && def_result != 0) ) // a scrollbar area might've been clicked
- if( !StillDown() ) // mouse was released, that is, the click is completed
- {
- BasicControl * this_ptr = (BasicControl *)((*the_control)->contrlRfCon);
- assert( this_ptr != nil && this_ptr->is_our_control() );
- this_ptr->track_action(def_result);
- }
- return def_result;
- }
-
- ControlDefProcPtr * BasicControl::real_defproc_handle = 0;
- ControlDefProcPtr * BasicControl::patch_defproc_handle = 0;
- ControlDefUPP BasicControl::defproc_patch_upp
- = NewControlDefProc(defproc_patch); // Universal pointer to BasicControl::defproc_patch
-
- #if 0
- // This is a relay between the Control Manager and real action
- // tracking procedure
- // We assume that the pointer to the BasicControl Object is stored
- // in the refcon
- pascal void BasicControl::track_action_relay(ControlHandle the_control, unsigned short part_no)
- {
- DebugStr("\pIn track action relay");
- if( part_no >= inThumb ) // Note when the mouse is clicked in the indicator,
- return; // parameters are probably invalid
- BasicControl * this_ptr = (BasicControl *)GetControlReference(the_control);
- assert( this_ptr != nil && this_ptr->is_our_control() );
- this_ptr->track_action(part_no);
- }
-
- ControlActionUPP BasicControl::track_action_relay_upp
- = NewControlActionProc(track_action_relay); // Universal pointer to the user_item_univ_handler
-
- #endif
-
- /*
- *-------------------------------------------------------------------------------------
- * Handling User items
- */
-
- // This is a relay between the Dialog Manager and a real user item
- // drawing procedure
- // We assume that the pointer to the Dialog Object is stored
- // in the refcon
- pascal void UserItem::user_item_relay_handler(WindowPtr the_dialog, short the_item)
- {
- ModelessDialog * this_ptr = (ModelessDialog *)GetWRefCon(the_dialog);
- assert( this_ptr != nil );
- this_ptr->draw_user_item(the_item);
- }
-
- UserItemUPP UserItem::user_item_relay_handler_upp
- = NewUserItemProc(user_item_relay_handler); // Universal pointer to the user_item_univ_handler
-
-
- // Binding and activating a user item
- // It sets up a relay function as an item handle
- void UserItem::bind(const ModelessDialog& the_dialog, const int item_no)
- {
- Handle item_handle;
- short item_type;
- GetDItem((DialogPtr)the_dialog.our_window(), item_no, &item_type, &item_handle, &rect);
- assert( item_handle == nil ); // make sure it hasn't been set up yet
- SetDItem((DialogPtr)the_dialog.our_window(), item_no, item_type, (Handle)user_item_relay_handler_upp, &rect);
- }
-
- #if 0
- // Standard way to draw a border around the default button.
- void ModelessDialog::draw_default_item_border(const int item_no)
- {
- const Item item(*this,item_no);
-
- PenSize(3,3);
- InsetRect((Rect *)(const Rect *)item, -4, -4);
- FrameRoundRect((const Rect *)item, 16,16);
- }
- #endif
-