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 tile = 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				break;
316			}
317			// else fall thru
318		case kAtOffset:
319		{
320			destinationBitmapBounds.OffsetTo(offset);
321			break;
322		}
323		case kTiled:
324			// Original Backgrounds Preferences center the tiled paper
325			// but Tracker doesn't do that
326			//if (fIsDesktop) {
327			destinationBitmapBounds.OffsetBy(
328				(viewBounds.Width() - destinationBitmapBounds.Width()) / 2,
329				(viewBounds.Height() - destinationBitmapBounds.Height()) / 2);
330			//}
331			tile = B_TILE_BITMAP;
332			break;
333	}
334
335	// switch to the bitmap and force a redraw
336	view->SetViewBitmap(bitmap, bitmapBounds, destinationBitmapBounds,
337		followFlags, tile);
338	view->Invalidate();
339
340	/*if (fShowingBitmap != info) {
341		if (fShowingBitmap)
342			fShowingBitmap->UnloadBitmap(fCacheMode);
343		fShowingBitmap = info;
344	}*/
345}
346
347
348float
349BackgroundImage::BRectRatio(BRect rect)
350{
351	return rect.Width() / rect.Height();
352}
353
354
355float
356BackgroundImage::BRectHorizontalOverlap(BRect hostRect, BRect resizedRect)
357{
358	return ((hostRect.Height() / resizedRect.Height() * resizedRect.Width())
359		- hostRect.Width()) / 2;
360}
361
362
363float
364BackgroundImage::BRectVerticalOverlap(BRect hostRect, BRect resizedRect)
365{
366	return ((hostRect.Width() / resizedRect.Width() * resizedRect.Height())
367		- hostRect.Height()) / 2;
368}
369
370
371void
372BackgroundImage::Remove()
373{
374	if (fShowingBitmap) {
375		fView->ClearViewBitmap();
376		fView->Invalidate();
377		/*BPoseView* poseView = dynamic_cast<BPoseView*>(fView);
378		// make sure text widgets draw the default way, erasing their background
379		if (poseView)
380			poseView->SetEraseWidgetTextBackground(true);*/
381	}
382	fShowingBitmap = NULL;
383}
384
385
386BackgroundImage::BackgroundImageInfo*
387BackgroundImage::ImageInfoForWorkspace(int32 workspace) const
388{
389	uint32 workspaceMask = 1;
390
391	for (; workspace; workspace--)
392		workspaceMask *= 2;
393
394	int32 count = fBitmapForWorkspaceList.CountItems();
395
396	// do a simple lookup for the most likely candidate bitmap -
397	// pick the imageInfo that is only defined for this workspace over one
398	// that supports multiple workspaces
399	BackgroundImageInfo* result = NULL;
400	for (int32 index = 0; index < count; index++) {
401		BackgroundImageInfo* info = fBitmapForWorkspaceList.ItemAt(index);
402		if (info->fImageSet != fShowingImageSet)
403			continue;
404
405		if (fIsDesktop) {
406			if (info->fWorkspace == workspaceMask)
407				return info;
408
409			if (info->fWorkspace & workspaceMask)
410				result = info;
411		} else
412			return info;
413	}
414	return result;
415}
416
417
418void
419BackgroundImage::WorkspaceActivated(BView* view, int32 workspace, bool state)
420{
421	if (!fIsDesktop) {
422		// we only care for desktop bitmaps
423		return;
424	}
425
426	if (!state) {
427		// we only care comming into a new workspace, not leaving one
428		return;
429	}
430
431	BackgroundImageInfo* info = ImageInfoForWorkspace(workspace);
432	if (info != fShowingBitmap) {
433		if (info)
434			Show(info, view);
435		else {
436			/*if (BPoseView* poseView = dynamic_cast<BPoseView*>(view))
437				poseView->SetEraseWidgetTextBackground(true);*/
438			view->ClearViewBitmap();
439			view->Invalidate();
440		}
441		fShowingBitmap = info;
442	}
443}
444
445
446void
447BackgroundImage::ScreenChanged(BRect, color_space)
448{
449	if (!fIsDesktop || !fShowingBitmap)
450		return;
451
452	/*if (fShowingBitmap->fMode == kCentered) {
453		BRect viewBounds(fView->Bounds());
454		BRect bitmapBounds(fShowingBitmap->fBitmap->Bounds());
455		BRect destinationBitmapBounds(bitmapBounds);
456		destinationBitmapBounds.OffsetBy(
457			(viewBounds.Width() - bitmapBounds.Width()) / 2,
458			(viewBounds.Height() - bitmapBounds.Height()) / 2);
459
460		fView->SetViewBitmap(fShowingBitmap->fBitmap, bitmapBounds,
461			destinationBitmapBounds, B_FOLLOW_NONE, 0);
462		fView->Invalidate();
463	}*/
464}
465
466
467status_t
468BackgroundImage::SetBackgroundImage(BNode* node)
469{
470	status_t err;
471	BMessage container;
472	int32 count = fBitmapForWorkspaceList.CountItems();
473
474	for (int32 index = 0; index < count; index++) {
475		BackgroundImageInfo* info = fBitmapForWorkspaceList.ItemAt(index);
476
477		container.AddBool(kBackgroundImageInfoTextOutline,
478			info->fTextWidgetLabelOutline);
479		if (fBackgroundsView->GetImage(info->fImageIndex) != NULL) {
480			container.AddString(kBackgroundImageInfoPath,
481				fBackgroundsView
482					->GetImage(info->fImageIndex)->GetPath().Path());
483		} else
484			container.AddString(kBackgroundImageInfoPath, "");
485
486		container.AddInt32(kBackgroundImageInfoWorkspaces, info->fWorkspace);
487		container.AddPoint(kBackgroundImageInfoOffset, info->fOffset);
488		container.AddInt32(kBackgroundImageInfoMode, info->fMode);
489
490		if (fIsDesktop)
491			container.AddInt32(kBackgroundImageInfoSet, info->fImageSet);
492	}
493
494	PRINT_OBJECT(container);
495
496	size_t flattenedSize = container.FlattenedSize();
497	char* buffer = new(std::nothrow) char[flattenedSize];
498	if (buffer == NULL)
499		return B_NO_MEMORY;
500
501	if ((err = container.Flatten(buffer, flattenedSize)) != B_OK) {
502		delete[] buffer;
503		return err;
504	}
505
506	ssize_t size = node->WriteAttr(kBackgroundImageInfo, B_MESSAGE_TYPE,
507		0, buffer, flattenedSize);
508
509	delete[] buffer;
510
511	if (size < B_OK)
512		return size;
513	if ((size_t)size != flattenedSize)
514		return B_ERROR;
515
516	return B_OK;
517}
518
519
520/*BackgroundImage*
521BackgroundImage::Refresh(BackgroundImage* oldBackgroundImage,
522	const BNode* fromNode, bool desktop, BPoseView* poseView)
523{
524	if (oldBackgroundImage) {
525		oldBackgroundImage->Remove();
526		delete oldBackgroundImage;
527	}
528
529	BackgroundImage* result = GetBackgroundImage(fromNode, desktop);
530	if (result && poseView->ViewMode() != kListMode)
531		result->Show(poseView, current_workspace());
532	return result;
533}
534
535
536void
537BackgroundImage::ChangeImageSet(BPoseView* poseView)
538{
539	if (fRandomChange) {
540		if (fImageSetCount > 1) {
541			uint32 oldShowingImageSet = fShowingImageSet;
542			while (oldShowingImageSet == fShowingImageSet)
543				fShowingImageSet = random()%fImageSetCount;
544		} else
545			fShowingImageSet = 0;
546	} else {
547		fShowingImageSet++;
548		if (fShowingImageSet > fImageSetCount - 1)
549			fShowingImageSet = 0;
550	}
551
552	this->Show(poseView, current_workspace());
553}*/
554
555
556//	#pragma mark -
557
558
559Image::Image(BPath path)
560	:
561	fBitmap(NULL),
562	fPath(path)
563{
564	const int32 kMaxNameChars = 40;
565	fName = path.Leaf();
566	int extra = fName.CountChars() - kMaxNameChars;
567	if (extra > 0) {
568		BString extension;
569		int offset = fName.FindLast('.');
570		if (offset > 0)
571			fName.CopyInto(extension, ++offset, -1);
572		fName.TruncateChars(kMaxNameChars) << B_UTF8_ELLIPSIS << extension;
573	}
574}
575
576
577Image::~Image()
578{
579	delete fBitmap;
580}
581
582
583BBitmap*
584Image::GetBitmap()
585{
586	if (!fBitmap)
587		fBitmap = BTranslationUtils::GetBitmap(fPath.Path());
588
589	return fBitmap;
590}
591
592