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 "Navigator.h"
37
38#include <ControlLook.h>
39#include <TextControl.h>
40#include <Window.h>
41
42#include "Bitmaps.h"
43#include "Commands.h"
44#include "FSUtils.h"
45#include "Tracker.h"
46
47
48namespace BPrivate {
49
50static const int32 kMaxHistory = 32;
51
52}
53
54
55//	#pragma mark - BNavigator
56
57
58BNavigator::BNavigator(const Model* model)
59	:
60	BToolBar(),
61	fBackHistory(8, true),
62	fForwHistory(8, true)
63{
64	// Get initial path
65	model->GetPath(&fPath);
66
67	fLocation = new BTextControl("Location", "", "",
68		new BMessage(kNavigatorCommandLocation));
69	fLocation->SetDivider(0);
70
71	GroupLayout()->SetInsets(0.0f, 0.0f, B_USE_HALF_ITEM_INSETS, 1.0f);
72		// 1px bottom inset used for border
73
74	// Needed to draw the bottom border
75	SetFlags(Flags() | B_WILL_DRAW);
76	SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
77}
78
79
80BNavigator::~BNavigator()
81{
82}
83
84
85void
86BNavigator::AttachedToWindow()
87{
88	const BRect iconRect(BPoint(0, 0),
89		be_control_look->ComposeIconSize(20));
90
91	// Set up toolbar items
92	BBitmap* bmpBack = new BBitmap(iconRect, B_RGBA32);
93	GetTrackerResources()->GetIconResource(R_ResBackNav, B_MINI_ICON, bmpBack);
94	AddAction(kNavigatorCommandBackward, this, bmpBack);
95	SetActionEnabled(kNavigatorCommandBackward, false);
96	delete bmpBack;
97
98	BBitmap* bmpForw = new BBitmap(iconRect, B_RGBA32);
99	GetTrackerResources()->GetIconResource(R_ResForwNav, B_MINI_ICON, bmpForw);
100	AddAction(kNavigatorCommandForward, this, bmpForw);
101	SetActionEnabled(kNavigatorCommandForward, false);
102	delete bmpForw;
103
104	BBitmap* bmpUp = new BBitmap(iconRect, B_RGBA32);
105	GetTrackerResources()->GetIconResource(R_ResUpNav, B_MINI_ICON, bmpUp);
106	AddAction(kNavigatorCommandUp, this, bmpUp);
107	SetActionEnabled(kNavigatorCommandUp, false);
108	delete bmpUp;
109
110	AddView(fLocation);
111	fLocation->SetTarget(this);
112}
113
114
115void
116BNavigator::AllAttached()
117{
118	// Inital setup of widget states
119	UpdateLocation(0, kActionSet);
120}
121
122
123void
124BNavigator::Draw(BRect updateRect)
125{
126	// Draw a 1px bottom border, like BMenuBar
127	BRect rect(Bounds());
128	rgb_color base = LowColor();
129	uint32 flags = 0;
130
131	be_control_look->DrawBorder(this, rect, updateRect, base,
132		B_PLAIN_BORDER, flags, BControlLook::B_BOTTOM_BORDER);
133
134	_inherited::Draw(rect & updateRect);
135}
136
137
138void
139BNavigator::MessageReceived(BMessage* message)
140{
141	switch (message->what) {
142		case kNavigatorCommandBackward:
143			GoBackward((modifiers() & B_OPTION_KEY) == B_OPTION_KEY);
144			break;
145
146		case kNavigatorCommandForward:
147			GoForward((modifiers() & B_OPTION_KEY) == B_OPTION_KEY);
148			break;
149
150		case kNavigatorCommandUp:
151			GoUp((modifiers() & B_OPTION_KEY) == B_OPTION_KEY);
152			break;
153
154		case kNavigatorCommandLocation:
155			GoTo();
156			break;
157
158		case kNavigatorCommandSetFocus:
159			fLocation->MakeFocus();
160			break;
161
162		default:
163		{
164			// Catch any dropped refs and try to switch to this new directory
165			entry_ref ref;
166			if (message->FindRef("refs", &ref) == B_OK) {
167				BMessage message(kSwitchDirectory);
168				BEntry entry(&ref, true);
169				if (!entry.IsDirectory()) {
170					entry.GetRef(&ref);
171					BPath path(&ref);
172					path.GetParent(&path);
173					get_ref_for_path(path.Path(), &ref);
174				}
175				message.AddRef("refs", &ref);
176				message.AddInt32("action", kActionSet);
177				Window()->PostMessage(&message);
178			}
179		}
180	}
181}
182
183
184void
185BNavigator::GoBackward(bool option)
186{
187	int32 itemCount = fBackHistory.CountItems();
188	if (itemCount >= 2 && fBackHistory.ItemAt(itemCount - 2)) {
189		BEntry entry;
190		if (entry.SetTo(fBackHistory.ItemAt(itemCount - 2)->Path()) == B_OK)
191			SendNavigationMessage(kActionBackward, &entry, option);
192	}
193}
194
195
196void
197BNavigator::GoForward(bool option)
198{
199	if (fForwHistory.CountItems() >= 1) {
200		BEntry entry;
201		if (entry.SetTo(fForwHistory.LastItem()->Path()) == B_OK)
202			SendNavigationMessage(kActionForward, &entry, option);
203	}
204}
205
206
207void
208BNavigator::GoUp(bool option)
209{
210	BEntry entry;
211	if (entry.SetTo(fPath.Path()) == B_OK) {
212		BEntry parentEntry;
213		if (entry.GetParent(&parentEntry) == B_OK) {
214			SendNavigationMessage(kActionUp, &parentEntry, option);
215		}
216	}
217}
218
219
220void
221BNavigator::SendNavigationMessage(NavigationAction action, BEntry* entry,
222	bool option)
223{
224	entry_ref ref;
225
226	if (entry->GetRef(&ref) == B_OK) {
227		BMessage message;
228		message.AddRef("refs", &ref);
229		message.AddInt32("action", action);
230
231		// get the node of this folder for selecting it in the new location
232		const node_ref* nodeRef;
233		if (Window() && Window()->TargetModel())
234			nodeRef = Window()->TargetModel()->NodeRef();
235		else
236			nodeRef = NULL;
237
238		// if the option key was held down, open in new window (send message
239		// to be_app) otherwise send message to this window. TTracker
240		// (be_app) understands nodeRefToSlection, BContainerWindow doesn't,
241		// so we have to select the item manually
242		if (option) {
243			message.what = B_REFS_RECEIVED;
244			if (nodeRef != NULL) {
245				message.AddData("nodeRefToSelect", B_RAW_TYPE, nodeRef,
246					sizeof(node_ref));
247			}
248			be_app->PostMessage(&message);
249		} else {
250			message.what = kSwitchDirectory;
251			Window()->PostMessage(&message);
252			UnlockLooper();
253				// This is to prevent a dead-lock situation.
254				// SelectChildInParentSoon() eventually locks the
255				// TaskLoop::fLock. Later, when StandAloneTaskLoop::Run()
256				// runs, it also locks TaskLoop::fLock and subsequently
257				// locks this window's looper. Therefore we can't call
258				// SelectChildInParentSoon with our Looper locked,
259				// because we would get different orders of locking
260				// (thus the risk of dead-locking).
261				//
262				// Todo: Change the locking behaviour of
263				// StandAloneTaskLoop::Run() and subsequently called
264				// functions.
265			if (nodeRef != NULL) {
266				TTracker* tracker = dynamic_cast<TTracker*>(be_app);
267				if (tracker != NULL)
268					tracker->SelectChildInParentSoon(&ref, nodeRef);
269			}
270
271			LockLooper();
272		}
273	}
274}
275
276
277void
278BNavigator::GoTo()
279{
280	BString pathname = fLocation->Text();
281
282	if (pathname.Compare("") == 0)
283		pathname = "/";
284
285	BEntry entry;
286	entry_ref ref;
287
288	if (entry.SetTo(pathname.String()) == B_OK
289		&& entry.GetRef(&ref) == B_OK) {
290		BMessage message(kSwitchDirectory);
291		message.AddRef("refs", &ref);
292		message.AddInt32("action", kActionLocation);
293		Window()->PostMessage(&message);
294	} else {
295		BPath path;
296
297		if (Window() && Window()->TargetModel()) {
298			Window()->TargetModel()->GetPath(&path);
299			fLocation->SetText(path.Path());
300		}
301	}
302}
303
304
305void
306BNavigator::UpdateLocation(const Model* newmodel, int32 action)
307{
308	if (newmodel)
309		newmodel->GetPath(&fPath);
310
311	// Modify history according to commands
312	switch (action) {
313		case kActionBackward:
314			fForwHistory.AddItem(fBackHistory.RemoveItemAt(
315				fBackHistory.CountItems() - 1));
316			break;
317
318		case kActionForward:
319			fBackHistory.AddItem(fForwHistory.RemoveItemAt(
320				fForwHistory.CountItems() - 1));
321			break;
322
323		case kActionUpdatePath:
324			break;
325
326		default:
327			fForwHistory.MakeEmpty();
328			fBackHistory.AddItem(new BPath(fPath));
329
330			while (fBackHistory.CountItems() > kMaxHistory)
331				fBackHistory.RemoveItem(fBackHistory.FirstItem(), true);
332			break;
333	}
334
335	// Enable Up button when there is any parent
336	BEntry entry;
337	if (entry.SetTo(fPath.Path()) == B_OK) {
338		BEntry parentEntry;
339		bool enable = entry.GetParent(&parentEntry) == B_OK;
340		SetActionEnabled(kNavigatorCommandUp, enable);
341	}
342
343	// Enable history buttons if history contains something
344	SetActionEnabled(kNavigatorCommandForward, fForwHistory.CountItems() > 0);
345	SetActionEnabled(kNavigatorCommandBackward, fBackHistory.CountItems() > 1);
346
347	// Avoid loss of selection and cursor position
348	if (action != kActionLocation)
349		fLocation->SetText(fPath.Path());
350}
351