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