xref: /haiku/src/kits/tracker/Navigator.cpp (revision 93aeb8c3bc3f13cb1f282e3e749258a23790d947)
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 #include "Bitmaps.h"
35 #include "Commands.h"
36 #include "ContainerWindow.h"
37 #include "FSUtils.h"
38 #include "Model.h"
39 #include "Navigator.h"
40 #include "Tracker.h"
41 #include <Window.h>
42 #include <Picture.h>
43 #include <TextControl.h>
44 
45 namespace BPrivate {
46 
47 static const int32 kMaxHistory = 32;
48 
49 static const rgb_color kBgColor = {220, 220, 220, 255};
50 static const rgb_color kShineColor = {255, 255, 255, 255};
51 static const rgb_color kHalfDarkColor = {200, 200, 200, 255};
52 static const rgb_color kDarkColor = {166, 166, 166, 255};
53 
54 }
55 
56 // BPictureButton() will crash when giving zero pointers,
57 // although we really want and have to set up the
58 // pictures when we can, e.g. on a AttachedToWindow.
59 static BPicture sPicture;
60 
61 BNavigatorButton::BNavigatorButton(BRect rect, const char *name, BMessage *message,
62 	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
69 	// avoid ugly border on click
70 	SetViewColor(kBgColor);
71 	SetHighColor(kBgColor);
72 	SetLowColor(kBgColor);
73 }
74 
75 BNavigatorButton::~BNavigatorButton()
76 {
77 }
78 
79 void
80 BNavigatorButton::AttachedToWindow()
81 {
82 	BBitmap *bmpOn = 0;
83 	GetTrackerResources()->GetBitmapResource(B_MESSAGE_TYPE, fResIDOn, &bmpOn);
84 	SetPicture(bmpOn, true, true);
85 	delete bmpOn;
86 
87 	BBitmap *bmpOff = 0;
88 	GetTrackerResources()->GetBitmapResource(B_MESSAGE_TYPE, fResIDOff, &bmpOff);
89 	SetPicture(bmpOff, true, false);
90 	delete bmpOff;
91 
92 	BBitmap *bmpDisabled = 0;
93 	GetTrackerResources()->GetBitmapResource(B_MESSAGE_TYPE, fResIDDisabled, &bmpDisabled);
94 	SetPicture(bmpDisabled, false, false);
95 	SetPicture(bmpDisabled, false, true);
96 	delete bmpDisabled;
97 }
98 
99 void
100 BNavigatorButton::SetPicture(BBitmap *bitmap, bool enabled, bool on)
101 {
102 	if (bitmap) {
103 		BPicture picture;
104 		BView view(bitmap->Bounds(), "", 0, 0);
105 		AddChild(&view);
106 		view.BeginPicture(&picture);
107 		view.SetHighColor(kBgColor);
108 		view.FillRect(view.Bounds());
109 		view.SetDrawingMode(B_OP_OVER);
110 		view.DrawBitmap(bitmap, BPoint(0, 0));
111 		view.EndPicture();
112 		RemoveChild(&view);
113 		if (enabled)
114 			if (on)
115 				SetEnabledOn(&picture);
116 			else
117 				SetEnabledOff(&picture);
118 		else
119 			if (on)
120 				SetDisabledOn(&picture);
121 			else
122 				SetDisabledOff(&picture);
123 	}
124 }
125 
126 
127 BNavigator::BNavigator(const Model *model, BRect rect, uint32 resizeMask)
128 	:	BView(rect, "Navigator", resizeMask, B_WILL_DRAW),
129 	fBack(0),
130 	fForw(0),
131 	fUp(0),
132 	fBackHistory(8, true),
133 	fForwHistory(8, true)
134 {
135 	// Get initial path
136 	model->GetPath(&fPath);
137 
138 	SetViewColor(kBgColor);
139 
140 	float top = 2 + (be_plain_font->Size() - 8) / 2;
141 
142 	// Set up widgets
143 	fBack = new BNavigatorButton(BRect(3, top, 21, top + 17), "Back",
144 		new BMessage(kNavigatorCommandBackward), kResBackNavActiveSel,
145 		kResBackNavActive, kResBackNavInactive);
146 	fBack->SetEnabled(false);
147 	AddChild(fBack);
148 
149 	fForw = new BNavigatorButton(BRect(35, top, 53, top + 17), "Forw",
150 		new BMessage(kNavigatorCommandForward), kResForwNavActiveSel,
151 		kResForwNavActive, kResForwNavInactive);
152 	fForw->SetEnabled(false);
153 	AddChild(fForw);
154 
155 	fUp = new BNavigatorButton(BRect(67, top, 84, top + 17), "Up",
156 		new BMessage(kNavigatorCommandUp), kResUpNavActiveSel,
157 		kResUpNavActive, kResUpNavInactive);
158 	fUp->SetEnabled(false);
159 	AddChild(fUp);
160 
161 	fLocation = new BTextControl(BRect(97, 2, rect.Width() - 2, 21),
162 		"Location", "", "", new BMessage(kNavigatorCommandLocation),
163 		B_FOLLOW_LEFT_RIGHT);
164 	fLocation->SetDivider(0);
165 	AddChild(fLocation);
166 
167 }
168 
169 BNavigator::~BNavigator()
170 {
171 }
172 
173 void
174 BNavigator::AttachedToWindow()
175 {
176 	// Inital setup of widget states
177 	UpdateLocation(0, kActionSet);
178 
179 	// All messages should arrive here
180 	fBack->SetTarget(this);
181 	fForw->SetTarget(this);
182 	fUp->SetTarget(this);
183 	fLocation->SetTarget(this);
184 }
185 
186 void
187 BNavigator::Draw(BRect)
188 {
189 	// Draws a beveled smooth border
190 	BeginLineArray(4);
191 	AddLine(Bounds().LeftTop(), Bounds().RightTop(), kShineColor);
192 	AddLine(Bounds().LeftTop(), Bounds().LeftBottom() - BPoint(0, 1), kShineColor);
193 	AddLine(Bounds().LeftBottom() - BPoint(-1, 1), Bounds().RightBottom() - BPoint(0, 1), kHalfDarkColor);
194 	AddLine(Bounds().LeftBottom(), Bounds().RightBottom(), kDarkColor);
195 	EndLineArray();
196 }
197 
198 void
199 BNavigator::MessageReceived(BMessage *message)
200 {
201 	switch (message->what) {
202 		case kNavigatorCommandBackward:
203 			GoBackward((modifiers() & B_OPTION_KEY) == B_OPTION_KEY);
204 			break;
205 
206 		case kNavigatorCommandForward:
207 			GoForward((modifiers() & B_OPTION_KEY) == B_OPTION_KEY);
208 			break;
209 
210 		case kNavigatorCommandUp:
211 			GoUp((modifiers() & B_OPTION_KEY) == B_OPTION_KEY);
212 			break;
213 
214 		case kNavigatorCommandLocation:
215 			GoTo();
216 			break;
217 
218 		default:
219 			{
220 				// Catch any dropped refs and try
221 				// to switch to this new directory
222 				entry_ref ref;
223 				if (message->FindRef("refs", &ref) == B_OK) {
224 					BMessage message(kSwitchDirectory);
225 					BEntry entry(&ref, true);
226 					if (!entry.IsDirectory()) {
227 						entry.GetRef(&ref);
228 						BPath path(&ref);
229 							path.GetParent(&path);
230 							get_ref_for_path(path.Path(), &ref);
231 					}
232 					message.AddRef("refs", &ref);
233 					message.AddInt32("action", kActionSet);
234 					Window()->PostMessage(&message);
235 				}
236 			}
237 	}
238 }
239 
240 void
241 BNavigator::GoBackward(bool option)
242 {
243 	int32 itemCount = fBackHistory.CountItems();
244 	if (itemCount >= 2 && fBackHistory.ItemAt(itemCount - 2)) {
245 		BEntry entry;
246 		if (entry.SetTo(fBackHistory.ItemAt(itemCount - 2)->Path()) == B_OK)
247 			SendNavigationMessage(kActionBackward, &entry, option);
248 	}
249 }
250 
251 void
252 BNavigator::GoForward(bool option)
253 {
254 	if (fForwHistory.CountItems() >= 1) {
255 		BEntry entry;
256 		if (entry.SetTo(fForwHistory.LastItem()->Path()) == B_OK)
257 			SendNavigationMessage(kActionForward, &entry, option);
258 	}
259 }
260 
261 void
262 BNavigator::GoUp(bool option)
263 {
264 	BEntry entry;
265 	if (entry.SetTo(fPath.Path()) == B_OK) {
266 		BEntry parentEntry;
267 		if (entry.GetParent(&parentEntry) == B_OK && !FSIsDeskDir(&parentEntry))
268 			SendNavigationMessage(kActionUp, &parentEntry, option);
269 	}
270 }
271 
272 void
273 BNavigator::SendNavigationMessage(NavigationAction action, BEntry *entry, bool option)
274 {
275 	entry_ref ref;
276 
277 	if (entry->GetRef(&ref) == B_OK) {
278 		BMessage message;
279 		message.AddRef("refs", &ref);
280 		message.AddInt32("action", action);
281 
282 		// get the node of this folder for selecting it in the new location
283 		const node_ref *nodeRef;
284 		if (Window() && Window()->TargetModel())
285 			nodeRef = Window()->TargetModel()->NodeRef();
286 		else
287 			nodeRef = NULL;
288 
289 		// if the option key was held down, open in new window (send message to be_app)
290 		// otherwise send message to this window. TTracker (be_app) understands nodeRefToSlection,
291 		// BContainerWindow doesn't, so we have to select the item manually
292 		if (option) {
293 			message.what = B_REFS_RECEIVED;
294 			if (nodeRef)
295 				message.AddData("nodeRefToSelect", B_RAW_TYPE, nodeRef, sizeof(node_ref));
296 			be_app->PostMessage(&message);
297 		} else {
298 			message.what = kSwitchDirectory;
299 			Window()->PostMessage(&message);
300 			UnlockLooper();
301 				// This is to prevent a dead-lock situation. SelectChildInParentSoon()
302 				// eventually locks the TaskLoop::fLock. Later, when StandAloneTaskLoop::Run()
303 				// runs, it also locks TaskLoop::fLock and subsequently locks this window's looper.
304 				// Therefore we can't call SelectChildInParentSoon with our Looper locked,
305 				// because we would get different orders of locking (thus the risk of dead-locking).
306 				//
307 				// Todo: Change the locking behaviour of StandAloneTaskLoop::Run() and sub-
308 				// sequently called functions.
309 			if (nodeRef)
310 				dynamic_cast<TTracker *>(be_app)->SelectChildInParentSoon(&ref, nodeRef);
311 			LockLooper();
312 		}
313 	}
314 }
315 
316 void
317 BNavigator::GoTo()
318 {
319 	BString pathname = fLocation->Text();
320 
321 	if (pathname.Compare("") == 0)
322 		pathname = "/";
323 
324 	BEntry entry;
325 	entry_ref ref;
326 
327 	if (entry.SetTo(pathname.String()) == B_OK
328 		&& !FSIsDeskDir(&entry)
329 		&& entry.GetRef(&ref) == B_OK) {
330 		BMessage message(kSwitchDirectory);
331 		message.AddRef("refs", &ref);
332 		message.AddInt32("action", kActionLocation);
333 		Window()->PostMessage(&message);
334 	} else {
335 		BPath path;
336 
337 		if (Window()
338 			&& Window()->TargetModel()) {
339 			Window()->TargetModel()->GetPath(&path);
340 			fLocation->SetText(path.Path());
341 		}
342 	}
343 }
344 
345 void
346 BNavigator::UpdateLocation(const Model *newmodel, int32 action)
347 {
348 	if (newmodel)
349 		newmodel->GetPath(&fPath);
350 
351 
352 	// Modify history according to commands
353 	switch (action) {
354 		case kActionBackward:
355 			fForwHistory.AddItem(fBackHistory.RemoveItemAt(fBackHistory.CountItems()-1));
356 			break;
357 		case kActionForward:
358 			fBackHistory.AddItem(fForwHistory.RemoveItemAt(fForwHistory.CountItems()-1));
359 			break;
360 		case kActionUpdatePath:
361 			break;
362 		default:
363 			fForwHistory.MakeEmpty();
364 			fBackHistory.AddItem(new BPath(fPath));
365 
366 			for (;fBackHistory.CountItems()>kMaxHistory;)
367 				fBackHistory.RemoveItem(fBackHistory.FirstItem(), true);
368 			break;
369 	}
370 
371 	// Enable Up button when there is any parent
372 	BEntry entry;
373 	if (entry.SetTo(fPath.Path()) == B_OK) {
374 		BEntry parentEntry;
375 		fUp->SetEnabled(entry.GetParent(&parentEntry) == B_OK && !FSIsDeskDir(&parentEntry));
376 	}
377 
378 	// Enable history buttons if history contains something
379 	fForw->SetEnabled(fForwHistory.CountItems() > 0);
380 	fBack->SetEnabled(fBackHistory.CountItems() > 1);
381 
382 	// Avoid loss of selection and cursor position
383 	if (action != kActionLocation)
384 		fLocation->SetText(fPath.Path());
385 }
386 
387 float
388 BNavigator::CalcNavigatorHeight(void)
389 {
390 	// Empiric formula from how much space the textview
391 	// will take once it is attached (using be_plain_font):
392 	return  ceilf(11.0f + be_plain_font->Size()*(1.0f + 7.0f / 30.0f));
393 }
394