xref: /haiku/src/kits/tracker/Navigator.cpp (revision 5e96d7d537fbec23bad4ae9b4c8e7b02e769f0c6)
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 		case kNavigatorCommandSetFocus:
238 			fLocation->MakeFocus();
239 			break;
240 
241 		default:
242 		{
243 			// Catch any dropped refs and try to switch to this new directory
244 			entry_ref ref;
245 			if (message->FindRef("refs", &ref) == B_OK) {
246 				BMessage message(kSwitchDirectory);
247 				BEntry entry(&ref, true);
248 				if (!entry.IsDirectory()) {
249 					entry.GetRef(&ref);
250 					BPath path(&ref);
251 					path.GetParent(&path);
252 					get_ref_for_path(path.Path(), &ref);
253 				}
254 				message.AddRef("refs", &ref);
255 				message.AddInt32("action", kActionSet);
256 				Window()->PostMessage(&message);
257 			}
258 		}
259 	}
260 }
261 
262 
263 void
264 BNavigator::GoBackward(bool option)
265 {
266 	int32 itemCount = fBackHistory.CountItems();
267 	if (itemCount >= 2 && fBackHistory.ItemAt(itemCount - 2)) {
268 		BEntry entry;
269 		if (entry.SetTo(fBackHistory.ItemAt(itemCount - 2)->Path()) == B_OK)
270 			SendNavigationMessage(kActionBackward, &entry, option);
271 	}
272 }
273 
274 
275 void
276 BNavigator::GoForward(bool option)
277 {
278 	if (fForwHistory.CountItems() >= 1) {
279 		BEntry entry;
280 		if (entry.SetTo(fForwHistory.LastItem()->Path()) == B_OK)
281 			SendNavigationMessage(kActionForward, &entry, option);
282 	}
283 }
284 
285 
286 void
287 BNavigator::GoUp(bool option)
288 {
289 	BEntry entry;
290 	if (entry.SetTo(fPath.Path()) == B_OK) {
291 		BEntry parentEntry;
292 		if (entry.GetParent(&parentEntry) == B_OK
293 			&& !FSIsDeskDir(&parentEntry)) {
294 			SendNavigationMessage(kActionUp, &parentEntry, option);
295 		}
296 	}
297 }
298 
299 
300 void
301 BNavigator::SendNavigationMessage(NavigationAction action, BEntry* entry,
302 	bool option)
303 {
304 	entry_ref ref;
305 
306 	if (entry->GetRef(&ref) == B_OK) {
307 		BMessage message;
308 		message.AddRef("refs", &ref);
309 		message.AddInt32("action", action);
310 
311 		// get the node of this folder for selecting it in the new location
312 		const node_ref* nodeRef;
313 		if (Window() && Window()->TargetModel())
314 			nodeRef = Window()->TargetModel()->NodeRef();
315 		else
316 			nodeRef = NULL;
317 
318 		// if the option key was held down, open in new window (send message
319 		// to be_app) otherwise send message to this window. TTracker
320 		// (be_app) understands nodeRefToSlection, BContainerWindow doesn't,
321 		// so we have to select the item manually
322 		if (option) {
323 			message.what = B_REFS_RECEIVED;
324 			if (nodeRef) {
325 				message.AddData("nodeRefToSelect", B_RAW_TYPE, nodeRef,
326 					sizeof(node_ref));
327 			}
328 			be_app->PostMessage(&message);
329 		} else {
330 			message.what = kSwitchDirectory;
331 			Window()->PostMessage(&message);
332 			UnlockLooper();
333 				// This is to prevent a dead-lock situation.
334 				// SelectChildInParentSoon() eventually locks the
335 				// TaskLoop::fLock. Later, when StandAloneTaskLoop::Run()
336 				// runs, it also locks TaskLoop::fLock and subsequently
337 				// locks this window's looper. Therefore we can't call
338 				// SelectChildInParentSoon with our Looper locked,
339 				// because we would get different orders of locking
340 				// (thus the risk of dead-locking).
341 				//
342 				// Todo: Change the locking behaviour of
343 				// StandAloneTaskLoop::Run() and subsequently called
344 				// functions.
345 			if (nodeRef)
346 				dynamic_cast<TTracker*>(be_app)->SelectChildInParentSoon(&ref, nodeRef);
347 			LockLooper();
348 		}
349 	}
350 }
351 
352 
353 void
354 BNavigator::GoTo()
355 {
356 	BString pathname = fLocation->Text();
357 
358 	if (pathname.Compare("") == 0)
359 		pathname = "/";
360 
361 	BEntry entry;
362 	entry_ref ref;
363 
364 	if (entry.SetTo(pathname.String()) == B_OK
365 		&& !FSIsDeskDir(&entry)
366 		&& entry.GetRef(&ref) == B_OK) {
367 		BMessage message(kSwitchDirectory);
368 		message.AddRef("refs", &ref);
369 		message.AddInt32("action", kActionLocation);
370 		Window()->PostMessage(&message);
371 	} else {
372 		BPath path;
373 
374 		if (Window() && Window()->TargetModel()) {
375 			Window()->TargetModel()->GetPath(&path);
376 			fLocation->SetText(path.Path());
377 		}
378 	}
379 }
380 
381 
382 void
383 BNavigator::UpdateLocation(const Model* newmodel, int32 action)
384 {
385 	if (newmodel)
386 		newmodel->GetPath(&fPath);
387 
388 	// Modify history according to commands
389 	switch (action) {
390 		case kActionBackward:
391 			fForwHistory.AddItem(fBackHistory.RemoveItemAt(fBackHistory.CountItems()-1));
392 			break;
393 
394 		case kActionForward:
395 			fBackHistory.AddItem(fForwHistory.RemoveItemAt(fForwHistory.CountItems()-1));
396 			break;
397 
398 		case kActionUpdatePath:
399 			break;
400 
401 		default:
402 			fForwHistory.MakeEmpty();
403 			fBackHistory.AddItem(new BPath(fPath));
404 
405 			while (fBackHistory.CountItems() > kMaxHistory)
406 				fBackHistory.RemoveItem(fBackHistory.FirstItem(), true);
407 			break;
408 	}
409 
410 	// Enable Up button when there is any parent
411 	BEntry entry;
412 	if (entry.SetTo(fPath.Path()) == B_OK) {
413 		BEntry parentEntry;
414 		fUp->SetEnabled(entry.GetParent(&parentEntry) == B_OK
415 			&& !FSIsDeskDir(&parentEntry));
416 	}
417 
418 	// Enable history buttons if history contains something
419 	fForw->SetEnabled(fForwHistory.CountItems() > 0);
420 	fBack->SetEnabled(fBackHistory.CountItems() > 1);
421 
422 	// Avoid loss of selection and cursor position
423 	if (action != kActionLocation)
424 		fLocation->SetText(fPath.Path());
425 }
426 
427 
428 float
429 BNavigator::CalcNavigatorHeight(void)
430 {
431 	// Empiric formula from how much space the textview
432 	// will take once it is attached (using be_plain_font):
433 	return  ceilf(11.0f + be_plain_font->Size()*(1.0f + 7.0f / 30.0f));
434 }
435