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