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