root/src/apps/deskbar/CalendarMenuWindow.cpp
/*
 * Copyright 2008 Karsten Heimrich, host.haiku@gmx.de. All rights reserved.
 * Distributed under the terms of the MIT License.
 */


#include "CalendarMenuWindow.h"

#include <Button.h>
#include <CalendarView.h>
#include <GridLayoutBuilder.h>
#include <GroupLayout.h>
#include <GroupLayoutBuilder.h>
#include <GroupView.h>
#include <Locale.h>
#include <Screen.h>
#include <SpaceLayoutItem.h>
#include <String.h>
#include <StringView.h>


using BPrivate::BCalendarView;

enum {
        kInvokationMessage,
        kMonthDownMessage,
        kMonthUpMessage,
        kYearDownMessage,
        kYearUpMessage
};


//      #pragma mark - FlatButton


class FlatButton : public BButton {
public:
                                        FlatButton(const BString& label, uint32 what)
                                                : BButton(label.String(), new BMessage(what)) {}
        virtual                 ~FlatButton() {}

        virtual void    Draw(BRect updateRect);
};


void
FlatButton::Draw(BRect updateRect)
{
        updateRect = Bounds();
        rgb_color highColor = HighColor();

        SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
        FillRect(updateRect);

        font_height fh;
        GetFontHeight(&fh);

        const char* label = Label();
        const float stringWidth = StringWidth(label);
        const float x = (updateRect.right - stringWidth) / 2.0f;
        const float labelY = updateRect.top
                + ((updateRect.Height() - fh.ascent - fh.descent) / 2.0f)
                + fh.ascent + 1.0f;

        SetHighColor(highColor);
        DrawString(label, BPoint(x, labelY));

        if (IsFocus()) {
                SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
                StrokeRect(updateRect);
        }
}


//      #pragma mark - CalendarMenuWindow


CalendarMenuWindow::CalendarMenuWindow(BPoint where)
        :
        BWindow(BRect(0.0, 0.0, 100.0, 130.0), "", B_BORDERED_WINDOW,
                B_AUTO_UPDATE_SIZE_LIMITS | B_ASYNCHRONOUS_CONTROLS | B_CLOSE_ON_ESCAPE
                        | B_NOT_MINIMIZABLE | B_NOT_MOVABLE | B_NOT_RESIZABLE
                        | B_NOT_ZOOMABLE),
        fYearLabel(NULL),
        fMonthLabel(NULL),
        fCalendarView(NULL),
        fSuppressFirstClose(true)
{
        SetFeel(B_FLOATING_ALL_WINDOW_FEEL);

        RemoveShortcut('H', B_COMMAND_KEY | B_CONTROL_KEY);
        AddShortcut('W', B_COMMAND_KEY, new BMessage(B_QUIT_REQUESTED));

        fYearLabel = new BStringView("year", "");
        fMonthLabel = new BStringView("month", "");

        fCalendarView = new BCalendarView(Bounds(), "calendar", B_FOLLOW_ALL);
        fCalendarView->SetInvocationMessage(new BMessage(kInvokationMessage));

        BGroupLayout* layout = new BGroupLayout(B_HORIZONTAL);
        SetLayout(layout);

        float width, height;
        fMonthLabel->GetPreferredSize(&width, &height);

        BGridLayout* gridLayout = BGridLayoutBuilder(5.0)
                .Add(_SetupButton("-", kMonthDownMessage, height), 0, 0)
                .Add(fMonthLabel, 1, 0)
                .Add(_SetupButton("+", kMonthUpMessage, height), 2, 0)
                .Add(BSpaceLayoutItem::CreateGlue(), 3, 0)
                .Add(_SetupButton("-", kYearDownMessage, height), 4, 0)
                .Add(fYearLabel, 5, 0)
                .Add(_SetupButton("+", kYearUpMessage, height), 6, 0)
                .SetInsets(5.0, 0.0, 5.0, 0.0);
        gridLayout->SetMinColumnWidth(1, be_plain_font->StringWidth("September"));

        BGroupView* groupView = new BGroupView(B_VERTICAL, 10.0);
        BView* view = BGroupLayoutBuilder(B_VERTICAL, 5.0)
                .Add(gridLayout->View())
                .Add(fCalendarView)
                .SetInsets(5.0, 5.0, 5.0, 5.0)
                .TopView();
        groupView->AddChild(view);
        AddChild(groupView);

        MoveTo(where);
        _UpdateDate(BDate::CurrentDate(B_LOCAL_TIME));
}


CalendarMenuWindow::~CalendarMenuWindow()
{
}


void
CalendarMenuWindow::Show()
{
        BRect screen(BScreen().Frame());

        float right = screen.right;
        float bottom = screen.bottom;

        BRect rightTop(right / 2.0, screen.top, right, bottom / 2.0);
        BRect rightBottom(right / 2.0, bottom / 2.0, right + 1.0, bottom + 1.0);
        BRect leftBottom(screen.left, bottom / 2.0, right / 2.0, bottom + 1.0);

        BPoint where = Frame().LeftTop();
        BSize size = GetLayout()->PreferredSize();

        if (rightTop.Contains(where)) {
                where.x -= size.width;
        } else if (leftBottom.Contains(where)) {
                where.y -= (size.height + 4.0);
        } else if (rightBottom.Contains(where)) {
                where.x -= size.width;
                where.y -= (size.height + 4.0);
        }

        MoveTo(where);
        fCalendarView->MakeFocus(true);

        BWindow::Show();
}


void
CalendarMenuWindow::WindowActivated(bool active)
{
        if (active)
                return;

        if (mouse_mode() != B_FOCUS_FOLLOWS_MOUSE) {
                if (!active)
                        PostMessage(B_QUIT_REQUESTED);
        } else {
                if (fSuppressFirstClose && !active) {
                        fSuppressFirstClose = false;
                        return;
                }

                if (!fSuppressFirstClose && !active)
                        PostMessage(B_QUIT_REQUESTED);
        }
}


void
CalendarMenuWindow::MessageReceived(BMessage* message)
{
        switch (message->what) {
                case kInvokationMessage:
                {
                        int32 day, month, year;
                        message->FindInt32("day", &day);
                        message->FindInt32("month", &month);
                        message->FindInt32("year", &year);

                        _UpdateDate(BDate(year, month, day));
                        break;
                }

                case kMonthDownMessage:
                case kMonthUpMessage:
                {
                        BDate date = fCalendarView->Date();
                        date.AddMonths(kMonthDownMessage == message->what ? -1 : 1);
                        _UpdateDate(date);
                        break;
                }

                case kYearDownMessage:
                case kYearUpMessage:
                {
                        BDate date = fCalendarView->Date();
                        date.AddYears(kYearDownMessage == message->what ? -1 : 1);
                        _UpdateDate(date);
                        break;
                }

                default:
                        BWindow::MessageReceived(message);
                        break;
        }
}


void
CalendarMenuWindow::_UpdateDate(const BDate& date)
{
        if (!date.IsValid())
                return;

        fCalendarView->SetDate(date);

        BString text;
        text << date.Year();
        fYearLabel->SetText(text.String());

        fMonthLabel->SetText(date.LongMonthName(date.Month()).String());
}


BButton*
CalendarMenuWindow::_SetupButton(const char* label, uint32 what, float height)
{
        FlatButton* button = new FlatButton(label, what);
        button->SetExplicitMinSize(BSize(height, height));

        return button;
}