1/*
2 * Copyright 2002-2017 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Axel D��rfler, axeld@pinc-software.de
7 *		Jerome Duval, jerome.duval@free.fr
8 *		Jonas Sundstr��m, jonas@kirilla.se
9 *		John Scipione, jscipione@gmail.com
10 *		Brian Hill, supernova@warpmail.net
11 */
12
13
14#include "BackgroundsView.h"
15
16#include <algorithm>
17
18#include <stdio.h>
19#include <stdlib.h>
20
21#include <Alert.h>
22#include <Bitmap.h>
23#include <Catalog.h>
24#include <ControlLook.h>
25#include <Cursor.h>
26#include <Debug.h>
27#include <File.h>
28#include <FindDirectory.h>
29#include <LayoutBuilder.h>
30#include <Locale.h>
31#include <MenuField.h>
32#include <Messenger.h>
33#include <MimeType.h>
34#include <Point.h>
35#include <PopUpMenu.h>
36
37#include <be_apps/Tracker/Background.h>
38#include <ScreenDefs.h>
39
40#include "ImageFilePanel.h"
41
42
43#undef B_TRANSLATION_CONTEXT
44#define B_TRANSLATION_CONTEXT "Main View"
45
46
47static const uint32 kMsgApplySettings = 'aply';
48static const uint32 kMsgRevertSettings = 'rvrt';
49
50static const uint32 kMsgUpdateColor = 'upcl';
51static const uint32 kMsgAllWorkspaces = 'alwk';
52static const uint32 kMsgCurrentWorkspace = 'crwk';
53static const uint32 kMsgDefaultFolder = 'dffl';
54static const uint32 kMsgOtherFolder = 'otfl';
55static const uint32 kMsgNoImage = 'noim';
56static const uint32 kMsgOtherImage = 'otim';
57static const uint32 kMsgImageSelected = 'imsl';
58static const uint32 kMsgFolderSelected = 'flsl';
59
60static const uint32 kMsgCenterPlacement = 'cnpl';
61static const uint32 kMsgManualPlacement = 'mnpl';
62static const uint32 kMsgScalePlacement = 'scpl';
63static const uint32 kMsgTilePlacement = 'tlpl';
64static const uint32 kMsgIconLabelOutline = 'ilol';
65
66static const uint32 kMsgImagePlacement = 'xypl';
67static const uint32 kMsgUpdatePreviewPlacement = 'pvpl';
68
69
70BackgroundsView::BackgroundsView()
71	:
72	BBox("BackgroundsView"),
73	fCurrent(NULL),
74	fCurrentInfo(NULL),
75	fLastImageIndex(-1),
76	fRecentFoldersLimit(10),
77	fPathList(1, true),
78	fImageList(1, true),
79	fFoundPositionSetting(false)
80{
81	SetBorder(B_NO_BORDER);
82
83	BBox* previewBox = new BBox("preview");
84	previewBox->SetLabel(B_TRANSLATE("Preview"));
85
86	fPreview = new Preview();
87
88	fTopLeft = new FramePart(FRAME_TOP_LEFT);
89	fTop = new FramePart(FRAME_TOP);
90	fTopRight = new FramePart(FRAME_TOP_RIGHT);
91	fLeft = new FramePart(FRAME_LEFT_SIDE);
92	fRight = new FramePart(FRAME_RIGHT_SIDE);
93	fBottomLeft = new FramePart(FRAME_BOTTOM_LEFT);
94	fBottom = new FramePart(FRAME_BOTTOM);
95	fBottomRight = new FramePart(FRAME_BOTTOM_RIGHT);
96
97	fXPlacementText = new BTextControl(B_TRANSLATE("X:"), NULL,
98		new BMessage(kMsgImagePlacement));
99	fYPlacementText = new BTextControl(B_TRANSLATE("Y:"), NULL,
100		new BMessage(kMsgImagePlacement));
101
102	// right-align text view
103	fXPlacementText->TextView()->SetAlignment(B_ALIGN_RIGHT);
104	fYPlacementText->TextView()->SetAlignment(B_ALIGN_RIGHT);
105
106	// max 5 characters allowed
107	fXPlacementText->TextView()->SetMaxBytes(5);
108	fYPlacementText->TextView()->SetMaxBytes(5);
109
110	// limit to numbers only
111	for (int32 i = 0; i < 256; i++) {
112		if ((i < '0' || i > '9') && i != '-') {
113			fXPlacementText->TextView()->DisallowChar(i);
114			fYPlacementText->TextView()->DisallowChar(i);
115		}
116	}
117
118	previewBox->AddChild(BLayoutBuilder::Group<>()
119		.AddGlue()
120		.AddGroup(B_VERTICAL, 0)
121			.AddGroup(B_HORIZONTAL, 0)
122				.AddGlue()
123				.AddGrid(0, 0, 1)
124					.Add(fTopLeft, 0, 0)
125					.Add(fTop, 1, 0)
126					.Add(fTopRight, 2, 0)
127					.Add(fLeft, 0, 1)
128					.Add(fPreview, 1, 1)
129					.Add(fRight, 2, 1)
130					.Add(fBottomLeft, 0, 2)
131					.Add(fBottom, 1, 2)
132					.Add(fBottomRight, 2, 2)
133					.End()
134				.AddGlue()
135				.End()
136			.AddStrut(be_control_look->DefaultItemSpacing() * 2)
137			.AddGroup(B_HORIZONTAL)
138				.Add(fXPlacementText)
139				.Add(fYPlacementText)
140				.End()
141			.AddGlue()
142			.SetInsets(B_USE_DEFAULT_SPACING)
143			.End()
144		.AddGlue()
145		.View());
146
147	BBox* rightbox = new BBox("rightbox");
148
149	fWorkspaceMenu = new BPopUpMenu(B_TRANSLATE("pick one"));
150	fWorkspaceMenu->AddItem(new BMenuItem(B_TRANSLATE("All workspaces"),
151		new BMessage(kMsgAllWorkspaces)));
152	BMenuItem* menuItem;
153	fWorkspaceMenu->AddItem(menuItem = new BMenuItem(
154		B_TRANSLATE("Current workspace"),
155		new BMessage(kMsgCurrentWorkspace)));
156	menuItem->SetMarked(true);
157	fWorkspaceMenu->AddSeparatorItem();
158	fWorkspaceMenu->AddItem(new BMenuItem(B_TRANSLATE("Default folder"),
159		new BMessage(kMsgDefaultFolder)));
160	fWorkspaceMenu->AddItem(new BMenuItem(
161		B_TRANSLATE("Other folder" B_UTF8_ELLIPSIS),
162		new BMessage(kMsgOtherFolder)));
163
164	BMenuField* workspaceMenuField = new BMenuField("workspaceMenuField",
165		NULL, fWorkspaceMenu);
166	workspaceMenuField->ResizeToPreferred();
167	rightbox->SetLabel(workspaceMenuField);
168
169	fImageMenu = new BPopUpMenu(B_TRANSLATE("pick one"));
170	fImageMenu->AddItem(new BGImageMenuItem(B_TRANSLATE("None"), -1,
171		new BMessage(kMsgNoImage)));
172	fImageMenu->AddSeparatorItem();
173	fImageMenu->AddItem(new BMenuItem(B_TRANSLATE("Other" B_UTF8_ELLIPSIS),
174		new BMessage(kMsgOtherImage)));
175
176	BMenuField* imageMenuField = new BMenuField("image",
177		B_TRANSLATE("Image:"), fImageMenu);
178	imageMenuField->SetAlignment(B_ALIGN_RIGHT);
179
180	fPlacementMenu = new BPopUpMenu(B_TRANSLATE("pick one"));
181	fPlacementMenu->AddItem(new BMenuItem(B_TRANSLATE("Manual"),
182		new BMessage(kMsgManualPlacement)));
183	fPlacementMenu->AddItem(new BMenuItem(B_TRANSLATE("Center"),
184		new BMessage(kMsgCenterPlacement)));
185	fPlacementMenu->AddItem(new BMenuItem(B_TRANSLATE("Scale to fit"),
186		new BMessage(kMsgScalePlacement)));
187	fPlacementMenu->AddItem(new BMenuItem(B_TRANSLATE("Tile"),
188		new BMessage(kMsgTilePlacement)));
189
190	BMenuField* placementMenuField = new BMenuField("placement",
191		B_TRANSLATE("Placement:"), fPlacementMenu);
192	placementMenuField->SetAlignment(B_ALIGN_RIGHT);
193
194	fIconLabelOutline = new BCheckBox(B_TRANSLATE("Icon label outline"),
195		new BMessage(kMsgIconLabelOutline));
196	fIconLabelOutline->SetValue(B_CONTROL_OFF);
197
198	fPicker = new BColorControl(BPoint(0, 0), B_CELLS_32x8, 8.0, "Picker",
199		new BMessage(kMsgUpdateColor));
200
201	rightbox->AddChild(BLayoutBuilder::Group<>(B_VERTICAL)
202		.AddGroup(B_HORIZONTAL, 0.0f)
203			.AddGrid(B_USE_DEFAULT_SPACING, B_USE_SMALL_SPACING)
204				.Add(imageMenuField->CreateLabelLayoutItem(), 0, 0)
205				.AddGroup(B_HORIZONTAL, 0.0f, 1, 0)
206					.Add(imageMenuField->CreateMenuBarLayoutItem(), 0.0f)
207					.AddGlue()
208					.End()
209				.Add(placementMenuField->CreateLabelLayoutItem(), 0, 1)
210				.AddGroup(B_HORIZONTAL, 0.0f, 1, 1)
211					.Add(placementMenuField->CreateMenuBarLayoutItem(), 0.0f)
212					.AddGlue()
213					.End()
214				.Add(fIconLabelOutline, 1, 2)
215				.End()
216			.AddGlue()
217			.End()
218		.AddGlue()
219		.Add(fPicker)
220		.SetInsets(B_USE_DEFAULT_SPACING)
221		.View());
222
223	fRevert = new BButton(B_TRANSLATE("Revert"),
224		new BMessage(kMsgRevertSettings));
225	fApply = new BButton(B_TRANSLATE("Apply"),
226		new BMessage(kMsgApplySettings));
227
228	fRevert->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
229		B_ALIGN_NO_VERTICAL));
230	fApply->SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT,
231		B_ALIGN_NO_VERTICAL));
232
233	BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
234		.SetInsets(B_USE_WINDOW_SPACING)
235		.AddGroup(B_HORIZONTAL, 0)
236			.AddGroup(B_VERTICAL, 0)
237				.AddStrut(floorf(rightbox->TopBorderOffset()
238					- previewBox->TopBorderOffset()) - 1)
239				.Add(previewBox)
240				.End()
241			.AddStrut(B_USE_DEFAULT_SPACING)
242			.Add(rightbox)
243			.End()
244		.AddGroup(B_HORIZONTAL, 0)
245			.Add(fRevert)
246			.Add(fApply)
247			.SetInsets(0, B_USE_DEFAULT_SPACING, 0,	0)
248			.End();
249
250	fApply->MakeDefault(true);
251}
252
253
254BackgroundsView::~BackgroundsView()
255{
256	// The order matter. The last panel saves the state
257	delete fFolderPanel;
258	delete fPanel;
259}
260
261
262void
263BackgroundsView::AllAttached()
264{
265	fPlacementMenu->SetTargetForItems(this);
266	fImageMenu->SetTargetForItems(this);
267	fWorkspaceMenu->SetTargetForItems(this);
268	fXPlacementText->SetTarget(this);
269	fYPlacementText->SetTarget(this);
270	fIconLabelOutline->SetTarget(this);
271	fPicker->SetTarget(this);
272	fApply->SetTarget(this);
273	fRevert->SetTarget(this);
274
275	BPath path;
276	entry_ref ref;
277	if (find_directory(B_SYSTEM_DATA_DIRECTORY, &path) == B_OK) {
278		path.Append("artwork");
279		get_ref_for_path(path.Path(), &ref);
280	}
281
282	BMessenger messenger(this);
283	fPanel = new ImageFilePanel(B_OPEN_PANEL, &messenger, &ref,
284		B_FILE_NODE, false, NULL, new ImageFilter(true));
285	fPanel->SetButtonLabel(B_DEFAULT_BUTTON, B_TRANSLATE("Select"));
286
287	fFolderPanel = new BFilePanel(B_OPEN_PANEL, &messenger, NULL,
288		B_DIRECTORY_NODE, false, NULL, new ImageFilter(false));
289	fFolderPanel->SetButtonLabel(B_DEFAULT_BUTTON, B_TRANSLATE("Select"));
290
291	_LoadSettings();
292	_LoadDesktopFolder();
293
294	BPoint point;
295	if (fSettings.FindPoint("pos", &point) == B_OK) {
296		fFoundPositionSetting = true;
297		Window()->MoveTo(point);
298	}
299
300	fApply->SetEnabled(false);
301	fRevert->SetEnabled(false);
302}
303
304
305void
306BackgroundsView::MessageReceived(BMessage* message)
307{
308	// Color drop
309	if (message->WasDropped()) {
310		rgb_color *clr;
311		ssize_t out_size;
312		if (message->FindData("RGBColor", B_RGB_COLOR_TYPE,
313			(const void **)&clr, &out_size) == B_OK) {
314			fPicker->SetValue(*clr);
315			_UpdatePreview();
316			_UpdateButtons();
317			return;
318		}
319	}
320
321	switch (message->what) {
322		case B_SIMPLE_DATA:
323		case B_REFS_RECEIVED:
324			RefsReceived(message);
325			break;
326
327		case kMsgUpdatePreviewPlacement:
328		{
329			BString xstring, ystring;
330			xstring << (int)fPreview->fPoint.x;
331			ystring << (int)fPreview->fPoint.y;
332			fXPlacementText->SetText(xstring.String());
333			fYPlacementText->SetText(ystring.String());
334			_UpdatePreview();
335			_UpdateButtons();
336			break;
337		}
338
339		case kMsgManualPlacement:
340		case kMsgTilePlacement:
341		case kMsgScalePlacement:
342		case kMsgCenterPlacement:
343			_UpdatePreview();
344			_UpdateButtons();
345			break;
346
347		case kMsgIconLabelOutline:
348			_UpdateButtons();
349			break;
350
351		case kMsgUpdateColor:
352		case kMsgImagePlacement:
353			_UpdatePreview();
354			_UpdateButtons();
355			break;
356
357		case kMsgCurrentWorkspace:
358		case kMsgAllWorkspaces:
359			fImageMenu->FindItem(kMsgNoImage)->SetLabel(B_TRANSLATE("None"));
360			if (fCurrent && fCurrent->IsDesktop()) {
361				_UpdateButtons();
362			} else {
363				_SetDesktop(true);
364				_LoadDesktopFolder();
365			}
366			break;
367
368		case kMsgDefaultFolder:
369			fImageMenu->FindItem(kMsgNoImage)->SetLabel(B_TRANSLATE("None"));
370			_SetDesktop(false);
371			_LoadDefaultFolder();
372			break;
373
374		case kMsgOtherFolder:
375			fFolderPanel->Show();
376			break;
377
378		case kMsgOtherImage:
379			fPanel->Show();
380			break;
381
382		case B_CANCEL:
383		{
384			PRINT(("cancel received\n"));
385			void* pointer;
386			message->FindPointer("source", &pointer);
387			if (pointer == fPanel) {
388				if (fLastImageIndex >= 0)
389					_FindImageItem(fLastImageIndex)->SetMarked(true);
390				else
391					fImageMenu->ItemAt(0)->SetMarked(true);
392			}
393			break;
394		}
395
396		case kMsgImageSelected:
397		case kMsgNoImage:
398			fLastImageIndex = ((BGImageMenuItem*)fImageMenu->FindMarked())
399				->ImageIndex();
400			_UpdatePreview();
401			_UpdateButtons();
402			break;
403
404		case kMsgFolderSelected:
405		{
406			fImageMenu->FindItem(kMsgNoImage)->SetLabel(B_TRANSLATE("Default"));
407			_SetDesktop(false);
408			BString folderPathStr;
409			if (message->FindString("folderPath", &folderPathStr) == B_OK) {
410				BPath folderPath(folderPathStr);
411				_LoadRecentFolder(folderPath);
412			}
413			break;
414		}
415		case kMsgApplySettings:
416		{
417			_Save();
418
419			// Notify the server and Screen preflet
420			thread_id notifyThread;
421			notifyThread = spawn_thread(BackgroundsView::_NotifyThread,
422				"notifyThread", B_NORMAL_PRIORITY, this);
423			resume_thread(notifyThread);
424			_UpdateButtons();
425			break;
426		}
427		case kMsgRevertSettings:
428			_UpdateWithCurrent();
429			break;
430
431		default:
432			BView::MessageReceived(message);
433			break;
434	}
435}
436
437
438void
439BackgroundsView::_LoadDesktopFolder()
440{
441	BPath path;
442	if (find_directory(B_DESKTOP_DIRECTORY, &path) == B_OK) {
443		status_t error = get_ref_for_path(path.Path(), &fCurrentRef);
444		if (error != B_OK)
445			printf("error in LoadDesktopSettings\n");
446		_LoadFolder(true);
447	}
448}
449
450
451void
452BackgroundsView::_LoadDefaultFolder()
453{
454	BPath path;
455	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
456		BString pathString = path.Path();
457		pathString << "/Tracker/DefaultFolderTemplate";
458		status_t error = get_ref_for_path(pathString.String(), &fCurrentRef);
459		if (error != B_OK)
460			printf("error in LoadDefaultFolderSettings\n");
461		_LoadFolder(false);
462	}
463}
464
465
466void
467BackgroundsView::_LoadRecentFolder(BPath path)
468{
469	status_t error = get_ref_for_path(path.Path(), &fCurrentRef);
470	if (error != B_OK)
471		printf("error in LoadRecentFolder\n");
472	_LoadFolder(false);
473}
474
475
476void
477BackgroundsView::_LoadFolder(bool isDesktop)
478{
479	if (fCurrent) {
480		delete fCurrent;
481		fCurrent = NULL;
482	}
483
484	BNode node(&fCurrentRef);
485	if (node.InitCheck() == B_OK)
486		fCurrent = BackgroundImage::GetBackgroundImage(&node, isDesktop, this);
487
488	_UpdateWithCurrent();
489}
490
491
492void
493BackgroundsView::_UpdateWithCurrent(void)
494{
495	if (fCurrent == NULL)
496		return;
497
498	fPlacementMenu->FindItem(kMsgScalePlacement)
499		->SetEnabled(fCurrent->IsDesktop());
500	fPlacementMenu->FindItem(kMsgCenterPlacement)
501		->SetEnabled(fCurrent->IsDesktop());
502
503	if (fWorkspaceMenu->IndexOf(fWorkspaceMenu->FindMarked()) > 5)
504		fImageMenu->FindItem(kMsgNoImage)->SetLabel(B_TRANSLATE("Default"));
505	else
506		fImageMenu->FindItem(kMsgNoImage)->SetLabel(B_TRANSLATE("None"));
507
508	for (int32 i = fImageMenu->CountItems() - 5; i >= 0; i--) {
509		fImageMenu->RemoveItem(2);
510	}
511
512	for (int32 i = fImageList.CountItems() - 1; i >= 0; i--) {
513		BMessage* message = new BMessage(kMsgImageSelected);
514		_AddItem(new BGImageMenuItem(GetImage(i)->GetName(), i, message));
515	}
516
517	fImageMenu->SetTargetForItems(this);
518
519	fCurrentInfo = fCurrent->ImageInfoForWorkspace(current_workspace());
520
521	if (!fCurrentInfo) {
522		fImageMenu->FindItem(kMsgNoImage)->SetMarked(true);
523		fPlacementMenu->FindItem(kMsgManualPlacement)->SetMarked(true);
524		fIconLabelOutline->SetValue(B_CONTROL_ON);
525	} else {
526		fIconLabelOutline->SetValue(fCurrentInfo->fTextWidgetLabelOutline
527			? B_CONTROL_ON : B_CONTROL_OFF);
528
529		fLastImageIndex = fCurrentInfo->fImageIndex;
530		_FindImageItem(fLastImageIndex)->SetMarked(true);
531
532		if (fLastImageIndex > -1) {
533
534			BString xtext, ytext;
535			int32 cmd = 0;
536			switch (fCurrentInfo->fMode) {
537				case BackgroundImage::kCentered:
538					cmd = kMsgCenterPlacement;
539					break;
540				case BackgroundImage::kScaledToFit:
541					cmd = kMsgScalePlacement;
542					break;
543				case BackgroundImage::kAtOffset:
544					cmd = kMsgManualPlacement;
545					xtext << (int)fCurrentInfo->fOffset.x;
546					ytext << (int)fCurrentInfo->fOffset.y;
547					break;
548				case BackgroundImage::kTiled:
549					cmd = kMsgTilePlacement;
550					break;
551			}
552
553			if (cmd != 0)
554				fPlacementMenu->FindItem(cmd)->SetMarked(true);
555
556			fXPlacementText->SetText(xtext.String());
557			fYPlacementText->SetText(ytext.String());
558		} else {
559			fPlacementMenu->FindItem(kMsgManualPlacement)->SetMarked(true);
560		}
561	}
562
563	rgb_color color = {255, 255, 255, 255};
564	if (fCurrent->IsDesktop()) {
565		color = BScreen().DesktopColor();
566		fPicker->SetEnabled(true);
567	} else
568		fPicker->SetEnabled(false);
569
570	fPicker->SetValue(color);
571
572	_UpdatePreview();
573	_UpdateButtons();
574}
575
576
577void
578BackgroundsView::_Save()
579{
580	bool textWidgetLabelOutline = fIconLabelOutline->Value() == B_CONTROL_ON;
581
582	BackgroundImage::Mode mode = _FindPlacementMode();
583	BPoint offset(atoi(fXPlacementText->Text()), atoi(fYPlacementText->Text()));
584
585	if (!fCurrent->IsDesktop()) {
586		if (fCurrentInfo == NULL) {
587			fCurrentInfo = new BackgroundImage::BackgroundImageInfo(
588				B_ALL_WORKSPACES, fLastImageIndex, mode, offset,
589				textWidgetLabelOutline, 0, 0);
590			fCurrent->Add(fCurrentInfo);
591		} else {
592			fCurrentInfo->fTextWidgetLabelOutline = textWidgetLabelOutline;
593			fCurrentInfo->fMode = mode;
594			if (fCurrentInfo->fMode == BackgroundImage::kAtOffset)
595				fCurrentInfo->fOffset = offset;
596			fCurrentInfo->fImageIndex = fLastImageIndex;
597		}
598	} else {
599		uint32 workspaceMask = 1;
600		int32 workspace = current_workspace();
601		for (; workspace; workspace--)
602			workspaceMask *= 2;
603
604		if (fCurrentInfo != NULL) {
605			if (fWorkspaceMenu->FindItem(kMsgCurrentWorkspace)->IsMarked()) {
606				if (fCurrentInfo->fWorkspace & workspaceMask
607					&& fCurrentInfo->fWorkspace != workspaceMask) {
608					fCurrentInfo->fWorkspace = fCurrentInfo->fWorkspace
609						^ workspaceMask;
610					fCurrentInfo = new BackgroundImage::BackgroundImageInfo(
611						workspaceMask, fLastImageIndex, mode, offset,
612						textWidgetLabelOutline, fCurrentInfo->fImageSet,
613						fCurrentInfo->fCacheMode);
614					fCurrent->Add(fCurrentInfo);
615				} else if (fCurrentInfo->fWorkspace == workspaceMask) {
616					fCurrentInfo->fTextWidgetLabelOutline
617						= textWidgetLabelOutline;
618					fCurrentInfo->fMode = mode;
619					if (fCurrentInfo->fMode == BackgroundImage::kAtOffset)
620						fCurrentInfo->fOffset = offset;
621
622					fCurrentInfo->fImageIndex = fLastImageIndex;
623				}
624			} else {
625				fCurrent->RemoveAll();
626
627				fCurrentInfo = new BackgroundImage::BackgroundImageInfo(
628					B_ALL_WORKSPACES, fLastImageIndex, mode, offset,
629					textWidgetLabelOutline, fCurrent->GetShowingImageSet(),
630					fCurrentInfo->fCacheMode);
631				fCurrent->Add(fCurrentInfo);
632			}
633		} else {
634			if (fWorkspaceMenu->FindItem(kMsgCurrentWorkspace)->IsMarked()) {
635				fCurrentInfo = new BackgroundImage::BackgroundImageInfo(
636					workspaceMask, fLastImageIndex, mode, offset,
637					textWidgetLabelOutline, fCurrent->GetShowingImageSet(), 0);
638			} else {
639				fCurrent->RemoveAll();
640				fCurrentInfo = new BackgroundImage::BackgroundImageInfo(
641					B_ALL_WORKSPACES, fLastImageIndex, mode, offset,
642					textWidgetLabelOutline, fCurrent->GetShowingImageSet(), 0);
643			}
644			fCurrent->Add(fCurrentInfo);
645		}
646
647		if (!fWorkspaceMenu->FindItem(kMsgCurrentWorkspace)->IsMarked()) {
648			for (int32 i = 0; i < count_workspaces(); i++) {
649				BScreen().SetDesktopColor(fPicker->ValueAsColor(), i, true);
650			}
651		} else
652			BScreen().SetDesktopColor(fPicker->ValueAsColor(), true);
653	}
654
655	BNode node(&fCurrentRef);
656
657	status_t status = fCurrent->SetBackgroundImage(&node);
658	if (status != B_OK) {
659		BString error(strerror(status));
660		BString text(B_TRANSLATE("Setting the background image failed:"));
661		text.Append("\n").Append(error);
662		BAlert* alert = new BAlert(B_TRANSLATE("Set background image error"),
663			text, B_TRANSLATE("OK"));
664		alert->SetShortcut(0, B_ESCAPE);
665		alert->Go(NULL);
666		printf("setting background image failed: %s\n", error.String());
667	}
668}
669
670
671void
672BackgroundsView::_NotifyServer()
673{
674	BMessenger tracker("application/x-vnd.Be-TRAK");
675
676	if (fCurrent->IsDesktop()) {
677		tracker.SendMessage(new BMessage(B_RESTORE_BACKGROUND_IMAGE));
678	} else {
679		int32 i = -1;
680		BMessage reply;
681		int32 error;
682		BEntry currentEntry(&fCurrentRef);
683		BPath currentPath(&currentEntry);
684		bool isCustomFolder
685			= !fWorkspaceMenu->FindItem(kMsgDefaultFolder)->IsMarked();
686
687		do {
688			BMessage message(B_GET_PROPERTY);
689			i++;
690
691			// look at the "Poses" in every Tracker window
692			message.AddSpecifier("Poses");
693			message.AddSpecifier("Window", i);
694
695			reply.MakeEmpty();
696			tracker.SendMessage(&message, &reply);
697
698			// break out of the loop when we're at the end of
699			// the windows
700			if (reply.what == B_MESSAGE_NOT_UNDERSTOOD
701				&& reply.FindInt32("error", &error) == B_OK
702				&& error == B_BAD_INDEX)
703				break;
704
705			// don't stop for windows that don't understand
706			// a request for "Poses"; they're not displaying
707			// folders
708			if (reply.what == B_MESSAGE_NOT_UNDERSTOOD
709				&& reply.FindInt32("error", &error) == B_OK
710				&& error != B_BAD_SCRIPT_SYNTAX)
711				continue;
712
713			BMessenger trackerWindow;
714			if (reply.FindMessenger("result", &trackerWindow) != B_OK)
715				continue;
716
717			if (isCustomFolder) {
718				// found a window with poses, ask for its path
719				message.MakeEmpty();
720				message.what = B_GET_PROPERTY;
721				message.AddSpecifier("Path");
722				message.AddSpecifier("Poses");
723				message.AddSpecifier("Window", i);
724
725				reply.MakeEmpty();
726				tracker.SendMessage(&message, &reply);
727
728				// go on with the next if this din't have a path
729				if (reply.what == B_MESSAGE_NOT_UNDERSTOOD)
730					continue;
731
732				entry_ref ref;
733				if (reply.FindRef("result", &ref) == B_OK) {
734					BEntry entry(&ref);
735					BPath path(&entry);
736
737					// these are not the paths you're looking for
738					if (currentPath != path)
739						continue;
740				}
741			}
742
743			trackerWindow.SendMessage(B_RESTORE_BACKGROUND_IMAGE);
744		} while (true);
745	}
746}
747
748
749void
750BackgroundsView::_NotifyScreenPreflet()
751{
752	BMessenger messenger("application/x-vnd.Haiku-Screen");
753	if (messenger.IsValid())
754		messenger.SendMessage(UPDATE_DESKTOP_COLOR_MSG);
755}
756
757
758int32
759BackgroundsView::_NotifyThread(void* data)
760{
761	BackgroundsView* view = (BackgroundsView*)data;
762
763	view->_NotifyServer();
764	view->_NotifyScreenPreflet();
765	return B_OK;
766}
767
768
769void
770BackgroundsView::SaveSettings(void)
771{
772	BPath path;
773	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
774		path.Append(SETTINGS_FILE);
775		BFile file(path.Path(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
776
777		BPoint point = Window()->Frame().LeftTop();
778		if (fSettings.ReplacePoint("pos", point) != B_OK)
779			fSettings.AddPoint("pos", point);
780
781		entry_ref ref;
782		BEntry entry;
783
784		fPanel->GetPanelDirectory(&ref);
785		entry.SetTo(&ref);
786		entry.GetPath(&path);
787		if (fSettings.ReplaceString("paneldir", path.Path()) != B_OK)
788			fSettings.AddString("paneldir", path.Path());
789
790		fFolderPanel->GetPanelDirectory(&ref);
791		entry.SetTo(&ref);
792		entry.GetPath(&path);
793		if (fSettings.ReplaceString("folderpaneldir", path.Path()) != B_OK)
794			fSettings.AddString("folderpaneldir", path.Path());
795
796		fSettings.RemoveName("recentfolder");
797		for (int32 i = 0; i < fPathList.CountItems(); i++) {
798			fSettings.AddString("recentfolder", fPathList.ItemAt(i)->Path());
799		}
800
801		fSettings.Flatten(&file);
802	}
803}
804
805
806void
807BackgroundsView::_LoadSettings()
808{
809	fSettings.MakeEmpty();
810
811	BPath path;
812	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
813		return;
814
815	path.Append(SETTINGS_FILE);
816	BFile file(path.Path(), B_READ_ONLY);
817	if (file.InitCheck() != B_OK)
818		return;
819
820	if (fSettings.Unflatten(&file) != B_OK) {
821		printf("Error unflattening settings file %s\n", path.Path());
822		return;
823	}
824
825	PRINT_OBJECT(fSettings);
826
827	BString settingStr;
828	if (fSettings.FindString("paneldir", &settingStr) == B_OK)
829		fPanel->SetPanelDirectory(settingStr.String());
830
831	if (fSettings.FindString("folderpaneldir", &settingStr) == B_OK)
832		fFolderPanel->SetPanelDirectory(settingStr.String());
833
834	int32 index = 0;
835	while (fSettings.FindString("recentfolder", index, &settingStr) == B_OK) {
836		path.SetTo(settingStr.String());
837		_AddRecentFolder(path);
838		index++;
839	}
840
841	fWorkspaceMenu->ItemAt(1)->SetMarked(true);
842	fWorkspaceMenu->SetTargetForItems(this);
843
844	PRINT(("Settings Loaded\n"));
845}
846
847
848void
849BackgroundsView::WorkspaceActivated(uint32 oldWorkspaces, bool active)
850{
851	_UpdateWithCurrent();
852}
853
854
855void
856BackgroundsView::_UpdatePreview()
857{
858	bool imageEnabled = !(fImageMenu->FindItem(kMsgNoImage)->IsMarked());
859	if (fPlacementMenu->IsEnabled() ^ imageEnabled)
860		fPlacementMenu->SetEnabled(imageEnabled);
861
862	bool textEnabled
863		= (fPlacementMenu->FindItem(kMsgManualPlacement)->IsMarked())
864		&& imageEnabled;
865	if (fXPlacementText->IsEnabled() ^ textEnabled)
866		fXPlacementText->SetEnabled(textEnabled);
867
868	if (fYPlacementText->IsEnabled() ^ textEnabled)
869		fYPlacementText->SetEnabled(textEnabled);
870
871	if (textEnabled && (strcmp(fXPlacementText->Text(), "") == 0)) {
872		fXPlacementText->SetText("0");
873		fYPlacementText->SetText("0");
874	}
875	if (!textEnabled) {
876		fXPlacementText->SetText(NULL);
877		fYPlacementText->SetText(NULL);
878	}
879
880	fXPlacementText->TextView()->MakeSelectable(textEnabled);
881	fYPlacementText->TextView()->MakeSelectable(textEnabled);
882	fXPlacementText->TextView()->MakeEditable(textEnabled);
883	fYPlacementText->TextView()->MakeEditable(textEnabled);
884
885	fPreview->ClearViewBitmap();
886
887	int32 index = ((BGImageMenuItem*)fImageMenu->FindMarked())->ImageIndex();
888	if (index >= 0) {
889		BBitmap* bitmap = GetImage(index)->GetBitmap();
890		if (bitmap) {
891			BackgroundImage::BackgroundImageInfo* info
892				= new BackgroundImage::BackgroundImageInfo(0, index,
893					_FindPlacementMode(), BPoint(atoi(fXPlacementText->Text()),
894						atoi(fYPlacementText->Text())),
895					fIconLabelOutline->Value() == B_CONTROL_ON, 0, 0);
896			if (info->fMode == BackgroundImage::kAtOffset) {
897				fPreview->SetEnabled(true);
898				fPreview->fPoint.x = atoi(fXPlacementText->Text());
899				fPreview->fPoint.y = atoi(fYPlacementText->Text());
900			} else
901				fPreview->SetEnabled(false);
902
903			fPreview->fImageBounds = BRect(bitmap->Bounds());
904			fCurrent->Show(info, fPreview);
905
906			delete info;
907		}
908	} else
909		fPreview->SetEnabled(false);
910
911	fPreview->SetViewColor(fPicker->ValueAsColor());
912	fPreview->Invalidate();
913}
914
915
916BackgroundImage::Mode
917BackgroundsView::_FindPlacementMode()
918{
919	BackgroundImage::Mode mode = BackgroundImage::kAtOffset;
920
921	if (fPlacementMenu->FindItem(kMsgCenterPlacement)->IsMarked())
922		mode = BackgroundImage::kCentered;
923
924	if (fPlacementMenu->FindItem(kMsgScalePlacement)->IsMarked())
925		mode = BackgroundImage::kScaledToFit;
926
927	if (fPlacementMenu->FindItem(kMsgManualPlacement)->IsMarked())
928		mode = BackgroundImage::kAtOffset;
929
930	if (fPlacementMenu->FindItem(kMsgTilePlacement)->IsMarked())
931		mode = BackgroundImage::kTiled;
932
933	return mode;
934}
935
936
937void
938BackgroundsView::_UpdateButtons()
939{
940	bool hasChanged = false;
941	if (fPicker->IsEnabled()
942		&& fPicker->ValueAsColor() != BScreen().DesktopColor()) {
943		hasChanged = true;
944	} else if (fCurrentInfo) {
945		if ((fIconLabelOutline->Value() == B_CONTROL_ON)
946			^ fCurrentInfo->fTextWidgetLabelOutline) {
947			hasChanged = true;
948		} else if (_FindPlacementMode() != fCurrentInfo->fMode) {
949			hasChanged = true;
950		} else if (fCurrentInfo->fImageIndex
951			!= ((BGImageMenuItem*)fImageMenu->FindMarked())->ImageIndex()) {
952			hasChanged = true;
953		} else if (fCurrent->IsDesktop()
954			&& ((fCurrentInfo->fWorkspace != B_ALL_WORKSPACES)
955				^ (fWorkspaceMenu->FindItem(kMsgCurrentWorkspace)->IsMarked())))
956		{
957			hasChanged = true;
958		} else if (fCurrentInfo->fImageIndex > -1
959			&& fCurrentInfo->fMode == BackgroundImage::kAtOffset) {
960			BString oldString, newString;
961			oldString << (int)fCurrentInfo->fOffset.x;
962			if (oldString != BString(fXPlacementText->Text())) {
963				hasChanged = true;
964			}
965			oldString = "";
966			oldString << (int)fCurrentInfo->fOffset.y;
967			if (oldString != BString(fYPlacementText->Text())) {
968				hasChanged = true;
969			}
970		}
971	} else if (fImageMenu->IndexOf(fImageMenu->FindMarked()) > 0) {
972		hasChanged = true;
973	} else if (fIconLabelOutline->Value() == B_CONTROL_OFF) {
974		hasChanged = true;
975	}
976
977	fApply->SetEnabled(hasChanged);
978	fRevert->SetEnabled(hasChanged);
979}
980
981
982void
983BackgroundsView::RefsReceived(BMessage* message)
984{
985	if (!message->HasRef("refs") && message->HasRef("dir_ref")) {
986		entry_ref dirRef;
987		if (message->FindRef("dir_ref", &dirRef) == B_OK)
988			message->AddRef("refs", &dirRef);
989	}
990
991	entry_ref ref;
992	int32 i = 0;
993	BMimeType imageType("image");
994	BPath desktopPath;
995	find_directory(B_DESKTOP_DIRECTORY, &desktopPath);
996
997	while (message->FindRef("refs", i++, &ref) == B_OK) {
998		BPath path;
999		BEntry entry(&ref, true);
1000		path.SetTo(&entry);
1001		BNode node(&entry);
1002
1003		if (node.IsFile()) {
1004			BMimeType refType;
1005			if (BMimeType::GuessMimeType(&ref, &refType) == B_OK
1006				&& !imageType.Contains(&refType)) {
1007				continue;
1008			}
1009
1010			BGImageMenuItem* item;
1011			int32 index = AddImage(path);
1012			if (index >= 0) {
1013				item = _FindImageItem(index);
1014				fLastImageIndex = index;
1015			} else {
1016				const char* name = GetImage(-index - 1)->GetName();
1017				item = new BGImageMenuItem(name, -index - 1,
1018					new BMessage(kMsgImageSelected));
1019				_AddItem(item);
1020				item->SetTarget(this);
1021				fLastImageIndex = -index - 1;
1022			}
1023
1024			// An optional placement may have been sent
1025			int32 placement = 0;
1026			if (message->FindInt32("placement", &placement) == B_OK) {
1027				BMenuItem* item = fPlacementMenu->FindItem(placement);
1028				if (item)
1029					item->SetMarked(true);
1030			}
1031			item->SetMarked(true);
1032			BMessenger(this).SendMessage(kMsgImageSelected);
1033		} else if (node.IsDirectory()) {
1034			if (desktopPath == path) {
1035				fWorkspaceMenu->FindItem(kMsgCurrentWorkspace)->SetMarked(true);
1036				BMessenger(this).SendMessage(kMsgCurrentWorkspace);
1037				break;
1038			}
1039
1040			// Add the newly selected path as a recent folder,
1041			// removing the oldest entry if needed
1042			_AddRecentFolder(path, true);
1043		}
1044	}
1045}
1046
1047
1048static BPath*
1049FindPath(BPath* currentPath, void* newPath)
1050{
1051	BPath* pathToCheck = static_cast<BPath*>(newPath);
1052	int compare = ICompare(currentPath->Path(), pathToCheck->Path());
1053	return compare == 0 ? currentPath : NULL;
1054}
1055
1056
1057void
1058BackgroundsView::_AddRecentFolder(BPath path, bool notifyApp)
1059{
1060	BPath* currentPath = fPathList.EachElement(FindPath, &path);
1061	BMenuItem* item;
1062	BMessage* folderSelectedMsg = new BMessage(kMsgFolderSelected);
1063	folderSelectedMsg->AddString("folderPath", path.Path());
1064
1065	if (currentPath == NULL) {
1066		// "All Workspaces", "Current Workspace", "--", "Default folder",
1067		// "Other folder...", If only these 5 exist,
1068		// we need a new separator to add path specific recent folders.
1069		if (fWorkspaceMenu->CountItems() <= 5)
1070			fWorkspaceMenu->AddSeparatorItem();
1071		// Maxed out the number of recent folders, remove the oldest entry
1072		if (fPathList.CountItems() == fRecentFoldersLimit) {
1073			fPathList.RemoveItemAt(0);
1074			fWorkspaceMenu->RemoveItem(6);
1075		}
1076		// Add the new recent folder
1077		BString folderMenuText(B_TRANSLATE("Folder: %path"));
1078		folderMenuText.ReplaceFirst("%path", path.Leaf());
1079		item = new BMenuItem(folderMenuText.String(), folderSelectedMsg);
1080		fWorkspaceMenu->AddItem(item);
1081		fPathList.AddItem(new BPath(path));
1082		item->SetTarget(this);
1083	} else {
1084		int32 itemIndex = fPathList.IndexOf(currentPath);
1085		item = fWorkspaceMenu->ItemAt(itemIndex + 6);
1086	}
1087
1088	item->SetMarked(true);
1089
1090	if (notifyApp)
1091		BMessenger(this).SendMessage(folderSelectedMsg);
1092}
1093
1094
1095int32
1096BackgroundsView::AddImage(BPath path)
1097{
1098	int32 count = fImageList.CountItems();
1099	int32 index = 0;
1100	for (; index < count; index++) {
1101		Image* image = fImageList.ItemAt(index);
1102		if (image->GetPath() == path)
1103			return index;
1104	}
1105
1106	fImageList.AddItem(new Image(path));
1107	return -index - 1;
1108}
1109
1110
1111Image*
1112BackgroundsView::GetImage(int32 imageIndex)
1113{
1114	return fImageList.ItemAt(imageIndex);
1115}
1116
1117
1118BGImageMenuItem*
1119BackgroundsView::_FindImageItem(const int32 imageIndex)
1120{
1121	if (imageIndex < 0)
1122		return (BGImageMenuItem*)fImageMenu->ItemAt(0);
1123
1124	int32 count = fImageMenu->CountItems() - 2;
1125	int32 index = 2;
1126	for (; index < count; index++) {
1127		BGImageMenuItem* image = (BGImageMenuItem*)fImageMenu->ItemAt(index);
1128		if (image->ImageIndex() == imageIndex)
1129			return image;
1130	}
1131	return NULL;
1132}
1133
1134
1135bool
1136BackgroundsView::_AddItem(BGImageMenuItem* item)
1137{
1138	int32 count = fImageMenu->CountItems() - 2;
1139	int32 index = 2;
1140	if (count < index) {
1141		fImageMenu->AddItem(new BSeparatorItem(), 1);
1142		count = fImageMenu->CountItems() - 2;
1143	}
1144
1145	for (; index < count; index++) {
1146		BGImageMenuItem* image = (BGImageMenuItem*)fImageMenu->ItemAt(index);
1147		int c = (BString(image->Label()).ICompare(BString(item->Label())));
1148		if (c > 0)
1149			break;
1150	}
1151	return fImageMenu->AddItem(item, index);
1152}
1153
1154
1155void
1156BackgroundsView::_SetDesktop(bool isDesktop)
1157{
1158	fTopLeft->SetDesktop(isDesktop);
1159	fTop->SetDesktop(isDesktop);
1160	fTopRight->SetDesktop(isDesktop);
1161	fLeft->SetDesktop(isDesktop);
1162	fRight->SetDesktop(isDesktop);
1163	fBottomLeft->SetDesktop(isDesktop);
1164	fBottom->SetDesktop(isDesktop);
1165	fBottomRight->SetDesktop(isDesktop);
1166
1167	Invalidate();
1168}
1169
1170
1171bool
1172BackgroundsView::FoundPositionSetting()
1173{
1174	return fFoundPositionSetting;
1175}
1176
1177
1178//	#pragma mark -
1179
1180
1181Preview::Preview()
1182	:
1183	BControl("PreView", NULL, NULL, B_WILL_DRAW | B_SUBPIXEL_PRECISE)
1184{
1185	float aspectRatio = BScreen().Frame().Width() / BScreen().Frame().Height();
1186	float previewWidth = be_control_look->DefaultLabelSpacing() * 20.0f;
1187	float previewHeight = ceil(previewWidth / aspectRatio);
1188
1189	ResizeTo(previewWidth, previewHeight);
1190	SetExplicitMinSize(BSize(previewWidth, previewHeight));
1191	SetExplicitMaxSize(BSize(previewWidth, previewHeight));
1192}
1193
1194
1195void
1196Preview::AttachedToWindow()
1197{
1198	rgb_color color = ViewColor();
1199	BControl::AttachedToWindow();
1200	SetViewColor(color);
1201}
1202
1203
1204void
1205Preview::MouseDown(BPoint point)
1206{
1207	if (IsEnabled() && Bounds().Contains(point)) {
1208		uint32 buttons;
1209		GetMouse(&point, &buttons);
1210		if (buttons & B_PRIMARY_MOUSE_BUTTON) {
1211			fOldPoint = point;
1212			SetTracking(true);
1213			BScreen().GetMode(&fMode);
1214			fXRatio = Bounds().Width() / fMode.virtual_width;
1215			fYRatio = Bounds().Height() / fMode.virtual_height;
1216			SetMouseEventMask(B_POINTER_EVENTS,
1217				B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY);
1218
1219			BCursor grabbingCursor(B_CURSOR_ID_GRABBING);
1220			SetViewCursor(&grabbingCursor);
1221		}
1222	}
1223}
1224
1225
1226void
1227Preview::MouseUp(BPoint point)
1228{
1229	if (IsTracking()) {
1230		SetTracking(false);
1231		BCursor grabCursor(B_CURSOR_ID_GRAB);
1232		SetViewCursor(&grabCursor);
1233	}
1234}
1235
1236
1237void
1238Preview::MouseMoved(BPoint point, uint32 transit, const BMessage* message)
1239{
1240	if (!IsTracking()) {
1241		BCursor cursor(IsEnabled()
1242			? B_CURSOR_ID_GRAB : B_CURSOR_ID_SYSTEM_DEFAULT);
1243		SetViewCursor(&cursor);
1244	} else {
1245		float x, y;
1246		x = fPoint.x + (point.x - fOldPoint.x) / fXRatio;
1247		y = fPoint.y + (point.y - fOldPoint.y) / fYRatio;
1248		bool min, max, mustSend = false;
1249		min = (x > -fImageBounds.Width());
1250		max = (x < fMode.virtual_width);
1251		if (min && max) {
1252			fOldPoint.x = point.x;
1253			fPoint.x = x;
1254			mustSend = true;
1255		} else {
1256			if (!min && fPoint.x > -fImageBounds.Width()) {
1257				fPoint.x = -fImageBounds.Width();
1258				fOldPoint.x = point.x - (x - fPoint.x) * fXRatio;
1259				mustSend = true;
1260			}
1261			if (!max && fPoint.x < fMode.virtual_width) {
1262				fPoint.x = fMode.virtual_width;
1263				fOldPoint.x = point.x - (x - fPoint.x) * fXRatio;
1264				mustSend = true;
1265			}
1266		}
1267
1268		min = (y > -fImageBounds.Height());
1269		max = (y < fMode.virtual_height);
1270		if (min && max) {
1271			fOldPoint.y = point.y;
1272			fPoint.y = y;
1273			mustSend = true;
1274		} else {
1275			if (!min && fPoint.y > -fImageBounds.Height()) {
1276				fPoint.y = -fImageBounds.Height();
1277				fOldPoint.y = point.y - (y - fPoint.y) * fYRatio;
1278				mustSend = true;
1279			}
1280			if (!max && fPoint.y < fMode.virtual_height) {
1281				fPoint.y = fMode.virtual_height;
1282				fOldPoint.y = point.y - (y - fPoint.y) * fYRatio;
1283				mustSend = true;
1284			}
1285		}
1286
1287		if (mustSend) {
1288			BMessenger messenger(Parent());
1289			messenger.SendMessage(kMsgUpdatePreviewPlacement);
1290		}
1291	}
1292
1293	BControl::MouseMoved(point, transit, message);
1294}
1295
1296
1297//	#pragma mark -
1298
1299
1300BGImageMenuItem::BGImageMenuItem(const char* label, int32 imageIndex,
1301	BMessage* message, char shortcut, uint32 modifiers)
1302	: BMenuItem(label, message, shortcut, modifiers),
1303	fImageIndex(imageIndex)
1304{
1305}
1306
1307
1308//	#pragma mark -
1309
1310
1311FramePart::FramePart(int32 part)
1312	:
1313	BView(NULL, B_WILL_DRAW | B_FRAME_EVENTS),
1314	fFramePart(part),
1315	fIsDesktop(true)
1316{
1317	_SetSizeAndAlignment();
1318}
1319
1320
1321void
1322FramePart::Draw(BRect rect)
1323{
1324	rgb_color color = HighColor();
1325	SetDrawingMode(B_OP_COPY);
1326	SetHighColor(Parent()->ViewColor());
1327
1328	if (fIsDesktop) {
1329		switch (fFramePart) {
1330			case FRAME_TOP_LEFT:
1331				FillRect(rect);
1332				SetHighColor(160, 160, 160);
1333				FillRoundRect(BRect(0, 0, 8, 8), 3, 3);
1334				SetHighColor(96, 96, 96);
1335				StrokeRoundRect(BRect(0, 0, 8, 8), 3, 3);
1336				break;
1337
1338			case FRAME_TOP:
1339				SetHighColor(160, 160, 160);
1340				FillRect(BRect(0, 1, rect.right, 3));
1341				SetHighColor(96, 96, 96);
1342				StrokeLine(BPoint(0, 0), BPoint(rect.right, 0));
1343				SetHighColor(0, 0, 0);
1344				StrokeLine(BPoint(0, 4), BPoint(rect.right, 4));
1345				break;
1346
1347			case FRAME_TOP_RIGHT:
1348				FillRect(rect);
1349				SetHighColor(160, 160, 160);
1350				FillRoundRect(BRect(-4, 0, 4, 8), 3, 3);
1351				SetHighColor(96, 96, 96);
1352				StrokeRoundRect(BRect(-4, 0, 4, 8), 3, 3);
1353				break;
1354
1355			case FRAME_LEFT_SIDE:
1356				SetHighColor(160, 160, 160);
1357				FillRect(BRect(1, 0, 3, rect.bottom));
1358				SetHighColor(96, 96, 96);
1359				StrokeLine(BPoint(0, 0), BPoint(0, rect.bottom));
1360				SetHighColor(0, 0, 0);
1361				StrokeLine(BPoint(4, 0), BPoint(4, rect.bottom));
1362				break;
1363
1364			case FRAME_RIGHT_SIDE:
1365				SetHighColor(160, 160, 160);
1366				FillRect(BRect(1, 0, 3, rect.bottom));
1367				SetHighColor(0, 0, 0);
1368				StrokeLine(BPoint(0, 0), BPoint(0, rect.bottom));
1369				SetHighColor(96, 96, 96);
1370				StrokeLine(BPoint(4, 0), BPoint(4, rect.bottom));
1371				break;
1372
1373			case FRAME_BOTTOM_LEFT:
1374				FillRect(rect);
1375				SetHighColor(160, 160, 160);
1376				FillRoundRect(BRect(0, -4, 8, 4), 3, 3);
1377				SetHighColor(96, 96, 96);
1378				StrokeRoundRect(BRect(0, -4, 8, 4), 3, 3);
1379				break;
1380
1381			case FRAME_BOTTOM:
1382				SetHighColor(160, 160, 160);
1383				FillRect(BRect(0, 1, rect.right, 3));
1384				SetHighColor(0, 0, 0);
1385				StrokeLine(BPoint(0, 0), BPoint(rect.right, 0));
1386				SetHighColor(96, 96, 96);
1387				StrokeLine(BPoint(0, 4), BPoint(rect.right, 4));
1388				SetHighColor(228, 0, 0);
1389				StrokeLine(BPoint(5, 2), BPoint(7, 2));
1390				break;
1391
1392			case FRAME_BOTTOM_RIGHT:
1393				FillRect(rect);
1394				SetHighColor(160, 160, 160);
1395				FillRoundRect(BRect(-4, -4, 4, 4), 3, 3);
1396				SetHighColor(96, 96, 96);
1397				StrokeRoundRect(BRect(-4, -4, 4, 4), 3, 3);
1398				break;
1399
1400			default:
1401				break;
1402		}
1403	} else {
1404		switch (fFramePart) {
1405			case FRAME_TOP_LEFT:
1406				SetHighColor(152, 152, 152);
1407				StrokeLine(BPoint(0, 0), BPoint(0, 12));
1408				StrokeLine(BPoint(0, 0), BPoint(4, 0));
1409				StrokeLine(BPoint(3, 12), BPoint(3, 12));
1410				SetHighColor(255, 203, 0);
1411				FillRect(BRect(1, 1, 3, 9));
1412				SetHighColor(240, 240, 240);
1413				StrokeLine(BPoint(1, 12), BPoint(1, 10));
1414				StrokeLine(BPoint(2, 10), BPoint(3, 10));
1415				SetHighColor(200, 200, 200);
1416				StrokeLine(BPoint(2, 12), BPoint(2, 11));
1417				StrokeLine(BPoint(3, 11), BPoint(3, 11));
1418				break;
1419
1420			case FRAME_TOP:
1421				FillRect(BRect(54, 0, rect.right, 8));
1422				SetHighColor(255, 203, 0);
1423				FillRect(BRect(0, 1, 52, 9));
1424				SetHighColor(152, 152, 152);
1425				StrokeLine(BPoint(0, 0), BPoint(53, 0));
1426				StrokeLine(BPoint(53, 1), BPoint(53, 9));
1427				StrokeLine(BPoint(54, 9), BPoint(rect.right, 9));
1428				SetHighColor(240, 240, 240);
1429				StrokeLine(BPoint(0, 10), BPoint(rect.right, 10));
1430				SetHighColor(200, 200, 200);
1431				StrokeLine(BPoint(0, 11), BPoint(rect.right, 11));
1432				SetHighColor(152, 152, 152);
1433				StrokeLine(BPoint(0, 12), BPoint(rect.right, 12));
1434				break;
1435
1436			case FRAME_TOP_RIGHT:
1437				FillRect(BRect(0, 0, 3, 8));
1438				SetHighColor(152, 152, 152);
1439				StrokeLine(BPoint(0, 12), BPoint(0, 12));
1440				StrokeLine(BPoint(0, 9), BPoint(3, 9));
1441				StrokeLine(BPoint(3, 12), BPoint(3, 9));
1442				SetHighColor(240, 240, 240);
1443				StrokeLine(BPoint(0, 10), BPoint(2, 10));
1444				StrokeLine(BPoint(1, 12), BPoint(1, 12));
1445				SetHighColor(200, 200, 200);
1446				StrokeLine(BPoint(2, 12), BPoint(2, 12));
1447				StrokeLine(BPoint(0, 11), BPoint(2, 11));
1448				break;
1449
1450			case FRAME_LEFT_SIDE:
1451			case FRAME_RIGHT_SIDE:
1452				SetHighColor(152, 152, 152);
1453				StrokeLine(BPoint(0, 0), BPoint(0, rect.bottom));
1454				SetHighColor(240, 240, 240);
1455				StrokeLine(BPoint(1, 0), BPoint(1, rect.bottom));
1456				SetHighColor(200, 200, 200);
1457				StrokeLine(BPoint(2, 0), BPoint(2, rect.bottom));
1458				SetHighColor(152, 152, 152);
1459				StrokeLine(BPoint(3, 0), BPoint(3, rect.bottom));
1460				break;
1461
1462			case FRAME_BOTTOM_LEFT:
1463				SetHighColor(152, 152, 152);
1464				StrokeLine(BPoint(0, 0), BPoint(0, 3));
1465				StrokeLine(BPoint(0, 3), BPoint(3, 3));
1466				StrokeLine(BPoint(3, 0), BPoint(3, 0));
1467				SetHighColor(240, 240, 240);
1468				StrokeLine(BPoint(1, 0), BPoint(1, 2));
1469				StrokeLine(BPoint(3, 1), BPoint(3, 1));
1470				SetHighColor(200, 200, 200);
1471				StrokeLine(BPoint(2, 0), BPoint(2, 2));
1472				StrokeLine(BPoint(3, 2), BPoint(3, 2));
1473				break;
1474
1475			case FRAME_BOTTOM:
1476				SetHighColor(152, 152, 152);
1477				StrokeLine(BPoint(0, 0), BPoint(rect.right, 0));
1478				SetHighColor(240, 240, 240);
1479				StrokeLine(BPoint(0, 1), BPoint(rect.right, 1));
1480				SetHighColor(200, 200, 200);
1481				StrokeLine(BPoint(0, 2), BPoint(rect.right, 2));
1482				SetHighColor(152, 152, 152);
1483				StrokeLine(BPoint(0, 3), BPoint(rect.right, 3));
1484				break;
1485
1486			case FRAME_BOTTOM_RIGHT:
1487				SetHighColor(152, 152, 152);
1488				StrokeLine(BPoint(0, 0), BPoint(0, 0));
1489				SetHighColor(240, 240, 240);
1490				StrokeLine(BPoint(1, 0), BPoint(1, 1));
1491				StrokeLine(BPoint(0, 1), BPoint(0, 1));
1492				SetHighColor(200, 200, 200);
1493				StrokeLine(BPoint(2, 0), BPoint(2, 2));
1494				StrokeLine(BPoint(0, 2), BPoint(1, 2));
1495				SetHighColor(152, 152, 152);
1496				StrokeLine(BPoint(3, 0), BPoint(3, 3));
1497				StrokeLine(BPoint(0, 3), BPoint(2, 3));
1498				break;
1499
1500			default:
1501				break;
1502		}
1503	}
1504
1505	SetHighColor(color);
1506}
1507
1508
1509void
1510FramePart::SetDesktop(bool isDesktop)
1511{
1512	fIsDesktop = isDesktop;
1513
1514	_SetSizeAndAlignment();
1515	Invalidate();
1516}
1517
1518
1519void
1520FramePart::_SetSizeAndAlignment()
1521{
1522	if (fIsDesktop) {
1523		switch (fFramePart) {
1524			case FRAME_TOP_LEFT:
1525				SetExplicitMinSize(BSize(4, 4));
1526				SetExplicitMaxSize(BSize(4, 4));
1527				break;
1528
1529			case FRAME_TOP:
1530				SetExplicitMinSize(BSize(1, 4));
1531				SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 4));
1532				break;
1533
1534			case FRAME_TOP_RIGHT:
1535				SetExplicitMinSize(BSize(4, 4));
1536				SetExplicitMaxSize(BSize(4, 4));
1537				break;
1538
1539			case FRAME_LEFT_SIDE:
1540				SetExplicitMinSize(BSize(4, 1));
1541				SetExplicitMaxSize(BSize(4, B_SIZE_UNLIMITED));
1542				break;
1543
1544			case FRAME_RIGHT_SIDE:
1545				SetExplicitMinSize(BSize(4, 1));
1546				SetExplicitMaxSize(BSize(4, B_SIZE_UNLIMITED));
1547				break;
1548
1549			case FRAME_BOTTOM_LEFT:
1550				SetExplicitMinSize(BSize(4, 4));
1551				SetExplicitMaxSize(BSize(4, 4));
1552				break;
1553
1554			case FRAME_BOTTOM:
1555				SetExplicitMinSize(BSize(1, 4));
1556				SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 4));
1557				break;
1558
1559			case FRAME_BOTTOM_RIGHT:
1560				SetExplicitMaxSize(BSize(4, 4));
1561				SetExplicitMinSize(BSize(4, 4));
1562				break;
1563
1564			default:
1565				break;
1566		}
1567	} else {
1568		switch (fFramePart) {
1569			case FRAME_TOP_LEFT:
1570				SetExplicitMinSize(BSize(3, 12));
1571				SetExplicitMaxSize(BSize(3, 12));
1572				break;
1573
1574			case FRAME_TOP:
1575				SetExplicitMinSize(BSize(1, 12));
1576				SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 12));
1577				break;
1578
1579			case FRAME_TOP_RIGHT:
1580				SetExplicitMinSize(BSize(3, 12));
1581				SetExplicitMaxSize(BSize(3, 12));
1582				break;
1583
1584			case FRAME_LEFT_SIDE:
1585				SetExplicitMinSize(BSize(3, 1));
1586				SetExplicitMaxSize(BSize(3, B_SIZE_UNLIMITED));
1587				break;
1588
1589			case FRAME_RIGHT_SIDE:
1590				SetExplicitMinSize(BSize(3, 1));
1591				SetExplicitMaxSize(BSize(3, B_SIZE_UNLIMITED));
1592				break;
1593
1594			case FRAME_BOTTOM_LEFT:
1595				SetExplicitMinSize(BSize(3, 3));
1596				SetExplicitMaxSize(BSize(3, 3));
1597				break;
1598
1599			case FRAME_BOTTOM:
1600				SetExplicitMinSize(BSize(1, 3));
1601				SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 3));
1602				break;
1603
1604			case FRAME_BOTTOM_RIGHT:
1605				SetExplicitMaxSize(BSize(3, 3));
1606				SetExplicitMinSize(BSize(3, 3));
1607				break;
1608
1609			default:
1610				break;
1611		}
1612	}
1613
1614	switch (fFramePart) {
1615		case FRAME_TOP_LEFT:
1616			SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT, B_ALIGN_BOTTOM));
1617			break;
1618
1619		case FRAME_TOP:
1620			SetExplicitAlignment(BAlignment(B_ALIGN_CENTER, B_ALIGN_BOTTOM));
1621			break;
1622
1623		case FRAME_TOP_RIGHT:
1624			SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_BOTTOM));
1625			break;
1626
1627		case FRAME_LEFT_SIDE:
1628			SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT, B_ALIGN_MIDDLE));
1629			break;
1630
1631		case FRAME_RIGHT_SIDE:
1632			SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_MIDDLE));
1633			break;
1634
1635		case FRAME_BOTTOM_LEFT:
1636			SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT, B_ALIGN_TOP));
1637			break;
1638
1639		case FRAME_BOTTOM:
1640			SetExplicitAlignment(BAlignment(B_ALIGN_CENTER, B_ALIGN_TOP));
1641			break;
1642
1643		case FRAME_BOTTOM_RIGHT:
1644			SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP));
1645			break;
1646
1647		default:
1648			break;
1649	}
1650}
1651
1652