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