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