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