xref: /haiku/src/kits/tracker/Navigator.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
1 /*
2 Open Tracker License
3 
4 Terms and Conditions
5 
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7 
8 Permission is hereby granted, free of charge, to any person obtaining a copy of
9 this software and associated documentation files (the "Software"), to deal in
10 the Software without restriction, including without limitation the rights to
11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 of the Software, and to permit persons to whom the Software is furnished to do
13 so, subject to the following conditions:
14 
15 The above copyright notice and this permission notice applies to all licensees
16 and shall be included in all copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 
25 Except as contained in this notice, the name of Be Incorporated shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings in
27 this Software without prior written authorization from Be Incorporated.
28 
29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
32 All 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 
48 namespace BPrivate {
49 
50 static const int32 kMaxHistory = 32;
51 
52 }
53 
54 
55 //	#pragma mark - BNavigator
56 
57 
58 BNavigator::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 
80 BNavigator::~BNavigator()
81 {
82 }
83 
84 
85 void
86 BNavigator::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 
115 void
116 BNavigator::AllAttached()
117 {
118 	// Inital setup of widget states
119 	UpdateLocation(0, kActionSet);
120 }
121 
122 
123 void
124 BNavigator::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 
138 void
139 BNavigator::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 
184 void
185 BNavigator::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 
196 void
197 BNavigator::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 
207 void
208 BNavigator::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 
220 void
221 BNavigator::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 
277 void
278 BNavigator::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 
305 void
306 BNavigator::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