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 "RecentItems.h" 37 38 #include <Roster.h> 39 40 #include "Attributes.h" 41 #include "IconMenuItem.h" 42 #include "Model.h" 43 #include "NavMenu.h" 44 #include "PoseView.h" 45 #include "SlowMenu.h" 46 #include "Tracker.h" 47 #include "Utilities.h" 48 49 50 class RecentItemsMenu : public BSlowMenu { 51 public: 52 RecentItemsMenu(const char* title, BMessage* openMessage, 53 BHandler* itemTarget, int32 maxItems); 54 virtual ~RecentItemsMenu(); 55 56 virtual bool StartBuildingItemList(); 57 virtual bool AddNextItem(); 58 virtual void DoneBuildingItemList() {} 59 virtual void ClearMenuBuildingState(); 60 61 protected: 62 virtual const BMessage* FileMessage() 63 { 64 return fTargetMesage; 65 } 66 67 virtual const BMessage* ContainerMessage() 68 { 69 return fTargetMesage; 70 } 71 72 BRecentItemsList* fIterator; 73 BMessage* fTargetMesage; 74 BHandler* fItemTarget; 75 int32 fCount; 76 int32 fSanityCount; 77 int32 fMaxCount; 78 }; 79 80 81 class RecentFilesMenu : public RecentItemsMenu { 82 public: 83 RecentFilesMenu(const char* title, BMessage* openFileMessage, 84 BMessage* openFolderMessage, BHandler* target, 85 int32 maxItems, bool navMenuFolders, const char* ofType, 86 const char* openedByAppSig); 87 88 RecentFilesMenu(const char* title, BMessage* openFileMessage, 89 BMessage* openFolderMessage, BHandler* target, 90 int32 maxItems, bool navMenuFolders, const char* ofTypeList[], 91 int32 ofTypeListCount, const char* openedByAppSig); 92 93 virtual ~RecentFilesMenu(); 94 95 protected: 96 virtual const BMessage* ContainerMessage() 97 { return openFolderMessage; } 98 99 private: 100 BMessage* openFolderMessage; 101 }; 102 103 104 class RecentFoldersMenu : public RecentItemsMenu { 105 public: 106 RecentFoldersMenu(const char* title, BMessage* openMessage, 107 BHandler* target, int32 maxItems, bool navMenuFolders, 108 const char* openedByAppSig); 109 }; 110 111 112 class RecentAppsMenu : public RecentItemsMenu { 113 public: 114 RecentAppsMenu(const char* title, BMessage* openMessage, 115 BHandler* target, int32 maxItems); 116 }; 117 118 119 // #pragma mark - RecentItemsMenu 120 121 122 RecentItemsMenu::RecentItemsMenu(const char* title, BMessage* openMessage, 123 BHandler* itemTarget, int32 maxItems) 124 : 125 BSlowMenu(title), 126 fIterator(NULL), 127 fTargetMesage(openMessage), 128 fItemTarget(itemTarget), 129 fCount(-1), 130 fSanityCount(-1), 131 fMaxCount(maxItems) 132 { 133 } 134 135 136 RecentItemsMenu::~RecentItemsMenu() 137 { 138 delete fIterator; 139 delete fTargetMesage; 140 } 141 142 143 bool 144 RecentItemsMenu::AddNextItem() 145 { 146 BMenuItem* item = fIterator->GetNextMenuItem(FileMessage(), 147 ContainerMessage(), fItemTarget); 148 if (item != NULL) { 149 AddItem(item); 150 fCount++; 151 } 152 fSanityCount++; 153 154 return fCount < fMaxCount - 1 && (fSanityCount < fMaxCount + 20); 155 // fSanityCount is a hacky way of dealing with a lot of stale 156 // recent apps 157 } 158 159 160 bool 161 RecentItemsMenu::StartBuildingItemList() 162 { 163 // remove any preexisting items 164 int32 itemCount = CountItems(); 165 while (itemCount--) 166 delete RemoveItem((int32)0); 167 168 fCount = 0; 169 fSanityCount = 0; 170 fIterator->Rewind(); 171 172 return true; 173 } 174 175 176 void 177 RecentItemsMenu::ClearMenuBuildingState() 178 { 179 fMenuBuilt = false; 180 // force rebuilding each time 181 fIterator->Rewind(); 182 } 183 184 185 // #pragma mark - RecentFilesMenu 186 187 188 RecentFilesMenu::RecentFilesMenu(const char* title, BMessage* openFileMessage, 189 BMessage* openFolderMessage, BHandler* target, int32 maxItems, 190 bool navMenuFolders, const char* ofType, const char* openedByAppSig) 191 : 192 RecentItemsMenu(title, openFileMessage, target, maxItems), 193 openFolderMessage(openFolderMessage) 194 { 195 fIterator = new BRecentFilesList(maxItems + 10, navMenuFolders, 196 ofType, openedByAppSig); 197 } 198 199 200 RecentFilesMenu::RecentFilesMenu(const char* title, BMessage* openFileMessage, 201 BMessage* openFolderMessage, BHandler* target, int32 maxItems, 202 bool navMenuFolders, const char* ofTypeList[], int32 ofTypeListCount, 203 const char* openedByAppSig) 204 : 205 RecentItemsMenu(title, openFileMessage, target, maxItems), 206 openFolderMessage(openFolderMessage) 207 { 208 fIterator = new BRecentFilesList(maxItems + 10, navMenuFolders, 209 ofTypeList, ofTypeListCount, openedByAppSig); 210 } 211 212 213 RecentFilesMenu::~RecentFilesMenu() 214 { 215 delete openFolderMessage; 216 } 217 218 219 // #pragma mark - RecentFoldersMenu 220 221 222 RecentFoldersMenu::RecentFoldersMenu(const char* title, BMessage* openMessage, 223 BHandler* target, int32 maxItems, bool navMenuFolders, 224 const char* openedByAppSig) 225 : 226 RecentItemsMenu(title, openMessage, target, maxItems) 227 { 228 fIterator = new BRecentFoldersList(maxItems + 10, navMenuFolders, 229 openedByAppSig); 230 } 231 232 233 // #pragma mark - RecentAppsMenu 234 235 236 RecentAppsMenu::RecentAppsMenu(const char* title, BMessage* openMessage, 237 BHandler* target, int32 maxItems) 238 : 239 RecentItemsMenu(title, openMessage, target, maxItems) 240 { 241 fIterator = new BRecentAppsList(maxItems); 242 } 243 244 245 // #pragma mark - BRecentItemsList 246 247 248 BRecentItemsList::BRecentItemsList(int32 maxItems, bool navMenuFolders) 249 : 250 fMaxItems(maxItems), 251 fNavMenuFolders(navMenuFolders) 252 { 253 InitIconPreloader(); 254 // need the icon cache 255 Rewind(); 256 } 257 258 259 void 260 BRecentItemsList::Rewind() 261 { 262 fIndex = 0; 263 fItems.MakeEmpty(); 264 } 265 266 267 BMenuItem* 268 BRecentItemsList::GetNextMenuItem(const BMessage* fileOpenInvokeMessage, 269 const BMessage* containerOpenInvokeMessage, BHandler* target, 270 entry_ref* currentItemRef) 271 { 272 entry_ref ref; 273 if (GetNextRef(&ref) != B_OK) 274 return NULL; 275 276 Model model(&ref, true); 277 if (model.InitCheck() != B_OK) 278 return NULL; 279 280 bool container = false; 281 if (model.IsSymLink()) { 282 283 Model* newResolvedModel = NULL; 284 Model* result = model.LinkTo(); 285 286 if (result == NULL) { 287 newResolvedModel = new Model(model.EntryRef(), true, true); 288 289 if (newResolvedModel->InitCheck() != B_OK) { 290 // broken link, still can show though, bail 291 delete newResolvedModel; 292 result = NULL; 293 } else 294 result = newResolvedModel; 295 } else { 296 BModelOpener opener(result); 297 // open the model, if it ain't open already 298 299 PoseInfo poseInfo; 300 BNode* resultNode = result->Node(); 301 if (resultNode != NULL) { 302 resultNode->ReadAttr(kAttrPoseInfo, B_RAW_TYPE, 0, 303 &poseInfo, sizeof(poseInfo)); 304 } 305 306 result->CloseNode(); 307 308 ref = *result->EntryRef(); 309 container = result->IsContainer(); 310 } 311 model.SetLinkTo(result); 312 } else { 313 ref = *model.EntryRef(); 314 container = model.IsContainer(); 315 } 316 317 // if user asked for it, return the current item ref 318 if (currentItemRef != NULL) 319 *currentItemRef = ref; 320 321 BMessage* message; 322 if (container && containerOpenInvokeMessage) 323 message = new BMessage(*containerOpenInvokeMessage); 324 else if (!container && fileOpenInvokeMessage) 325 message = new BMessage(*fileOpenInvokeMessage); 326 else 327 message = new BMessage(B_REFS_RECEIVED); 328 329 message->AddRef("refs", model.EntryRef()); 330 331 // Truncate the name if necessary 332 BString truncatedString(model.Name()); 333 be_plain_font->TruncateString(&truncatedString, B_TRUNCATE_END, 334 BNavMenu::GetMaxMenuWidth()); 335 336 ModelMenuItem* item = NULL; 337 if (!container || !fNavMenuFolders) 338 item = new ModelMenuItem(&model, truncatedString.String(), message); 339 else { 340 // add another nav menu item if it's a directory 341 BNavMenu* menu = new BNavMenu(truncatedString.String(), message->what, 342 target, 0); 343 344 menu->SetNavDir(&ref); 345 item = new ModelMenuItem(&model, menu); 346 item->SetMessage(message); 347 } 348 349 if (item != NULL && target != NULL) 350 item->SetTarget(target); 351 352 return item; 353 } 354 355 356 status_t 357 BRecentItemsList::GetNextRef(entry_ref* result) 358 { 359 return fItems.FindRef("refs", fIndex++, result); 360 } 361 362 363 // #pragma mark - BRecentFilesList 364 365 366 BRecentFilesList::BRecentFilesList(int32 maxItems, bool navMenuFolders, 367 const char* ofType, const char* openedByAppSig) 368 : 369 BRecentItemsList(maxItems, navMenuFolders), 370 fType(ofType), 371 fTypes(NULL), 372 fTypeCount(0), 373 fAppSig(openedByAppSig) 374 { 375 } 376 377 378 BRecentFilesList::BRecentFilesList(int32 maxItems, bool navMenuFolders, 379 const char* ofTypeList[], int32 ofTypeListCount, 380 const char* openedByAppSig) 381 : 382 BRecentItemsList(maxItems, navMenuFolders), 383 fType(NULL), 384 fTypes(NULL), 385 fTypeCount(ofTypeListCount), 386 fAppSig(openedByAppSig) 387 { 388 if (fTypeCount > 0) { 389 fTypes = new char *[ofTypeListCount]; 390 for (int32 index = 0; index < ofTypeListCount; index++) 391 fTypes[index] = strdup(ofTypeList[index]); 392 } 393 } 394 395 396 BRecentFilesList::~BRecentFilesList() 397 { 398 if (fTypeCount > 0) { 399 for (int32 index = 0; index < fTypeCount; index++) 400 free(fTypes[index]); 401 delete[] fTypes; 402 } 403 } 404 405 406 status_t 407 BRecentFilesList::GetNextRef(entry_ref* ref) 408 { 409 if (fIndex == 0) { 410 // Lazy roster Get 411 if (fTypes != NULL) { 412 BRoster().GetRecentDocuments(&fItems, fMaxItems, 413 const_cast<const char**>(fTypes), 414 fTypeCount, fAppSig.Length() ? fAppSig.String() : NULL); 415 } else { 416 BRoster().GetRecentDocuments(&fItems, fMaxItems, 417 fType.Length() ? fType.String() : NULL, 418 fAppSig.Length() ? fAppSig.String() : NULL); 419 } 420 421 } 422 423 return BRecentItemsList::GetNextRef(ref); 424 } 425 426 427 BMenu* 428 BRecentFilesList::NewFileListMenu(const char* title, 429 BMessage* openFileMessage, BMessage* openFolderMessage, 430 BHandler* target, int32 maxItems, bool navMenuFolders, const char* ofType, 431 const char* openedByAppSig) 432 { 433 return new RecentFilesMenu(title, openFileMessage, 434 openFolderMessage, target, maxItems, navMenuFolders, ofType, 435 openedByAppSig); 436 } 437 438 439 BMenu* 440 BRecentFilesList::NewFileListMenu(const char* title, 441 BMessage* openFileMessage, BMessage* openFolderMessage, 442 BHandler* target, int32 maxItems, bool navMenuFolders, 443 const char* ofTypeList[], int32 ofTypeListCount, 444 const char* openedByAppSig) 445 { 446 return new RecentFilesMenu(title, openFileMessage, 447 openFolderMessage, target, maxItems, navMenuFolders, ofTypeList, 448 ofTypeListCount, openedByAppSig); 449 } 450 451 452 // #pragma mark - BRecentFoldersList 453 454 455 BMenu* 456 BRecentFoldersList::NewFolderListMenu(const char* title, 457 BMessage* openMessage, BHandler* target, int32 maxItems, 458 bool navMenuFolders, const char* openedByAppSig) 459 { 460 return new RecentFoldersMenu(title, openMessage, target, maxItems, 461 navMenuFolders, openedByAppSig); 462 } 463 464 465 BRecentFoldersList::BRecentFoldersList(int32 maxItems, bool navMenuFolders, 466 const char* openedByAppSig) 467 : 468 BRecentItemsList(maxItems, navMenuFolders), 469 fAppSig(openedByAppSig) 470 { 471 } 472 473 474 status_t 475 BRecentFoldersList::GetNextRef(entry_ref* ref) 476 { 477 if (fIndex == 0) { 478 // Lazy roster Get 479 BRoster().GetRecentFolders(&fItems, fMaxItems, 480 fAppSig.Length() ? fAppSig.String() : NULL); 481 482 } 483 484 return BRecentItemsList::GetNextRef(ref); 485 } 486 487 488 // #pragma mark - BRecentAppsList 489 490 491 BRecentAppsList::BRecentAppsList(int32 maxItems) 492 : 493 BRecentItemsList(maxItems, false) 494 { 495 } 496 497 498 status_t 499 BRecentAppsList::GetNextRef(entry_ref* ref) 500 { 501 if (fIndex == 0) { 502 // Lazy roster Get 503 BRoster().GetRecentApps(&fItems, fMaxItems); 504 } 505 506 return BRecentItemsList::GetNextRef(ref); 507 } 508 509 510 BMenu* 511 BRecentAppsList::NewAppListMenu(const char* title, BMessage* openMessage, 512 BHandler* target, int32 maxItems) 513 { 514 return new RecentAppsMenu(title, openMessage, target, maxItems); 515 } 516