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 // menu font 332 menu_info info; 333 get_menu_info(&info); 334 BFont menuFont; 335 menuFont.SetFamilyAndStyle(info.f_family, info.f_style); 336 menuFont.SetSize(info.font_size); 337 338 // Truncate the name if necessary 339 BString truncatedString(model.Name()); 340 menuFont.TruncateString(&truncatedString, B_TRUNCATE_END, BNavMenu::GetMaxMenuWidth()); 341 342 ModelMenuItem* item = NULL; 343 if (!container || !fNavMenuFolders) 344 item = new ModelMenuItem(&model, truncatedString.String(), message); 345 else { 346 // add another nav menu item if it's a directory 347 BNavMenu* menu = new BNavMenu(truncatedString.String(), message->what, 348 target, 0); 349 350 menu->SetNavDir(&ref); 351 item = new ModelMenuItem(&model, menu); 352 item->SetMessage(message); 353 } 354 355 if (item != NULL && target != NULL) 356 item->SetTarget(target); 357 358 return item; 359 } 360 361 362 status_t 363 BRecentItemsList::GetNextRef(entry_ref* result) 364 { 365 return fItems.FindRef("refs", fIndex++, result); 366 } 367 368 369 // #pragma mark - BRecentFilesList 370 371 372 BRecentFilesList::BRecentFilesList(int32 maxItems, bool navMenuFolders, 373 const char* ofType, const char* openedByAppSig) 374 : 375 BRecentItemsList(maxItems, navMenuFolders), 376 fType(ofType), 377 fTypes(NULL), 378 fTypeCount(0), 379 fAppSig(openedByAppSig) 380 { 381 } 382 383 384 BRecentFilesList::BRecentFilesList(int32 maxItems, bool navMenuFolders, 385 const char* ofTypeList[], int32 ofTypeListCount, 386 const char* openedByAppSig) 387 : 388 BRecentItemsList(maxItems, navMenuFolders), 389 fType(NULL), 390 fTypes(NULL), 391 fTypeCount(ofTypeListCount), 392 fAppSig(openedByAppSig) 393 { 394 if (fTypeCount > 0) { 395 fTypes = new char *[ofTypeListCount]; 396 for (int32 index = 0; index < ofTypeListCount; index++) 397 fTypes[index] = strdup(ofTypeList[index]); 398 } 399 } 400 401 402 BRecentFilesList::~BRecentFilesList() 403 { 404 if (fTypeCount > 0) { 405 for (int32 index = 0; index < fTypeCount; index++) 406 free(fTypes[index]); 407 delete[] fTypes; 408 } 409 } 410 411 412 status_t 413 BRecentFilesList::GetNextRef(entry_ref* ref) 414 { 415 if (fIndex == 0) { 416 // Lazy roster Get 417 if (fTypes != NULL) { 418 BRoster().GetRecentDocuments(&fItems, fMaxItems, 419 const_cast<const char**>(fTypes), 420 fTypeCount, fAppSig.Length() ? fAppSig.String() : NULL); 421 } else { 422 BRoster().GetRecentDocuments(&fItems, fMaxItems, 423 fType.Length() ? fType.String() : NULL, 424 fAppSig.Length() ? fAppSig.String() : NULL); 425 } 426 427 } 428 429 return BRecentItemsList::GetNextRef(ref); 430 } 431 432 433 BMenu* 434 BRecentFilesList::NewFileListMenu(const char* title, 435 BMessage* openFileMessage, BMessage* openFolderMessage, 436 BHandler* target, int32 maxItems, bool navMenuFolders, const char* ofType, 437 const char* openedByAppSig) 438 { 439 return new RecentFilesMenu(title, openFileMessage, 440 openFolderMessage, target, maxItems, navMenuFolders, ofType, 441 openedByAppSig); 442 } 443 444 445 BMenu* 446 BRecentFilesList::NewFileListMenu(const char* title, 447 BMessage* openFileMessage, BMessage* openFolderMessage, 448 BHandler* target, int32 maxItems, bool navMenuFolders, 449 const char* ofTypeList[], int32 ofTypeListCount, 450 const char* openedByAppSig) 451 { 452 return new RecentFilesMenu(title, openFileMessage, 453 openFolderMessage, target, maxItems, navMenuFolders, ofTypeList, 454 ofTypeListCount, openedByAppSig); 455 } 456 457 458 // #pragma mark - BRecentFoldersList 459 460 461 BMenu* 462 BRecentFoldersList::NewFolderListMenu(const char* title, 463 BMessage* openMessage, BHandler* target, int32 maxItems, 464 bool navMenuFolders, const char* openedByAppSig) 465 { 466 return new RecentFoldersMenu(title, openMessage, target, maxItems, 467 navMenuFolders, openedByAppSig); 468 } 469 470 471 BRecentFoldersList::BRecentFoldersList(int32 maxItems, bool navMenuFolders, 472 const char* openedByAppSig) 473 : 474 BRecentItemsList(maxItems, navMenuFolders), 475 fAppSig(openedByAppSig) 476 { 477 } 478 479 480 status_t 481 BRecentFoldersList::GetNextRef(entry_ref* ref) 482 { 483 if (fIndex == 0) { 484 // Lazy roster Get 485 BRoster().GetRecentFolders(&fItems, fMaxItems, 486 fAppSig.Length() ? fAppSig.String() : NULL); 487 488 } 489 490 return BRecentItemsList::GetNextRef(ref); 491 } 492 493 494 // #pragma mark - BRecentAppsList 495 496 497 BRecentAppsList::BRecentAppsList(int32 maxItems) 498 : 499 BRecentItemsList(maxItems, false) 500 { 501 } 502 503 504 status_t 505 BRecentAppsList::GetNextRef(entry_ref* ref) 506 { 507 if (fIndex == 0) { 508 // Lazy roster Get 509 BRoster().GetRecentApps(&fItems, fMaxItems); 510 } 511 512 return BRecentItemsList::GetNextRef(ref); 513 } 514 515 516 BMenu* 517 BRecentAppsList::NewAppListMenu(const char* title, BMessage* openMessage, 518 BHandler* target, int32 maxItems) 519 { 520 return new RecentAppsMenu(title, openMessage, target, maxItems); 521 } 522