xref: /haiku/src/kits/tracker/Navigator.cpp (revision f5821a1aee77d3b9a979b42c68a79e50b5ebaefe)
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 	// 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 
193 void
194 BNavigator::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 
212 void
213 BNavigator::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 
254 void
255 BNavigator::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 
266 void
267 BNavigator::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 
277 void
278 BNavigator::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 
291 void
292 BNavigator::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 
344 void
345 BNavigator::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 
373 void
374 BNavigator::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 
419 float
420 BNavigator::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