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