root/src/libs/print/libprint/PageSetupDlg.cpp
/*
 * PageSetupDlg.cpp
 * Copyright 1999-2000 Y.Takagi. All Rights Reserved.
 */

#include <string>
#include <cstring>
#include <cstdlib>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>

#include <Alert.h>
#include <Bitmap.h>
#include <Box.h>
#include <Button.h>
#include <Debug.h>
#include <Font.h>
#include <GridView.h>
#include <GroupLayout.h>
#include <GroupLayoutBuilder.h>
#include <Looper.h>
#include <MessageFilter.h>
#include <MenuField.h>
#include <MenuItem.h>
#include <Message.h>
#include <Point.h>
#include <PopUpMenu.h>
#include <PrintJob.h>
#include <RadioButton.h>
#include <Rect.h>
#include <String.h>
#include <SupportDefs.h>
#include <TextControl.h>
#include <View.h>


#include "DbgMsg.h"
#include "JobData.h"
#include "MarginView.h"
#include "PageSetupDlg.h"
#include "PrinterData.h"
#include "PrinterCap.h"
#include "PrintUtils.h"


using namespace std;


enum MSGS {
        kMsgCancel = 1,
        kMsgOK,
        kMsgOrientationChanged,
        kMsgPaperChanged,
};

PageSetupView::PageSetupView(JobData *job_data, PrinterData *printer_data,
        const PrinterCap *printer_cap)
        : BView("pageSetupView", B_WILL_DRAW)
        , fJobData(job_data)
        , fPrinterData(printer_data)
        , fPrinterCap(printer_cap)
{
        SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
}

PageSetupView::~PageSetupView()
{
}

void
PageSetupView::AddOrientationItem(const char* name, JobData::Orientation orientation)
{
        BMessage *msg = new BMessage(kMsgOrientationChanged);
        msg->AddInt32("orientation", orientation);
        BMenuItem *item = new BMenuItem(name, msg);

        fOrientation->AddItem(item);
        item->SetTarget(this);
        if (fJobData->GetOrientation() == orientation) {
                item->SetMarked(true);
        } else if (fOrientation->CountItems() == 1) {
                item->SetMarked(true);
        }
}

void
PageSetupView::AttachedToWindow()
{
        BMenuItem  *item = NULL;
        bool       marked;
        int        count;

        // margin
        MarginUnit units = fJobData->GetMarginUnit();
        BRect paper = fJobData->GetPaperRect();
        BRect margin = fJobData->GetPrintableRect();

        // re-calculate the margin from the printable rect in points
        margin.top -= paper.top;
        margin.left -= paper.left;
        margin.right = paper.right - margin.right;
        margin.bottom = paper.bottom - margin.bottom;

        fMarginView = new MarginView(
                paper.IntegerWidth(),
                paper.IntegerHeight(),
                        margin, units);

        // paper selection
        marked = false;
        fPaper = new BPopUpMenu("paperSize");
        fPaper->SetRadioMode(true);
        count = fPrinterCap->CountCap(PrinterCap::kPaper);
        PaperCap **paper_cap = (PaperCap **)fPrinterCap->GetCaps(PrinterCap::kPaper);
        while (count--) {
                BMessage *msg = new BMessage(kMsgPaperChanged);
                msg->AddPointer("paperCap", *paper_cap);
                item = new BMenuItem((*paper_cap)->fLabel.c_str(), msg);
                fPaper->AddItem(item);
                item->SetTarget(this);
                if ((*paper_cap)->fPaper == fJobData->GetPaper()) {
                        item->SetMarked(true);
                        marked = true;
                }
                paper_cap++;
        }
        if (!marked)
                item->SetMarked(true);
        BMenuField* paperSize = new BMenuField("paperSize", "Paper size:", fPaper);

        // orientation
        fOrientation = new BPopUpMenu("orientation");
        fOrientation->SetRadioMode(true);

        BMenuField* orientation = new BMenuField("orientation", "Orientation:", fOrientation);

        count = fPrinterCap->CountCap(PrinterCap::kOrientation);
        if (count == 0) {
                AddOrientationItem("Portrait", JobData::kPortrait);
                AddOrientationItem("Landscape", JobData::kLandscape);
        } else {
                OrientationCap **orientation_cap = (OrientationCap **)fPrinterCap->GetCaps(PrinterCap::kOrientation);
                while (count--) {
                        AddOrientationItem((*orientation_cap)->fLabel.c_str(),
                                (*orientation_cap)->fOrientation);
                        orientation_cap++;
                }
        }

        // resolution
        marked = false;
        fResolution = new BPopUpMenu("resolution");
        fResolution->SetRadioMode(true);
        count = fPrinterCap->CountCap(PrinterCap::kResolution);
        ResolutionCap **resolution_cap = (ResolutionCap **)fPrinterCap->GetCaps(PrinterCap::kResolution);
        while (count--) {
                item = new BMenuItem((*resolution_cap)->fLabel.c_str(), NULL);
                fResolution->AddItem(item);
                item->SetTarget(this);
                if (((*resolution_cap)->fXResolution == fJobData->GetXres()) &&
                        ((*resolution_cap)->fYResolution == fJobData->GetYres())) {
                        item->SetMarked(true);
                        marked = true;
                }
                resolution_cap++;
        }
        if (!marked)
                item->SetMarked(true);
        BMenuField* resolution = new BMenuField("resolution", "Resolution:", fResolution);

        // scale
        BString scale;
        scale << (int)fJobData->GetScaling();
        fScaling = new BTextControl("scale", "Scale [%]:",
                                                                        scale.String(),
                                        NULL);
        int num;
        for (num = 0; num <= 255; num++) {
                fScaling->TextView()->DisallowChar(num);
        }
        for (num = 0; num <= 9; num++) {
                fScaling->TextView()->AllowChar('0' + num);
        }
        fScaling->TextView()->SetMaxBytes(3);

        // cancel and ok
        BButton* cancel = new BButton("cancel", "Cancel", new BMessage(kMsgCancel));
        BButton* ok = new BButton("ok", "OK", new BMessage(kMsgOK));

        ok->MakeDefault(true);

        BGridView* settings = new BGridView();
        BGridLayout* settingsLayout = settings->GridLayout();
        settingsLayout->AddItem(paperSize->CreateLabelLayoutItem(), 0, 0);
        settingsLayout->AddItem(paperSize->CreateMenuBarLayoutItem(), 1, 0);
        settingsLayout->AddItem(orientation->CreateLabelLayoutItem(), 0, 1);
        settingsLayout->AddItem(orientation->CreateMenuBarLayoutItem(), 1, 1);
        settingsLayout->AddItem(resolution->CreateLabelLayoutItem(), 0, 2);
        settingsLayout->AddItem(resolution->CreateMenuBarLayoutItem(), 1, 2);
        settingsLayout->AddItem(fScaling->CreateLabelLayoutItem(), 0, 3);
        settingsLayout->AddItem(fScaling->CreateTextViewLayoutItem(), 1, 3);
        settingsLayout->SetSpacing(0, 0);

        SetLayout(new BGroupLayout(B_VERTICAL));
        AddChild(BGroupLayoutBuilder(B_VERTICAL, 0)
                .AddGroup(B_HORIZONTAL, 5, 1.0f)
                        .AddGroup(B_VERTICAL, 0, 1.0f)
                                .Add(fMarginView)
                                .AddGlue()
                        .End()
                        .AddGroup(B_VERTICAL, 0, 1.0f)
                                .Add(settings)
                                .AddGlue()
                        .End()
                .End()
                .AddGroup(B_HORIZONTAL, 10, 1.0f)
                        .AddGlue()
                        .Add(cancel)
                        .Add(ok)
                .End()
                .SetInsets(10, 10, 10, 10)
        );
}

inline void
swap(float *e1, float *e2)
{
        float e = *e1;
        *e1 = *e2;
        *e2 = e;
}

JobData::Orientation
PageSetupView::GetOrientation()
{
        BMenuItem *item = fOrientation->FindMarked();
        int32 orientation;
        if (item != NULL &&
                item->Message()->FindInt32("orientation", &orientation) == B_OK) {
                return (JobData::Orientation)orientation;
        } else {
                return JobData::kPortrait;
        }
}

PaperCap *
PageSetupView::GetPaperCap()
{
        BMenuItem *item = fPaper->FindMarked();
        void *pointer;
        if (item != NULL &&
                item->Message()->FindPointer("paperCap", &pointer) == B_OK) {
                return (PaperCap*)pointer;
        } else {
                return (PaperCap*)fPrinterCap->GetDefaultCap(PrinterCap::kPaper);
        }
}

bool
PageSetupView::UpdateJobData()
{
        fJobData->SetOrientation(GetOrientation());

        PaperCap *paperCap = GetPaperCap();
        BRect paper_rect = paperCap->fPaperRect;
        BRect physical_rect = paperCap->fPhysicalRect;
        fJobData->SetPaper(paperCap->fPaper);

        const char *resolutionLabel = fResolution->FindMarked()->Label();
        const ResolutionCap* resolution = static_cast<const ResolutionCap*>(
                fPrinterCap->FindCap(PrinterCap::kResolution, resolutionLabel));
        ASSERT(resolution != NULL);
        if (resolution != NULL) {
                fJobData->SetXres(resolution->fXResolution);
                fJobData->SetYres(resolution->fYResolution);
                fJobData->SetResolutionID(resolution->ID());
        }

        // rotate paper and physical rectangle if landscape orientation
        if (JobData::kLandscape == fJobData->GetOrientation()) {
                swap(&paper_rect.left, &paper_rect.top);
                swap(&paper_rect.right, &paper_rect.bottom);
                swap(&physical_rect.left, &physical_rect.top);
                swap(&physical_rect.right, &physical_rect.bottom);
        }

        // adjust printable rect by margin
        fJobData->SetMarginUnit(fMarginView->Unit());
        BRect margin = fMarginView->Margin();
        BRect printable_rect;
        printable_rect.left = paper_rect.left + margin.left;
        printable_rect.top = paper_rect.top + margin.top;
        printable_rect.right = paper_rect.right - margin.right;
        printable_rect.bottom = paper_rect.bottom - margin.bottom;

        printable_rect.left = max_c(printable_rect.left, physical_rect.left);
        printable_rect.top = max_c(printable_rect.top, physical_rect.top);
        printable_rect.right = min_c(printable_rect.right, physical_rect.right);
        printable_rect.bottom = min_c(printable_rect.bottom, physical_rect.bottom);

        float scaling = atoi(fScaling->Text());
        if (scaling <= 0.0) { // sanity check
                scaling = 100.0;
        }
        if (scaling > 1000.0) {
                scaling = 1000.0;
        }

        float scalingR = 100.0 / scaling;

        fJobData->SetScaling(scaling);
        fJobData->SetPaperRect(paper_rect);
        fJobData->SetScaledPaperRect(ScaleRect(paper_rect, scalingR));
        fJobData->SetPrintableRect(printable_rect);
        fJobData->SetScaledPrintableRect(ScaleRect(printable_rect, scalingR));
        fJobData->SetPhysicalRect(physical_rect);
        fJobData->SetScaledPhysicalRect(ScaleRect(physical_rect, scalingR));

        fJobData->Save();
        return true;
}

void
PageSetupView::MessageReceived(BMessage *msg)
{
        switch (msg->what) {
                case kMsgPaperChanged:
                case kMsgOrientationChanged:
                        {
                                JobData::Orientation orientation = GetOrientation();
                                PaperCap *paperCap = GetPaperCap();
                                float width = paperCap->fPaperRect.Width();
                                float height = paperCap->fPaperRect.Height();
                                if (orientation != JobData::kPortrait) {
                                        swap(&width, &height);
                                }
                                fMarginView->SetPageSize(width, height);
                                fMarginView->UpdateView(MARGIN_CHANGED);

                        }
                        break;
        }
}

//====================================================================

// TODO center window on screen
PageSetupDlg::PageSetupDlg(JobData *job_data, PrinterData *printer_data, const PrinterCap *printer_cap)
        : DialogWindow(BRect(100, 100, 160, 160),
                "Paper setup", B_TITLED_WINDOW_LOOK, B_MODAL_APP_WINDOW_FEEL,
                B_NOT_RESIZABLE | B_NOT_MINIMIZABLE | B_NOT_ZOOMABLE
                        | B_AUTO_UPDATE_SIZE_LIMITS | B_CLOSE_ON_ESCAPE)
{
        AddShortcut('W',B_COMMAND_KEY,new BMessage(B_QUIT_REQUESTED));

        fPageSetupView = new PageSetupView(job_data, printer_data,
                printer_cap);

        SetLayout(new BGroupLayout(B_HORIZONTAL));
        AddChild(BGroupLayoutBuilder(B_HORIZONTAL, 0)
                .Add(fPageSetupView)
        );

        SetResult(B_ERROR);
}

void
PageSetupDlg::MessageReceived(BMessage *msg)
{
        switch (msg->what) {
        case kMsgOK:
                Lock();
                fPageSetupView->UpdateJobData();
                Unlock();
                SetResult(B_NO_ERROR);
                PostMessage(B_QUIT_REQUESTED);
                break;

        case kMsgCancel:
                PostMessage(B_QUIT_REQUESTED);
                break;

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