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