1/*
2
3DocInfoWindow.cpp
4
5Copyright (c) 2002 OpenBeOS.
6
7Author:
8	Michael Pfeiffer
9
10Permission is hereby granted, free of charge, to any person obtaining a copy of
11this software and associated documentation files (the "Software"), to deal in
12the Software without restriction, including without limitation the rights to
13use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
14of the Software, and to permit persons to whom the Software is furnished to do
15so, subject to the following conditions:
16
17The above copyright notice and this permission notice shall be included in all
18copies or substantial portions of the Software.
19
20THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26THE SOFTWARE.
27
28*/
29
30#include "DocInfoWindow.h"
31#include "PrintUtils.h"
32
33
34#include <Box.h>
35#include <Button.h>
36#include <Menu.h>
37#include <MenuItem.h>
38#include <MenuField.h>
39#include <Message.h>
40#include <Screen.h>
41#include <ScrollView.h>
42#include <TabView.h>
43#include <TextControl.h>
44
45
46#include <ctype.h>
47
48
49// #pragma mark -- DocInfoWindow
50
51
52DocInfoWindow::DocInfoWindow(BMessage *docInfo)
53	: HWindow(BRect(0, 0, 400, 250), "Document Information", B_TITLED_WINDOW_LOOK,
54		B_MODAL_APP_WINDOW_FEEL, B_NOT_MINIMIZABLE | B_CLOSE_ON_ESCAPE),
55	fDocInfo(docInfo)
56{
57	BRect bounds(Bounds());
58	BView *background = new BView(bounds, "bachground", B_FOLLOW_ALL, B_WILL_DRAW);
59	background->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
60	AddChild(background);
61
62	bounds.InsetBy(10.0, 10.0);
63	BButton *button = new BButton(bounds, "ok", "OK", new BMessage(OK_MSG),
64		B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
65	background->AddChild(button);
66	button->ResizeToPreferred();
67	button->MoveTo(bounds.right - button->Bounds().Width(),
68		bounds.bottom - button->Bounds().Height());
69
70	BRect buttonFrame(button->Frame());
71	button = new BButton(buttonFrame, "cancel", "Cancel", new BMessage(CANCEL_MSG),
72		B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
73	background->AddChild(button);
74	button->ResizeToPreferred();
75	button->MoveTo(buttonFrame.left - (button->Bounds().Width() + 10.0),
76		buttonFrame.top);
77
78	bounds.bottom = buttonFrame.top - 10.0;
79
80#if HAVE_FULLVERSION_PDF_LIB
81	BString permissions;
82	if (_DocInfo()->FindString("permissions", &permissions) == B_OK)
83		fPermissions.Decode(permissions.String());
84
85	BTabView *tabView = new BTabView(bounds, "tabView");
86	_SetupDocInfoView(_CreateTabPanel(tabView, "Information"));
87	_SetupPasswordView(_CreateTabPanel(tabView, "Password"));
88	_SetupPermissionsView(_CreateTabPanel(tabView, "Permissions"));
89
90	background->AddChild(tabView);
91#else
92	BBox* panel = new BBox(bounds, "top_panel", B_FOLLOW_ALL,
93		B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE_JUMP, B_NO_BORDER);
94
95	_SetupDocInfoView(panel);
96	background->AddChild(panel);
97#endif
98
99	if (fTable->ChildAt(0))
100		fTable->ChildAt(0)->MakeFocus();
101
102	BRect winFrame(Frame());
103	BRect screenFrame(BScreen().Frame());
104	MoveTo((screenFrame.right - winFrame.right) / 2,
105		(screenFrame.bottom - winFrame.bottom) / 2);
106
107	SetSizeLimits(400.0, 10000.0, 250.0, 10000.0);
108}
109
110
111void
112DocInfoWindow::Quit()
113{
114	_EmptyKeyList();
115	inherited::Quit();
116}
117
118
119bool
120DocInfoWindow::QuitRequested()
121{
122	return true;
123}
124
125
126void
127DocInfoWindow::MessageReceived(BMessage *msg)
128{
129	switch (msg->what) {
130		case OK_MSG: {
131				BMessage doc_info;
132				_ReadFieldsFromTable(doc_info);
133				_DocInfo()->RemoveName("doc_info");
134				_DocInfo()->AddMessage("doc_info", &doc_info);
135#if HAVE_FULLVERSION_PDF_LIB
136				_ReadPasswords();
137				_ReadPermissions();
138#endif
139				Quit();
140		}	break;
141
142		case CANCEL_MSG: {
143			Quit();
144		}	break;
145
146		case ADD_KEY_MSG: {
147		case DEFAULT_KEY_MSG:
148			_AddKey(msg, msg->what == ADD_KEY_MSG);
149		}	break;
150
151		case REMOVE_KEY_MSG: {
152			_RemoveKey(msg);
153		}	break;
154
155		default: {
156			inherited::MessageReceived(msg);
157		}	break;
158	}
159}
160
161
162void
163DocInfoWindow::FrameResized(float newWidth, float newHeight)
164{
165	BTextControl *textControl = dynamic_cast<BTextControl*> (fTable->ChildAt(0));
166	if (textControl) {
167		float width, height;
168		textControl->GetPreferredSize(&width, &height);
169
170		int32 count = fKeyList->CountItems();
171		float fieldsHeight = (height * count) + (2 * count);
172
173		_AdjustScrollBar(height, fieldsHeight);
174
175		while (textControl) {
176			textControl->SetDivider(textControl->Bounds().Width() / 2.0);
177			textControl = dynamic_cast<BTextControl*> (textControl->NextSibling());
178		}
179	}
180}
181
182
183void
184DocInfoWindow::_SetupDocInfoView(BBox* panel)
185{
186	BRect bounds(panel->Bounds());
187#if HAVE_FULLVERSION_PDF_LIB
188	bounds.InsetBy(10.0, 10.0);
189#endif
190
191	// add list of keys
192	fKeyList = new BMenu("Delete Key");
193	BMenuField *menu = new BMenuField(bounds, "delete", "", fKeyList,
194		B_FOLLOW_BOTTOM);
195	panel->AddChild(menu);
196	menu->SetDivider(0);
197
198#ifdef __HAIKU__
199	menu->ResizeToPreferred();
200	menu->MoveTo(bounds.left, bounds.bottom - menu->Bounds().Height());
201#else
202	menu->ResizeTo(menu->StringWidth("Delete Key") + 15.0, 25.0);
203	menu->MoveTo(bounds.left, bounds.bottom - 25.0);
204#endif
205
206	const char* title[6] = { "Title", "Author", "Subject", "Keywords", "Creator",
207		NULL };	// PDFlib sets these: "Producer", "CreationDate", not "ModDate"
208	BMenu* defaultKeys = new BMenu("Default Keys");
209	for (int32 i = 0; title[i] != NULL; ++i)
210		defaultKeys->AddItem(new BMenuItem(title[i], new BMessage(DEFAULT_KEY_MSG)));
211
212	BRect frame(menu->Frame());
213	menu = new BMenuField(frame, "add", "", defaultKeys, B_FOLLOW_BOTTOM);
214	panel->AddChild(menu);
215	menu->SetDivider(0);
216
217#ifdef __HAIKU__
218	menu->ResizeToPreferred();
219	menu->MoveBy(frame.Width() + 10.0, 0.0);
220#else
221	menu->ResizeTo(menu->StringWidth("Default Keys") + 15.0, 25.0);
222	menu->MoveBy(menu->Bounds().Width() + 10.0, 0.0);
223#endif
224
225	frame = menu->Frame();
226	frame.left = frame.right + 10.0;
227	frame.right = bounds.right;
228	BTextControl *add = new BTextControl(frame, "add", "Add Key:", "",
229		new BMessage(ADD_KEY_MSG), B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM);
230	panel->AddChild(add);
231
232	float width, height;
233	add->GetPreferredSize(&width, &height);
234	add->ResizeTo(bounds.right - frame.left, height);
235	add->SetDivider(be_plain_font->StringWidth("Add Key: "));
236
237	bounds.bottom = frame.top - 10.0;
238	bounds.right -= B_V_SCROLL_BAR_WIDTH;
239	bounds.InsetBy(2.0, 2.0);
240	fTable = new BView(bounds, "table", B_FOLLOW_ALL, B_WILL_DRAW);
241	fTable->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
242
243	fTableScrollView = new BScrollView("scroll_table", fTable, B_FOLLOW_ALL, 0,
244		false, true);
245	panel->AddChild(fTableScrollView);
246
247	BMessage docInfo;
248	fDocInfo->FindMessage("doc_info", &docInfo);
249
250	// fill table
251	_BuildTable(docInfo);
252}
253
254
255void
256DocInfoWindow::_BuildTable(const BMessage& docInfo)
257{
258	_EmptyKeyList();
259
260	while (fTable->ChildAt(0)) {
261		BView *child = fTable->ChildAt(0);
262		fTable->RemoveChild(child);
263		delete child;
264	}
265
266	fTable->ScrollTo(0, 0);
267
268#ifdef B_BEOS_VERSION_DANO
269	const
270#endif
271	char *name;
272	uint32 type;
273	int32 count;
274
275	float rowHeight = 20.0;
276	float fieldsHeight = 2.0;
277	float width = fTable->Bounds().Width() - 4.0;
278	for (int32 i = 0; docInfo.GetInfo(B_STRING_TYPE, i, &name, &type, &count)
279		== B_OK; i++) {
280		if (type != B_STRING_TYPE)
281			continue;
282
283		BString value;
284		BTextControl* textControl;
285		if (docInfo.FindString(name, &value) == B_OK) {
286			BRect rect(2.0, fieldsHeight, width, rowHeight);
287			textControl = _AddFieldToTable(rect, name, value.String());
288
289			rowHeight = textControl->Bounds().Height();
290			fieldsHeight += rowHeight + 2.0;
291		}
292	}
293
294	_AdjustScrollBar(rowHeight, fieldsHeight);
295}
296
297
298BTextControl*
299DocInfoWindow::_AddFieldToTable(BRect rect, const char* name, const char* value)
300{
301	BTextControl *textControl = new BTextControl(rect, name, name, value, NULL,
302		B_FOLLOW_LEFT_RIGHT, B_WILL_DRAW | B_NAVIGABLE);
303	fTable->AddChild(textControl);
304	float width, height;
305	textControl->GetPreferredSize(&width, &height);
306
307	textControl->ResizeTo(rect.Width(), height);
308	textControl->SetDivider(rect.Width() / 2.0);
309
310	fKeyList->AddItem(new BMenuItem(name, new BMessage(REMOVE_KEY_MSG)));
311
312	return textControl;
313}
314
315
316void
317DocInfoWindow::_AdjustScrollBar(float controlHeight, float fieldsHeight)
318{
319	BScrollBar *sb = fTableScrollView->ScrollBar(B_VERTICAL);
320	if (!sb)
321		return;
322
323	sb->SetRange(0.0, 0.0);
324	float tableHeight = fTable->Bounds().Height();
325	if ((fieldsHeight - tableHeight) > 0.0) {
326		sb->SetProportion(tableHeight / fieldsHeight);
327		sb->SetRange(0.0, fieldsHeight - tableHeight);
328		sb->SetSteps(controlHeight + 2.0, tableHeight);
329	}
330}
331
332
333void
334DocInfoWindow::_ReadFieldsFromTable(BMessage& docInfo)
335{
336	docInfo.MakeEmpty();
337
338	BView* child;
339	for (int32 i = 0; (child = fTable->ChildAt(i)) != NULL; i++) {
340		BTextControl* textControl = dynamic_cast<BTextControl*>(child);
341		if (textControl)
342			docInfo.AddString(textControl->Label(), textControl->Text());
343	}
344}
345
346
347void
348DocInfoWindow::_RemoveKey(BMessage *msg)
349{
350	void *p;
351	if (msg->FindPointer("source", &p) != B_OK)
352		return;
353
354	BMenuItem *item = reinterpret_cast<BMenuItem*>(p);
355	if (!item)
356		return;
357
358	BMessage docInfo;
359	_ReadFieldsFromTable(docInfo);
360
361	const char *label = item->Label();
362	if (docInfo.HasString(label)) {
363		docInfo.RemoveName(label);
364		_BuildTable(docInfo);
365	}
366}
367
368
369bool
370DocInfoWindow::_IsKeyValid(const char* key) const
371{
372	if (*key == 0)
373		return false;
374
375	while (*key) {
376		if (isspace(*key) || iscntrl(*key))
377			break;
378		key++;
379	}
380	return *key == 0;
381}
382
383
384void
385DocInfoWindow::_AddKey(BMessage *msg, bool textControl)
386{
387	void *p;
388	if (msg->FindPointer("source", &p) != B_OK || p == NULL)
389		return;
390
391	const char* key = NULL;
392	if (textControl) {
393		BTextControl *text = reinterpret_cast<BTextControl*>(p);
394		key = text->Text();
395	} else {
396		BMenuItem *item = reinterpret_cast<BMenuItem*>(p);
397		key = item->Label();
398	}
399
400	if (!_IsKeyValid(key))
401		return;
402
403	BMessage docInfo;
404	_ReadFieldsFromTable(docInfo);
405
406	if (!docInfo.HasString(key)) {
407		// key is valid and is not in list already
408		docInfo.AddString(key, "");
409
410		float width = fTable->Bounds().Width() - 4.0;
411		BTextControl *textControl =
412			_AddFieldToTable(BRect(2.0, 0.0, width, 20.0), key, "");
413
414		float rowHeight = textControl->Bounds().Height();
415		int32 count = fKeyList->CountItems();
416		float fieldsHeight = (rowHeight * count) + (2 * count);
417		textControl->MoveTo(2.0, fieldsHeight - rowHeight);
418
419		_AdjustScrollBar(rowHeight, fieldsHeight);
420	}
421}
422
423
424void
425DocInfoWindow::_EmptyKeyList()
426{
427	while (fKeyList->CountItems() > 0L)
428		delete fKeyList->RemoveItem((int32)0);
429}
430
431
432#if HAVE_FULLVERSION_PDF_LIB
433
434void
435DocInfoWindow::_SetupPasswordView(BBox* panel)
436{
437	BRect bounds(panel->Bounds().InsetByCopy(10.0, 10.0));
438
439	fMasterPassword = _AddPasswordControl(bounds, panel, "master_password",
440		"Master Password:");
441	bounds.OffsetBy(0, fMasterPassword->Bounds().Height());
442	fUserPassword = _AddPasswordControl(bounds, panel, "user_password",
443		"User Password:");
444
445	float divider = max_c(panel->StringWidth("Master Password:"),
446		panel->StringWidth("User Password:"));
447	fMasterPassword->SetDivider(divider);
448	fUserPassword->SetDivider(divider);
449}
450
451
452void
453DocInfoWindow::_SetupPermissionsView(BBox* panel)
454{
455	(void)panel;
456}
457
458
459BBox*
460DocInfoWindow::_CreateTabPanel(BTabView* tabView, const char* label)
461{
462	BRect rect(tabView->Bounds().InsetByCopy(3.0, 15.0));
463	rect.OffsetTo(B_ORIGIN);
464	BBox* panel = new BBox(rect, "top_panel", B_FOLLOW_ALL,
465		B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE_JUMP, B_NO_BORDER);
466
467	BTab* tab = new BTab();
468	tabView->AddTab(panel, tab);
469	tab->SetLabel(label);
470
471	return panel;
472}
473
474
475BTextControl*
476DocInfoWindow::_AddPasswordControl(BRect r, BView* panel, const char* name,
477	const char* label)
478{
479	BString text;
480	_DocInfo()->FindString(name, &text);
481	BTextControl* textControl = new BTextControl(r, name, label, "", NULL,
482		B_FOLLOW_LEFT_RIGHT);
483	panel->AddChild(textControl);
484	textControl->ResizeToPreferred();
485	textControl->TextView()->HideTyping(true);
486	textControl->TextView()->SetText(text.String());
487
488	return textControl;
489}
490
491
492void
493DocInfoWindow::_ReadPasswords()
494{
495	SetString(_DocInfo(), "master_password", fMasterPassword->TextView()->Text());
496	SetString(_DocInfo(), "user_password", fUserPassword->TextView()->Text());
497}
498
499
500void
501DocInfoWindow::_ReadPermissions()
502{
503	BString permissions;
504	fPermissions.Encode(permissions);
505
506	SetString(_DocInfo(), "permissions", permissions.String());
507}
508
509
510// #pragma mark -- Permissions
511
512
513// pdflib 5.x supports password protection and permissions in the commercial version only!
514static const PermissionLabels gPermissionLabels[] = {
515	PermissionLabels("Prevent printing the file.", "noprint"),
516	PermissionLabels("Prevent making any changes.", "nomodify"),
517	PermissionLabels("Prevent copying or extracting text or graphics.", "nocopy"),
518	PermissionLabels("Prevent adding or changing comments or form fields.", "noannots"),
519	PermissionLabels("Prevent form field filling.", "noforms"),
520	PermissionLabels("Prevent extracting text of graphics.", "noaccessible"),
521	PermissionLabels("Prevent inserting, deleting, or rotating pages and creating "
522		"bookmarks and thumbnails, even if nomodify hasn't been specified", "noassemble"),
523	PermissionLabels("Prevent high-resolution printing.", "nohiresprint")
524};
525
526Permissions::Permissions()
527{
528	fNofPermissions = sizeof(gPermissionLabels) / sizeof(PermissionLabels);
529	fPermissions = new Permission[fNofPermissions];
530	for (int32 i = 0; i < fNofPermissions; i++)
531		fPermissions[i].SetLabels(&gPermissionLabels[i]);
532}
533
534
535void Permissions::Decode(const char* s)
536{
537	for (int32 i = 0; i < fNofPermissions; i++)
538		At(i)->SetAllowed((strstr(s, At(i)->GetPDFName()) == NULL));
539}
540
541
542void Permissions::Encode(BString& permissions)
543{
544	permissions.Truncate(0);
545	for (int32 i = 0; i < fNofPermissions; i++) {
546		if (!At(i)->IsAllowed())
547			permissions.Append(At(i)->GetPDFName()).Append(" ");
548	}
549}
550
551#endif	// HAVE_FULLVERSION_PDF_LIB
552