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