root/src/apps/bootmanager/DefaultPartitionPage.cpp
/*
 * Copyright 2008-2011, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Michael Pfeiffer <laplace@users.sourceforge.net>
 */


#include "DefaultPartitionPage.h"


#include <Catalog.h>
#include <ControlLook.h>
#include <Locale.h>
#include <Message.h>
#include <MenuItem.h>
#include <MenuField.h>
#include <PopUpMenu.h>
#include <RadioButton.h>
#include <Slider.h>
#include <string.h>
#include <String.h>
#include <TextView.h>


#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "DefaultPartitionPage"


enum {
        kMsgPartition = 'part',
        kMsgTimeout = 'time'
};


// The timeout code to wait indefinitely
// Note: The timeout is encoded in seconds, -1 indicates to wait indefinitely
const int32 kTimeoutIndefinitely = -1;
const int32 kDefaultTimeout = kTimeoutIndefinitely;

struct TimeoutOption {
        int32 timeout;
        const char* label;
};

static const TimeoutOption gTimeoutOptions[] = {
        { 0, B_TRANSLATE_MARK("Immediately")},
        { 1, B_TRANSLATE_MARK("After one second")},
        { 2, B_TRANSLATE_MARK("After two seconds")},
        { 3, B_TRANSLATE_MARK("After three seconds")},
        { 4, B_TRANSLATE_MARK("After four seconds")},
        { 5, B_TRANSLATE_MARK("After five seconds")},
        { 60, B_TRANSLATE_MARK("After one minute")},
        { kTimeoutIndefinitely, B_TRANSLATE_MARK("Never")}
};


#define kNumberOfTimeoutOptions \
        (int32)(sizeof(gTimeoutOptions) / sizeof(TimeoutOption))


static int32
get_index_for_timeout(int32 timeout)
{
        int32 defaultIndex = 0;
        for (int32 i = 0; i < kNumberOfTimeoutOptions; i ++) {
                if (gTimeoutOptions[i].timeout == timeout)
                        return i;

                if (gTimeoutOptions[i].timeout == kDefaultTimeout)
                        defaultIndex = i;
        }
        return defaultIndex;
}


static int32
get_timeout_for_index(int32 index)
{
        if (index < 0)
                return gTimeoutOptions[0].timeout;
        if (index >= kNumberOfTimeoutOptions)
                return gTimeoutOptions[kNumberOfTimeoutOptions-1].timeout;
        return gTimeoutOptions[index].timeout;
}


const char*
get_label_for_timeout(int32 timeout)
{
        int32 index = get_index_for_timeout(timeout);
        return gTimeoutOptions[index].label;
}


// #pragma mark -


DefaultPartitionPage::DefaultPartitionPage(BMessage* settings, BRect frame,
        const char* name)
        :
        WizardPageView(settings, frame, name, B_FOLLOW_ALL,
                B_WILL_DRAW | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE)
{
        _BuildUI();
}


DefaultPartitionPage::~DefaultPartitionPage()
{
}


void
DefaultPartitionPage::FrameResized(float width, float height)
{
        WizardPageView::FrameResized(width, height);
        _Layout();
}


void
DefaultPartitionPage::AttachedToWindow()
{
        fDefaultPartition->Menu()->SetTargetForItems(this);
        fTimeoutSlider->SetTarget(this);
}


void
DefaultPartitionPage::MessageReceived(BMessage* msg)
{
        switch (msg->what) {
                case kMsgPartition:
                {
                        int32 index;
                        msg->FindInt32("index", &index);
                        fSettings->ReplaceInt32("defaultPartition", index);
                        break;
                }
                case kMsgTimeout:
                {
                        int32 sliderValue = fTimeoutSlider->Value();
                        int32 timeout = get_timeout_for_index(sliderValue);
                        fSettings->ReplaceInt32("timeout", timeout);

                        BString label;
                        _GetTimeoutLabel(timeout, label);
                        fTimeoutSlider->SetLabel(label.String());
                        break;
                }

                default:
                        WizardPageView::MessageReceived(msg);
        }
}


void
DefaultPartitionPage::_BuildUI()
{
        const float kTextDistance = be_control_look->DefaultItemSpacing();
        BRect rect(Bounds());

        BString text;
        text << B_TRANSLATE_COMMENT("Default Partition", "Title") << "\n"
                << B_TRANSLATE("Please specify a default partition and a timeout.\n"
                        "The boot menu will load the default partition after "
                        "the timeout unless you select another partition. You "
                        "can also have the boot menu wait indefinitely for you "
                        "to select a partition.\n"
                        "Keep the 'ALT' key pressed to disable the timeout at boot time.");

        fDescription = CreateDescription(rect, "description", text);
        MakeHeading(fDescription);
        AddChild(fDescription);
        LayoutDescriptionVertically(fDescription);
        rect.top = fDescription->Frame().bottom + kTextDistance;

        BPopUpMenu* popUpMenu = _CreatePopUpMenu();
        fDefaultPartition = new BMenuField(rect, "partitions",
                B_TRANSLATE_COMMENT("Default Partition:", "Menu field label"),
                popUpMenu);
        float divider = be_plain_font->StringWidth(fDefaultPartition->Label()) + 3;
        fDefaultPartition->SetDivider(divider);
        AddChild(fDefaultPartition);
        fDefaultPartition->ResizeToPreferred();

        // timeout slider
        rect.top = fDefaultPartition->Frame().bottom + kTextDistance;
        int32 timeout;
        fSettings->FindInt32("timeout", &timeout);
        BString timeoutLabel;
        _GetTimeoutLabel(timeout, timeoutLabel);

        int32 sliderValue = get_index_for_timeout(timeout);

        fTimeoutSlider = new BSlider(rect, "timeout", timeoutLabel.String(),
                new BMessage(kMsgTimeout), 0, kNumberOfTimeoutOptions-1,
                B_BLOCK_THUMB,
                B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP);
        fTimeoutSlider->SetModificationMessage(new BMessage(kMsgTimeout));
        fTimeoutSlider->SetValue(sliderValue);
        fTimeoutSlider->SetLimitLabels(B_TRANSLATE("Immediately"),
                B_TRANSLATE("Never"));
        fTimeoutSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
        fTimeoutSlider->SetHashMarkCount(kNumberOfTimeoutOptions);
        fTimeoutSlider->ResizeToPreferred();
        AddChild(fTimeoutSlider);

        _Layout();
}


BPopUpMenu*
DefaultPartitionPage::_CreatePopUpMenu()
{
        int32 defaultPartitionIndex;
        fSettings->FindInt32("defaultPartition", &defaultPartitionIndex);

        BMenuItem* selectedItem = NULL;
        int32 selectedItemIndex = 0;

        BPopUpMenu* menu = new BPopUpMenu(B_TRANSLATE_COMMENT("Partitions",
                "Pop up menu title"));
        BMessage message;
        for (int32 i = 0; fSettings->FindMessage("partition", i, &message) == B_OK;
                        i++) {
                bool show;
                if (message.FindBool("show", &show) != B_OK || !show)
                        continue;

                BString name;
                message.FindString("name", &name);

                BMessage* msg = new BMessage(kMsgPartition);
                msg->AddInt32("index", i);
                BMenuItem* item = new BMenuItem(name.String(), msg);
                menu->AddItem(item);
                if (defaultPartitionIndex == i || selectedItem == NULL) {
                        selectedItem = item;
                        selectedItemIndex = i;
                }
        }
        fSettings->ReplaceInt32("defaultPartition", selectedItemIndex);
        selectedItem->SetMarked(true);
        return menu;
}


void
DefaultPartitionPage::_GetTimeoutLabel(int32 timeout, BString& label)
{
        const char* text = B_TRANSLATE_NOCOLLECT(get_label_for_timeout(timeout));
        label = B_TRANSLATE("Timeout: %s");
        label.ReplaceFirst("%s", text);
}


void
DefaultPartitionPage::_Layout()
{
        LayoutDescriptionVertically(fDescription);

        const float kTextDistance = be_control_look->DefaultItemSpacing();
        float left = fDefaultPartition->Frame().left;
        float top = fDescription->Frame().bottom + kTextDistance;

        fDefaultPartition->MoveTo(left, top);
        top = fDefaultPartition->Frame().bottom + kTextDistance;

        fTimeoutSlider->MoveTo(left, top);
}