1/* 2Open Tracker License 3 4Terms and Conditions 5 6Copyright (c) 1991-2000, Be Incorporated. All rights reserved. 7 8Permission is hereby granted, free of charge, to any person obtaining a copy of 9this software and associated documentation files (the "Software"), to deal in 10the Software without restriction, including without limitation the rights to 11use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12of the Software, and to permit persons to whom the Software is furnished to do 13so, subject to the following conditions: 14 15The above copyright notice and this permission notice applies to all licensees 16and shall be included in all copies or substantial portions of the Software. 17 18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, 20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 22AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION 23WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25Except as contained in this notice, the name of Be Incorporated shall not be 26used in advertising or otherwise to promote the sale, use or other dealings in 27this Software without prior written authorization from Be Incorporated. 28 29Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks 30of Be Incorporated in the United States and other countries. Other brand product 31names are registered trademarks or trademarks of their respective holders. 32All rights reserved. 33*/ 34 35// Classes used for setting up and managing background images 36// 37 38#include "BackgroundImage.h" 39 40#include <new> 41#include <stdlib.h> 42 43#include <Bitmap.h> 44#include <Debug.h> 45#include <fs_attr.h> 46#include <Node.h> 47#include <TranslationKit.h> 48#include <View.h> 49#include <Window.h> 50#include <Message.h> 51#include <Entry.h> 52#include <Path.h> 53#include <Screen.h> 54#include <String.h> 55 56#include "BackgroundsView.h" 57 58 59const char* kBackgroundImageInfo = "be:bgndimginfo"; 60const char* kBackgroundImageInfoOffset = "be:bgndimginfooffset"; 61// const char* kBackgroundImageInfoTextOutline = "be:bgndimginfotextoutline"; 62const char* kBackgroundImageInfoTextOutline = "be:bgndimginfoerasetext"; 63// NOTE: the attribute keeps the old name for backwards compatibility, 64// just in case some users spend time configuring a few windows with 65// this feature on or off... 66const char* kBackgroundImageInfoMode = "be:bgndimginfomode"; 67const char* kBackgroundImageInfoWorkspaces = "be:bgndimginfoworkspaces"; 68const char* kBackgroundImageInfoPath = "be:bgndimginfopath"; 69const char* kBackgroundImageInfoSet = "be:bgndimginfoset"; 70const char* kBackgroundImageInfoCacheMode = "be:bgndimginfocachemode"; 71const char* kBackgroundImageSetPeriod = "be:bgndimgsetperiod"; 72const char* kBackgroundImageRandomChange = "be:bgndimgrandomchange"; 73const char* kBackgroundImageCacheMode = "be:bgndimgcachemode"; 74 75 76BackgroundImage* 77BackgroundImage::GetBackgroundImage(const BNode* node, bool isDesktop, 78 BackgroundsView* view) 79{ 80 BackgroundImage* result = new BackgroundImage(node, isDesktop, view); 81 attr_info info; 82 if (node->GetAttrInfo(kBackgroundImageInfo, &info) != B_OK) 83 return result; 84 85 BMessage container; 86 char* buffer = new char [info.size]; 87 88 status_t error = node->ReadAttr(kBackgroundImageInfo, info.type, 0, buffer, 89 (size_t)info.size); 90 if (error == info.size) 91 error = container.Unflatten(buffer); 92 93 delete [] buffer; 94 95 if (error != B_OK) 96 return result; 97 98 PRINT_OBJECT(container); 99 100 uint32 imageSetPeriod = 0; 101 uint32 globalCacheMode = 0; 102 bool randomChange = false; 103 uint32 maxImageSet = 0; 104 105 if (isDesktop) { 106 container.FindInt32(kBackgroundImageSetPeriod, (int32*)&imageSetPeriod); 107 container.FindInt32(kBackgroundImageCacheMode, 108 (int32*)&globalCacheMode); 109 container.FindBool(kBackgroundImageRandomChange, &randomChange); 110 } 111 112 for (int32 index = 0; ; index++) { 113 const char* path; 114 uint32 workspaces = B_ALL_WORKSPACES; 115 Mode mode = kTiled; 116 bool textWidgetLabelOutline = false; 117 BPoint offset; 118 uint32 imageSet = 0; 119 uint32 cacheMode = 0; 120 int32 imageIndex = -1; 121 122 if (container.FindString(kBackgroundImageInfoPath, index, &path) 123 == B_OK) { 124 if (strcmp(path, "")) { 125 BPath bpath(path); 126 imageIndex = view->AddImage(bpath); 127 if (imageIndex < 0) { 128 imageIndex = -imageIndex - 1; 129 } 130 } 131 } else 132 break; 133 134 container.FindInt32(kBackgroundImageInfoWorkspaces, index, 135 (int32*)&workspaces); 136 container.FindInt32(kBackgroundImageInfoMode, index, (int32*)&mode); 137 container.FindBool(kBackgroundImageInfoTextOutline, index, 138 &textWidgetLabelOutline); 139 container.FindPoint(kBackgroundImageInfoOffset, index, &offset); 140 141 if (isDesktop) { 142 container.FindInt32(kBackgroundImageInfoSet, index, 143 (int32*)&imageSet); 144 container.FindInt32(kBackgroundImageInfoCacheMode, index, 145 (int32*)&cacheMode); 146 } 147 148 BackgroundImage::BackgroundImageInfo* imageInfo = new 149 BackgroundImage::BackgroundImageInfo(workspaces, imageIndex, 150 mode, offset, textWidgetLabelOutline, imageSet, cacheMode); 151 152 // imageInfo->UnloadBitmap(globalCacheMode); 153 154 if (imageSet > maxImageSet) 155 maxImageSet = imageSet; 156 157 result->Add(imageInfo); 158 } 159 160 if (result) { 161 result->fImageSetCount = maxImageSet + 1; 162 result->fRandomChange = randomChange; 163 result->fImageSetPeriod = imageSetPeriod; 164 result->fCacheMode = globalCacheMode; 165 if (result->fImageSetCount > 1) 166 result->fShowingImageSet = random() % result->fImageSetCount; 167 } 168 169 return result; 170} 171 172 173BackgroundImage::BackgroundImageInfo::BackgroundImageInfo(uint32 workspaces, 174 int32 imageIndex, Mode mode, BPoint offset, bool textWidgetLabelOutline, 175 uint32 imageSet, uint32 cacheMode) 176 : 177 fWorkspace(workspaces), 178 fImageIndex(imageIndex), 179 fMode(mode), 180 fOffset(offset), 181 fTextWidgetLabelOutline(textWidgetLabelOutline), 182 fImageSet(imageSet), 183 fCacheMode(cacheMode) 184{ 185} 186 187 188BackgroundImage::BackgroundImageInfo::~BackgroundImageInfo() 189{ 190} 191 192 193// #pragma mark - 194 195 196BackgroundImage::BackgroundImage(const BNode* node, bool desktop, 197 BackgroundsView* view) 198 : 199 fIsDesktop(desktop), 200 fDefinedByNode(*node), 201 fView(NULL), 202 fBackgroundsView(view), 203 fShowingBitmap(NULL), 204 fBitmapForWorkspaceList(1, true), 205 fImageSetPeriod(0), 206 fShowingImageSet(0), 207 fImageSetCount(0), 208 fCacheMode(0), 209 fRandomChange(false) 210{ 211} 212 213 214BackgroundImage::~BackgroundImage() 215{ 216} 217 218 219void 220BackgroundImage::Add(BackgroundImageInfo* info) 221{ 222 fBitmapForWorkspaceList.AddItem(info); 223} 224 225 226void 227BackgroundImage::Remove(BackgroundImageInfo* info) 228{ 229 fBitmapForWorkspaceList.RemoveItem(info); 230} 231 232 233void 234BackgroundImage::RemoveAll() 235{ 236 for (int32 index = 0; index < fBitmapForWorkspaceList.CountItems();) { 237 BackgroundImageInfo* info = fBitmapForWorkspaceList.ItemAt(index); 238 if (info->fImageSet != fShowingImageSet) 239 index++; 240 else 241 fBitmapForWorkspaceList.RemoveItemAt(index); 242 } 243} 244 245 246void 247BackgroundImage::Show(BView* view, int32 workspace) 248{ 249 fView = view; 250 251 BackgroundImageInfo* info = ImageInfoForWorkspace(workspace); 252 if (info) { 253 /*BPoseView* poseView = dynamic_cast<BPoseView*>(fView); 254 if (poseView) 255 poseView 256 ->SetEraseWidgetTextBackground(info->fTextWidgetLabelOutline);*/ 257 Show(info, fView); 258 } 259} 260 261 262void 263BackgroundImage::Show(BackgroundImageInfo* info, BView* view) 264{ 265 BBitmap* bitmap 266 = fBackgroundsView->GetImage(info->fImageIndex)->GetBitmap(); 267 268 if (!bitmap) 269 return; 270 271 BRect viewBounds(view->Bounds()); 272 273 display_mode mode; 274 BScreen().GetMode(&mode); 275 float x_ratio = viewBounds.Width() / mode.virtual_width; 276 float y_ratio = viewBounds.Height() / mode.virtual_height; 277 278 BRect bitmapBounds(bitmap->Bounds()); 279 BRect destinationBitmapBounds(bitmapBounds); 280 destinationBitmapBounds.right *= x_ratio; 281 destinationBitmapBounds.bottom *= y_ratio; 282 BPoint offset(info->fOffset); 283 offset.x *= x_ratio; 284 offset.y *= y_ratio; 285 286 uint32 options = 0; 287 uint32 followFlags = B_FOLLOW_TOP | B_FOLLOW_LEFT; 288 289 // figure out the display mode and the destination bounds for the bitmap 290 switch (info->fMode) { 291 case kCentered: 292 if (fIsDesktop) { 293 destinationBitmapBounds.OffsetBy( 294 (viewBounds.Width() - destinationBitmapBounds.Width()) / 2, 295 (viewBounds.Height() - destinationBitmapBounds.Height()) 296 / 2); 297 break; 298 } 299 // else fall thru 300 case kScaledToFit: 301 if (fIsDesktop) { 302 if (BRectRatio(destinationBitmapBounds) 303 >= BRectRatio(viewBounds)) { 304 float overlap = BRectHorizontalOverlap(viewBounds, 305 destinationBitmapBounds); 306 destinationBitmapBounds.Set(-overlap, 0, 307 viewBounds.Width() + overlap, viewBounds.Height()); 308 } else { 309 float overlap = BRectVerticalOverlap(viewBounds, 310 destinationBitmapBounds); 311 destinationBitmapBounds.Set(0, -overlap, 312 viewBounds.Width(), viewBounds.Height() + overlap); 313 } 314 followFlags = B_FOLLOW_ALL; 315 options |= B_FILTER_BITMAP_BILINEAR; 316 break; 317 } 318 // else fall thru 319 case kAtOffset: 320 { 321 destinationBitmapBounds.OffsetTo(offset); 322 break; 323 } 324 case kTiled: 325 // Original Backgrounds Preferences center the tiled paper 326 // but Tracker doesn't do that 327 //if (fIsDesktop) { 328 destinationBitmapBounds.OffsetBy( 329 (viewBounds.Width() - destinationBitmapBounds.Width()) / 2, 330 (viewBounds.Height() - destinationBitmapBounds.Height()) / 2); 331 //} 332 options |= B_TILE_BITMAP; 333 break; 334 } 335 336 // switch to the bitmap and force a redraw 337 view->SetViewBitmap(bitmap, bitmapBounds, destinationBitmapBounds, 338 followFlags, options); 339 view->Invalidate(); 340 341 /*if (fShowingBitmap != info) { 342 if (fShowingBitmap) 343 fShowingBitmap->UnloadBitmap(fCacheMode); 344 fShowingBitmap = info; 345 }*/ 346} 347 348 349float 350BackgroundImage::BRectRatio(BRect rect) 351{ 352 return rect.Width() / rect.Height(); 353} 354 355 356float 357BackgroundImage::BRectHorizontalOverlap(BRect hostRect, BRect resizedRect) 358{ 359 return ((hostRect.Height() / resizedRect.Height() * resizedRect.Width()) 360 - hostRect.Width()) / 2; 361} 362 363 364float 365BackgroundImage::BRectVerticalOverlap(BRect hostRect, BRect resizedRect) 366{ 367 return ((hostRect.Width() / resizedRect.Width() * resizedRect.Height()) 368 - hostRect.Height()) / 2; 369} 370 371 372void 373BackgroundImage::Remove() 374{ 375 if (fShowingBitmap) { 376 fView->ClearViewBitmap(); 377 fView->Invalidate(); 378 /*BPoseView* poseView = dynamic_cast<BPoseView*>(fView); 379 // make sure text widgets draw the default way, erasing their background 380 if (poseView) 381 poseView->SetEraseWidgetTextBackground(true);*/ 382 } 383 fShowingBitmap = NULL; 384} 385 386 387BackgroundImage::BackgroundImageInfo* 388BackgroundImage::ImageInfoForWorkspace(int32 workspace) const 389{ 390 uint32 workspaceMask = 1; 391 392 for (; workspace; workspace--) 393 workspaceMask *= 2; 394 395 int32 count = fBitmapForWorkspaceList.CountItems(); 396 397 // do a simple lookup for the most likely candidate bitmap - 398 // pick the imageInfo that is only defined for this workspace over one 399 // that supports multiple workspaces 400 BackgroundImageInfo* result = NULL; 401 for (int32 index = 0; index < count; index++) { 402 BackgroundImageInfo* info = fBitmapForWorkspaceList.ItemAt(index); 403 if (info->fImageSet != fShowingImageSet) 404 continue; 405 406 if (fIsDesktop) { 407 if (info->fWorkspace == workspaceMask) 408 return info; 409 410 if (info->fWorkspace & workspaceMask) 411 result = info; 412 } else 413 return info; 414 } 415 return result; 416} 417 418 419void 420BackgroundImage::WorkspaceActivated(BView* view, int32 workspace, bool state) 421{ 422 if (!fIsDesktop) { 423 // we only care for desktop bitmaps 424 return; 425 } 426 427 if (!state) { 428 // we only care comming into a new workspace, not leaving one 429 return; 430 } 431 432 BackgroundImageInfo* info = ImageInfoForWorkspace(workspace); 433 if (info != fShowingBitmap) { 434 if (info) 435 Show(info, view); 436 else { 437 /*if (BPoseView* poseView = dynamic_cast<BPoseView*>(view)) 438 poseView->SetEraseWidgetTextBackground(true);*/ 439 view->ClearViewBitmap(); 440 view->Invalidate(); 441 } 442 fShowingBitmap = info; 443 } 444} 445 446 447void 448BackgroundImage::ScreenChanged(BRect, color_space) 449{ 450 if (!fIsDesktop || !fShowingBitmap) 451 return; 452 453 /*if (fShowingBitmap->fMode == kCentered) { 454 BRect viewBounds(fView->Bounds()); 455 BRect bitmapBounds(fShowingBitmap->fBitmap->Bounds()); 456 BRect destinationBitmapBounds(bitmapBounds); 457 destinationBitmapBounds.OffsetBy( 458 (viewBounds.Width() - bitmapBounds.Width()) / 2, 459 (viewBounds.Height() - bitmapBounds.Height()) / 2); 460 461 fView->SetViewBitmap(fShowingBitmap->fBitmap, bitmapBounds, 462 destinationBitmapBounds, B_FOLLOW_NONE, 0); 463 fView->Invalidate(); 464 }*/ 465} 466 467 468status_t 469BackgroundImage::SetBackgroundImage(BNode* node) 470{ 471 status_t err; 472 BMessage container; 473 int32 count = fBitmapForWorkspaceList.CountItems(); 474 475 for (int32 index = 0; index < count; index++) { 476 BackgroundImageInfo* info = fBitmapForWorkspaceList.ItemAt(index); 477 478 container.AddBool(kBackgroundImageInfoTextOutline, 479 info->fTextWidgetLabelOutline); 480 if (fBackgroundsView->GetImage(info->fImageIndex) != NULL) { 481 container.AddString(kBackgroundImageInfoPath, 482 fBackgroundsView 483 ->GetImage(info->fImageIndex)->GetPath().Path()); 484 } else 485 container.AddString(kBackgroundImageInfoPath, ""); 486 487 container.AddInt32(kBackgroundImageInfoWorkspaces, info->fWorkspace); 488 container.AddPoint(kBackgroundImageInfoOffset, info->fOffset); 489 container.AddInt32(kBackgroundImageInfoMode, info->fMode); 490 491 if (fIsDesktop) 492 container.AddInt32(kBackgroundImageInfoSet, info->fImageSet); 493 } 494 495 PRINT_OBJECT(container); 496 497 ssize_t flattenedSize = container.FlattenedSize(); 498 if (flattenedSize < B_OK) 499 return flattenedSize; 500 501 char* buffer = new(std::nothrow) char[flattenedSize]; 502 if (buffer == NULL) 503 return B_NO_MEMORY; 504 505 if ((err = container.Flatten(buffer, flattenedSize)) != B_OK) { 506 delete[] buffer; 507 return err; 508 } 509 510 ssize_t size = node->WriteAttr(kBackgroundImageInfo, B_MESSAGE_TYPE, 511 0, buffer, flattenedSize); 512 513 delete[] buffer; 514 515 if (size < B_OK) 516 return size; 517 if (size != flattenedSize) 518 return B_ERROR; 519 520 return B_OK; 521} 522 523 524/*BackgroundImage* 525BackgroundImage::Refresh(BackgroundImage* oldBackgroundImage, 526 const BNode* fromNode, bool desktop, BPoseView* poseView) 527{ 528 if (oldBackgroundImage) { 529 oldBackgroundImage->Remove(); 530 delete oldBackgroundImage; 531 } 532 533 BackgroundImage* result = GetBackgroundImage(fromNode, desktop); 534 if (result && poseView->ViewMode() != kListMode) 535 result->Show(poseView, current_workspace()); 536 return result; 537} 538 539 540void 541BackgroundImage::ChangeImageSet(BPoseView* poseView) 542{ 543 if (fRandomChange) { 544 if (fImageSetCount > 1) { 545 uint32 oldShowingImageSet = fShowingImageSet; 546 while (oldShowingImageSet == fShowingImageSet) 547 fShowingImageSet = random()%fImageSetCount; 548 } else 549 fShowingImageSet = 0; 550 } else { 551 fShowingImageSet++; 552 if (fShowingImageSet > fImageSetCount - 1) 553 fShowingImageSet = 0; 554 } 555 556 this->Show(poseView, current_workspace()); 557}*/ 558 559 560// #pragma mark - 561 562 563Image::Image(BPath path) 564 : 565 fBitmap(NULL), 566 fPath(path) 567{ 568 const int32 kMaxNameChars = 40; 569 fName = path.Leaf(); 570 int extra = fName.CountChars() - kMaxNameChars; 571 if (extra > 0) { 572 BString extension; 573 int offset = fName.FindLast('.'); 574 if (offset > 0) 575 fName.CopyInto(extension, ++offset, -1); 576 fName.TruncateChars(kMaxNameChars) << B_UTF8_ELLIPSIS << extension; 577 } 578} 579 580 581Image::~Image() 582{ 583 delete fBitmap; 584} 585 586 587BBitmap* 588Image::GetBitmap() 589{ 590 if (!fBitmap) 591 fBitmap = BTranslationUtils::GetBitmap(fPath.Path()); 592 593 return fBitmap; 594} 595 596