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