#include "BackgroundImage.h"
#include <new>
#include <stdlib.h>
#include <Bitmap.h>
#include <Debug.h>
#include <fs_attr.h>
#include <Node.h>
#include <TranslationKit.h>
#include <View.h>
#include <Window.h>
#include <Message.h>
#include <Entry.h>
#include <Path.h>
#include <Screen.h>
#include <String.h>
#include "BackgroundsView.h"
const char* kBackgroundImageInfo = "be:bgndimginfo";
const char* kBackgroundImageInfoOffset = "be:bgndimginfooffset";
const char* kBackgroundImageInfoTextOutline = "be:bgndimginfoerasetext";
const char* kBackgroundImageInfoMode = "be:bgndimginfomode";
const char* kBackgroundImageInfoWorkspaces = "be:bgndimginfoworkspaces";
const char* kBackgroundImageInfoPath = "be:bgndimginfopath";
const char* kBackgroundImageInfoSet = "be:bgndimginfoset";
const char* kBackgroundImageInfoCacheMode = "be:bgndimginfocachemode";
const char* kBackgroundImageSetPeriod = "be:bgndimgsetperiod";
const char* kBackgroundImageRandomChange = "be:bgndimgrandomchange";
const char* kBackgroundImageCacheMode = "be:bgndimgcachemode";
BackgroundImage*
BackgroundImage::GetBackgroundImage(const BNode* node, bool isDesktop,
BackgroundsView* view)
{
BackgroundImage* result = new BackgroundImage(node, isDesktop, view);
attr_info info;
if (node->GetAttrInfo(kBackgroundImageInfo, &info) != B_OK)
return result;
BMessage container;
char* buffer = new char [info.size];
status_t error = node->ReadAttr(kBackgroundImageInfo, info.type, 0, buffer,
(size_t)info.size);
if (error == info.size)
error = container.Unflatten(buffer);
delete [] buffer;
if (error != B_OK)
return result;
PRINT_OBJECT(container);
uint32 imageSetPeriod = 0;
uint32 globalCacheMode = 0;
bool randomChange = false;
uint32 maxImageSet = 0;
if (isDesktop) {
container.FindInt32(kBackgroundImageSetPeriod, (int32*)&imageSetPeriod);
container.FindInt32(kBackgroundImageCacheMode,
(int32*)&globalCacheMode);
container.FindBool(kBackgroundImageRandomChange, &randomChange);
}
for (int32 index = 0; ; index++) {
const char* path;
uint32 workspaces = B_ALL_WORKSPACES;
Mode mode = kTiled;
bool textWidgetLabelOutline = false;
BPoint offset;
uint32 imageSet = 0;
uint32 cacheMode = 0;
int32 imageIndex = -1;
if (container.FindString(kBackgroundImageInfoPath, index, &path)
== B_OK) {
if (strcmp(path, "")) {
BPath bpath(path);
imageIndex = view->AddImage(bpath);
if (imageIndex < 0) {
imageIndex = -imageIndex - 1;
}
}
} else
break;
container.FindInt32(kBackgroundImageInfoWorkspaces, index,
(int32*)&workspaces);
container.FindInt32(kBackgroundImageInfoMode, index, (int32*)&mode);
container.FindBool(kBackgroundImageInfoTextOutline, index,
&textWidgetLabelOutline);
container.FindPoint(kBackgroundImageInfoOffset, index, &offset);
if (isDesktop) {
container.FindInt32(kBackgroundImageInfoSet, index,
(int32*)&imageSet);
container.FindInt32(kBackgroundImageInfoCacheMode, index,
(int32*)&cacheMode);
}
BackgroundImage::BackgroundImageInfo* imageInfo = new
BackgroundImage::BackgroundImageInfo(workspaces, imageIndex,
mode, offset, textWidgetLabelOutline, imageSet, cacheMode);
if (imageSet > maxImageSet)
maxImageSet = imageSet;
result->Add(imageInfo);
}
if (result) {
result->fImageSetCount = maxImageSet + 1;
result->fRandomChange = randomChange;
result->fImageSetPeriod = imageSetPeriod;
result->fCacheMode = globalCacheMode;
if (result->fImageSetCount > 1)
result->fShowingImageSet = random() % result->fImageSetCount;
}
return result;
}
BackgroundImage::BackgroundImageInfo::BackgroundImageInfo(uint32 workspaces,
int32 imageIndex, Mode mode, BPoint offset, bool textWidgetLabelOutline,
uint32 imageSet, uint32 cacheMode)
:
fWorkspace(workspaces),
fImageIndex(imageIndex),
fMode(mode),
fOffset(offset),
fTextWidgetLabelOutline(textWidgetLabelOutline),
fImageSet(imageSet),
fCacheMode(cacheMode)
{
}
BackgroundImage::BackgroundImageInfo::~BackgroundImageInfo()
{
}
BackgroundImage::BackgroundImage(const BNode* node, bool desktop,
BackgroundsView* view)
:
fIsDesktop(desktop),
fDefinedByNode(*node),
fView(NULL),
fBackgroundsView(view),
fShowingBitmap(NULL),
fBitmapForWorkspaceList(1),
fImageSetPeriod(0),
fShowingImageSet(0),
fImageSetCount(0),
fCacheMode(0),
fRandomChange(false)
{
}
BackgroundImage::~BackgroundImage()
{
}
void
BackgroundImage::Add(BackgroundImageInfo* info)
{
fBitmapForWorkspaceList.AddItem(info);
}
void
BackgroundImage::Remove(BackgroundImageInfo* info)
{
fBitmapForWorkspaceList.RemoveItem(info);
}
void
BackgroundImage::RemoveAll()
{
for (int32 index = 0; index < fBitmapForWorkspaceList.CountItems();) {
BackgroundImageInfo* info = fBitmapForWorkspaceList.ItemAt(index);
if (info->fImageSet != fShowingImageSet)
index++;
else
fBitmapForWorkspaceList.RemoveItemAt(index);
}
}
void
BackgroundImage::Show(BView* view, int32 workspace)
{
fView = view;
BackgroundImageInfo* info = ImageInfoForWorkspace(workspace);
if (info) {
Show(info, fView);
}
}
void
BackgroundImage::Show(BackgroundImageInfo* info, BView* view)
{
BBitmap* bitmap
= fBackgroundsView->GetImage(info->fImageIndex)->GetBitmap();
if (!bitmap)
return;
BRect viewBounds(view->Bounds());
display_mode mode;
BScreen().GetMode(&mode);
float x_ratio = viewBounds.Width() / mode.virtual_width;
float y_ratio = viewBounds.Height() / mode.virtual_height;
BRect bitmapBounds(bitmap->Bounds());
BRect destinationBitmapBounds(bitmapBounds);
destinationBitmapBounds.right *= x_ratio;
destinationBitmapBounds.bottom *= y_ratio;
BPoint offset(info->fOffset);
offset.x *= x_ratio;
offset.y *= y_ratio;
uint32 options = 0;
uint32 followFlags = B_FOLLOW_TOP | B_FOLLOW_LEFT;
switch (info->fMode) {
case kCentered:
if (fIsDesktop) {
destinationBitmapBounds.OffsetBy(
(viewBounds.Width() - destinationBitmapBounds.Width()) / 2,
(viewBounds.Height() - destinationBitmapBounds.Height())
/ 2);
break;
}
case kScaledToFit:
if (fIsDesktop) {
if (BRectRatio(destinationBitmapBounds)
>= BRectRatio(viewBounds)) {
float overlap = BRectHorizontalOverlap(viewBounds,
destinationBitmapBounds);
destinationBitmapBounds.Set(-overlap, 0,
viewBounds.Width() + overlap, viewBounds.Height());
} else {
float overlap = BRectVerticalOverlap(viewBounds,
destinationBitmapBounds);
destinationBitmapBounds.Set(0, -overlap,
viewBounds.Width(), viewBounds.Height() + overlap);
}
followFlags = B_FOLLOW_ALL;
options |= B_FILTER_BITMAP_BILINEAR;
break;
}
case kAtOffset:
{
destinationBitmapBounds.OffsetTo(offset);
break;
}
case kTiled:
destinationBitmapBounds.OffsetBy(
(viewBounds.Width() - destinationBitmapBounds.Width()) / 2,
(viewBounds.Height() - destinationBitmapBounds.Height()) / 2);
options |= B_TILE_BITMAP;
break;
}
view->SetViewBitmap(bitmap, bitmapBounds, destinationBitmapBounds,
followFlags, options);
view->Invalidate();
}
float
BackgroundImage::BRectRatio(BRect rect)
{
return rect.Width() / rect.Height();
}
float
BackgroundImage::BRectHorizontalOverlap(BRect hostRect, BRect resizedRect)
{
return ((hostRect.Height() / resizedRect.Height() * resizedRect.Width())
- hostRect.Width()) / 2;
}
float
BackgroundImage::BRectVerticalOverlap(BRect hostRect, BRect resizedRect)
{
return ((hostRect.Width() / resizedRect.Width() * resizedRect.Height())
- hostRect.Height()) / 2;
}
void
BackgroundImage::Remove()
{
if (fShowingBitmap) {
fView->ClearViewBitmap();
fView->Invalidate();
}
fShowingBitmap = NULL;
}
BackgroundImage::BackgroundImageInfo*
BackgroundImage::ImageInfoForWorkspace(int32 workspace) const
{
uint32 workspaceMask = 1;
for (; workspace; workspace--)
workspaceMask *= 2;
int32 count = fBitmapForWorkspaceList.CountItems();
BackgroundImageInfo* result = NULL;
for (int32 index = 0; index < count; index++) {
BackgroundImageInfo* info = fBitmapForWorkspaceList.ItemAt(index);
if (info->fImageSet != fShowingImageSet)
continue;
if (fIsDesktop) {
if (info->fWorkspace == workspaceMask)
return info;
if (info->fWorkspace & workspaceMask)
result = info;
} else
return info;
}
return result;
}
void
BackgroundImage::WorkspaceActivated(BView* view, int32 workspace, bool state)
{
if (!fIsDesktop) {
return;
}
if (!state) {
return;
}
BackgroundImageInfo* info = ImageInfoForWorkspace(workspace);
if (info != fShowingBitmap) {
if (info)
Show(info, view);
else {
view->ClearViewBitmap();
view->Invalidate();
}
fShowingBitmap = info;
}
}
void
BackgroundImage::ScreenChanged(BRect, color_space)
{
if (!fIsDesktop || !fShowingBitmap)
return;
}
status_t
BackgroundImage::SetBackgroundImage(BNode* node)
{
status_t err;
BMessage container;
int32 count = fBitmapForWorkspaceList.CountItems();
for (int32 index = 0; index < count; index++) {
BackgroundImageInfo* info = fBitmapForWorkspaceList.ItemAt(index);
container.AddBool(kBackgroundImageInfoTextOutline,
info->fTextWidgetLabelOutline);
if (fBackgroundsView->GetImage(info->fImageIndex) != NULL) {
container.AddString(kBackgroundImageInfoPath,
fBackgroundsView
->GetImage(info->fImageIndex)->GetPath().Path());
} else
container.AddString(kBackgroundImageInfoPath, "");
container.AddInt32(kBackgroundImageInfoWorkspaces, info->fWorkspace);
container.AddPoint(kBackgroundImageInfoOffset, info->fOffset);
container.AddInt32(kBackgroundImageInfoMode, info->fMode);
if (fIsDesktop)
container.AddInt32(kBackgroundImageInfoSet, info->fImageSet);
}
PRINT_OBJECT(container);
ssize_t flattenedSize = container.FlattenedSize();
if (flattenedSize < B_OK)
return flattenedSize;
char* buffer = new(std::nothrow) char[flattenedSize];
if (buffer == NULL)
return B_NO_MEMORY;
if ((err = container.Flatten(buffer, flattenedSize)) != B_OK) {
delete[] buffer;
return err;
}
ssize_t size = node->WriteAttr(kBackgroundImageInfo, B_MESSAGE_TYPE,
0, buffer, flattenedSize);
delete[] buffer;
if (size < B_OK)
return size;
if (size != flattenedSize)
return B_ERROR;
return B_OK;
}
Image::Image(BPath path)
:
fBitmap(NULL),
fPath(path)
{
const int32 kMaxNameChars = 40;
fName = path.Leaf();
int extra = fName.CountChars() - kMaxNameChars;
if (extra > 0) {
BString extension;
int offset = fName.FindLast('.');
if (offset > 0)
fName.CopyInto(extension, ++offset, -1);
fName.TruncateChars(kMaxNameChars) << B_UTF8_ELLIPSIS << extension;
}
}
Image::~Image()
{
delete fBitmap;
}
BBitmap*
Image::GetBitmap()
{
if (!fBitmap)
fBitmap = BTranslationUtils::GetBitmap(fPath.Path());
return fBitmap;
}