#include "TimeView.h"
#include <algorithm>
#include <string.h>
#include <Application.h>
#include <Catalog.h>
#include <Debug.h>
#include <Locale.h>
#include <MenuItem.h>
#include <MessageRunner.h>
#include <PopUpMenu.h>
#include <Roster.h>
#include <Screen.h>
#include <Window.h>
#include "BarApp.h"
#include "BarView.h"
#include "BarWindow.h"
#include "StatusView.h"
#include "CalendarMenuWindow.h"
#include "StatusView.h"
static const float kHMargin = 2.0;
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "TimeView"
TTimeView::TTimeView(float maxWidth, float height, TBarView* barView)
:
BView(BRect(-100, -100, -90, -90), "_deskbar_tv_",
B_FOLLOW_RIGHT | B_FOLLOW_TOP,
B_WILL_DRAW | B_PULSE_NEEDED | B_FRAME_EVENTS),
fBarView(barView),
fParent(NULL),
fMaxWidth(maxWidth),
fHeight(height),
fShowLevel(0),
fShowSeconds(false),
fShowDayOfWeek(false),
fShowTimeZone(false),
fCalendarWindow(NULL),
fTimeFormat(NULL),
fDateFormat(NULL)
{
fCurrentTime = fLastTime = time(NULL);
fSeconds = fMinute = fHour = 0;
fCurrentTimeStr[0] = 0;
fCurrentDateStr[0] = 0;
fLastTimeStr[0] = 0;
fLastDateStr[0] = 0;
fNeedToUpdate = true;
UpdateTimeFormat();
}
#ifdef AS_REPLICANT
TTimeView::TTimeView(BMessage* data)
: BView(data),
fTimeFormat(NULL),
fDateFormat(NULL)
{
fCurrentTime = fLastTime = time(NULL);
data->FindBool("seconds", &fShowSeconds);
UpdateTimeFormat();
}
#endif
TTimeView::~TTimeView()
{
if (fCalendarWindowMessenger.IsValid())
fCalendarWindowMessenger.SendMessage(B_QUIT_REQUESTED);
delete fTimeFormat;
delete fDateFormat;
}
#ifdef AS_REPLICANT
BArchivable*
TTimeView::Instantiate(BMessage* data)
{
if (!validate_instantiation(data, "TTimeView"))
return NULL;
return new TTimeView(data);
}
status_t
TTimeView::Archive(BMessage* data, bool deep) const
{
BView::Archive(data, deep);
data->AddBool("orientation", Vertical());
data->AddInt16("showLevel", fShowLevel);
data->AddBool("showSeconds", fShowSeconds);
data->AddBool("showDayOfWeek", fShowDayOfWeek);
data->AddBool("showTimeZone", fShowTimeZone);
data->AddInt32("deskbar:private_align", B_ALIGN_RIGHT);
return B_OK;
}
#endif
void
TTimeView::AttachedToWindow()
{
fCurrentTime = time(NULL);
SetFont(be_plain_font);
if (Parent()) {
fParent = Parent();
AdoptParentColors();
} else
SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
ResizeToPreferred();
CalculateTextPlacement();
}
void
TTimeView::Draw(BRect )
{
PushState();
SetHighColor(ViewColor());
SetLowColor(ViewColor());
FillRect(Bounds());
SetHighUIColor(B_MENU_ITEM_TEXT_COLOR);
DrawString(fCurrentTimeStr, fTimeLocation);
PopState();
}
void
TTimeView::FrameMoved(BPoint)
{
Update();
}
void
TTimeView::GetPreferredSize(float* width, float* height)
{
float timeWidth = StringWidth(fCurrentTimeStr);
font_height fontHeight;
GetFontHeight(&fontHeight);
fHeight = fontHeight.ascent + fontHeight.descent - 2;
if (Vertical()) {
float appWidth = static_cast<TBarApp*>(be_app)->Settings()->width;
*width = fMaxWidth
= std::min(appWidth - (gDragRegionWidth + kHMargin) * 2, timeWidth);
} else
*width = fMaxWidth = timeWidth;
*height = fHeight;
}
void
TTimeView::MessageReceived(BMessage* message)
{
switch (message->what) {
case kChangeTime:
{
be_roster->Launch("application/x-vnd.Haiku-Time");
BMessenger messenger("application/x-vnd.Haiku-Time");
BMessage switchToClock('SlCk');
messenger.SendMessage(&switchToClock);
break;
}
case kShowHideTime:
{
be_app->MessageReceived(message);
break;
}
case kShowCalendar:
{
BRect bounds(Bounds());
BPoint center(bounds.LeftTop());
center += BPoint(bounds.Width() / 2, bounds.Height() / 2);
ShowCalendar(center);
break;
}
default:
BView::MessageReceived(message);
break;
}
}
void
TTimeView::MouseDown(BPoint point)
{
uint32 buttons;
Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons);
if (buttons == B_SECONDARY_MOUSE_BUTTON) {
ShowTimeOptions(ConvertToScreen(point));
return;
} else if (buttons == B_PRIMARY_MOUSE_BUTTON)
ShowCalendar(point);
fLastDateStr[0] = '\0';
fLastTimeStr[0] = '\0';
Pulse();
}
void
TTimeView::Pulse()
{
time_t curTime = time(NULL);
tm* ct = localtime(&curTime);
if (ct == NULL)
return;
fCurrentTime = curTime;
GetCurrentTime();
GetCurrentDate();
if (strcmp(fCurrentTimeStr, fLastTimeStr) != 0) {
Update();
strlcpy(fLastTimeStr, fCurrentTimeStr, sizeof(fLastTimeStr));
fNeedToUpdate = true;
}
if (strcmp(fCurrentDateStr, fLastDateStr) != 0) {
strlcpy(fLastDateStr, fCurrentDateStr, sizeof(fLastDateStr));
SetToolTip(fCurrentDateStr);
}
if (fNeedToUpdate) {
fSeconds = ct->tm_sec;
fMinute = ct->tm_min;
fHour = ct->tm_hour;
Draw(Bounds());
fNeedToUpdate = false;
}
}
void
TTimeView::ResizeToPreferred()
{
float width;
float height;
float oldWidth = Bounds().Width();
float oldHeight = Bounds().Height();
GetPreferredSize(&width, &height);
if (height != oldHeight || width != oldWidth) {
ResizeTo(width, height);
MoveBy(oldWidth - width, 0);
fNeedToUpdate = true;
}
}
bool
TTimeView::ShowSeconds() const
{
return fShowSeconds;
}
void
TTimeView::SetShowSeconds(bool show)
{
fShowSeconds = show;
UpdateTimeFormat();
Update();
}
bool
TTimeView::ShowDayOfWeek() const
{
return fShowDayOfWeek;
}
void
TTimeView::SetShowDayOfWeek(bool show)
{
fShowDayOfWeek = show;
UpdateTimeFormat();
Update();
}
bool
TTimeView::ShowTimeZone() const
{
return fShowTimeZone;
}
void
TTimeView::SetShowTimeZone(bool show)
{
fShowTimeZone = show;
UpdateTimeFormat();
Update();
}
void
TTimeView::ShowCalendar(BPoint where)
{
if (fCalendarWindowMessenger.IsValid()) {
BMessage activate(B_SET_PROPERTY);
activate.AddSpecifier("Active");
activate.AddBool("data", true);
if (fCalendarWindowMessenger.SendMessage(&activate) == B_OK)
return;
}
where.y = Bounds().bottom + 4.0;
ConvertToScreen(&where);
if (where.y >= BScreen().Frame().bottom)
where.y -= (Bounds().Height() + 4.0);
fCalendarWindow = new CalendarMenuWindow(where);
fCalendarWindowMessenger = BMessenger(fCalendarWindow);
fCalendarWindow->Show();
}
bool
TTimeView::IsShowingCalendar()
{
return fCalendarWindow != NULL && !fCalendarWindow->IsHidden();
}
void
TTimeView::UpdateTimeFormat()
{
int32 fields = B_DATE_ELEMENT_HOUR | B_DATE_ELEMENT_MINUTE;
if (fShowSeconds)
fields |= B_DATE_ELEMENT_SECOND;
if (fShowDayOfWeek)
fields |= B_DATE_ELEMENT_WEEKDAY;
if (fShowTimeZone)
fields |= B_DATE_ELEMENT_TIMEZONE;
delete fTimeFormat;
fTimeFormat = new BDateTimeFormat(BLocale::Default());
fTimeFormat->SetDateTimeFormat(B_SHORT_DATE_FORMAT, B_SHORT_TIME_FORMAT,
fields);
delete fDateFormat;
fDateFormat = new BDateFormat(BLocale::Default());
}
void
TTimeView::GetCurrentTime()
{
fTimeFormat->Format(fCurrentTimeStr, sizeof(fCurrentTimeStr), fCurrentTime,
B_SHORT_DATE_FORMAT, B_SHORT_TIME_FORMAT);
}
void
TTimeView::GetCurrentDate()
{
char tmp[sizeof(fCurrentDateStr)];
fDateFormat->Format(tmp, sizeof(fCurrentDateStr), fCurrentTime,
B_FULL_DATE_FORMAT);
const char* str = tmp;
if (str[0] == '0')
str++;
strlcpy(fCurrentDateStr, str, sizeof(fCurrentDateStr));
}
void
TTimeView::CalculateTextPlacement()
{
fDateLocation.x = 0.0;
fTimeLocation.x = 0.0;
BFont font;
GetFont(&font);
const char* stringArray[1];
stringArray[0] = fCurrentTimeStr;
BRect rectArray[1];
escapement_delta delta = { 0.0, 0.0 };
font.GetBoundingBoxesForStrings(stringArray, 1, B_SCREEN_METRIC, &delta,
rectArray);
fTimeLocation.y = fDateLocation.y = ceilf((Bounds().Height()
- rectArray[0].Height() + 1.0) / 2.0 - rectArray[0].top);
if (Vertical()) {
float timeWidth = StringWidth(fCurrentTimeStr);
if (timeWidth > fMaxWidth) {
float difference = timeWidth - fMaxWidth;
fDateLocation.x -= difference;
fTimeLocation.x -= difference;
}
}
}
void
TTimeView::ShowTimeOptions(BPoint point)
{
BPopUpMenu* menu = new BPopUpMenu("", false, false);
menu->SetFont(be_plain_font);
BMenuItem* item;
item = new BMenuItem(B_TRANSLATE("Time preferences" B_UTF8_ELLIPSIS),
new BMessage(kChangeTime));
menu->AddItem(item);
item = new BMenuItem(B_TRANSLATE("Hide clock"),
new BMessage(kShowHideTime));
menu->AddItem(item);
item = new BMenuItem(B_TRANSLATE("Show calendar" B_UTF8_ELLIPSIS),
new BMessage(kShowCalendar));
menu->AddItem(item);
menu->SetTargetForItems(this);
menu->Go(point, true, true, BRect(point.x - 4, point.y - 4,
point.x + 4, point.y +4), true);
}
void
TTimeView::Update()
{
GetCurrentTime();
GetCurrentDate();
SetToolTip(fCurrentDateStr);
ResizeToPreferred();
CalculateTextPlacement();
if (fParent != NULL)
fParent->Invalidate();
}
bool
TTimeView::Vertical()
{
if (fBarView == NULL)
return true;
return fBarView->Vertical();
}