root/src/kits/interface/PictureButton.cpp
/*
 * Copyright 2001-2010, Haiku, Inc.
 * Distributed under the terms of the MIT license.
 *
 * Authors:
 *              Graham MacDonald (macdonag@btopenworld.com)
 */


#include <PictureButton.h>

#include <new>

#include <binary_compatibility/Interface.h>


BPictureButton::BPictureButton(BRect frame, const char* name,
        BPicture* off, BPicture* on, BMessage* message,
        uint32 behavior, uint32 resizingMode, uint32 flags)
        :
        BControl(frame, name, "", message, resizingMode, flags),
        fEnabledOff(new(std::nothrow) BPicture(*off)),
        fEnabledOn(new(std::nothrow) BPicture(*on)),
        fDisabledOff(NULL),
        fDisabledOn(NULL),
        fBehavior(behavior)
{
}


BPictureButton::BPictureButton(BMessage* data)
        :
        BControl(data),
        fEnabledOff(NULL),
        fEnabledOn(NULL),
        fDisabledOff(NULL),
        fDisabledOn(NULL)
{
        BMessage pictureArchive;

        // Default to 1 state button if not here - is this valid?
        if (data->FindInt32("_behave", (int32*)&fBehavior) != B_OK)
                fBehavior = B_ONE_STATE_BUTTON;

        // Now expand the pictures:
        if (data->FindMessage("_e_on", &pictureArchive) == B_OK)
                fEnabledOn = new(std::nothrow) BPicture(&pictureArchive);

        if (data->FindMessage("_e_off", &pictureArchive) == B_OK)
                fEnabledOff = new(std::nothrow) BPicture(&pictureArchive);

        if (data->FindMessage("_d_on", &pictureArchive) == B_OK)
                fDisabledOn = new(std::nothrow) BPicture(&pictureArchive);

        if (data->FindMessage("_d_off", &pictureArchive) == B_OK)
                fDisabledOff = new(std::nothrow) BPicture(&pictureArchive);
}


BPictureButton::~BPictureButton()
{
        delete fEnabledOn;
        delete fEnabledOff;
        delete fDisabledOn;
        delete fDisabledOff;
}


BArchivable*
BPictureButton::Instantiate(BMessage* data)
{
        if (validate_instantiation(data, "BPictureButton"))
                return new (std::nothrow) BPictureButton(data);

        return NULL;
}


status_t
BPictureButton::Archive(BMessage* data, bool deep) const
{
        status_t err = BControl::Archive(data, deep);
        if (err != B_OK)
                return err;

        // Fill out message, depending on whether a deep copy is required or not.
        if (deep) {
                BMessage pictureArchive;
                if (fEnabledOn->Archive(&pictureArchive, deep) == B_OK) {
                        err = data->AddMessage("_e_on", &pictureArchive);
                        if (err != B_OK)
                                return err;
                }

                pictureArchive.MakeEmpty();
                if (fEnabledOff->Archive(&pictureArchive, deep) == B_OK) {
                        err = data->AddMessage("_e_off", &pictureArchive);
                        if (err != B_OK)
                                return err;
                }

                pictureArchive.MakeEmpty();
                if (fDisabledOn && fDisabledOn->Archive(&pictureArchive, deep) == B_OK) {
                        err = data->AddMessage("_d_on", &pictureArchive);
                        if (err != B_OK)
                                return err;
                }

                pictureArchive.MakeEmpty();
                if (fDisabledOff && fDisabledOff->Archive(&pictureArchive, deep) == B_OK) {
                        err = data->AddMessage("_d_off", &pictureArchive);
                        if (err != B_OK)
                                return err;
                }
        }

        return data->AddInt32("_behave", fBehavior);
}


void
BPictureButton::AttachedToWindow()
{
        BControl::AttachedToWindow();
}


void
BPictureButton::DetachedFromWindow()
{
        BControl::DetachedFromWindow();
}


void
BPictureButton::AllAttached()
{
        BControl::AllAttached();
}


void
BPictureButton::AllDetached()
{
        BControl::AllDetached();
}


void
BPictureButton::ResizeToPreferred()
{
        BControl::ResizeToPreferred();
}


void
BPictureButton::GetPreferredSize(float* _width, float* _height)
{
        BControl::GetPreferredSize(_width, _height);
}


void
BPictureButton::FrameMoved(BPoint newPosition)
{
        BControl::FrameMoved(newPosition);
}


void
BPictureButton::FrameResized(float newWidth, float newHeight)
{
        BControl::FrameResized(newWidth, newHeight);
}


void
BPictureButton::WindowActivated(bool active)
{
        BControl::WindowActivated(active);
}


void
BPictureButton::MakeFocus(bool focus)
{
        BControl::MakeFocus(focus);
}


void
BPictureButton::Draw(BRect updateRect)
{
        if (IsEnabled()) {
                if (Value() == B_CONTROL_ON)
                        DrawPicture(fEnabledOn);
                else
                        DrawPicture(fEnabledOff);
        } else {

                if (fDisabledOff == NULL
                        || (fDisabledOn == NULL && fBehavior == B_TWO_STATE_BUTTON))
                        debugger("Need to set the 'disabled' pictures for this BPictureButton ");

                if (Value() == B_CONTROL_ON)
                        DrawPicture(fDisabledOn);
                else
                        DrawPicture(fDisabledOff);
        }

        if (IsFocus()) {
                SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
                StrokeRect(Bounds(), B_SOLID_HIGH);
        }
}


void
BPictureButton::MessageReceived(BMessage* message)
{
        BControl::MessageReceived(message);
}


void
BPictureButton::KeyDown(const char* bytes, int32 numBytes)
{
        if (numBytes == 1) {
                switch (bytes[0]) {
                        case B_ENTER:
                        case B_SPACE:
                                if (fBehavior == B_ONE_STATE_BUTTON) {
                                        SetValue(B_CONTROL_ON);
                                        snooze(50000);
                                        SetValue(B_CONTROL_OFF);
                                } else {
                                        if (Value() == B_CONTROL_ON)
                                                SetValue(B_CONTROL_OFF);
                                        else
                                                SetValue(B_CONTROL_ON);
                                }
                                Invoke();
                                return;
                }
        }

        BControl::KeyDown(bytes, numBytes);
}


void
BPictureButton::MouseDown(BPoint where)
{
        if (!IsEnabled()) {
                BControl::MouseDown(where);
                return;
        }

        SetMouseEventMask(B_POINTER_EVENTS,
                B_NO_POINTER_HISTORY | B_SUSPEND_VIEW_FOCUS);

        if (fBehavior == B_ONE_STATE_BUTTON) {
                SetValue(B_CONTROL_ON);
        } else {
                if (Value() == B_CONTROL_ON)
                        SetValue(B_CONTROL_OFF);
                else
                        SetValue(B_CONTROL_ON);
        }
        SetTracking(true);
}


void
BPictureButton::MouseUp(BPoint where)
{
        if (IsEnabled() && IsTracking()) {
                if (Bounds().Contains(where)) {
                        if (fBehavior == B_ONE_STATE_BUTTON) {
                                if (Value() == B_CONTROL_ON) {
                                        snooze(75000);
                                        SetValue(B_CONTROL_OFF);
                                }
                        }
                        Invoke();
                }

                SetTracking(false);
        }
}


void
BPictureButton::MouseMoved(BPoint where, uint32 code,
        const BMessage* dragMessage)
{
        if (IsEnabled() && IsTracking()) {
                if (code == B_EXITED_VIEW)
                        SetValue(B_CONTROL_OFF);
                else if (code == B_ENTERED_VIEW)
                        SetValue(B_CONTROL_ON);
        } else
                BControl::MouseMoved(where, code, dragMessage);
}


// #pragma mark -


void
BPictureButton::SetEnabledOn(BPicture* picture)
{
        delete fEnabledOn;
        fEnabledOn = new (std::nothrow) BPicture(*picture);
}


void
BPictureButton::SetEnabledOff(BPicture* picture)
{
        delete fEnabledOff;
        fEnabledOff = new (std::nothrow) BPicture(*picture);
}


void
BPictureButton::SetDisabledOn(BPicture* picture)
{
        delete fDisabledOn;
        fDisabledOn = new (std::nothrow) BPicture(*picture);
}


void
BPictureButton::SetDisabledOff(BPicture* picture)
{
        delete fDisabledOff;
        fDisabledOff = new (std::nothrow) BPicture(*picture);
}


BPicture*
BPictureButton::EnabledOn() const
{
        return fEnabledOn;
}


BPicture*
BPictureButton::EnabledOff() const
{
        return fEnabledOff;
}


BPicture*
BPictureButton::DisabledOn() const
{
        return fDisabledOn;
}


BPicture*
BPictureButton::DisabledOff() const
{
        return fDisabledOff;
}


void
BPictureButton::SetBehavior(uint32 behavior)
{
        fBehavior = behavior;
}


uint32
BPictureButton::Behavior() const
{
        return fBehavior;
}


void
BPictureButton::SetValue(int32 value)
{
        BControl::SetValue(value);
}


status_t
BPictureButton::Invoke(BMessage* message)
{
        return BControl::Invoke(message);
}


BHandler*
BPictureButton::ResolveSpecifier(BMessage* message, int32 index,
        BMessage* specifier, int32 what, const char* property)
{
        return BControl::ResolveSpecifier(message, index, specifier,
                what, property);
}


status_t
BPictureButton::GetSupportedSuites(BMessage* data)
{
        return BControl::GetSupportedSuites(data);
}


status_t
BPictureButton::Perform(perform_code code, void* _data)
{
        switch (code) {
                case PERFORM_CODE_MIN_SIZE:
                        ((perform_data_min_size*)_data)->return_value
                                = BPictureButton::MinSize();
                        return B_OK;
                case PERFORM_CODE_MAX_SIZE:
                        ((perform_data_max_size*)_data)->return_value
                                = BPictureButton::MaxSize();
                        return B_OK;
                case PERFORM_CODE_PREFERRED_SIZE:
                        ((perform_data_preferred_size*)_data)->return_value
                                = BPictureButton::PreferredSize();
                        return B_OK;
                case PERFORM_CODE_LAYOUT_ALIGNMENT:
                        ((perform_data_layout_alignment*)_data)->return_value
                                = BPictureButton::LayoutAlignment();
                        return B_OK;
                case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
                        ((perform_data_has_height_for_width*)_data)->return_value
                                = BPictureButton::HasHeightForWidth();
                        return B_OK;
                case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
                {
                        perform_data_get_height_for_width* data
                                = (perform_data_get_height_for_width*)_data;
                        BPictureButton::GetHeightForWidth(data->width, &data->min, &data->max,
                                &data->preferred);
                        return B_OK;
                }
                case PERFORM_CODE_SET_LAYOUT:
                {
                        perform_data_set_layout* data = (perform_data_set_layout*)_data;
                        BPictureButton::SetLayout(data->layout);
                        return B_OK;
                }
                case PERFORM_CODE_LAYOUT_INVALIDATED:
                {
                        perform_data_layout_invalidated* data
                                = (perform_data_layout_invalidated*)_data;
                        BPictureButton::LayoutInvalidated(data->descendants);
                        return B_OK;
                }
                case PERFORM_CODE_DO_LAYOUT:
                {
                        BPictureButton::DoLayout();
                        return B_OK;
                }
                case PERFORM_CODE_SET_ICON:
                {
                        perform_data_set_icon* data = (perform_data_set_icon*)_data;
                        return BPictureButton::SetIcon(data->icon, data->flags);
                }
        }

        return BControl::Perform(code, _data);
}


status_t
BPictureButton::SetIcon(const BBitmap* icon, uint32 flags)
{
        return BControl::SetIcon(icon, flags);
}


// #pragma mark - BPictureButton private methods


void BPictureButton::_ReservedPictureButton1() {}
void BPictureButton::_ReservedPictureButton2() {}
void BPictureButton::_ReservedPictureButton3() {}


BPictureButton&
BPictureButton::operator=(const BPictureButton &button)
{
        return *this;
}