1 /* 2 * Copyright 2007-2015 Haiku, Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Ryan Leavengood <leavengood@gmail.com> 7 * John Scipione <jscipione@gmail.com> 8 * Joseph Groover <looncraz@looncraz.net> 9 */ 10 11 12 #include <AboutWindow.h> 13 14 #include <stdarg.h> 15 #include <time.h> 16 17 #include <Alert.h> 18 #include <AppFileInfo.h> 19 #include <Bitmap.h> 20 #include <Button.h> 21 #include <File.h> 22 #include <Font.h> 23 #include <GroupLayoutBuilder.h> 24 #include <GroupView.h> 25 #include <InterfaceDefs.h> 26 #include <LayoutBuilder.h> 27 #include <Message.h> 28 #include <MessageFilter.h> 29 #include <Point.h> 30 #include <Roster.h> 31 #include <Screen.h> 32 #include <ScrollView.h> 33 #include <Size.h> 34 #include <String.h> 35 #include <StringView.h> 36 #include <SystemCatalog.h> 37 #include <TextView.h> 38 #include <View.h> 39 #include <Window.h> 40 41 42 static const float kStripeWidth = 30.0; 43 44 using BPrivate::gSystemCatalog; 45 46 47 #undef B_TRANSLATION_CONTEXT 48 #define B_TRANSLATION_CONTEXT "AboutWindow" 49 50 51 namespace BPrivate { 52 53 class StripeView : public BView { 54 public: 55 StripeView(BBitmap* icon); 56 virtual ~StripeView(); 57 58 virtual void Draw(BRect updateRect); 59 60 BBitmap* Icon() const { return fIcon; }; 61 void SetIcon(BBitmap* icon); 62 63 private: 64 BBitmap* fIcon; 65 }; 66 67 68 class AboutView : public BGroupView { 69 public: 70 AboutView(const char* name, 71 const char* signature); 72 virtual ~AboutView(); 73 74 virtual void AllAttached(); 75 76 BTextView* InfoView() const { return fInfoView; }; 77 78 BBitmap* Icon(); 79 status_t SetIcon(BBitmap* icon); 80 81 const char* Name(); 82 status_t SetName(const char* name); 83 84 const char* Version(); 85 status_t SetVersion(const char* version); 86 87 private: 88 BString _GetVersionFromSignature(const char* signature); 89 BBitmap* _GetIconFromSignature(const char* signature); 90 91 private: 92 BStringView* fNameView; 93 BStringView* fVersionView; 94 BTextView* fInfoView; 95 StripeView* fStripeView; 96 }; 97 98 99 // #pragma mark - StripeView 100 101 102 StripeView::StripeView(BBitmap* icon) 103 : 104 BView("StripeView", B_WILL_DRAW), 105 fIcon(icon) 106 { 107 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 108 109 float width = 0.0f; 110 if (icon != NULL) 111 width += icon->Bounds().Width() + 24.0f; 112 113 SetExplicitSize(BSize(width, B_SIZE_UNSET)); 114 SetExplicitPreferredSize(BSize(width, B_SIZE_UNLIMITED)); 115 } 116 117 118 StripeView::~StripeView() 119 { 120 } 121 122 123 void 124 StripeView::Draw(BRect updateRect) 125 { 126 if (fIcon == NULL) 127 return; 128 129 SetHighColor(ViewColor()); 130 FillRect(updateRect); 131 132 BRect stripeRect = Bounds(); 133 stripeRect.right = kStripeWidth; 134 SetHighColor(tint_color(ViewColor(), B_DARKEN_1_TINT)); 135 FillRect(stripeRect); 136 137 SetDrawingMode(B_OP_ALPHA); 138 SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 139 DrawBitmapAsync(fIcon, BPoint(15.0f, 10.0f)); 140 } 141 142 143 void 144 StripeView::SetIcon(BBitmap* icon) 145 { 146 if (fIcon != NULL) 147 delete fIcon; 148 149 fIcon = icon; 150 151 float width = 0.0f; 152 if (icon != NULL) 153 width += icon->Bounds().Width() + 24.0f; 154 155 SetExplicitSize(BSize(width, B_SIZE_UNSET)); 156 SetExplicitPreferredSize(BSize(width, B_SIZE_UNLIMITED)); 157 }; 158 159 160 // #pragma mark - AboutView 161 162 163 AboutView::AboutView(const char* appName, const char* signature) 164 : 165 BGroupView("AboutView", B_VERTICAL) 166 { 167 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 168 fNameView = new BStringView("name", appName); 169 BFont font; 170 fNameView->GetFont(&font); 171 font.SetFace(B_BOLD_FACE); 172 font.SetSize(font.Size() * 2.0); 173 fNameView->SetFont(&font, B_FONT_FAMILY_AND_STYLE | B_FONT_SIZE 174 | B_FONT_FLAGS); 175 fNameView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 176 177 fVersionView = new BStringView("version", 178 _GetVersionFromSignature(signature).String()); 179 fVersionView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 180 181 rgb_color documentColor = ui_color(B_DOCUMENT_TEXT_COLOR); 182 fInfoView = new BTextView("info", NULL, &documentColor, B_WILL_DRAW); 183 fInfoView->SetExplicitMinSize(BSize(210.0, 160.0)); 184 fInfoView->MakeEditable(false); 185 fInfoView->SetWordWrap(true); 186 fInfoView->SetInsets(5.0, 5.0, 5.0, 5.0); 187 fInfoView->SetStylable(true); 188 189 BScrollView* infoViewScroller = new BScrollView( 190 "infoViewScroller", fInfoView, B_WILL_DRAW | B_FRAME_EVENTS, 191 false, true, B_PLAIN_BORDER); 192 193 fStripeView = new StripeView(_GetIconFromSignature(signature)); 194 195 const char* ok = B_TRANSLATE_MARK("OK"); 196 BButton* closeButton = new BButton("ok", 197 gSystemCatalog.GetString(ok, "AboutWindow"), 198 new BMessage(B_QUIT_REQUESTED)); 199 200 GroupLayout()->SetSpacing(0); 201 BLayoutBuilder::Group<>(this, B_HORIZONTAL, 0) 202 .Add(fStripeView) 203 .AddGroup(B_VERTICAL) 204 .SetInsets(0, B_USE_DEFAULT_SPACING, 205 B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING) 206 .AddGroup(B_VERTICAL, 0) 207 .Add(fNameView) 208 .Add(fVersionView) 209 .AddStrut(B_USE_SMALL_SPACING) 210 .Add(infoViewScroller) 211 .End() 212 .AddGroup(B_HORIZONTAL, 0) 213 .AddGlue() 214 .Add(closeButton) 215 .End() 216 .End() 217 .View()->SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 218 } 219 220 221 AboutView::~AboutView() 222 { 223 } 224 225 226 void 227 AboutView::AllAttached() 228 { 229 fNameView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 230 fInfoView->SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR); 231 fVersionView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 232 } 233 234 235 // #pragma mark - AboutView private methods 236 237 238 BString 239 AboutView::_GetVersionFromSignature(const char* signature) 240 { 241 if (signature == NULL) 242 return NULL; 243 244 entry_ref ref; 245 if (be_roster->FindApp(signature, &ref) != B_OK) 246 return NULL; 247 248 BFile file(&ref, B_READ_ONLY); 249 BAppFileInfo appMime(&file); 250 if (appMime.InitCheck() != B_OK) 251 return NULL; 252 253 version_info versionInfo; 254 if (appMime.GetVersionInfo(&versionInfo, B_APP_VERSION_KIND) == B_OK) { 255 if (versionInfo.major == 0 && versionInfo.middle == 0 256 && versionInfo.minor == 0) { 257 return NULL; 258 } 259 260 const char* version = B_TRANSLATE_MARK("Version"); 261 version = gSystemCatalog.GetString(version, "AboutWindow"); 262 BString appVersion(version); 263 appVersion << " " << versionInfo.major << "." << versionInfo.middle; 264 if (versionInfo.minor > 0) 265 appVersion << "." << versionInfo.minor; 266 267 // Add the version variety 268 const char* variety = NULL; 269 switch (versionInfo.variety) { 270 case B_DEVELOPMENT_VERSION: 271 variety = B_TRANSLATE_MARK("development"); 272 break; 273 case B_ALPHA_VERSION: 274 variety = B_TRANSLATE_MARK("alpha"); 275 break; 276 case B_BETA_VERSION: 277 variety = B_TRANSLATE_MARK("beta"); 278 break; 279 case B_GAMMA_VERSION: 280 variety = B_TRANSLATE_MARK("gamma"); 281 break; 282 case B_GOLDEN_MASTER_VERSION: 283 variety = B_TRANSLATE_MARK("gold master"); 284 break; 285 } 286 287 if (variety != NULL) { 288 variety = gSystemCatalog.GetString(variety, "AboutWindow"); 289 appVersion << "-" << variety; 290 } 291 292 return appVersion; 293 } 294 295 return NULL; 296 } 297 298 299 BBitmap* 300 AboutView::_GetIconFromSignature(const char* signature) 301 { 302 if (signature == NULL) 303 return NULL; 304 305 entry_ref ref; 306 if (be_roster->FindApp(signature, &ref) != B_OK) 307 return NULL; 308 309 BFile file(&ref, B_READ_ONLY); 310 BAppFileInfo appMime(&file); 311 if (appMime.InitCheck() != B_OK) 312 return NULL; 313 314 BBitmap* icon = new BBitmap(BRect(0.0, 0.0, 63.0, 63.0), B_RGBA32); 315 if (appMime.GetIcon(icon, (icon_size)64) == B_OK) 316 return icon; 317 318 delete icon; 319 return NULL; 320 } 321 322 323 // #pragma mark - AboutView public methods 324 325 326 BBitmap* 327 AboutView::Icon() 328 { 329 if (fStripeView == NULL) 330 return NULL; 331 332 return fStripeView->Icon(); 333 } 334 335 336 status_t 337 AboutView::SetIcon(BBitmap* icon) 338 { 339 if (fStripeView == NULL) 340 return B_NO_INIT; 341 342 fStripeView->SetIcon(icon); 343 344 return B_OK; 345 } 346 347 348 const char* 349 AboutView::Name() 350 { 351 return fNameView->Text(); 352 } 353 354 355 status_t 356 AboutView::SetName(const char* name) 357 { 358 fNameView->SetText(name); 359 360 return B_OK; 361 } 362 363 364 const char* 365 AboutView::Version() 366 { 367 return fVersionView->Text(); 368 } 369 370 371 status_t 372 AboutView::SetVersion(const char* version) 373 { 374 fVersionView->SetText(version); 375 376 return B_OK; 377 } 378 379 } // namespace BPrivate 380 381 382 // #pragma mark - BAboutWindow 383 384 385 BAboutWindow::BAboutWindow(const char* appName, const char* signature) 386 : 387 BWindow(BRect(0.0, 0.0, 400.0, 200.0), appName, B_MODAL_WINDOW, 388 B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE 389 | B_AUTO_UPDATE_SIZE_LIMITS | B_CLOSE_ON_ESCAPE) 390 { 391 SetLayout(new BGroupLayout(B_VERTICAL)); 392 393 const char* about = B_TRANSLATE_MARK("About %app%"); 394 about = gSystemCatalog.GetString(about, "AboutWindow"); 395 396 BString title(about); 397 title.ReplaceFirst("%app%", appName); 398 SetTitle(title.String()); 399 400 fAboutView = new BPrivate::AboutView(appName, signature); 401 AddChild(fAboutView); 402 403 MoveTo(AboutPosition(Frame().Width(), Frame().Height())); 404 } 405 406 407 BAboutWindow::~BAboutWindow() 408 { 409 fAboutView->RemoveSelf(); 410 delete fAboutView; 411 fAboutView = NULL; 412 } 413 414 415 // #pragma mark - BAboutWindow virtual methods 416 417 418 void 419 BAboutWindow::Show() 420 { 421 if (IsHidden()) { 422 // move to current workspace 423 SetWorkspaces(B_CURRENT_WORKSPACE); 424 } 425 426 BWindow::Show(); 427 } 428 429 430 // #pragma mark - BAboutWindow public methods 431 432 433 BPoint 434 BAboutWindow::AboutPosition(float width, float height) 435 { 436 BPoint result(100, 100); 437 438 BWindow* window = 439 dynamic_cast<BWindow*>(BLooper::LooperForThread(find_thread(NULL))); 440 441 BScreen screen(window); 442 BRect screenFrame(0, 0, 640, 480); 443 if (screen.IsValid()) 444 screenFrame = screen.Frame(); 445 446 // Horizontally, we're smack in the middle 447 result.x = screenFrame.left + (screenFrame.Width() / 2.0) - (width / 2.0); 448 449 // This is probably sooo wrong, but it looks right on 1024 x 768 450 result.y = screenFrame.top + (screenFrame.Height() / 4.0) 451 - ceil(height / 3.0); 452 453 return result; 454 } 455 456 457 void 458 BAboutWindow::AddDescription(const char* description) 459 { 460 if (description == NULL) 461 return; 462 463 AddText(description); 464 } 465 466 467 void 468 BAboutWindow::AddCopyright(int32 firstCopyrightYear, 469 const char* copyrightHolder, const char** extraCopyrights) 470 { 471 BString copyright(B_UTF8_COPYRIGHT " %years% %holder%"); 472 473 // Get current year 474 time_t tp; 475 time(&tp); 476 char currentYear[5]; 477 strftime(currentYear, 5, "%Y", localtime(&tp)); 478 BString copyrightYears; 479 copyrightYears << firstCopyrightYear; 480 if (copyrightYears != currentYear) 481 copyrightYears << "-" << currentYear; 482 483 BString text(""); 484 if (fAboutView->InfoView()->TextLength() > 0) 485 text << "\n\n"; 486 487 text << copyright; 488 489 // Fill out the copyright year placeholder 490 text.ReplaceAll("%years%", copyrightYears.String()); 491 492 // Fill in the copyright holder placeholder 493 text.ReplaceAll("%holder%", copyrightHolder); 494 495 // Add extra copyright strings 496 if (extraCopyrights != NULL) { 497 // Add optional extra copyright information 498 for (int32 i = 0; extraCopyrights[i]; i++) 499 text << "\n" << B_UTF8_COPYRIGHT << " " << extraCopyrights[i]; 500 } 501 502 const char* allRightsReserved = B_TRANSLATE_MARK("All Rights Reserved."); 503 allRightsReserved = gSystemCatalog.GetString(allRightsReserved, 504 "AboutWindow"); 505 text << "\n " << allRightsReserved; 506 507 fAboutView->InfoView()->Insert(text.String()); 508 } 509 510 511 void 512 BAboutWindow::AddAuthors(const char** authors) 513 { 514 if (authors == NULL) 515 return; 516 517 const char* writtenBy = B_TRANSLATE_MARK("Written by:"); 518 writtenBy = gSystemCatalog.GetString(writtenBy, "AboutWindow"); 519 520 AddText(writtenBy, authors); 521 } 522 523 524 void 525 BAboutWindow::AddSpecialThanks(const char** thanks) 526 { 527 if (thanks == NULL) 528 return; 529 530 const char* specialThanks = B_TRANSLATE_MARK("Special Thanks:"); 531 specialThanks = gSystemCatalog.GetString(specialThanks, "AboutWindow"); 532 533 AddText(specialThanks, thanks); 534 } 535 536 537 void 538 BAboutWindow::AddVersionHistory(const char** history) 539 { 540 if (history == NULL) 541 return; 542 543 const char* versionHistory = B_TRANSLATE_MARK("Version history:"); 544 versionHistory = gSystemCatalog.GetString(versionHistory, "AboutWindow"); 545 546 AddText(versionHistory, history); 547 } 548 549 550 void 551 BAboutWindow::AddExtraInfo(const char* extraInfo) 552 { 553 if (extraInfo == NULL) 554 return; 555 556 const char* appExtraInfo = B_TRANSLATE_MARK(extraInfo); 557 appExtraInfo = gSystemCatalog.GetString(extraInfo, "AboutWindow"); 558 559 BString extra(""); 560 if (fAboutView->InfoView()->TextLength() > 0) 561 extra << "\n\n"; 562 563 extra << appExtraInfo; 564 565 fAboutView->InfoView()->Insert(extra.String()); 566 } 567 568 569 void 570 BAboutWindow::AddText(const char* header, const char** contents) 571 { 572 BTextView* infoView = fAboutView->InfoView(); 573 int32 textLength = infoView->TextLength(); 574 BString text(""); 575 576 if (textLength > 0) { 577 text << "\n\n"; 578 textLength += 2; 579 } 580 581 const char* indent = ""; 582 if (header != NULL) { 583 indent = " "; 584 text << header; 585 } 586 587 if (contents != NULL) { 588 text << "\n"; 589 for (int32 i = 0; contents[i]; i++) 590 text << indent << contents[i] << "\n"; 591 } 592 593 infoView->Insert(text.String()); 594 595 if (contents != NULL && header != NULL) { 596 infoView->SetFontAndColor(textLength, textLength + strlen(header), 597 be_bold_font); 598 } 599 } 600 601 602 BBitmap* 603 BAboutWindow::Icon() 604 { 605 return fAboutView->Icon(); 606 } 607 608 609 void 610 BAboutWindow::SetIcon(BBitmap* icon) 611 { 612 fAboutView->SetIcon(icon); 613 } 614 615 616 const char* 617 BAboutWindow::Name() 618 { 619 return fAboutView->Name(); 620 } 621 622 623 void 624 BAboutWindow::SetName(const char* name) 625 { 626 fAboutView->SetName(name); 627 } 628 629 630 const char* 631 BAboutWindow::Version() 632 { 633 return fAboutView->Version(); 634 } 635 636 637 void 638 BAboutWindow::SetVersion(const char* version) 639 { 640 fAboutView->SetVersion(version); 641 } 642 643 644 // FBC padding 645 646 void BAboutWindow::_ReservedAboutWindow20() {} 647 void BAboutWindow::_ReservedAboutWindow19() {} 648 void BAboutWindow::_ReservedAboutWindow18() {} 649 void BAboutWindow::_ReservedAboutWindow17() {} 650 void BAboutWindow::_ReservedAboutWindow16() {} 651 void BAboutWindow::_ReservedAboutWindow15() {} 652 void BAboutWindow::_ReservedAboutWindow14() {} 653 void BAboutWindow::_ReservedAboutWindow13() {} 654 void BAboutWindow::_ReservedAboutWindow12() {} 655 void BAboutWindow::_ReservedAboutWindow11() {} 656 void BAboutWindow::_ReservedAboutWindow10() {} 657 void BAboutWindow::_ReservedAboutWindow9() {} 658 void BAboutWindow::_ReservedAboutWindow8() {} 659 void BAboutWindow::_ReservedAboutWindow7() {} 660 void BAboutWindow::_ReservedAboutWindow6() {} 661 void BAboutWindow::_ReservedAboutWindow5() {} 662 void BAboutWindow::_ReservedAboutWindow4() {} 663 void BAboutWindow::_ReservedAboutWindow3() {} 664 void BAboutWindow::_ReservedAboutWindow2() {} 665 void BAboutWindow::_ReservedAboutWindow1() {} 666