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 <Bitmap.h>
39#include <ControlLook.h>
40#include <Node.h>
41#include <TranslationKit.h>
42#include <View.h>
43#include <Window.h>
44
45#include <fs_attr.h>
46
47#include "BackgroundImage.h"
48
49#include "Background.h"
50#include "Commands.h"
51#include "PoseView.h"
52
53
54namespace BPrivate {
55
56const char* kBackgroundImageInfo 			= B_BACKGROUND_INFO;
57const char* kBackgroundImageInfoOffset 		= B_BACKGROUND_ORIGIN;
58const char* kBackgroundImageInfoTextOutline	= B_BACKGROUND_TEXT_OUTLINE;
59const char* kBackgroundImageInfoMode 		= B_BACKGROUND_MODE;
60const char* kBackgroundImageInfoWorkspaces 	= B_BACKGROUND_WORKSPACES;
61const char* kBackgroundImageInfoPath 		= B_BACKGROUND_IMAGE;
62
63}	// namespace BPrivate
64
65
66//	#pragma mark - BackgroundImage
67
68
69BackgroundImage*
70BackgroundImage::GetBackgroundImage(const BNode* node, bool isDesktop)
71{
72	attr_info info;
73	if (node->GetAttrInfo(kBackgroundImageInfo, &info) != B_OK)
74		return NULL;
75
76	BMessage container;
77	char* buffer = new char[info.size];
78
79	status_t error = node->ReadAttr(kBackgroundImageInfo, info.type, 0,
80		buffer, (size_t)info.size);
81	if (error == info.size)
82		error = container.Unflatten(buffer);
83
84	delete[] buffer;
85
86	if (error != B_OK)
87		return NULL;
88
89	BackgroundImage* backgroundImage = NULL;
90	for (int32 index = 0; ; index++) {
91		const char* path;
92		uint32 workspaces = B_ALL_WORKSPACES;
93		Mode mode = kTiled;
94		bool textWidgetLabelOutline = false;
95		BPoint offset;
96		BBitmap* bitmap = NULL;
97
98		if (container.FindString(kBackgroundImageInfoPath, index, &path)
99				== B_OK) {
100			bitmap = BTranslationUtils::GetBitmap(path);
101			if (!bitmap)
102				PRINT(("failed to load background bitmap from path\n"));
103		} else
104			break;
105
106		if (isDesktop)
107			be_control_look->SetBackgroundInfo(container);
108
109		container.FindInt32(kBackgroundImageInfoWorkspaces, index,
110			(int32*)&workspaces);
111		container.FindInt32(kBackgroundImageInfoMode, index, (int32*)&mode);
112		container.FindBool(kBackgroundImageInfoTextOutline, index,
113			&textWidgetLabelOutline);
114		container.FindPoint(kBackgroundImageInfoOffset, index, &offset);
115
116		BackgroundImage::BackgroundImageInfo* imageInfo = new
117			BackgroundImage::BackgroundImageInfo(workspaces, bitmap, mode,
118				offset, textWidgetLabelOutline);
119
120		if (backgroundImage == NULL)
121			backgroundImage = new BackgroundImage(node, isDesktop);
122
123		backgroundImage->Add(imageInfo);
124	}
125
126	return backgroundImage;
127}
128
129
130BackgroundImage::BackgroundImageInfo::BackgroundImageInfo(uint32 workspaces,
131	BBitmap* bitmap, Mode mode, BPoint offset, bool textWidgetOutline)
132	:
133	fWorkspace(workspaces),
134	fBitmap(bitmap),
135	fMode(mode),
136	fOffset(offset),
137	fTextWidgetOutline(textWidgetOutline)
138{
139}
140
141
142BackgroundImage::BackgroundImageInfo::~BackgroundImageInfo()
143{
144	delete fBitmap;
145}
146
147
148BackgroundImage::BackgroundImage(const BNode* node, bool desktop)
149	:
150	fIsDesktop(desktop),
151	fDefinedByNode(*node),
152	fView(NULL),
153	fShowingBitmap(NULL),
154	fBitmapForWorkspaceList(1, true)
155{
156}
157
158
159BackgroundImage::~BackgroundImage()
160{
161}
162
163
164void
165BackgroundImage::Add(BackgroundImageInfo* info)
166{
167	fBitmapForWorkspaceList.AddItem(info);
168}
169
170
171void
172BackgroundImage::Show(BView* view, int32 workspace)
173{
174	fView = view;
175
176	BackgroundImageInfo* info = ImageInfoForWorkspace(workspace);
177	if (info) {
178		BPoseView* poseView = dynamic_cast<BPoseView*>(fView);
179		if (poseView != NULL)
180			poseView->SetWidgetTextOutline(info->fTextWidgetOutline);
181
182		Show(info, fView);
183	}
184}
185
186
187void
188BackgroundImage::Show(BackgroundImageInfo* info, BView* view)
189{
190	BPoseView* poseView = dynamic_cast<BPoseView*>(view);
191	if (poseView != NULL)
192		poseView->SetWidgetTextOutline(info->fTextWidgetOutline);
193
194	if (info->fBitmap == NULL) {
195		view->ClearViewBitmap();
196		view->Invalidate();
197		fShowingBitmap = info;
198		return;
199	}
200	BRect viewBounds(view->Bounds());
201	BRect bitmapBounds(info->fBitmap->Bounds());
202	BRect destinationBitmapBounds(bitmapBounds);
203
204	uint32 options = 0;
205	uint32 followFlags = B_FOLLOW_TOP | B_FOLLOW_LEFT;
206
207	// figure out the display mode and the destination bounds for the bitmap
208	switch (info->fMode) {
209		case kCentered:
210			if (fIsDesktop) {
211				destinationBitmapBounds.OffsetBy(
212					(viewBounds.Width() - bitmapBounds.Width()) / 2,
213					(viewBounds.Height() - bitmapBounds.Height()) / 2);
214				break;
215			}
216			// else fall thru
217		case kScaledToFit:
218			if (fIsDesktop) {
219				if (BRectRatio(destinationBitmapBounds)
220						>= BRectRatio(viewBounds)) {
221					float overlap = BRectHorizontalOverlap(viewBounds,
222						destinationBitmapBounds);
223					destinationBitmapBounds.Set(-overlap, 0,
224						viewBounds.Width() + overlap, viewBounds.Height());
225				} else {
226					float overlap = BRectVerticalOverlap(viewBounds,
227						destinationBitmapBounds);
228					destinationBitmapBounds.Set(0, -overlap,
229						viewBounds.Width(), viewBounds.Height() + overlap);
230				}
231				followFlags = B_FOLLOW_ALL;
232				options |= B_FILTER_BITMAP_BILINEAR;
233				break;
234			}
235			// else fall thru
236		case kAtOffset:
237			destinationBitmapBounds.OffsetTo(info->fOffset);
238			break;
239
240		case kTiled:
241			if (fIsDesktop) {
242				destinationBitmapBounds.OffsetBy(
243					(viewBounds.Width() - bitmapBounds.Width()) / 2,
244					(viewBounds.Height() - bitmapBounds.Height()) / 2);
245			}
246			options |= B_TILE_BITMAP;
247			break;
248	}
249
250	// switch to the bitmap and force a redraw
251	view->SetViewBitmap(info->fBitmap, bitmapBounds, destinationBitmapBounds,
252		followFlags, options);
253	view->Invalidate();
254	fShowingBitmap = info;
255}
256
257
258float
259BackgroundImage::BRectRatio(BRect rect)
260{
261	return rect.Width() / rect.Height();
262}
263
264
265float
266BackgroundImage::BRectHorizontalOverlap(BRect hostRect, BRect resizedRect)
267{
268	return ((hostRect.Height() / resizedRect.Height() * resizedRect.Width())
269		- hostRect.Width()) / 2;
270}
271
272
273float
274BackgroundImage::BRectVerticalOverlap(BRect hostRect, BRect resizedRect)
275{
276	return ((hostRect.Width() / resizedRect.Width() * resizedRect.Height())
277		- hostRect.Height()) / 2;
278}
279
280
281void
282BackgroundImage::Remove()
283{
284	if (fShowingBitmap != NULL) {
285		fView->ClearViewBitmap();
286		fView->Invalidate();
287		BPoseView* poseView = dynamic_cast<BPoseView*>(fView);
288		// make sure text widgets draw the default way, erasing
289		// their background
290		if (poseView != NULL)
291			poseView->SetWidgetTextOutline(true);
292	}
293
294	fShowingBitmap = NULL;
295}
296
297
298BackgroundImage::BackgroundImageInfo*
299BackgroundImage::ImageInfoForWorkspace(int32 workspace) const
300{
301	uint32 workspaceMask = 1;
302
303	for ( ; workspace; workspace--)
304		workspaceMask *= 2;
305
306	int32 count = fBitmapForWorkspaceList.CountItems();
307
308	// do a simple lookup for the most likely candidate bitmap -
309	// pick the imageInfo that is only defined for this workspace over one
310	// that supports multiple workspaces
311	BackgroundImageInfo* result = NULL;
312	for (int32 index = 0; index < count; index++) {
313		BackgroundImageInfo* info = fBitmapForWorkspaceList.ItemAt(index);
314		if (info->fWorkspace == workspaceMask)
315			return info;
316
317		if (info->fWorkspace & workspaceMask)
318			result = info;
319	}
320
321	return result;
322}
323
324
325void
326BackgroundImage::WorkspaceActivated(BView* view, int32 workspace, bool state)
327{
328	if (!fIsDesktop) {
329		// we only care for desktop bitmaps
330		return;
331	}
332
333	if (!state) {
334		// we only care comming into a new workspace, not leaving one
335		return;
336	}
337
338	BackgroundImageInfo* info = ImageInfoForWorkspace(workspace);
339	if (info != fShowingBitmap) {
340		if (info != NULL)
341			Show(info, view);
342		else {
343			BPoseView* poseView = dynamic_cast<BPoseView*>(view);
344			if (poseView != NULL)
345				poseView->SetWidgetTextOutline(true);
346
347			view->ClearViewBitmap();
348			view->Invalidate();
349		}
350
351		fShowingBitmap = info;
352	}
353}
354
355
356void
357BackgroundImage::ScreenChanged(BRect, color_space)
358{
359	if (!fIsDesktop || fShowingBitmap == NULL)
360		return;
361
362	if (fShowingBitmap->fMode == kCentered) {
363		BRect viewBounds(fView->Bounds());
364		BRect bitmapBounds(fShowingBitmap->fBitmap->Bounds());
365		BRect destinationBitmapBounds(bitmapBounds);
366		destinationBitmapBounds.OffsetBy(
367			(viewBounds.Width() - bitmapBounds.Width()) / 2,
368			(viewBounds.Height() - bitmapBounds.Height()) / 2);
369
370		fView->SetViewBitmap(fShowingBitmap->fBitmap, bitmapBounds,
371			destinationBitmapBounds, B_FOLLOW_NONE, 0);
372		fView->Invalidate();
373	}
374}
375
376
377BackgroundImage*
378BackgroundImage::Refresh(BackgroundImage* oldBackgroundImage,
379	const BNode* fromNode, bool desktop, BPoseView* poseView)
380{
381	if (oldBackgroundImage != NULL) {
382		oldBackgroundImage->Remove();
383		delete oldBackgroundImage;
384	}
385
386	BackgroundImage* backgroundImage = GetBackgroundImage(fromNode, desktop);
387	if (backgroundImage != NULL && poseView->ViewMode() != kListMode)
388		backgroundImage->Show(poseView, current_workspace());
389
390	return backgroundImage;
391}
392