1/*
2 * PrinterDriver.cpp
3 * Copyright 1999-2000 Y.Takagi. All Rights Reserved.
4 * Copyright 2004 Michael Pfeiffer.
5 */
6
7#include "PrinterDriver.h"
8
9#include <fs_attr.h> // for attr_info
10#include <DataIO.h>
11#include <File.h>
12#include <FindDirectory.h>
13#include <Message.h>
14#include <Node.h>
15#include <Path.h>
16#include <StackOrHeapArray.h>
17#include <String.h>
18
19#include "AboutBox.h"
20#include "AddPrinterDlg.h"
21#include "DbgMsg.h"
22#include "Exports.h"
23#include "GraphicsDriver.h"
24#include "PrinterCap.h"
25#include "PrinterData.h"
26#include "UIDriver.h"
27#include "Preview.h"
28#include "PrintUtils.h"
29
30
31// Implementation of PrinterDriver
32
33PrinterDriver::PrinterDriver(BNode* spoolFolder)
34	:
35	fSpoolFolder(spoolFolder),
36	fPrinterData(NULL),
37	fPrinterCap(NULL),
38	fGraphicsDriver(NULL)
39{
40}
41
42PrinterDriver::~PrinterDriver()
43{
44	delete fGraphicsDriver;
45	fGraphicsDriver = NULL;
46
47	delete fPrinterCap;
48	fPrinterCap = NULL;
49
50	delete fPrinterData;
51	fPrinterData = NULL;
52}
53
54
55PrinterData*
56PrinterDriver::InstantiatePrinterData(BNode* node)
57{
58	return new PrinterData(node);
59}
60
61void
62PrinterDriver::InitPrinterDataAndCap() {
63	fPrinterData = InstantiatePrinterData(fSpoolFolder);
64	fPrinterData->Load();
65	// NOTE: moved the load above from the constructor of PrinterData as
66	//   we're inheriting from PrinterData and want our overridden versions
67	//   of load to be called
68	fPrinterCap = InstantiatePrinterCap(fPrinterData);
69}
70
71void
72PrinterDriver::About()
73{
74	BString copyright;
75	copyright = "libprint Copyright �� 1999-2000 Y.Takagi\n";
76	copyright << GetCopyright();
77	copyright << "All Rights Reserved.";
78
79	AboutBox app(GetSignature(), GetDriverName(), GetVersion(), copyright.String());
80	app.Run();
81}
82
83char*
84PrinterDriver::AddPrinter(char* printerName)
85{
86	// print_server has created a spool folder with name printerName in
87	// folder B_USER_PRINTERS_DIRECTORY. It can be used to store
88	// settings in the folder attributes.
89	DBGMSG((">%s: add_printer\n", GetDriverName()));
90	DBGMSG(("\tprinter_name: %s\n", printerName));
91	DBGMSG(("<%s: add_printer\n", GetDriverName()));
92
93	if (fPrinterCap->Supports(PrinterCap::kProtocolClass)) {
94		if (fPrinterCap->CountCap(PrinterCap::kProtocolClass) > 1) {
95			AddPrinterDlg *dialog;
96			dialog = new AddPrinterDlg(fPrinterData, fPrinterCap);
97			if (dialog->Go() != B_OK) {
98				// dialog canceled
99				return NULL;
100			}
101		} else {
102			const ProtocolClassCap* pcCap;
103			pcCap = (const ProtocolClassCap*)fPrinterCap->GetDefaultCap(
104				PrinterCap::kProtocolClass);
105			if (pcCap != NULL) {
106				fPrinterData->SetProtocolClass(pcCap->fProtocolClass);
107				fPrinterData->Save();
108			}
109		}
110	}
111	return printerName;
112}
113
114BMessage*
115PrinterDriver::ConfigPage(BMessage* settings)
116{
117	DBGMSG((">%s: config_page\n", GetDriverName()));
118	DUMP_BMESSAGE(settings);
119	DUMP_BNODE(fSpoolFolder);
120
121	BMessage pageSettings(*settings);
122	_MergeWithPreviousSettings(kAttrPageSettings, &pageSettings);
123	UIDriver uiDriver(&pageSettings, fPrinterData, fPrinterCap);
124	BMessage *result = uiDriver.ConfigPage();
125	_WriteSettings(kAttrPageSettings, result);
126
127	DUMP_BMESSAGE(result);
128	DBGMSG(("<%s: config_page\n", GetDriverName()));
129	return result;
130}
131
132BMessage*
133PrinterDriver::ConfigJob(BMessage* settings)
134{
135	DBGMSG((">%s: config_job\n", GetDriverName()));
136	DUMP_BMESSAGE(settings);
137	DUMP_BNODE(fSpoolFolder);
138
139	BMessage jobSettings(*settings);
140	_MergeWithPreviousSettings(kAttrJobSettings, &jobSettings);
141	UIDriver uiDriver(&jobSettings, fPrinterData, fPrinterCap);
142	BMessage *result = uiDriver.ConfigJob();
143	_WriteSettings(kAttrJobSettings, result);
144
145	DUMP_BMESSAGE(result);
146	DBGMSG(("<%s: config_job\n", GetDriverName()));
147	return result;
148}
149
150BMessage*
151PrinterDriver::TakeJob(BFile* printJob, BMessage* settings)
152{
153	DBGMSG((">%s: take_job\n", GetDriverName()));
154	DUMP_BMESSAGE(settings);
155	DUMP_BNODE(fSpoolFolder);
156
157	fGraphicsDriver = InstantiateGraphicsDriver(settings, fPrinterData, fPrinterCap);
158	const JobData* jobData = fGraphicsDriver->GetJobData(printJob);
159	if (jobData != NULL && jobData->GetShowPreview()) {
160		off_t offset = printJob->Position();
161		PreviewWindow *preview = new PreviewWindow(printJob, true);
162		if (preview->Go() != B_OK) {
163			return new BMessage('okok');
164		}
165		printJob->Seek(offset, SEEK_SET);
166	}
167	BMessage *result = fGraphicsDriver->TakeJob(printJob);
168
169	DUMP_BMESSAGE(result);
170	DBGMSG(("<%s: take_job\n", GetDriverName()));
171	return result;
172}
173
174// read settings from spool folder attribute
175bool
176PrinterDriver::_ReadSettings(const char* attrName, BMessage* settings)
177{
178	attr_info info;
179	ssize_t size;
180
181	settings->MakeEmpty();
182
183	if (fSpoolFolder->GetAttrInfo(attrName, &info) == B_OK && info.size > 0) {
184		BStackOrHeapArray<char, 0> data(info.size);
185		if (!data.IsValid())
186			return false;
187		size = fSpoolFolder->ReadAttr(attrName, B_MESSAGE_TYPE, 0, data, info.size);
188		if (size == info.size && settings->Unflatten(data) == B_OK) {
189			return true;
190		}
191	}
192	return false;
193}
194
195// write settings to spool folder attribute
196void
197PrinterDriver::_WriteSettings(const char* attrName, BMessage* settings)
198{
199	if (settings == NULL || settings->what != 'okok') return;
200
201	status_t status;
202	BMallocIO data;
203	status = settings->Flatten(&data);
204
205	if (status == B_OK) {
206		fSpoolFolder->WriteAttr(attrName, B_MESSAGE_TYPE, 0, data.Buffer(),
207			data.BufferLength());
208	}
209}
210
211// read settings from spool folder attribute and merge them to current settings
212void
213PrinterDriver::_MergeWithPreviousSettings(const char* attrName, BMessage* settings)
214{
215	if (settings == NULL) return;
216
217	BMessage stored;
218	if (_ReadSettings(attrName, &stored)) {
219		AddFields(&stored, settings);
220		*settings = stored;
221	}
222}
223
224// Implementation of PrinterDriverInstance
225
226class PrinterDriverInstance
227{
228public:
229	PrinterDriverInstance(BNode* spoolFolder = NULL);
230	~PrinterDriverInstance();
231	PrinterDriver* GetPrinterDriver() { return fInstance; }
232
233private:
234	PrinterDriver* fInstance;
235};
236
237PrinterDriverInstance::PrinterDriverInstance(BNode* spoolFolder)
238{
239	fInstance = instantiate_printer_driver(spoolFolder);
240	if (fInstance != NULL) {
241		fInstance->InitPrinterDataAndCap();
242	}
243}
244
245PrinterDriverInstance::~PrinterDriverInstance()
246{
247	delete fInstance;
248	fInstance = NULL;
249}
250
251
252// printer driver add-on functions
253
254char *add_printer(char *printerName)
255{
256	BPath path;
257	BNode folder;
258	BNode* spoolFolder = NULL;
259	// get spool folder
260	if (find_directory(B_USER_PRINTERS_DIRECTORY, &path) == B_OK &&
261		path.Append(printerName) == B_OK &&
262		folder.SetTo(path.Path()) == B_OK) {
263		spoolFolder = &folder;
264	}
265
266	PrinterDriverInstance instance(spoolFolder);
267	return instance.GetPrinterDriver()->AddPrinter(printerName);
268}
269
270BMessage *config_page(BNode *spoolFolder, BMessage *settings)
271{
272	PrinterDriverInstance instance(spoolFolder);
273	return instance.GetPrinterDriver()->ConfigPage(settings);
274}
275
276BMessage *config_job(BNode *spoolFolder, BMessage *settings)
277{
278	PrinterDriverInstance instance(spoolFolder);
279	return instance.GetPrinterDriver()->ConfigJob(settings);
280}
281
282BMessage *take_job(BFile *printJob, BNode *spoolFolder, BMessage *settings)
283{
284	PrinterDriverInstance instance(spoolFolder);
285	return instance.GetPrinterDriver()->TakeJob(printJob, settings);
286}
287
288// main entry if printer driver is launched directly
289
290int main(int argc, char* argv[])
291{
292	PrinterDriverInstance instance;
293	instance.GetPrinterDriver()->About();
294	return 0;
295}
296