1/*
2Open Tracker License
3
4Terms and Conditions
5
6Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7
8Permission is hereby granted, free of charge, to any person obtaining a copy of
9this software and associated documentation files (the "Software"), to deal in
10the Software without restriction, including without limitation the rights to
11use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12of the Software, and to permit persons to whom the Software is furnished to do
13so, subject to the following conditions:
14
15The above copyright notice and this permission notice applies to all licensees
16and shall be included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
25Except as contained in this notice, the name of Be Incorporated shall not be
26used in advertising or otherwise to promote the sale, use or other dealings in
27this Software without prior written authorization from Be Incorporated.
28
29Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30of Be Incorporated in the United States and other countries. Other brand product
31names are registered trademarks or trademarks of their respective holders.
32All rights reserved.
33*/
34
35
36#include "Bitmaps.h"
37#include "Commands.h"
38#include "ContainerWindow.h"
39#include "FSUtils.h"
40#include "Model.h"
41#include "Navigator.h"
42#include "Tracker.h"
43
44#include <Picture.h>
45#include <TextControl.h>
46#include <Window.h>
47
48
49namespace BPrivate {
50
51static const int32 kMaxHistory = 32;
52
53}
54
55// BPictureButton() will crash when giving zero pointers,
56// although we really want and have to set up the
57// pictures when we can, e.g. on a AttachedToWindow.
58static BPicture sPicture;
59
60
61BNavigatorButton::BNavigatorButton(BRect rect, const char* name,
62	BMessage* message, int32 resIDon, int32 resIDoff, int32 resIDdisabled)
63	:	BPictureButton(rect, name, &sPicture, &sPicture, message),
64		fResIDOn(resIDon),
65		fResIDOff(resIDoff),
66		fResIDDisabled(resIDdisabled)
67{
68	// Clear to background color to avoid ugly border on click
69	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
70	SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
71	SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
72}
73
74
75BNavigatorButton::~BNavigatorButton()
76{
77}
78
79
80void
81BNavigatorButton::AttachedToWindow()
82{
83	BBitmap* bmpOn = 0;
84	GetTrackerResources()->GetBitmapResource(B_MESSAGE_TYPE, fResIDOn,
85		&bmpOn);
86	SetPicture(bmpOn, true, true);
87	delete bmpOn;
88
89	BBitmap* bmpOff = 0;
90	GetTrackerResources()->GetBitmapResource(B_MESSAGE_TYPE, fResIDOff,
91		&bmpOff);
92	SetPicture(bmpOff, true, false);
93	delete bmpOff;
94
95	BBitmap* bmpDisabled = 0;
96	GetTrackerResources()->GetBitmapResource(B_MESSAGE_TYPE, fResIDDisabled,
97		&bmpDisabled);
98	SetPicture(bmpDisabled, false, false);
99	SetPicture(bmpDisabled, false, true);
100	delete bmpDisabled;
101}
102
103
104void
105BNavigatorButton::SetPicture(BBitmap* bitmap, bool enabled, bool on)
106{
107	if (bitmap) {
108		BPicture picture;
109		BView view(bitmap->Bounds(), "", 0, 0);
110		AddChild(&view);
111		view.BeginPicture(&picture);
112		view.SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
113		view.FillRect(view.Bounds());
114		view.SetDrawingMode(B_OP_OVER);
115		view.DrawBitmap(bitmap, BPoint(0, 0));
116		view.EndPicture();
117		RemoveChild(&view);
118		if (enabled)
119			if (on)
120				SetEnabledOn(&picture);
121			else
122				SetEnabledOff(&picture);
123		else
124			if (on)
125				SetDisabledOn(&picture);
126			else
127				SetDisabledOff(&picture);
128	}
129}
130
131
132BNavigator::BNavigator(const Model* model, BRect rect, uint32 resizeMask)
133	:	BView(rect, "Navigator", resizeMask, B_WILL_DRAW),
134	fBack(0),
135	fForw(0),
136	fUp(0),
137	fBackHistory(8, true),
138	fForwHistory(8, true)
139{
140	// Get initial path
141	model->GetPath(&fPath);
142
143	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
144
145	float top = 2 + (be_plain_font->Size() - 8) / 2;
146
147	// Set up widgets
148	fBack = new BNavigatorButton(BRect(3, top, 21, top + 17), "Back",
149		new BMessage(kNavigatorCommandBackward), R_ResBackNavActiveSel,
150		R_ResBackNavActive, R_ResBackNavInactive);
151	fBack->SetEnabled(false);
152	AddChild(fBack);
153
154	fForw = new BNavigatorButton(BRect(35, top, 53, top + 17), "Forw",
155		new BMessage(kNavigatorCommandForward), R_ResForwNavActiveSel,
156		R_ResForwNavActive, R_ResForwNavInactive);
157	fForw->SetEnabled(false);
158	AddChild(fForw);
159
160	fUp = new BNavigatorButton(BRect(67, top, 84, top + 17), "Up",
161		new BMessage(kNavigatorCommandUp), R_ResUpNavActiveSel,
162		R_ResUpNavActive, R_ResUpNavInactive);
163	fUp->SetEnabled(false);
164	AddChild(fUp);
165
166	fLocation = new BTextControl(BRect(97, 2, rect.Width() - 2, 21),
167		"Location", "", "", new BMessage(kNavigatorCommandLocation),
168		B_FOLLOW_LEFT_RIGHT);
169	fLocation->SetDivider(0);
170	AddChild(fLocation);
171}
172
173
174BNavigator::~BNavigator()
175{
176}
177
178
179void
180BNavigator::AttachedToWindow()
181{
182	// Inital setup of widget states
183	UpdateLocation(0, kActionSet);
184
185	// All messages should arrive here
186	fBack->SetTarget(this);
187	fForw->SetTarget(this);
188	fUp->SetTarget(this);
189	fLocation->SetTarget(this);
190}
191
192
193void
194BNavigator::Draw(BRect)
195{
196	rgb_color bgColor = ui_color(B_PANEL_BACKGROUND_COLOR);
197	rgb_color shineColor = ui_color(B_SHINE_COLOR);
198	rgb_color halfDarkColor = tint_color(bgColor, B_DARKEN_1_TINT);
199	rgb_color darkColor = tint_color(bgColor, B_DARKEN_2_TINT);
200	// Draws a beveled smooth border
201	BeginLineArray(4);
202	AddLine(Bounds().LeftTop(), Bounds().RightTop(), shineColor);
203	AddLine(Bounds().LeftTop(), Bounds().LeftBottom() - BPoint(0, 1),
204		shineColor);
205	AddLine(Bounds().LeftBottom() - BPoint(-1, 1),
206		Bounds().RightBottom() - BPoint(0, 1), halfDarkColor);
207	AddLine(Bounds().LeftBottom(), Bounds().RightBottom(), darkColor);
208	EndLineArray();
209}
210
211
212void
213BNavigator::MessageReceived(BMessage* message)
214{
215	switch (message->what) {
216		case kNavigatorCommandBackward:
217			GoBackward((modifiers() & B_OPTION_KEY) == B_OPTION_KEY);
218			break;
219
220		case kNavigatorCommandForward:
221			GoForward((modifiers() & B_OPTION_KEY) == B_OPTION_KEY);
222			break;
223
224		case kNavigatorCommandUp:
225			GoUp((modifiers() & B_OPTION_KEY) == B_OPTION_KEY);
226			break;
227
228		case kNavigatorCommandLocation:
229			GoTo();
230			break;
231
232		default:
233		{
234			// Catch any dropped refs and try to switch to this new directory
235			entry_ref ref;
236			if (message->FindRef("refs", &ref) == B_OK) {
237				BMessage message(kSwitchDirectory);
238				BEntry entry(&ref, true);
239				if (!entry.IsDirectory()) {
240					entry.GetRef(&ref);
241					BPath path(&ref);
242					path.GetParent(&path);
243					get_ref_for_path(path.Path(), &ref);
244				}
245				message.AddRef("refs", &ref);
246				message.AddInt32("action", kActionSet);
247				Window()->PostMessage(&message);
248			}
249		}
250	}
251}
252
253
254void
255BNavigator::GoBackward(bool option)
256{
257	int32 itemCount = fBackHistory.CountItems();
258	if (itemCount >= 2 && fBackHistory.ItemAt(itemCount - 2)) {
259		BEntry entry;
260		if (entry.SetTo(fBackHistory.ItemAt(itemCount - 2)->Path()) == B_OK)
261			SendNavigationMessage(kActionBackward, &entry, option);
262	}
263}
264
265
266void
267BNavigator::GoForward(bool option)
268{
269	if (fForwHistory.CountItems() >= 1) {
270		BEntry entry;
271		if (entry.SetTo(fForwHistory.LastItem()->Path()) == B_OK)
272			SendNavigationMessage(kActionForward, &entry, option);
273	}
274}
275
276
277void
278BNavigator::GoUp(bool option)
279{
280	BEntry entry;
281	if (entry.SetTo(fPath.Path()) == B_OK) {
282		BEntry parentEntry;
283		if (entry.GetParent(&parentEntry) == B_OK
284			&& !FSIsDeskDir(&parentEntry)) {
285			SendNavigationMessage(kActionUp, &parentEntry, option);
286		}
287	}
288}
289
290
291void
292BNavigator::SendNavigationMessage(NavigationAction action, BEntry* entry,
293	bool option)
294{
295	entry_ref ref;
296
297	if (entry->GetRef(&ref) == B_OK) {
298		BMessage message;
299		message.AddRef("refs", &ref);
300		message.AddInt32("action", action);
301
302		// get the node of this folder for selecting it in the new location
303		const node_ref* nodeRef;
304		if (Window() && Window()->TargetModel())
305			nodeRef = Window()->TargetModel()->NodeRef();
306		else
307			nodeRef = NULL;
308
309		// if the option key was held down, open in new window (send message
310		// to be_app) otherwise send message to this window. TTracker
311		// (be_app) understands nodeRefToSlection, BContainerWindow doesn't,
312		// so we have to select the item manually
313		if (option) {
314			message.what = B_REFS_RECEIVED;
315			if (nodeRef) {
316				message.AddData("nodeRefToSelect", B_RAW_TYPE, nodeRef,
317					sizeof(node_ref));
318			}
319			be_app->PostMessage(&message);
320		} else {
321			message.what = kSwitchDirectory;
322			Window()->PostMessage(&message);
323			UnlockLooper();
324				// This is to prevent a dead-lock situation.
325				// SelectChildInParentSoon() eventually locks the
326				// TaskLoop::fLock. Later, when StandAloneTaskLoop::Run()
327				// runs, it also locks TaskLoop::fLock and subsequently
328				// locks this window's looper. Therefore we can't call
329				// SelectChildInParentSoon with our Looper locked,
330				// because we would get different orders of locking
331				// (thus the risk of dead-locking).
332				//
333				// Todo: Change the locking behaviour of
334				// StandAloneTaskLoop::Run() and subsequently called
335				// functions.
336			if (nodeRef)
337				dynamic_cast<TTracker*>(be_app)->SelectChildInParentSoon(&ref, nodeRef);
338			LockLooper();
339		}
340	}
341}
342
343
344void
345BNavigator::GoTo()
346{
347	BString pathname = fLocation->Text();
348
349	if (pathname.Compare("") == 0)
350		pathname = "/";
351
352	BEntry entry;
353	entry_ref ref;
354
355	if (entry.SetTo(pathname.String()) == B_OK
356		&& !FSIsDeskDir(&entry)
357		&& entry.GetRef(&ref) == B_OK) {
358		BMessage message(kSwitchDirectory);
359		message.AddRef("refs", &ref);
360		message.AddInt32("action", kActionLocation);
361		Window()->PostMessage(&message);
362	} else {
363		BPath path;
364
365		if (Window() && Window()->TargetModel()) {
366			Window()->TargetModel()->GetPath(&path);
367			fLocation->SetText(path.Path());
368		}
369	}
370}
371
372
373void
374BNavigator::UpdateLocation(const Model* newmodel, int32 action)
375{
376	if (newmodel)
377		newmodel->GetPath(&fPath);
378
379	// Modify history according to commands
380	switch (action) {
381		case kActionBackward:
382			fForwHistory.AddItem(fBackHistory.RemoveItemAt(fBackHistory.CountItems()-1));
383			break;
384
385		case kActionForward:
386			fBackHistory.AddItem(fForwHistory.RemoveItemAt(fForwHistory.CountItems()-1));
387			break;
388
389		case kActionUpdatePath:
390			break;
391
392		default:
393			fForwHistory.MakeEmpty();
394			fBackHistory.AddItem(new BPath(fPath));
395
396			while (fBackHistory.CountItems() > kMaxHistory)
397				fBackHistory.RemoveItem(fBackHistory.FirstItem(), true);
398			break;
399	}
400
401	// Enable Up button when there is any parent
402	BEntry entry;
403	if (entry.SetTo(fPath.Path()) == B_OK) {
404		BEntry parentEntry;
405		fUp->SetEnabled(entry.GetParent(&parentEntry) == B_OK
406			&& !FSIsDeskDir(&parentEntry));
407	}
408
409	// Enable history buttons if history contains something
410	fForw->SetEnabled(fForwHistory.CountItems() > 0);
411	fBack->SetEnabled(fBackHistory.CountItems() > 1);
412
413	// Avoid loss of selection and cursor position
414	if (action != kActionLocation)
415		fLocation->SetText(fPath.Path());
416}
417
418
419float
420BNavigator::CalcNavigatorHeight(void)
421{
422	// Empiric formula from how much space the textview
423	// will take once it is attached (using be_plain_font):
424	return  ceilf(11.0f + be_plain_font->Size()*(1.0f + 7.0f / 30.0f));
425}
426