Here's the problem we are going to solve. Buttons are some of the most basic things in Flash-world along with graphics and movieclips. Buttons are very easy to create with their no-frills frames based mechanism but they are also limited in that they have only four states including the hit state which is never actually seen. You would think that something as basic as a 'disabled' state would be covered by buttons but the enabled property was in fact introduced in Flash 6, while the button has been available since Flash 2 (!). A couple of other states like 'locked', 'toggled', 'dragover', etc. might also seem very useful. Another thing is that buttons don't behave like MovieClips in some odd places; for example, there is no Button.getBounds function. TextField names inside buttons are not exported, which means changing labels or translating buttons at runtime is impossible.

Of course MovieClips have none of these limitations but you'll have to write everything from scratch. Ideally we'd like to have some middle way that offers all the power of MovieClip, a lot of button states yet the ease of use buttons; ideally we'd need backwards compatibility with buttons so that the first, second and third frame of our new scheme correspond to the up, over and down states. You might think if you need those advanced functions the Button component would be the way to go; but it's heavy (a good 70k) and it's a pain in the neck to skin, so that's a no go.

Here's my solution: we make a PseudoButton class that extends MovieClip. We associate this class through the library with any movieclip we want to turn into a PseudoButton. That class has some generic functionality to make it work and feel like a button (ie. Frames 1, 2 and 3 work like they work with buttons). To do this we'll need to tap into the on* callbacks of the movieclip; we'll forward these using EventDispatcher, with the added bonus that several functions can tap into the new events at the same time. Sounds great? Here's the script then:

import mx.utils.Delegate;
import mx.events.EventDispatcher;

class PseudoButton extends MovieClip
{
        private var __label:String = "";
        public var labeltf:String = "lbl";
       
        public var toggable:Boolean = false;
        public var lockable:Boolean = false;
        public var toggled:Boolean = false;
        public var locked:Boolean = false;
       
        public var addEventListener:Function;
        public var removeEventListener:Function;
        public var dispatchEvent:Function;
       
        public var over:Boolean = false;
        public var down:Boolean = false;
        public var currentFrame:Number = 1;
       
        function PseudoButton()
        {
                EventDispatcher.initialize(this);
                init();
        }
       
        function init()
        {
                this.stop();
        }
       
        function onRollOver()
        {
                over = true;
                if(!locked)
                {
                        if(!toggled)
                        {
                                gotoState(2);
                        }
                        else
                        {
                                gotoState(7);
                        }
                        dispatchEvent({target:this, type:'rollOver'});
                }
        }
       
        function onRollOut()
        {
                over = false;
                if(!locked)
                {
                        if(!toggled)
                        {
                                gotoState(1);
                        }
                        else
                        {
                                gotoState(6);
                        }
                        dispatchEvent({target:this, type:'rollOut'});
                }
        }
       
        function onPress()
        {
                down = true;
                if(!locked)
                {
                        if(!toggled)
                        {
                                gotoState(3);
                        }
                        else
                        {
                                gotoState(8);
                        }
                        dispatchEvent({target:this, type:'press'});
                }
        }
       
        function onRelease()
        {
                down = false;
                if(!lockable)
                {
                        if(!toggable)
                        {
                                gotoState(2);
                        }
                        else
                        {
                                if(!toggled)
                                {
                                        toggled = true;
                                        gotoState(7);
                                }
                                else
                                {
                                        toggled = false;
                                        gotoState(2);
                                }
                        }
                        dispatchEvent({target:this, type:'release'});
                }
                else
                {
                        if(!locked)
                        {
                                locked = true;
                                gotoState(6);
                                dispatchEvent({target:this, type:'release'});
                                dispatchEvent({target:this, type:'lock'});
                        }
                }
        }
       
        function onReleaseOutside()
        {
                down = false;
                if(!locked)
                {
                        gotoState(1);
                        dispatchEvent({target:this, type:'releaseoutside'});
                }
        }
       
        function unlock()
        {
                locked = false;
                gotoState(1);
        }
       
        function lock()
        {
                locked = true;
                gotoState(6);
        }
       
        function setEnabled(val)
        {
                enabled = val;
                if(!enabled)
                {
                        gotoState(5);
                }
                else
                {
                        if(down)
                        {
                                gotoState(3);
                        }
                        else if(over)
                        {
                                gotoState(2);
                        }
                        else
                        {
                                gotoState(1);
                        }
                }
        }
       
        function gotoState(num)
        {
                gotoAndStop(num);
                currentFrame = num;
                updateText();
        }
       
        function updateText()
        {
                if(labeltf != null)
                {
                        this[labeltf].text = label;
                }
        }
       
        public function set label(val:String)
        {
                this.__label = val;
                updateText();
        }
       
        public function get label():String
        {
                return __label;
        }
}

Now not only do we have a movieclip that follows the conventions of the three first button frames, it also includes a disabled state on frame 5 (triggered using setEnabled(false)) and a toggled state on frame 6 (activated through toggable), along with over and down states for the toggled state. We can tap into the release, releaseOutside, rollOver, rollOut and press events corresponding to the usual button events. In addition, we get access to new events like toggle. We've added a label setter that allows the button labels to be carried across frames if labeltf (label text field) is set properly, thus making translation dead easy. You can see this is a powerful solution that you can easily extend by modifying the class.