xref: /haiku/src/kits/tracker/Navigator.cpp (revision 16c83730262f1e4f0fc69d80744bb36dcfbbe3af)
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 "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 
49 namespace BPrivate {
50 
51 static 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.
58 static BPicture sPicture;
59 
60 
61 BNavigatorButton::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 
75 BNavigatorButton::~BNavigatorButton()
76 {
77 }
78 
79 
80 void
81 BNavigatorButton::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 
104 void
105 BNavigatorButton::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 
132 BNavigator::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 
174 BNavigator::~BNavigator()
175 {
176 }
177 
178 
179 void
180 BNavigator::AttachedToWindow()
181 {
182 	// All messages should arrive here
183 	fBack->SetTarget(this);
184 	fForw->SetTarget(this);
185 	fUp->SetTarget(this);
186 	fLocation->SetTarget(this);
187 }
188 
189 
190 void
191 BNavigator::AllAttached()
192 {
193 	// Inital setup of widget states
194 	UpdateLocation(0, kActionSet);
195 }
196 
197 
198 void
199 BNavigator::Draw(BRect)
200 {
201 	rgb_color bgColor = ui_color(B_PANEL_BACKGROUND_COLOR);
202 	rgb_color shineColor = ui_color(B_SHINE_COLOR);
203 	rgb_color halfDarkColor = tint_color(bgColor, B_DARKEN_1_TINT);
204 	rgb_color darkColor = tint_color(bgColor, B_DARKEN_2_TINT);
205 	// Draws a beveled smooth border
206 	BeginLineArray(4);
207 	AddLine(Bounds().LeftTop(), Bounds().RightTop(), shineColor);
208 	AddLine(Bounds().LeftTop(), Bounds().LeftBottom() - BPoint(0, 1),
209 		shineColor);
210 	AddLine(Bounds().LeftBottom() - BPoint(-1, 1),
211 		Bounds().RightBottom() - BPoint(0, 1), halfDarkColor);
212 	AddLine(Bounds().LeftBottom(), Bounds().RightBottom(), darkColor);
213 	EndLineArray();
214 }
215 
216 
217 void
218 BNavigator::MessageReceived(BMessage* message)
219 {
220 	switch (message->what) {
221 		case kNavigatorCommandBackward:
222 			GoBackward((modifiers() & B_OPTION_KEY) == B_OPTION_KEY);
223 			break;
224 
225 		case kNavigatorCommandForward:
226 			GoForward((modifiers() & B_OPTION_KEY) == B_OPTION_KEY);
227 			break;
228 
229 		case kNavigatorCommandUp:
230 			GoUp((modifiers() & B_OPTION_KEY) == B_OPTION_KEY);
231 			break;
232 
233 		case kNavigatorCommandLocation:
234 			GoTo();
235 			break;
236 
237 		default:
238 		{
239 			// Catch any dropped refs and try to switch to this new directory
240 			entry_ref ref;
241 			if (message->FindRef("refs", &ref) == B_OK) {
242 				BMessage message(kSwitchDirectory);
243 				BEntry entry(&ref, true);
244 				if (!entry.IsDirectory()) {
245 					entry.GetRef(&ref);
246 					BPath path(&ref);
247 					path.GetParent(&path);
248 					get_ref_for_path(path.Path(), &ref);
249 				}
250 				message.AddRef("refs", &ref);
251 				message.AddInt32("action", kActionSet);
252 				Window()->PostMessage(&message);
253 			}
254 		}
255 	}
256 }
257 
258 
259 void
260 BNavigator::GoBackward(bool option)
261 {
262 	int32 itemCount = fBackHistory.CountItems();
263 	if (itemCount >= 2 && fBackHistory.ItemAt(itemCount - 2)) {
264 		BEntry entry;
265 		if (entry.SetTo(fBackHistory.ItemAt(itemCount - 2)->Path()) == B_OK)
266 			SendNavigationMessage(kActionBackward, &entry, option);
267 	}
268 }
269 
270 
271 void
272 BNavigator::GoForward(bool option)
273 {
274 	if (fForwHistory.CountItems() >= 1) {
275 		BEntry entry;
276 		if (entry.SetTo(fForwHistory.LastItem()->Path()) == B_OK)
277 			SendNavigationMessage(kActionForward, &entry, option);
278 	}
279 }
280 
281 
282 void
283 BNavigator::GoUp(bool option)
284 {
285 	BEntry entry;
286 	if (entry.SetTo(fPath.Path()) == B_OK) {
287 		BEntry parentEntry;
288 		if (entry.GetParent(&parentEntry) == B_OK
289 			&& !FSIsDeskDir(&parentEntry)) {
290 			SendNavigationMessage(kActionUp, &parentEntry, option);
291 		}
292 	}
293 }
294 
295 
296 void
297 BNavigator::SendNavigationMessage(NavigationAction action, BEntry* entry,
298 	bool option)
299 {
300 	entry_ref ref;
301 
302 	if (entry->GetRef(&ref) == B_OK) {
303 		BMessage message;
304 		message.AddRef("refs", &ref);
305 		message.AddInt32("action", action);
306 
307 		// get the node of this folder for selecting it in the new location
308 		const node_ref* nodeRef;
309 		if (Window() && Window()->TargetModel())
310 			nodeRef = Window()->TargetModel()->NodeRef();
311 		else
312 			nodeRef = NULL;
313 
314 		// if the option key was held down, open in new window (send message
315 		// to be_app) otherwise send message to this window. TTracker
316 		// (be_app) understands nodeRefToSlection, BContainerWindow doesn't,
317 		// so we have to select the item manually
318 		if (option) {
319 			message.what = B_REFS_RECEIVED;
320 			if (nodeRef) {
321 				message.AddData("nodeRefToSelect", B_RAW_TYPE, nodeRef,
322 					sizeof(node_ref));
323 			}
324 			be_app->PostMessage(&message);
325 		} else {
326 			message.what = kSwitchDirectory;
327 			Window()->PostMessage(&message);
328 			UnlockLooper();
329 				// This is to prevent a dead-lock situation.
330 				// SelectChildInParentSoon() eventually locks the
331 				// TaskLoop::fLock. Later, when StandAloneTaskLoop::Run()
332 				// runs, it also locks TaskLoop::fLock and subsequently
333 				// locks this window's looper. Therefore we can't call
334 				// SelectChildInParentSoon with our Looper locked,
335 				// because we would get different orders of locking
336 				// (thus the risk of dead-locking).
337 				//
338 				// Todo: Change the locking behaviour of
339 				// StandAloneTaskLoop::Run() and subsequently called
340 				// functions.
341 			if (nodeRef)
342 				dynamic_cast<TTracker*>(be_app)->SelectChildInParentSoon(&ref, nodeRef);
343 			LockLooper();
344 		}
345 	}
346 }
347 
348 
349 void
350 BNavigator::GoTo()
351 {
352 	BString pathname = fLocation->Text();
353 
354 	if (pathname.Compare("") == 0)
355 		pathname = "/";
356 
357 	BEntry entry;
358 	entry_ref ref;
359 
360 	if (entry.SetTo(pathname.String()) == B_OK
361 		&& !FSIsDeskDir(&entry)
362 		&& entry.GetRef(&ref) == B_OK) {
363 		BMessage message(kSwitchDirectory);
364 		message.AddRef("refs", &ref);
365 		message.AddInt32("action", kActionLocation);
366 		Window()->PostMessage(&message);
367 	} else {
368 		BPath path;
369 
370 		if (Window() && Window()->TargetModel()) {
371 			Window()->TargetModel()->GetPath(&path);
372 			fLocation->SetText(path.Path());
373 		}
374 	}
375 }
376 
377 
378 void
379 BNavigator::UpdateLocation(const Model* newmodel, int32 action)
380 {
381 	if (newmodel)
382 		newmodel->GetPath(&fPath);
383 
384 	// Modify history according to commands
385 	switch (action) {
386 		case kActionBackward:
387 			fForwHistory.AddItem(fBackHistory.RemoveItemAt(fBackHistory.CountItems()-1));
388 			break;
389 
390 		case kActionForward:
391 			fBackHistory.AddItem(fForwHistory.RemoveItemAt(fForwHistory.CountItems()-1));
392 			break;
393 
394 		case kActionUpdatePath:
395 			break;
396 
397 		default:
398 			fForwHistory.MakeEmpty();
399 			fBackHistory.AddItem(new BPath(fPath));
400 
401 			while (fBackHistory.CountItems() > kMaxHistory)
402 				fBackHistory.RemoveItem(fBackHistory.FirstItem(), true);
403 			break;
404 	}
405 
406 	// Enable Up button when there is any parent
407 	BEntry entry;
408 	if (entry.SetTo(fPath.Path()) == B_OK) {
409 		BEntry parentEntry;
410 		fUp->SetEnabled(entry.GetParent(&parentEntry) == B_OK
411 			&& !FSIsDeskDir(&parentEntry));
412 	}
413 
414 	// Enable history buttons if history contains something
415 	fForw->SetEnabled(fForwHistory.CountItems() > 0);
416 	fBack->SetEnabled(fBackHistory.CountItems() > 1);
417 
418 	// Avoid loss of selection and cursor position
419 	if (action != kActionLocation)
420 		fLocation->SetText(fPath.Path());
421 }
422 
423 
424 float
425 BNavigator::CalcNavigatorHeight(void)
426 {
427 	// Empiric formula from how much space the textview
428 	// will take once it is attached (using be_plain_font):
429 	return  ceilf(11.0f + be_plain_font->Size()*(1.0f + 7.0f / 30.0f));
430 }
431