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