1/* 2 * Copyright 2007-2012 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 */ 9 10 11#include <AboutWindow.h> 12 13#include <stdarg.h> 14#include <time.h> 15 16#include <Alert.h> 17#include <AppFileInfo.h> 18#include <Bitmap.h> 19#include <File.h> 20#include <Font.h> 21#include <GroupLayoutBuilder.h> 22#include <GroupView.h> 23#include <LayoutBuilder.h> 24#include <Point.h> 25#include <Roster.h> 26#include <Screen.h> 27#include <ScrollView.h> 28#include <Size.h> 29#include <String.h> 30#include <StringView.h> 31#include <SystemCatalog.h> 32#include <TextView.h> 33#include <View.h> 34#include <Window.h> 35 36 37static const float kStripeWidth = 30.0; 38 39using BPrivate::gSystemCatalog; 40 41#undef B_TRANSLATION_CONTEXT 42#define B_TRANSLATION_CONTEXT "AboutWindow" 43 44 45class StripeView : public BView { 46 public: 47 StripeView(BBitmap* icon); 48 virtual ~StripeView(); 49 50 virtual void Draw(BRect updateRect); 51 52 private: 53 BBitmap* fIcon; 54}; 55 56 57class AboutView : public BGroupView { 58 public: 59 AboutView(const char* name, 60 const char* signature); 61 virtual ~AboutView(); 62 63 BTextView* InfoView() const { return fInfoView; }; 64 65 protected: 66 const char* AppVersion(const char* signature); 67 BBitmap* AppIcon(const char* signature); 68 69 private: 70 BStringView* fNameView; 71 BStringView* fVersionView; 72 BTextView* fInfoView; 73}; 74 75 76// #pragma mark - 77 78 79StripeView::StripeView(BBitmap* icon) 80 : 81 BView("StripeView", B_WILL_DRAW), 82 fIcon(icon) 83{ 84 float width = 0.0f; 85 if (icon != NULL) 86 width += icon->Bounds().Width() + 32.0f; 87 88 SetExplicitMinSize(BSize(width, B_SIZE_UNSET)); 89 SetExplicitPreferredSize(BSize(width, B_SIZE_UNLIMITED)); 90 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 91} 92 93 94StripeView::~StripeView() 95{ 96} 97 98 99void 100StripeView::Draw(BRect updateRect) 101{ 102 if (fIcon == NULL) 103 return; 104 105 SetHighColor(ViewColor()); 106 FillRect(updateRect); 107 108 BRect stripeRect = Bounds(); 109 stripeRect.right = kStripeWidth; 110 SetHighColor(tint_color(ViewColor(), B_DARKEN_1_TINT)); 111 FillRect(stripeRect); 112 113 SetDrawingMode(B_OP_ALPHA); 114 SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 115 DrawBitmapAsync(fIcon, BPoint(15.0f, 10.0f)); 116} 117 118 119// #pragma mark - 120 121 122AboutView::AboutView(const char* appName, const char* signature) 123 : 124 BGroupView("AboutView", B_VERTICAL) 125{ 126 fNameView = new BStringView("name", appName); 127 BFont font; 128 fNameView->GetFont(&font); 129 font.SetFace(B_BOLD_FACE); 130 font.SetSize(font.Size() * 2.0); 131 fNameView->SetFont(&font, B_FONT_FAMILY_AND_STYLE | B_FONT_SIZE 132 | B_FONT_FLAGS); 133 134 fVersionView = new BStringView("version", AppVersion(signature)); 135 136 fInfoView = new BTextView("info", B_WILL_DRAW); 137 fInfoView->SetExplicitMinSize(BSize(210.0, 160.0)); 138 fInfoView->MakeEditable(false); 139 fInfoView->SetWordWrap(true); 140 fInfoView->SetInsets(5.0, 5.0, 5.0, 5.0); 141 fInfoView->SetViewColor(ui_color(B_DOCUMENT_BACKGROUND_COLOR)); 142 fInfoView->SetHighColor(ui_color(B_DOCUMENT_TEXT_COLOR)); 143 144 BScrollView* infoViewScroller = new BScrollView( 145 "infoViewScroller", fInfoView, B_WILL_DRAW | B_FRAME_EVENTS, 146 false, true, B_PLAIN_BORDER); 147 148 GroupLayout()->SetSpacing(0); 149 BLayoutBuilder::Group<>(this) 150 .AddGroup(B_HORIZONTAL, 0) 151 .Add(new StripeView(AppIcon(signature))) 152 .AddGroup(B_VERTICAL, B_USE_SMALL_SPACING) 153 .SetInsets(0, B_USE_DEFAULT_SPACING, 154 B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING) 155 .Add(fNameView) 156 .Add(fVersionView) 157 .Add(infoViewScroller) 158 .End() 159 .AddGlue() 160 .End(); 161} 162 163 164AboutView::~AboutView() 165{ 166} 167 168 169const char* 170AboutView::AppVersion(const char* signature) 171{ 172 if (signature == NULL) 173 return NULL; 174 175 entry_ref ref; 176 if (be_roster->FindApp(signature, &ref) != B_OK) 177 return NULL; 178 179 BFile file(&ref, B_READ_ONLY); 180 BAppFileInfo appMime(&file); 181 if (appMime.InitCheck() != B_OK) 182 return NULL; 183 184 version_info versionInfo; 185 if (appMime.GetVersionInfo(&versionInfo, B_APP_VERSION_KIND) == B_OK) { 186 if (versionInfo.major == 0 && versionInfo.middle == 0 187 && versionInfo.minor == 0) { 188 return NULL; 189 } 190 191 const char* version = B_TRANSLATE_MARK("Version"); 192 version = gSystemCatalog.GetString(version, "AboutWindow"); 193 BString appVersion(version); 194 appVersion << " " << versionInfo.major << "." << versionInfo.middle; 195 if (versionInfo.minor > 0) 196 appVersion << "." << versionInfo.minor; 197 198 // Add the version variety 199 const char* variety = NULL; 200 switch (versionInfo.variety) { 201 case B_DEVELOPMENT_VERSION: 202 variety = B_TRANSLATE_MARK("development"); 203 break; 204 case B_ALPHA_VERSION: 205 variety = B_TRANSLATE_MARK("alpha"); 206 break; 207 case B_BETA_VERSION: 208 variety = B_TRANSLATE_MARK("beta"); 209 break; 210 case B_GAMMA_VERSION: 211 variety = B_TRANSLATE_MARK("gamma"); 212 break; 213 case B_GOLDEN_MASTER_VERSION: 214 variety = B_TRANSLATE_MARK("gold master"); 215 break; 216 } 217 218 if (variety != NULL) { 219 variety = gSystemCatalog.GetString(variety, "AboutWindow"); 220 appVersion << "-" << variety; 221 } 222 223 return appVersion.String(); 224 } 225 226 return NULL; 227} 228 229 230BBitmap* 231AboutView::AppIcon(const char* signature) 232{ 233 if (signature == NULL) 234 return NULL; 235 236 entry_ref ref; 237 if (be_roster->FindApp(signature, &ref) != B_OK) 238 return NULL; 239 240 BFile file(&ref, B_READ_ONLY); 241 BAppFileInfo appMime(&file); 242 if (appMime.InitCheck() != B_OK) 243 return NULL; 244 245 BBitmap* icon = new BBitmap(BRect(0.0, 0.0, 127.0, 127.0), B_RGBA32); 246 if (appMime.GetIcon(icon, (icon_size)128) == B_OK) 247 return icon; 248 249 delete icon; 250 return NULL; 251} 252 253 254// #pragma mark - 255 256 257BAboutWindow::BAboutWindow(const char* appName, const char* signature) 258 : BWindow(BRect(0.0, 0.0, 200.0, 200.0), appName, B_TITLED_WINDOW, 259 B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE | B_NOT_RESIZABLE 260 | B_AUTO_UPDATE_SIZE_LIMITS) 261{ 262 SetLayout(new BGroupLayout(B_VERTICAL)); 263 264 const char* about = B_TRANSLATE_MARK("About"); 265 about = gSystemCatalog.GetString(about, "AboutWindow"); 266 267 BString title(about); 268 title << " " << appName; 269 SetTitle(title.String()); 270 271 fAboutView = new AboutView(appName, signature); 272 AddChild(fAboutView); 273 274 MoveTo(AboutPosition(Frame().Width(), Frame().Height())); 275} 276 277 278BAboutWindow::~BAboutWindow() 279{ 280 fAboutView->RemoveSelf(); 281 delete fAboutView; 282 fAboutView = NULL; 283} 284 285 286bool 287BAboutWindow::QuitRequested() 288{ 289 Hide(); 290 291 return false; 292} 293 294 295// #pragma mark - 296 297 298BPoint 299BAboutWindow::AboutPosition(float width, float height) 300{ 301 BPoint result(100, 100); 302 303 BWindow* window = 304 dynamic_cast<BWindow*>(BLooper::LooperForThread(find_thread(NULL))); 305 306 BScreen screen(window); 307 BRect screenFrame(0, 0, 640, 480); 308 if (screen.IsValid()) 309 screenFrame = screen.Frame(); 310 311 // Horizontally, we're smack in the middle 312 result.x = screenFrame.left + (screenFrame.Width() / 2.0) - (width / 2.0); 313 314 // This is probably sooo wrong, but it looks right on 1024 x 768 315 result.y = screenFrame.top + (screenFrame.Height() / 4.0) 316 - ceil(height / 3.0); 317 318 return result; 319} 320 321 322void 323BAboutWindow::AddDescription(const char* description) 324{ 325 if (description == NULL) 326 return; 327 328 const char* appDesc = B_TRANSLATE_MARK(description); 329 appDesc = gSystemCatalog.GetString(appDesc, "AboutWindow"); 330 331 BString desc(""); 332 if (fAboutView->InfoView()->TextLength() > 0) 333 desc << "\n\n"; 334 335 desc << appDesc; 336 337 fAboutView->InfoView()->Insert(desc.String()); 338} 339 340 341void 342BAboutWindow::AddCopyright(int32 firstCopyrightYear, 343 const char* copyrightHolder, const char** extraCopyrights) 344{ 345 BString copyright(B_UTF8_COPYRIGHT " %years% %holder%"); 346 347 // Get current year 348 time_t tp; 349 time(&tp); 350 char currentYear[5]; 351 strftime(currentYear, 5, "%Y", localtime(&tp)); 352 BString copyrightYears; 353 copyrightYears << firstCopyrightYear; 354 if (copyrightYears != currentYear) 355 copyrightYears << "-" << currentYear; 356 357 BString text(""); 358 if (fAboutView->InfoView()->TextLength() > 0) 359 text << "\n\n"; 360 361 text << copyright; 362 363 // Fill out the copyright year placeholder 364 text.ReplaceAll("%years%", copyrightYears.String()); 365 366 // Fill in the copyright holder placeholder 367 text.ReplaceAll("%holder%", copyrightHolder); 368 369 // Add extra copyright strings 370 if (extraCopyrights != NULL) { 371 // Add optional extra copyright information 372 for (int32 i = 0; extraCopyrights[i]; i++) 373 text << "\n" << B_UTF8_COPYRIGHT << " " << extraCopyrights[i]; 374 } 375 376 const char* allRightsReserved = B_TRANSLATE_MARK("All Rights Reserved."); 377 allRightsReserved = gSystemCatalog.GetString(allRightsReserved, 378 "AboutWindow"); 379 text << "\n " << allRightsReserved; 380 381 fAboutView->InfoView()->Insert(text.String()); 382} 383 384 385void 386BAboutWindow::AddAuthors(const char** authors) 387{ 388 if (authors == NULL) 389 return; 390 391 const char* writtenBy = B_TRANSLATE_MARK("Written by:"); 392 writtenBy = gSystemCatalog.GetString(writtenBy, "AboutWindow"); 393 394 BString text(""); 395 if (fAboutView->InfoView()->TextLength() > 0) 396 text << "\n\n"; 397 398 text << writtenBy; 399 text << "\n"; 400 for (int32 i = 0; authors[i]; i++) 401 text << " " << authors[i] << "\n"; 402 403 fAboutView->InfoView()->Insert(text.String()); 404} 405 406 407void 408BAboutWindow::AddSpecialThanks(const char** thanks) 409{ 410 if (thanks == NULL) 411 return; 412 413 const char* specialThanks = B_TRANSLATE_MARK("Special Thanks:"); 414 specialThanks = gSystemCatalog.GetString(specialThanks, "AboutWindow"); 415 416 BString text(""); 417 if (fAboutView->InfoView()->TextLength() > 0) 418 text << "\n\n"; 419 420 text << specialThanks << "\n"; 421 for (int32 i = 0; thanks[i]; i++) 422 text << " " << thanks[i] << "\n"; 423 424 fAboutView->InfoView()->Insert(text.String()); 425} 426 427 428void 429BAboutWindow::AddVersionHistory(const char** history) 430{ 431 if (history == NULL) 432 return; 433 434 const char* versionHistory = B_TRANSLATE_MARK("Version history:"); 435 versionHistory = gSystemCatalog.GetString(versionHistory, "AboutWindow"); 436 437 BString text(""); 438 if (fAboutView->InfoView()->TextLength() > 0) 439 text << "\n\n"; 440 441 text << versionHistory << "\n"; 442 for (int32 i = 0; history[i]; i++) 443 text << " " << history[i] << "\n"; 444 445 fAboutView->InfoView()->Insert(text.String()); 446} 447 448 449void 450BAboutWindow::AddExtraInfo(const char* extraInfo) 451{ 452 if (extraInfo == NULL) 453 return; 454 455 const char* appExtraInfo = B_TRANSLATE_MARK(extraInfo); 456 appExtraInfo = gSystemCatalog.GetString(extraInfo, "AboutWindow"); 457 458 BString extra(""); 459 if (fAboutView->InfoView()->TextLength() > 0) 460 extra << "\n\n"; 461 462 extra << appExtraInfo; 463 464 fAboutView->InfoView()->Insert(extra.String()); 465} 466