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 "Navigator.h" 37 38 #include <ControlLook.h> 39 #include <TextControl.h> 40 #include <Window.h> 41 42 #include "Bitmaps.h" 43 #include "Commands.h" 44 #include "FSUtils.h" 45 #include "Tracker.h" 46 47 48 namespace BPrivate { 49 50 static const int32 kMaxHistory = 32; 51 52 } 53 54 55 // #pragma mark - BNavigator 56 57 58 BNavigator::BNavigator(const Model* model) 59 : 60 BToolBar(), 61 fBackHistory(8, true), 62 fForwHistory(8, true) 63 { 64 // Get initial path 65 model->GetPath(&fPath); 66 67 fLocation = new BTextControl("Location", "", "", 68 new BMessage(kNavigatorCommandLocation)); 69 fLocation->SetDivider(0); 70 71 GroupLayout()->SetInsets(0.0f, 0.0f, B_USE_HALF_ITEM_INSETS, 1.0f); 72 // 1px bottom inset used for border 73 74 // Needed to draw the bottom border 75 SetFlags(Flags() | B_WILL_DRAW); 76 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 77 } 78 79 80 BNavigator::~BNavigator() 81 { 82 } 83 84 85 void 86 BNavigator::AttachedToWindow() 87 { 88 // Set up toolbar items 89 BBitmap* bmpBack = new BBitmap(BRect(0, 0, 19, 19), B_RGBA32); 90 GetTrackerResources()->GetIconResource(R_ResBackNav, B_MINI_ICON, 91 bmpBack); 92 AddAction(kNavigatorCommandBackward, this, bmpBack); 93 SetActionEnabled(kNavigatorCommandBackward, false); 94 delete bmpBack; 95 96 BBitmap* bmpForw = new BBitmap(BRect(0, 0, 19, 19), B_RGBA32); 97 GetTrackerResources()->GetIconResource(R_ResForwNav, B_MINI_ICON, 98 bmpForw); 99 AddAction(kNavigatorCommandForward, this, bmpForw); 100 SetActionEnabled(kNavigatorCommandForward, false); 101 delete bmpForw; 102 103 BBitmap* bmpUp = new BBitmap(BRect(0, 0, 19, 19), B_RGBA32); 104 GetTrackerResources()->GetIconResource(R_ResUpNav, B_MINI_ICON, 105 bmpUp); 106 AddAction(kNavigatorCommandUp, this, bmpUp); 107 SetActionEnabled(kNavigatorCommandUp, false); 108 delete bmpUp; 109 110 AddView(fLocation); 111 fLocation->SetTarget(this); 112 } 113 114 115 void 116 BNavigator::AllAttached() 117 { 118 // Inital setup of widget states 119 UpdateLocation(0, kActionSet); 120 } 121 122 123 void 124 BNavigator::Draw(BRect updateRect) 125 { 126 // Draw a 1px bottom border, like BMenuBar 127 BRect rect(Bounds()); 128 rgb_color base = LowColor(); 129 uint32 flags = 0; 130 131 be_control_look->DrawBorder(this, rect, updateRect, base, 132 B_PLAIN_BORDER, flags, BControlLook::B_BOTTOM_BORDER); 133 134 _inherited::Draw(rect & updateRect); 135 } 136 137 138 void 139 BNavigator::MessageReceived(BMessage* message) 140 { 141 switch (message->what) { 142 case kNavigatorCommandBackward: 143 GoBackward((modifiers() & B_OPTION_KEY) == B_OPTION_KEY); 144 break; 145 146 case kNavigatorCommandForward: 147 GoForward((modifiers() & B_OPTION_KEY) == B_OPTION_KEY); 148 break; 149 150 case kNavigatorCommandUp: 151 GoUp((modifiers() & B_OPTION_KEY) == B_OPTION_KEY); 152 break; 153 154 case kNavigatorCommandLocation: 155 GoTo(); 156 break; 157 158 case kNavigatorCommandSetFocus: 159 fLocation->MakeFocus(); 160 break; 161 162 default: 163 { 164 // Catch any dropped refs and try to switch to this new directory 165 entry_ref ref; 166 if (message->FindRef("refs", &ref) == B_OK) { 167 BMessage message(kSwitchDirectory); 168 BEntry entry(&ref, true); 169 if (!entry.IsDirectory()) { 170 entry.GetRef(&ref); 171 BPath path(&ref); 172 path.GetParent(&path); 173 get_ref_for_path(path.Path(), &ref); 174 } 175 message.AddRef("refs", &ref); 176 message.AddInt32("action", kActionSet); 177 Window()->PostMessage(&message); 178 } 179 } 180 } 181 } 182 183 184 void 185 BNavigator::GoBackward(bool option) 186 { 187 int32 itemCount = fBackHistory.CountItems(); 188 if (itemCount >= 2 && fBackHistory.ItemAt(itemCount - 2)) { 189 BEntry entry; 190 if (entry.SetTo(fBackHistory.ItemAt(itemCount - 2)->Path()) == B_OK) 191 SendNavigationMessage(kActionBackward, &entry, option); 192 } 193 } 194 195 196 void 197 BNavigator::GoForward(bool option) 198 { 199 if (fForwHistory.CountItems() >= 1) { 200 BEntry entry; 201 if (entry.SetTo(fForwHistory.LastItem()->Path()) == B_OK) 202 SendNavigationMessage(kActionForward, &entry, option); 203 } 204 } 205 206 207 void 208 BNavigator::GoUp(bool option) 209 { 210 BEntry entry; 211 if (entry.SetTo(fPath.Path()) == B_OK) { 212 BEntry parentEntry; 213 if (entry.GetParent(&parentEntry) == B_OK) { 214 SendNavigationMessage(kActionUp, &parentEntry, option); 215 } 216 } 217 } 218 219 220 void 221 BNavigator::SendNavigationMessage(NavigationAction action, BEntry* entry, 222 bool option) 223 { 224 entry_ref ref; 225 226 if (entry->GetRef(&ref) == B_OK) { 227 BMessage message; 228 message.AddRef("refs", &ref); 229 message.AddInt32("action", action); 230 231 // get the node of this folder for selecting it in the new location 232 const node_ref* nodeRef; 233 if (Window() && Window()->TargetModel()) 234 nodeRef = Window()->TargetModel()->NodeRef(); 235 else 236 nodeRef = NULL; 237 238 // if the option key was held down, open in new window (send message 239 // to be_app) otherwise send message to this window. TTracker 240 // (be_app) understands nodeRefToSlection, BContainerWindow doesn't, 241 // so we have to select the item manually 242 if (option) { 243 message.what = B_REFS_RECEIVED; 244 if (nodeRef != NULL) { 245 message.AddData("nodeRefToSelect", B_RAW_TYPE, nodeRef, 246 sizeof(node_ref)); 247 } 248 be_app->PostMessage(&message); 249 } else { 250 message.what = kSwitchDirectory; 251 Window()->PostMessage(&message); 252 UnlockLooper(); 253 // This is to prevent a dead-lock situation. 254 // SelectChildInParentSoon() eventually locks the 255 // TaskLoop::fLock. Later, when StandAloneTaskLoop::Run() 256 // runs, it also locks TaskLoop::fLock and subsequently 257 // locks this window's looper. Therefore we can't call 258 // SelectChildInParentSoon with our Looper locked, 259 // because we would get different orders of locking 260 // (thus the risk of dead-locking). 261 // 262 // Todo: Change the locking behaviour of 263 // StandAloneTaskLoop::Run() and subsequently called 264 // functions. 265 if (nodeRef != NULL) { 266 TTracker* tracker = dynamic_cast<TTracker*>(be_app); 267 if (tracker != NULL) 268 tracker->SelectChildInParentSoon(&ref, nodeRef); 269 } 270 271 LockLooper(); 272 } 273 } 274 } 275 276 277 void 278 BNavigator::GoTo() 279 { 280 BString pathname = fLocation->Text(); 281 282 if (pathname.Compare("") == 0) 283 pathname = "/"; 284 285 BEntry entry; 286 entry_ref ref; 287 288 if (entry.SetTo(pathname.String()) == B_OK 289 && entry.GetRef(&ref) == B_OK) { 290 BMessage message(kSwitchDirectory); 291 message.AddRef("refs", &ref); 292 message.AddInt32("action", kActionLocation); 293 Window()->PostMessage(&message); 294 } else { 295 BPath path; 296 297 if (Window() && Window()->TargetModel()) { 298 Window()->TargetModel()->GetPath(&path); 299 fLocation->SetText(path.Path()); 300 } 301 } 302 } 303 304 305 void 306 BNavigator::UpdateLocation(const Model* newmodel, int32 action) 307 { 308 if (newmodel) 309 newmodel->GetPath(&fPath); 310 311 // Modify history according to commands 312 switch (action) { 313 case kActionBackward: 314 fForwHistory.AddItem(fBackHistory.RemoveItemAt( 315 fBackHistory.CountItems() - 1)); 316 break; 317 318 case kActionForward: 319 fBackHistory.AddItem(fForwHistory.RemoveItemAt( 320 fForwHistory.CountItems() - 1)); 321 break; 322 323 case kActionUpdatePath: 324 break; 325 326 default: 327 fForwHistory.MakeEmpty(); 328 fBackHistory.AddItem(new BPath(fPath)); 329 330 while (fBackHistory.CountItems() > kMaxHistory) 331 fBackHistory.RemoveItem(fBackHistory.FirstItem(), true); 332 break; 333 } 334 335 // Enable Up button when there is any parent 336 BEntry entry; 337 if (entry.SetTo(fPath.Path()) == B_OK) { 338 BEntry parentEntry; 339 bool enable = entry.GetParent(&parentEntry) == B_OK; 340 SetActionEnabled(kNavigatorCommandUp, enable); 341 } 342 343 // Enable history buttons if history contains something 344 SetActionEnabled(kNavigatorCommandForward, fForwHistory.CountItems() > 0); 345 SetActionEnabled(kNavigatorCommandBackward, fBackHistory.CountItems() > 1); 346 347 // Avoid loss of selection and cursor position 348 if (action != kActionLocation) 349 fLocation->SetText(fPath.Path()); 350 } 351