#include "MarginView.h"
#include <AppKit.h>
#include <GridView.h>
#include <GridLayout.h>
#include <GroupLayout.h>
#include <GroupLayoutBuilder.h>
#include <SupportKit.h>
#include <TextControl.h>
#include <stdio.h>
#include <stdlib.h>
const int kOffsetY = 20;
const int kOffsetX = 10;
const int kStringSize = 50;
const int kWidth = 50;
const int kNumCount = 10;
const static float kPointUnits = 1;
const static float kInchUnits = 72;
const static float kCMUnits = 28.346;
const static float kMinFieldWidth = 100;
const static float kMinUnitHeight = 30;
const static float kUnitFormat[] = { kInchUnits, kCMUnits, kPointUnits };
const static char *kUnitNames[] = { "inch", "cm", "points", NULL };
const static MarginUnit kUnitMsg[] = { kUnitInch, kUnitCM, kUnitPoint };
const pattern kDots = {{ 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 }};
const rgb_color kBlack = { 0,0,0,0 };
const rgb_color kRed = { 255,0,0,0 };
const rgb_color kWhite = { 255,255,255,0 };
const rgb_color kGray = { 220,220,220,0 };
PageView::PageView()
: BView("pageView", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE)
, fPageWidth(0)
, fPageHeight(0)
, fMargins(0, 0, 0, 0)
{
}
void
PageView::SetPageSize(float pageWidth, float pageHeight)
{
fPageWidth = pageWidth;
fPageHeight = pageHeight;
}
void
PageView::SetMargins(BRect margins)
{
fMargins = margins;
}
void
PageView::Draw(BRect bounds)
{
BRect frame(Frame());
float totalWidth = frame.Width();
float totalHeight = frame.Height();
float pageWidth = totalWidth;
float pageHeight = totalWidth * fPageHeight / fPageWidth;
if (pageHeight > totalHeight) {
pageHeight = totalHeight;
pageWidth = totalHeight * fPageWidth / fPageHeight;
}
BPoint offset(0, 0);
offset.x = static_cast<int>((totalWidth - pageWidth) / 2);
offset.y = static_cast<int>((totalHeight - pageHeight) / 2);
SetHighColor(kWhite);
BRect r = BRect(0, 0, pageWidth, pageHeight);
r.OffsetBy(offset);
FillRect(r);
SetHighColor(kBlack);
StrokeRect(r);
SetHighColor(kRed);
SetLowColor(kWhite);
r.top += (fMargins.top / fPageHeight) * pageHeight;
r.right -= (fMargins.right / fPageWidth) * pageWidth;
r.bottom -= (fMargins.bottom / fPageHeight) * pageHeight;
r.left += (fMargins.left / fPageWidth) * pageWidth;
StrokeRect(r, kDots);
}
MarginView::MarginView(int32 pageWidth, int32 pageHeight,
BRect margins, MarginUnit units)
: BBox("marginView")
{
fMarginUnit = units;
fUnitValue = kUnitFormat[units];
SetLabel("Margins");
fMargins = margins;
fPageWidth = pageWidth;
fPageHeight = pageHeight;
}
MarginView::~MarginView()
{
}
void
MarginView::AttachedToWindow()
{
if (Parent())
SetViewColor(Parent()->ViewColor());
_ConstructGUI();
}
void
MarginView::MessageReceived(BMessage *msg)
{
switch (msg->what) {
case CHANGE_PAGE_SIZE: {
float w;
float h;
msg->FindFloat("width", &w);
msg->FindFloat("height", &h);
SetPageSize(w, h);
UpdateView(MARGIN_CHANGED);
} break;
case FLIP_PAGE: {
BPoint p = PageSize();
SetPageSize(p.y, p.x);
UpdateView(MARGIN_CHANGED);
} break;
case MARGIN_CHANGED:
UpdateView(MARGIN_CHANGED);
break;
case TOP_MARGIN_CHANGED:
UpdateView(TOP_MARGIN_CHANGED);
break;
case LEFT_MARGIN_CHANGED:
UpdateView(LEFT_MARGIN_CHANGED);
break;
case RIGHT_MARGIN_CHANGED:
UpdateView(RIGHT_MARGIN_CHANGED);
break;
case BOTTOM_MARGIN_CHANGED:
UpdateView(BOTTOM_MARGIN_CHANGED);
break;
case MARGIN_UNIT_CHANGED: {
int32 marginUnit;
if (msg->FindInt32("marginUnit", &marginUnit) == B_OK)
_SetMarginUnit((MarginUnit)marginUnit);
} break;
default:
BView::MessageReceived(msg);
break;
}
}
BPoint
MarginView::PageSize() const
{
return BPoint(fPageWidth, fPageHeight);
}
void
MarginView::SetPageSize(float pageWidth, float pageHeight)
{
fPageWidth = pageWidth;
fPageHeight = pageHeight;
}
BRect
MarginView::Margin() const
{
BRect margin;
float top = atof(fTop->Text());
float right = atof(fRight->Text());
float left = atof(fLeft->Text());
float bottom = atof(fBottom->Text());
switch (fMarginUnit) {
case kUnitInch:
top *= kInchUnits;
right *= kInchUnits;
left *= kInchUnits;
bottom *= kInchUnits;
break;
case kUnitCM:
top *= kCMUnits;
right *= kCMUnits;
left *= kCMUnits;
bottom *= kCMUnits;
break;
case kUnitPoint:
break;
}
margin.Set(left, top, right, bottom);
return margin;
}
MarginUnit
MarginView::Unit() const
{
return fMarginUnit;
}
void
MarginView::UpdateView(uint32 msg)
{
Window()->Lock();
{
char pageSize[kStringSize];
sprintf(pageSize, "%2.1f x %2.1f",
fPageWidth / fUnitValue,
fPageHeight / fUnitValue);
fPageSize->SetText(pageSize);
fPage->SetPageSize(fPageWidth, fPageHeight);
fPage->SetMargins(Margin());
fPage->Invalidate();
}
Invalidate();
Window()->Unlock();
}
void
MarginView::_ConstructGUI()
{
fPage = new PageView();
fPage->SetViewColor(ViewColor());
fPageSize = new BStringView("pageSize", "?x?");
BString str;
str << fMargins.top/fUnitValue;
fTop = new BTextControl("top", "Top:", str.String(), NULL);
fTop->SetModificationMessage(new BMessage(TOP_MARGIN_CHANGED));
fTop->SetTarget(this);
_AllowOnlyNumbers(fTop, kNumCount);
str = "";
str << fMargins.left/fUnitValue;
fLeft = new BTextControl("left", "Left:", str.String(), NULL);
fLeft->SetModificationMessage(new BMessage(LEFT_MARGIN_CHANGED));
fLeft->SetTarget(this);
_AllowOnlyNumbers(fLeft, kNumCount);
str = "";
str << fMargins.bottom/fUnitValue;
fBottom = new BTextControl("bottom", "Bottom:", str.String(), NULL);
fBottom->SetModificationMessage(new BMessage(BOTTOM_MARGIN_CHANGED));
fBottom->SetTarget(this);
_AllowOnlyNumbers(fBottom, kNumCount);
str = "";
str << fMargins.right/fUnitValue;
fRight = new BTextControl("right", "Right:", str.String(), NULL);
fRight->SetModificationMessage(new BMessage(RIGHT_MARGIN_CHANGED));
fRight->SetTarget(this);
_AllowOnlyNumbers(fRight, kNumCount);
BPopUpMenu *menu = new BPopUpMenu("units");
BMenuField *units = new BMenuField("units", "Units:", menu);
BMenuItem *item;
for (int32 i = 0; kUnitNames[i] != NULL; i++) {
BMessage *msg = new BMessage(MARGIN_UNIT_CHANGED);
msg->AddInt32("marginUnit", kUnitMsg[i]);
menu->AddItem(item = new BMenuItem(kUnitNames[i], msg));
item->SetTarget(this);
if (fMarginUnit == kUnitMsg[i])
item->SetMarked(true);
}
BGridView* settings = new BGridView();
BGridLayout* settingsLayout = settings->GridLayout();
settingsLayout->AddItem(fTop->CreateLabelLayoutItem(), 0, 0);
settingsLayout->AddItem(fTop->CreateTextViewLayoutItem(), 1, 0);
settingsLayout->AddItem(fLeft->CreateLabelLayoutItem(), 0, 1);
settingsLayout->AddItem(fLeft->CreateTextViewLayoutItem(), 1, 1);
settingsLayout->AddItem(fBottom->CreateLabelLayoutItem(), 0, 2);
settingsLayout->AddItem(fBottom->CreateTextViewLayoutItem(), 1, 2);
settingsLayout->AddItem(fRight->CreateLabelLayoutItem(), 0, 3);
settingsLayout->AddItem(fRight->CreateTextViewLayoutItem(), 1, 3);
settingsLayout->AddItem(units->CreateLabelLayoutItem(), 0, 4);
settingsLayout->AddItem(units->CreateMenuBarLayoutItem(), 1, 4);
settingsLayout->SetSpacing(0, 0);
BGroupView* groupView = new BGroupView(B_HORIZONTAL, 10);
BGroupLayout* groupLayout = groupView->GroupLayout();
groupLayout->AddView(BGroupLayoutBuilder(B_VERTICAL, 0)
.Add(fPage)
.Add(fPageSize)
.SetInsets(0, 0, 0, 0)
.TopView()
);
groupLayout->AddView(settings);
groupLayout->SetInsets(5, 5, 5, 5);
AddChild(groupView);
UpdateView(MARGIN_CHANGED);
}
void
MarginView::_AllowOnlyNumbers(BTextControl *textControl, int32 maxNum)
{
BTextView *tv = textControl->TextView();
for (int32 i = 0; i < 256; i++)
tv->DisallowChar(i);
for (int32 i = '0'; i <= '9'; i++)
tv->AllowChar(i);
tv->AllowChar(B_BACKSPACE);
tv->AllowChar('.');
tv->SetMaxBytes(maxNum);
}
void
MarginView::_SetMargin(BRect margin)
{
fMargins = margin;
}
void
MarginView::_SetMarginUnit(MarginUnit unit)
{
if (unit == fMarginUnit) {
return;
}
fUnitValue = kUnitFormat[unit];
float top = atof(fTop->Text());
float right = atof(fRight->Text());
float left = atof(fLeft->Text());
float bottom = atof(fBottom->Text());
switch (fMarginUnit)
{
case kUnitInch:
top *= kInchUnits;
right *= kInchUnits;
left *= kInchUnits;
bottom *= kInchUnits;
if (unit == kUnitCM) {
top /= kCMUnits;
right /= kCMUnits;
left /= kCMUnits;
bottom /= kCMUnits;
}
break;
case kUnitCM:
top *= kCMUnits;
right *= kCMUnits;
left *= kCMUnits;
bottom *= kCMUnits;
if (unit == kUnitInch) {
top /= kInchUnits;
right /= kInchUnits;
left /= kInchUnits;
bottom /= kInchUnits;
}
break;
case kUnitPoint:
if (unit == kUnitCM) {
top /= kCMUnits;
right /= kCMUnits;
left /= kCMUnits;
bottom /= kCMUnits;
}
if (unit == kUnitInch) {
top /= kInchUnits;
right /= kInchUnits;
left /= kInchUnits;
bottom /= kInchUnits;
}
break;
}
fMarginUnit = unit;
Window()->Lock();
BString str;
str << top;
fTop->SetText(str.String());
str = "";
str << left;
fLeft->SetText(str.String());
str = "";
str << right;
fRight->SetText(str.String());
str = "";
str << bottom;
fBottom->SetText(str.String());
Invalidate();
Window()->Unlock();
}