/*
 * JobSetupDlg.cpp
 * Copyright 1999-2000 Y.Takagi. All Rights Reserved.
 */
 
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <string>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <math.h>
 
#include <Alert.h>
#include <Bitmap.h>
#include <Box.h>
#include <Button.h>
#include <CheckBox.h>
#include <Debug.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 <Slider.h>
#include <String.h>
#include <TextControl.h>
#include <TextView.h>
#include <View.h>
 
#include "HalftoneView.h"
#include "JobSetupDlg.h"
#include "JobData.h"
#include "JSDSlider.h"
#include "PagesView.h"
#include "PrinterData.h"
#include "PrinterCap.h"
#include "DbgMsg.h"
 
 
using namespace std;
 
 
struct NupCap : public EnumCap {
	NupCap(const string &label, bool isDefault, int nup)
		:
		EnumCap(label, isDefault),
		fNup(nup)
	{}
 
	int32	ID() const { return fNup; }
 
	int	fNup;
};
 
 
struct DitherCap : public EnumCap {
	DitherCap(const string &label, bool isDefault,
		Halftone::DitherType ditherType)
		:
		EnumCap(label, isDefault),
		fDitherType(ditherType)
	{}
 
	int32	ID() const { return fDitherType; }
 
	Halftone::DitherType fDitherType;
};
 
 
static const NupCap gNup1("1", true,  1);
static const NupCap gNup2("2",   false, 2);
static const NupCap gNup4("4",   false, 4);
static const NupCap gNup8("8",   false, 8);
static const NupCap gNup9("9",   false, 9);
static const NupCap gNup16("16", false, 16);
static const NupCap gNup25("25", false, 25);
static const NupCap gNup32("32", false, 32);
static const NupCap gNup36("36", false, 36);
 
 
static const DitherCap gDitherType1("Crosshatch", false, Halftone::kType1);
static const DitherCap gDitherType2("Grid", false, Halftone::kType2);
static const DitherCap gDitherType3("Stipple", false, Halftone::kType3);
static const DitherCap gDitherFloydSteinberg("Floyd-Steinberg", false,
	Halftone::kTypeFloydSteinberg);
 
 
const BaseCap *gNups[] = {
	&gNup1,
	&gNup2,
	&gNup4,
	&gNup8,
	&gNup9,
	&gNup16,
	&gNup25,
	&gNup32,
	&gNup36
};
 
 
const BaseCap *gDitherTypes[] = {
	&gDitherType1,
	&gDitherType2,
	&gDitherType3,
	&gDitherFloydSteinberg
};
 
 
static const char* kCategoryID = "id";
 
 
enum {
	kMsgRangeAll = 'JSdl',
	kMsgRangeSelection,
	kMsgCancel,
	kMsgOK,
	kMsgQuality,
	kMsgCollateChanged,
	kMsgReverseChanged,
	kMsgDuplexChanged,
	kMsgIntSliderChanged,
	kMsgDoubleSliderChanged,
	kMsgNone = 0
};
 
 
JobSetupView::JobSetupView(JobData* jobData, PrinterData* printerData,
	const PrinterCap *printerCap)
	:
	BView("jobSetup", B_WILL_DRAW),
	fCopies(NULL),
	fFromPage(NULL),
	fToPage(NULL),
	fJobData(jobData),
	fPrinterData(printerData),
	fPrinterCap(printerCap),
	fColorType(NULL),
	fDitherType(NULL),
	fGamma(NULL),
	fInkDensity(NULL),
	fHalftone(NULL),
	fAll(NULL),
	fCollate(NULL),
	fReverse(NULL),
	fPages(NULL),
	fPaperFeed(NULL),
	fDuplex(NULL),
	fNup(NULL),
	fAllPages(NULL),
	fOddNumberedPages(NULL),
	fEvenNumberedPages(NULL)
{
	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
}
 
 
BRadioButton*
JobSetupView::CreatePageSelectionItem(const char* name, const char* label,
	JobData::PageSelection pageSelection)
{
	BRadioButton* button = new BRadioButton(name, label, NULL);
	if (fJobData->GetPageSelection() == pageSelection) {
		button->SetValue(B_CONTROL_ON);
	}
	return button;
}
 
 
void
JobSetupView::AllowOnlyDigits(BTextView* textView, int maxDigits)
{
	int num;
	for (num = 0; num <= 255; num++) {
		textView->DisallowChar(num);
	}
	for (num = 0; num <= 9; num++) {
		textView->AllowChar('0' + num);
	}
	textView->SetMaxBytes(maxDigits);
}
 
 
void
JobSetupView::AttachedToWindow()
{
	// quality
	BBox* qualityBox = new BBox("quality");
	qualityBox->SetLabel("Quality");
 
	// color
	fColorType = new BPopUpMenu("color");
	fColorType->SetRadioMode(true);
	FillCapabilityMenu(fColorType, kMsgQuality, PrinterCap::kColor,
		fJobData->GetColor());
	BMenuField* colorMenuField = new BMenuField("color", "Color:", fColorType);
	fColorType->SetTargetForItems(this);
 
	if (IsHalftoneConfigurationNeeded())
		CreateHalftoneConfigurationUI();
 
	// page range
 
	BBox* pageRangeBox = new BBox("pageRange");
	pageRangeBox->SetLabel("Page range");
 
	fAll = new BRadioButton("all", "Print all Pages", new BMessage(kMsgRangeAll));
 
	BRadioButton *range = new BRadioButton("selection", "Print selected Pages:",
		new BMessage(kMsgRangeSelection));
 
	fFromPage = new BTextControl("from", "From:", "", NULL);
	fFromPage->SetAlignment(B_ALIGN_LEFT, B_ALIGN_RIGHT);
	AllowOnlyDigits(fFromPage->TextView(), 6);
 
	fToPage = new BTextControl("to", "To:", "", NULL);
	fToPage->SetAlignment(B_ALIGN_LEFT, B_ALIGN_RIGHT);
	AllowOnlyDigits(fToPage->TextView(), 6);
 
	int first_page = fJobData->GetFirstPage();
	int last_page  = fJobData->GetLastPage();
 
	if (first_page <= 1 && last_page <= 0) {
		fAll->SetValue(B_CONTROL_ON);
	} else {
		range->SetValue(B_CONTROL_ON);
		if (first_page < 1)
			first_page = 1;
		if (first_page > last_page)
			last_page = -1;
 
		BString oss1;
		oss1 << first_page;
		fFromPage->SetText(oss1.String());
 
		BString oss2;
		oss2 << last_page;
		fToPage->SetText(oss2.String());
	}
 
	fAll->SetTarget(this);
	range->SetTarget(this);
 
	// paper source
	fPaperFeed = new BPopUpMenu("");
	fPaperFeed->SetRadioMode(true);
	FillCapabilityMenu(fPaperFeed, kMsgNone, PrinterCap::kPaperSource,
		fJobData->GetPaperSource());
	BMenuField* paperSourceMenufield = new BMenuField("paperSource",
		"Paper source:", fPaperFeed);
 
	// Pages per sheet
	fNup = new BPopUpMenu("");
	fNup->SetRadioMode(true);
	FillCapabilityMenu(fNup, kMsgNone, gNups,
		sizeof(gNups) / sizeof(gNups[0]), (int)fJobData->GetNup());
	BMenuField* pagesPerSheet = new BMenuField("pagesPerSheet",
		"Pages per sheet:", fNup);
 
	// duplex
	if (fPrinterCap->Supports(PrinterCap::kPrintStyle)) {
		fDuplex = new BCheckBox("duplex", "Duplex",
			new BMessage(kMsgDuplexChanged));
		if (fJobData->GetPrintStyle() != JobData::kSimplex) {
			fDuplex->SetValue(B_CONTROL_ON);
		}
		fDuplex->SetTarget(this);
	} else {
		fDuplex = NULL;
	}
 
	// copies
	fCopies = new BTextControl("copies", "Number of copies:", "", NULL);
	AllowOnlyDigits(fCopies->TextView(), 3);
 
	BString copies;
	copies << fJobData->GetCopies();
	fCopies->SetText(copies.String());
 
	// collate
	fCollate = new BCheckBox("collate", "Collate",
		new BMessage(kMsgCollateChanged));
	if (fJobData->GetCollate()) {
		fCollate->SetValue(B_CONTROL_ON);
	}
	fCollate->SetTarget(this);
 
	// reverse
	fReverse = new BCheckBox("reverse", "Reverse order",
		new BMessage(kMsgReverseChanged));
	if (fJobData->GetReverse()) {
		fReverse->SetValue(B_CONTROL_ON);
	}
	fReverse->SetTarget(this);
 
	// pages view
	// TODO make layout API compatible
	fPages = new PagesView(BRect(0, 0, 150, 40), "pages", B_FOLLOW_ALL,
		B_WILL_DRAW);
	fPages->SetCollate(fJobData->GetCollate());
	fPages->SetReverse(fJobData->GetReverse());
	fPages->SetExplicitMinSize(BSize(150, 40));
	fPages->SetExplicitMaxSize(BSize(150, 40));
 
	// page selection
	BBox* pageSelectionBox = new BBox("pageSelection");
	pageSelectionBox->SetLabel("Page selection");
 
	fAllPages = CreatePageSelectionItem("allPages", "All pages",
		JobData::kAllPages);
	fOddNumberedPages = CreatePageSelectionItem("oddPages",
		"Odd-numbered pages", JobData::kOddNumberedPages);
	fEvenNumberedPages = CreatePageSelectionItem("evenPages",
		"Even-numbered pages", JobData::kEvenNumberedPages);
 
	fPreview = new BCheckBox("preview", "Show preview before printing", NULL);
	if (fJobData->GetShowPreview())
		fPreview->SetValue(B_CONTROL_ON);
 
	// separator line
	BBox *separator = new BBox("separator");
	separator->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 1));
 
	// buttons
	BButton* cancel = new BButton("cancel", "Cancel",
		new BMessage(kMsgCancel));
	BButton* ok = new BButton("ok", "OK", new BMessage(kMsgOK));
	ok->MakeDefault(true);
 
	if (IsHalftoneConfigurationNeeded()) {
		BGroupView* halftoneGroup = new BGroupView(B_VERTICAL, 0);
		BGroupLayout* halftoneLayout = halftoneGroup->GroupLayout();
		halftoneLayout->AddView(fHalftone);
		fHalftoneBox->AddChild(halftoneGroup);
	}
 
	BGridView* qualityGrid = new BGridView();
	BGridLayout* qualityGridLayout = qualityGrid->GridLayout();
	qualityGridLayout->AddItem(colorMenuField->CreateLabelLayoutItem(), 0, 0);
	qualityGridLayout->AddItem(colorMenuField->CreateMenuBarLayoutItem(), 1, 0);
	if (IsHalftoneConfigurationNeeded()) {
		qualityGridLayout->AddItem(fDitherMenuField->CreateLabelLayoutItem(),
			0, 1);
		qualityGridLayout->AddItem(fDitherMenuField->CreateMenuBarLayoutItem(),
			1, 1);
		qualityGridLayout->AddView(fGamma, 0, 2, 2);
		qualityGridLayout->AddView(fInkDensity, 0, 3, 2);
		qualityGridLayout->AddView(fHalftoneBox, 0, 4, 2);
	} else {
		AddDriverSpecificSettings(qualityGridLayout, 1);
	}
	qualityGridLayout->SetSpacing(0, 0);
	qualityGridLayout->SetInsets(5, 5, 5, 5);
	qualityBox->AddChild(qualityGrid);
	// TODO put qualityGrid in a scroll view
	// the layout of the box surrounding the scroll view using the following
	// code is not correct; the box still has the size of the qualityGird;
	// and the scroll view is vertically centered inside the box!
	//BScrollView* qualityScroller = new BScrollView("qualityScroller",
	//	qualityGrid, 0, false, true);
	//qualityScroller->SetExplicitMaxSize(BSize(500, 500));
	//qualityBox->AddChild(qualityScroller);
 
	BGridView* pageRangeGrid = new BGridView();
	BGridLayout* pageRangeLayout = pageRangeGrid->GridLayout();
	pageRangeLayout->AddItem(fFromPage->CreateLabelLayoutItem(), 0, 0);
	pageRangeLayout->AddItem(fFromPage->CreateTextViewLayoutItem(), 1, 0);
	pageRangeLayout->AddItem(fToPage->CreateLabelLayoutItem(), 0, 1);
	pageRangeLayout->AddItem(fToPage->CreateTextViewLayoutItem(), 1, 1);
	pageRangeLayout->SetInsets(0, 0, 0, 0);
	pageRangeLayout->SetSpacing(0, 0);
 
	BGroupView* pageRangeGroup = new BGroupView(B_VERTICAL, 0);
	BGroupLayout* pageRangeGroupLayout = pageRangeGroup->GroupLayout();
	pageRangeGroupLayout->AddView(fAll);
	pageRangeGroupLayout->AddView(range);
	pageRangeGroupLayout->AddView(pageRangeGrid);
	pageRangeGroupLayout->SetInsets(5, 5, 5, 5);
	pageRangeBox->AddChild(pageRangeGroup);
 
	BGridView* settings = new BGridView();
	BGridLayout* settingsLayout = settings->GridLayout();
	settingsLayout->AddItem(paperSourceMenufield->CreateLabelLayoutItem(), 0,
		0);
	settingsLayout->AddItem(paperSourceMenufield->CreateMenuBarLayoutItem(), 1,
		0);
	settingsLayout->AddItem(pagesPerSheet->CreateLabelLayoutItem(), 0, 1);
	settingsLayout->AddItem(pagesPerSheet->CreateMenuBarLayoutItem(), 1, 1);
	int row = 2;
	if (fDuplex != NULL) {
		settingsLayout->AddView(fDuplex, 0, row, 2);
		row ++;
	}
	settingsLayout->AddItem(fCopies->CreateLabelLayoutItem(), 0, row);
	settingsLayout->AddItem(fCopies->CreateTextViewLayoutItem(), 1, row);
	settingsLayout->SetSpacing(0, 0);
 
 
	BGroupView* pageSelectionGroup = new BGroupView(B_VERTICAL, 0);
	BGroupLayout* groupLayout = pageSelectionGroup->GroupLayout();
	groupLayout->AddView(fAllPages);
	groupLayout->AddView(fOddNumberedPages);
	groupLayout->AddView(fEvenNumberedPages);
	groupLayout->SetInsets(5, 5, 5, 5);
	pageSelectionBox->AddChild(pageSelectionGroup);
 
	SetLayout(new BGroupLayout(B_VERTICAL));
	AddChild(BGroupLayoutBuilder(B_VERTICAL, 0)
		.AddGroup(B_HORIZONTAL, 10, 1.0f)
			.AddGroup(B_VERTICAL, 10, 1.0f)
				.Add(qualityBox)
				.Add(pageRangeBox)
				.AddGlue()
			.End()
			.AddGroup(B_VERTICAL, 0, 1.0f)
				.Add(settings)
				.AddStrut(5)
				.Add(fCollate)
				.Add(fReverse)
				.Add(fPages)
				.AddStrut(5)
				.Add(pageSelectionBox)
				.AddGlue()
			.End()
		.End()
		.Add(fPreview)
		.AddGlue()
		.Add(separator)
		.AddGroup(B_HORIZONTAL, 10, 1.0f)
			.AddGlue()
			.Add(cancel)
			.Add(ok)
		.End()
		.SetInsets(0, 0, 0, 0)
	);
 
	UpdateHalftonePreview();
 
	UpdateButtonEnabledState();
}
 
 
bool
JobSetupView::IsHalftoneConfigurationNeeded()
{
	return fPrinterCap->Supports(PrinterCap::kHalftone);
}
 
 
void
JobSetupView::CreateHalftoneConfigurationUI()
{
	// dither type
	fDitherType = new BPopUpMenu("");
	fDitherType->SetRadioMode(true);
	FillCapabilityMenu(fDitherType, kMsgQuality, gDitherTypes,
		sizeof(gDitherTypes) / sizeof(gDitherTypes[0]),
		(int)fJobData->GetDitherType());
	fDitherMenuField = new BMenuField("dithering", "Dot Pattern:",
		fDitherType);
	fDitherType->SetTargetForItems(this);
 
	// halftone preview view
	fHalftoneBox = new BBox("halftoneBox");
	fHalftoneBox->SetBorder(B_PLAIN_BORDER);
 
	// TODO make layout compatible
	BSize size(240, 14 * 4);
	BRect rect(0, 0, size.width, size.height);
	fHalftone = new HalftoneView(rect, "halftone",
		B_FOLLOW_ALL, B_WILL_DRAW);
	fHalftone->SetExplicitMinSize(size);
	fHalftone->SetExplicitMaxSize(size);
 
	// gamma
	fGamma = new JSDSlider("gamma", "Gamma", new BMessage(kMsgQuality),
		-300, 300);
 
	fGamma->SetLimitLabels("Lighter", "Darker");
	fGamma->SetValue((int32)(100 * log(fJobData->GetGamma()) / log(2.0)));
	fGamma->SetHashMarks(B_HASH_MARKS_BOTH);
	fGamma->SetHashMarkCount(7);
	fGamma->SetModificationMessage(new BMessage(kMsgQuality));
	fGamma->SetTarget(this);
 
	// ink density
	fInkDensity = new JSDSlider("inkDensity", "Ink usage",
		new BMessage(kMsgQuality), 0, 127);
 
	fInkDensity->SetLimitLabels("Min", "Max");
	fInkDensity->SetValue((int32)fJobData->GetInkDensity());
	fInkDensity->SetHashMarks(B_HASH_MARKS_BOTH);
	fInkDensity->SetHashMarkCount(10);
	fInkDensity->SetModificationMessage(new BMessage(kMsgQuality));
	fInkDensity->SetTarget(this);
}
 
 
void
JobSetupView::AddDriverSpecificSettings(BGridLayout* gridLayout, int row)
{
	if (!fPrinterCap->Supports(PrinterCap::kDriverSpecificCapabilities))
		return;
 
	int count = fPrinterCap->CountCap(PrinterCap::kDriverSpecificCapabilities);
	const BaseCap** capabilities = fPrinterCap->GetCaps(
		PrinterCap::kDriverSpecificCapabilities);
 
	for (int i = 0; i < count; i ++) {
		const DriverSpecificCap* capability =
			static_cast<const DriverSpecificCap*>(capabilities[i]);
 
		switch (capability->fType) {
			case DriverSpecificCap::kList:
				AddPopUpMenu(capability, gridLayout, row);
				break;
			case DriverSpecificCap::kBoolean:
				AddCheckBox(capability, gridLayout, row);
				break;
			case DriverSpecificCap::kIntRange:
			case DriverSpecificCap::kIntDimension:
				AddIntSlider(capability, gridLayout, row);
				break;
			case DriverSpecificCap::kDoubleRange:
				AddDoubleSlider(capability, gridLayout, row);
				break;
 
		}
	}
}
 
 
void
JobSetupView::AddPopUpMenu(const DriverSpecificCap* capability,
	BGridLayout* gridLayout, int& row)
{
	const char* label = capability->fLabel.c_str();
	BPopUpMenu* popUpMenu = new BPopUpMenu(label);
	popUpMenu->SetRadioMode(true);
 
	PrinterCap::CapID category = static_cast<PrinterCap::CapID>(
		capability->ID());
 
	const BaseCap** categoryCapabilities = fPrinterCap->GetCaps(category);
 
	int categoryCount = fPrinterCap->CountCap(category);
 
	string value = GetDriverSpecificValue(category, capability->Key());
	PrinterCap::KeyPredicate predicate(value.c_str());
 
	FillCapabilityMenu(popUpMenu, kMsgNone, categoryCapabilities,
		categoryCount, predicate);
 
	BString menuLabel = label;
	menuLabel << ":";
	BMenuField* menuField = new BMenuField(label, menuLabel.String(),
		popUpMenu);
	popUpMenu->SetTargetForItems(this);
 
	gridLayout->AddItem(menuField->CreateLabelLayoutItem(),
		0, row);
	gridLayout->AddItem(menuField->CreateMenuBarLayoutItem(),
		1, row);
	row ++;
 
	fDriverSpecificPopUpMenus[category] = popUpMenu;
}
 
 
void
JobSetupView::AddCheckBox(const DriverSpecificCap* capability,
	BGridLayout* gridLayout, int& row)
{
	PrinterCap::CapID category = static_cast<PrinterCap::CapID>(
		capability->ID());
	const BooleanCap* booleanCap = fPrinterCap->FindBooleanCap(category);
	if (booleanCap == NULL) {
		fprintf(stderr, "Internal error: BooleanCap for '%s' not found!\n",
			capability->Label());
		return;
	}
 
	const char* key = capability->Key();
	BString name;
	name << "pds_" << key;
	BCheckBox* checkBox = new BCheckBox(name.String(), capability->Label(),
		NULL);
 
	bool value = booleanCap->DefaultValue();
	if (fJobData->Settings().HasBoolean(key))
		value = fJobData->Settings().GetBoolean(key);
	if (value)
		checkBox->SetValue(B_CONTROL_ON);
 
	gridLayout->AddView(checkBox, 0, row, 2);
	row ++;
 
	fDriverSpecificCheckBoxes[capability->Key()] = checkBox;
}
 
 
void
JobSetupView::AddIntSlider(const DriverSpecificCap* capability,
	BGridLayout* gridLayout, int& row)
{
	PrinterCap::CapID category = static_cast<PrinterCap::CapID>(
		capability->ID());
	const IntRangeCap* range = fPrinterCap->FindIntRangeCap(category);
	if (range == NULL) {
		fprintf(stderr, "Internal error: IntRangeCap for '%s' not found!\n",
			capability->Label());
		return;
	}
 
	const char* label = capability->Label();
	const char* key = capability->Key();
	BString name;
	name << "pds_" << key;
	BMessage* message = new BMessage(kMsgIntSliderChanged);
	message->AddInt32(kCategoryID, category);
	BSlider* slider = new BSlider(name.String(), label,
		message, 0, 1000, B_HORIZONTAL);
	slider->SetModificationMessage(new BMessage(*message));
	slider->SetTarget(this);
 
	int32 value = range->DefaultValue();
	if (fJobData->Settings().HasInt(key))
		value = fJobData->Settings().GetInt(key);
	float position = (value - range->Lower()) /
		(range->Upper() - range->Lower());
	slider->SetPosition(position);
 
	gridLayout->AddView(slider, 0, row, 2);
	row ++;
 
	IntRange intRange(label, key, range, slider);
	fDriverSpecificIntSliders[category] = intRange;
	intRange.UpdateLabel();
}
 
 
void
JobSetupView::AddDoubleSlider(const DriverSpecificCap* capability,
	BGridLayout* gridLayout, int& row)
{
	PrinterCap::CapID category = static_cast<PrinterCap::CapID>(
		capability->ID());
	const DoubleRangeCap* range = fPrinterCap->FindDoubleRangeCap(category);
	if (range == NULL) {
		fprintf(stderr, "Internal error: DoubleRangeCap for '%s' not found!\n",
			capability->Label());
		return;
	}
 
	const char* label = capability->Label();
	const char* key = capability->Key();
	BString name;
	name << "pds_" << key;
	BMessage* message = new BMessage(kMsgDoubleSliderChanged);
	message->AddInt32(kCategoryID, category);
	BSlider* slider = new BSlider(name.String(), label,
		message, 0, 1000, B_HORIZONTAL);
	slider->SetModificationMessage(new BMessage(*message));
	slider->SetTarget(this);
 
	double value = range->DefaultValue();
	if (fJobData->Settings().HasDouble(key))
		value = fJobData->Settings().GetDouble(key);
	float position = static_cast<float>((value - range->Lower()) /
		(range->Upper() - range->Lower()));
	slider->SetPosition(position);
 
	gridLayout->AddView(slider, 0, row, 2);
	row ++;
 
	DoubleRange doubleRange(label, key, range, slider);
	fDriverSpecificDoubleSliders[category] = doubleRange;
	doubleRange.UpdateLabel();
}
 
 
string
JobSetupView::GetDriverSpecificValue(PrinterCap::CapID category,
	const char* key)
{
	if (fJobData->Settings().HasString(key))
		return fJobData->Settings().GetString(key);
 
	const EnumCap* defaultCapability = fPrinterCap->GetDefaultCap(category);
	return defaultCapability->fKey;
}
 
 
template<typename Predicate>
void
JobSetupView::FillCapabilityMenu(BPopUpMenu* menu, uint32 message,
	const BaseCap** capabilities, int count, Predicate& predicate)
{
	bool marked = false;
 
	BMenuItem* firstItem = NULL;
	BMenuItem* defaultItem = NULL;
	BMenuItem* item = NULL;
	while (count--) {
		const EnumCap* capability = dynamic_cast<const EnumCap*>(*capabilities);
		if (message != kMsgNone)
			item = new BMenuItem(capability->fLabel.c_str(),
				new BMessage(message));
		else
			item = new BMenuItem(capability->fLabel.c_str(), NULL);
 
		menu->AddItem(item);
 
		if (firstItem == NULL)
			firstItem = item;
 
		if (capability->fIsDefault)
			defaultItem = item;
 
 
		if (predicate(capability)) {
			item->SetMarked(true);
			marked = true;
		}
 
		capabilities++;
	}
 
	if (marked)
		return;
 
	if (defaultItem != NULL)
		defaultItem->SetMarked(true);
	else if (firstItem != NULL)
		firstItem->SetMarked(true);
}
 
 
void
JobSetupView::FillCapabilityMenu(BPopUpMenu* menu, uint32 message,
	PrinterCap::CapID category, int id)
{
	PrinterCap::IDPredicate predicate(id);
	int count = fPrinterCap->CountCap(category);
	const BaseCap **capabilities = fPrinterCap->GetCaps(category);
	FillCapabilityMenu(menu, message, capabilities, count, predicate);
}
 
 
void
JobSetupView::FillCapabilityMenu(BPopUpMenu* menu, uint32 message,
	const BaseCap** capabilities, int count, int id)
{
	PrinterCap::IDPredicate predicate(id);
	FillCapabilityMenu(menu, message, capabilities, count, predicate);
}
 
 
int
JobSetupView::GetID(const BaseCap** capabilities, int count, const char* label,
	int defaultValue)
{
	while (count--) {
		const EnumCap* capability =
			dynamic_cast<const EnumCap*>(*capabilities);
		if (capability == NULL)
			break;
 
		if (capability->fLabel == label)
			return capability->ID();
	}
	return defaultValue;
}
 
 
void
JobSetupView::UpdateButtonEnabledState()
{
	bool pageRangeEnabled = fAll->Value() != B_CONTROL_ON;
	fFromPage->SetEnabled(pageRangeEnabled);
	fToPage->SetEnabled(pageRangeEnabled);
 
	bool pageSelectionEnabled = fDuplex == NULL ||
		fDuplex->Value() != B_CONTROL_ON;
	fAllPages->SetEnabled(pageSelectionEnabled);
	fOddNumberedPages->SetEnabled(pageSelectionEnabled);
	fEvenNumberedPages->SetEnabled(pageSelectionEnabled);
}
 
 
void
JobSetupView::MessageReceived(BMessage* message)
{
	switch (message->what) {
	case kMsgRangeAll:
	case kMsgRangeSelection:
	case kMsgDuplexChanged:
		UpdateButtonEnabledState();
		break;
 
	case kMsgQuality:
		UpdateHalftonePreview();
		break;
 
	case kMsgCollateChanged:
		fPages->SetCollate(fCollate->Value() == B_CONTROL_ON);
		break;
 
	case kMsgReverseChanged:
		fPages->SetReverse(fReverse->Value() == B_CONTROL_ON);
		break;
 
	case kMsgIntSliderChanged:
		UpdateIntSlider(message);
		break;
 
	case kMsgDoubleSliderChanged:
		UpdateDoubleSlider(message);
		break;
	}
}
 
 
void
JobSetupView::UpdateHalftonePreview()
{
	if (!IsHalftoneConfigurationNeeded())
		return;
 
	fHalftone->Preview(Gamma(), InkDensity(), DitherType(),
		Color() != JobData::kMonochrome);
}
 
 
void
JobSetupView::UpdateIntSlider(BMessage* message)
{
	int32 id;
	if (message->FindInt32(kCategoryID, &id) != B_OK)
		return;
	PrinterCap::CapID capID = static_cast<PrinterCap::CapID>(id);
	fDriverSpecificIntSliders[capID].UpdateLabel();
}
 
 
void
JobSetupView::UpdateDoubleSlider(BMessage* message)
{
	int32 id;
	if (message->FindInt32(kCategoryID, &id) != B_OK)
		return;
	PrinterCap::CapID capID = static_cast<PrinterCap::CapID>(id);
	fDriverSpecificDoubleSliders[capID].UpdateLabel();
}
 
 
JobData::Color
JobSetupView::Color()
{
	const char *label = fColorType->FindMarked()->Label();
	const BaseCap* capability = fPrinterCap->FindCap(PrinterCap::kColor, label);
	if (capability == NULL)
		return JobData::kMonochrome;
 
	const ColorCap* colorCap = static_cast<const ColorCap*>(capability);
	return colorCap->fColor;
}
 
 
Halftone::DitherType
JobSetupView::DitherType()
{
	const char *label = fDitherType->FindMarked()->Label();
	int id = GetID(gDitherTypes, sizeof(gDitherTypes) / sizeof(gDitherTypes[0]),
		label, Halftone::kTypeFloydSteinberg);
	return static_cast<Halftone::DitherType>(id);
}
 
float
JobSetupView::Gamma()
{
	const float value = (float)fGamma->Value();
	return pow(2.0, value / 100.0);
}
 
 
float
JobSetupView::InkDensity()
{
	const float value = (float)(127 - fInkDensity->Value());
	return value;
}
 
 
JobData::PaperSource
JobSetupView::PaperSource()
{
	const char *label = fPaperFeed->FindMarked()->Label();
	const BaseCap* capability = fPrinterCap->FindCap(PrinterCap::kPaperSource,
		label);
 
	if (capability == NULL)
		capability = fPrinterCap->GetDefaultCap(PrinterCap::kPaperSource);
	return static_cast<const PaperSourceCap*>(capability)->fPaperSource;
 
}
 
bool
JobSetupView::UpdateJobData()
{
	fJobData->SetShowPreview(fPreview->Value() == B_CONTROL_ON);
	fJobData->SetColor(Color());
	if (IsHalftoneConfigurationNeeded()) {
		fJobData->SetGamma(Gamma());
		fJobData->SetInkDensity(InkDensity());
		fJobData->SetDitherType(DitherType());
	}
 
	int first_page;
	int last_page;
 
	if (B_CONTROL_ON == fAll->Value()) {
		first_page = 1;
		last_page  = -1;
	} else {
		first_page = atoi(fFromPage->Text());
		last_page  = atoi(fToPage->Text());
	}
 
	fJobData->SetFirstPage(first_page);
	fJobData->SetLastPage(last_page);
 
	fJobData->SetPaperSource(PaperSource());
 
	fJobData->SetNup(GetID(gNups, sizeof(gNups) / sizeof(gNups[0]),
		fNup->FindMarked()->Label(), 1));
 
	if (fPrinterCap->Supports(PrinterCap::kPrintStyle)) {
		fJobData->SetPrintStyle((B_CONTROL_ON == fDuplex->Value())
			? JobData::kDuplex : JobData::kSimplex);
	}
 
	fJobData->SetCopies(atoi(fCopies->Text()));
 
	fJobData->SetCollate(B_CONTROL_ON == fCollate->Value());
	fJobData->SetReverse(B_CONTROL_ON == fReverse->Value());
 
	JobData::PageSelection pageSelection = JobData::kAllPages;
	if (fOddNumberedPages->Value() == B_CONTROL_ON)
		pageSelection = JobData::kOddNumberedPages;
	if (fEvenNumberedPages->Value() == B_CONTROL_ON)
		pageSelection = JobData::kEvenNumberedPages;
	fJobData->SetPageSelection(pageSelection);
 
	{
		std::map<PrinterCap::CapID, BPopUpMenu*>::iterator it =
			fDriverSpecificPopUpMenus.begin();
		for(; it != fDriverSpecificPopUpMenus.end(); it++) {
			PrinterCap::CapID category = it->first;
			BPopUpMenu* popUpMenu = it->second;
			const char* key = fPrinterCap->FindCap(
				PrinterCap::kDriverSpecificCapabilities, (int)category)->Key();
			const char* label = popUpMenu->FindMarked()->Label();
			const char* value = static_cast<const EnumCap*>(fPrinterCap->
				FindCap(category, label))->Key();
			fJobData->Settings().SetString(key, value);
		}
	}
 
	{
		std::map<string, BCheckBox*>::iterator it =
			fDriverSpecificCheckBoxes.begin();
		for(; it != fDriverSpecificCheckBoxes.end(); it++) {
			const char* key = it->first.c_str();
			BCheckBox* checkBox = it->second;
			bool value = checkBox->Value() == B_CONTROL_ON;
			fJobData->Settings().SetBoolean(key, value);
		}
	}
 
	{
		std::map<PrinterCap::CapID, IntRange>::iterator it =
			fDriverSpecificIntSliders.begin();
		for(; it != fDriverSpecificIntSliders.end(); it++) {
			IntRange& range = it->second;
			fJobData->Settings().SetInt(range.Key(), range.Value());
		}
	}
 
	{
		std::map<PrinterCap::CapID, DoubleRange>::iterator it =
			fDriverSpecificDoubleSliders.begin();
		for(; it != fDriverSpecificDoubleSliders.end(); it++) {
			DoubleRange& range = it->second;
			fJobData->Settings().SetDouble(range.Key(), range.Value());
		}
	}
 
	fJobData->Save();
	return true;
}
 
 
JobSetupDlg::JobSetupDlg(JobData* jobData, PrinterData* printerData,
	const PrinterCap* printerCap)
	:
	DialogWindow(BRect(100, 100, 200, 200), "Print job setup",
		B_TITLED_WINDOW_LOOK, B_MODAL_APP_WINDOW_FEEL,
		B_NOT_RESIZABLE | B_NOT_MINIMIZABLE | B_NOT_ZOOMABLE
			| B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS
			| B_CLOSE_ON_ESCAPE)
{
	SetResult(B_ERROR);
	AddShortcut('W', B_COMMAND_KEY, new BMessage(B_QUIT_REQUESTED));
 
	fJobSetup = new JobSetupView(jobData, printerData, printerCap);
	SetLayout(new BGroupLayout(B_VERTICAL));
	AddChild(BGroupLayoutBuilder(B_VERTICAL, 0)
		.Add(fJobSetup)
		.SetInsets(10, 10, 10, 10)
	);
}
 
 
void
JobSetupDlg::MessageReceived(BMessage* message)
{
	switch (message->what) {
	case kMsgOK:
		fJobSetup->UpdateJobData();
		SetResult(B_NO_ERROR);
		PostMessage(B_QUIT_REQUESTED);
		break;
 
	case kMsgCancel:
		PostMessage(B_QUIT_REQUESTED);
		break;
 
	default:
		DialogWindow::MessageReceived(message);
		break;
	}
}

V773 Visibility scope of the 'pagesPerSheet' pointer was exited without releasing the memory. A memory leak is possible.

V773 Visibility scope of the 'paperSourceMenufield' pointer was exited without releasing the memory. A memory leak is possible.

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fDitherMenuField, fHalftoneBox, fPreview.

V773 Visibility scope of the 'colorMenuField' pointer was exited without releasing the memory. A memory leak is possible.