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 "FavoritesMenu.h" 37 38 #include <compat/sys/stat.h> 39 40 #include <Application.h> 41 #include <Catalog.h> 42 #include <FindDirectory.h> 43 #include <FilePanel.h> 44 #include <Locale.h> 45 #include <Message.h> 46 #include <Path.h> 47 #include <Query.h> 48 #include <Roster.h> 49 50 #include <functional> 51 #include <algorithm> 52 53 #include "IconMenuItem.h" 54 #include "PoseView.h" 55 #include "QueryPoseView.h" 56 #include "Tracker.h" 57 #include "Utilities.h" 58 #include "VirtualDirectoryEntryList.h" 59 60 61 #undef B_TRANSLATION_CONTEXT 62 #define B_TRANSLATION_CONTEXT "FavoritesMenu" 63 64 65 // #pragma mark - FavoritesMenu 66 67 68 FavoritesMenu::FavoritesMenu(const char* title, BMessage* openFolderMessage, 69 BMessage* openFileMessage, const BMessenger &target, 70 bool isSavePanel, BRefFilter* filter) 71 : 72 BSlowMenu(title), 73 fOpenFolderMessage(openFolderMessage), 74 fOpenFileMessage(openFileMessage), 75 fTarget(target), 76 fState(kStart), 77 fIndex(-1), 78 fSectionItemCount(-1), 79 fAddedSeparatorForSection(false), 80 fContainer(NULL), 81 fItemList(NULL), 82 fInitialItemCount(0), 83 fIsSavePanel(isSavePanel), 84 fRefFilter(filter) 85 { 86 } 87 88 89 FavoritesMenu::~FavoritesMenu() 90 { 91 delete fOpenFolderMessage; 92 delete fOpenFileMessage; 93 delete fContainer; 94 } 95 96 97 void 98 FavoritesMenu::SetRefFilter(BRefFilter* filter) 99 { 100 fRefFilter = filter; 101 } 102 103 104 bool 105 FavoritesMenu::StartBuildingItemList() 106 { 107 // initialize the menu building state 108 109 if (fInitialItemCount == 0) 110 fInitialItemCount = CountItems(); 111 else { 112 // strip the old items so we can add new fresh ones 113 int32 count = CountItems() - fInitialItemCount; 114 // keep the items that were added by the FavoritesMenu creator 115 while (count--) 116 delete RemoveItem(fInitialItemCount); 117 } 118 119 fUniqueRefCheck.clear(); 120 fState = kStart; 121 return true; 122 } 123 124 125 bool 126 FavoritesMenu::AddNextItem() 127 { 128 // run the next chunk of code for a given item adding state 129 130 if (fState == kStart) { 131 fState = kAddingFavorites; 132 fSectionItemCount = 0; 133 fAddedSeparatorForSection = false; 134 // set up adding the GoTo menu items 135 136 try { 137 BPath path; 138 ThrowOnError(find_directory(B_USER_SETTINGS_DIRECTORY, 139 &path, true)); 140 path.Append(kGoDirectory); 141 mkdir(path.Path(), 0777); 142 143 BEntry entry(path.Path()); 144 Model startModel(&entry, true); 145 ThrowOnInitCheckError(&startModel); 146 147 if (!startModel.IsContainer()) 148 throw B_ERROR; 149 150 if (startModel.IsQuery()) 151 fContainer = new QueryEntryListCollection(&startModel); 152 else if (startModel.IsVirtualDirectory()) 153 fContainer = new VirtualDirectoryEntryList(&startModel); 154 else { 155 BDirectory* directory 156 = dynamic_cast<BDirectory*>(startModel.Node()); 157 if (directory != NULL) 158 fContainer = new DirectoryEntryList(*directory); 159 } 160 161 ThrowOnInitCheckError(fContainer); 162 ThrowOnError(fContainer->Rewind()); 163 } catch (...) { 164 delete fContainer; 165 fContainer = NULL; 166 } 167 } 168 169 if (fState == kAddingFavorites) { 170 entry_ref ref; 171 if (fContainer != NULL && fContainer->GetNextRef(&ref) == B_OK) { 172 Model model(&ref, true); 173 if (model.InitCheck() != B_OK) 174 return true; 175 176 if (!ShouldShowModel(&model)) 177 return true; 178 179 BMenuItem* item = BNavMenu::NewModelItem(&model, 180 model.IsDirectory() ? fOpenFolderMessage : fOpenFileMessage, 181 fTarget); 182 183 if (item == NULL) 184 return true; 185 186 item->SetLabel(ref.name); 187 // this is the name of the link in the Go dir 188 189 if (!fAddedSeparatorForSection) { 190 fAddedSeparatorForSection = true; 191 AddItem(new TitledSeparatorItem(B_TRANSLATE("Favorites"))); 192 } 193 fUniqueRefCheck.push_back(*model.EntryRef()); 194 AddItem(item); 195 fSectionItemCount++; 196 197 return true; 198 } 199 200 // done with favorites, set up for adding recent files 201 fState = kAddingFiles; 202 203 fAddedSeparatorForSection = false; 204 205 app_info info; 206 be_app->GetAppInfo(&info); 207 fItems.MakeEmpty(); 208 209 int32 apps, docs, folders; 210 TrackerSettings().RecentCounts(&apps, &docs, &folders); 211 212 BRoster().GetRecentDocuments(&fItems, docs, NULL, info.signature); 213 fIndex = 0; 214 fSectionItemCount = 0; 215 } 216 217 if (fState == kAddingFiles) { 218 // if this is a Save panel, not an Open panel 219 // then don't add the recent documents 220 if (!fIsSavePanel) { 221 for (;;) { 222 entry_ref ref; 223 if (fItems.FindRef("refs", fIndex++, &ref) != B_OK) 224 break; 225 226 Model model(&ref, true); 227 if (model.InitCheck() != B_OK) 228 return true; 229 230 if (!ShouldShowModel(&model)) 231 return true; 232 233 BMenuItem* item = BNavMenu::NewModelItem(&model, 234 fOpenFileMessage, fTarget); 235 if (item) { 236 if (!fAddedSeparatorForSection) { 237 fAddedSeparatorForSection = true; 238 AddItem(new TitledSeparatorItem( 239 B_TRANSLATE("Recent documents"))); 240 } 241 AddItem(item); 242 fSectionItemCount++; 243 return true; 244 } 245 } 246 } 247 248 // done with recent files, set up for adding recent folders 249 fState = kAddingFolders; 250 251 fAddedSeparatorForSection = false; 252 253 app_info info; 254 be_app->GetAppInfo(&info); 255 fItems.MakeEmpty(); 256 257 int32 apps, docs, folders; 258 TrackerSettings().RecentCounts(&apps, &docs, &folders); 259 260 BRoster().GetRecentFolders(&fItems, folders, info.signature); 261 fIndex = 0; 262 } 263 264 if (fState == kAddingFolders) { 265 for (;;) { 266 entry_ref ref; 267 if (fItems.FindRef("refs", fIndex++, &ref) != B_OK) 268 break; 269 270 // don't add folders that are already in the GoTo section 271 if (find_if(fUniqueRefCheck.begin(), fUniqueRefCheck.end(), 272 #if __GNUC__ <= 2 273 bind2nd(std::equal_to<entry_ref>(), ref) 274 #else 275 [ref](entry_ref compared) { return ref == compared; } 276 #endif 277 ) 278 != fUniqueRefCheck.end()) { 279 continue; 280 } 281 282 Model model(&ref, true); 283 if (model.InitCheck() != B_OK) 284 return true; 285 286 if (!ShouldShowModel(&model)) 287 return true; 288 289 BMenuItem* item = BNavMenu::NewModelItem(&model, 290 fOpenFolderMessage, fTarget, true); 291 if (item != NULL) { 292 if (!fAddedSeparatorForSection) { 293 fAddedSeparatorForSection = true; 294 AddItem(new TitledSeparatorItem( 295 B_TRANSLATE("Recent folders"))); 296 } 297 AddItem(item); 298 item->SetEnabled(true); 299 // BNavMenu::NewModelItem returns a disabled item here - 300 // need to fix this in BNavMenu::NewModelItem 301 302 return true; 303 } 304 } 305 } 306 307 return false; 308 } 309 310 311 void 312 FavoritesMenu::DoneBuildingItemList() 313 { 314 SetTargetForItems(fTarget); 315 } 316 317 318 void 319 FavoritesMenu::ClearMenuBuildingState() 320 { 321 delete fContainer; 322 fContainer = NULL; 323 fState = kDone; 324 325 // force the menu to get rebuilt each time 326 fMenuBuilt = false; 327 } 328 329 330 bool 331 FavoritesMenu::ShouldShowModel(const Model* model) 332 { 333 if (fIsSavePanel && model->IsFile()) 334 return false; 335 336 if (!fRefFilter || model->Node() == NULL) 337 return true; 338 339 struct stat_beos statBeOS; 340 convert_to_stat_beos(model->StatBuf(), &statBeOS); 341 342 return fRefFilter->Filter(model->EntryRef(), model->Node(), &statBeOS, 343 model->MimeType()); 344 } 345 346 347 // #pragma mark - RecentsMenu 348 349 350 RecentsMenu::RecentsMenu(const char* name, int32 which, uint32 what, 351 BHandler* target) 352 : 353 BNavMenu(name, what, target), 354 fWhich(which), 355 fRecentsCount(0), 356 fItemIndex(0) 357 { 358 int32 applications; 359 int32 documents; 360 int32 folders; 361 TrackerSettings().RecentCounts(&applications,&documents,&folders); 362 363 if (fWhich == 0) 364 fRecentsCount = documents; 365 else if (fWhich == 1) 366 fRecentsCount = applications; 367 else if (fWhich == 2) 368 fRecentsCount = folders; 369 } 370 371 372 void 373 RecentsMenu::DetachedFromWindow() 374 { 375 // 376 // BNavMenu::DetachedFromWindow sets the TypesList to NULL 377 // 378 BMenu::DetachedFromWindow(); 379 } 380 381 382 bool 383 RecentsMenu::StartBuildingItemList() 384 { 385 int32 count = CountItems()-1; 386 for (int32 index = count; index >= 0; index--) { 387 BMenuItem* item = ItemAt(index); 388 ASSERT(item); 389 390 RemoveItem(index); 391 delete item; 392 } 393 // 394 // !! note: don't call inherited from here 395 // the navref is not set for this menu 396 // but it still needs to be a draggable navmenu 397 // simply return true so that AddNextItem is called 398 // 399 // return BNavMenu::StartBuildingItemList(); 400 return true; 401 } 402 403 404 bool 405 RecentsMenu::AddNextItem() 406 { 407 if (fRecentsCount > 0 && AddRecents(fRecentsCount)) 408 return true; 409 410 fItemIndex = 0; 411 return false; 412 } 413 414 415 bool 416 RecentsMenu::AddRecents(int32 count) 417 { 418 if (fItemIndex == 0) { 419 fRecentList.MakeEmpty(); 420 BRoster roster; 421 422 switch(fWhich) { 423 case 0: 424 roster.GetRecentDocuments(&fRecentList, count); 425 break; 426 case 1: 427 roster.GetRecentApps(&fRecentList, count); 428 break; 429 case 2: 430 roster.GetRecentFolders(&fRecentList, count); 431 break; 432 default: 433 return false; 434 break; 435 } 436 } 437 for (;;) { 438 entry_ref ref; 439 if (fRecentList.FindRef("refs", fItemIndex++, &ref) != B_OK) 440 break; 441 442 if (ref.name != NULL && strlen(ref.name) > 0) { 443 Model model(&ref, true); 444 ModelMenuItem* item = BNavMenu::NewModelItem(&model, 445 new BMessage(fMessage.what), 446 Target(), false, NULL, TypesList()); 447 448 if (item != NULL) { 449 AddItem(item); 450 451 // return true so that we know to reenter this list 452 return true; 453 } 454 455 return true; 456 } 457 } 458 459 // 460 // return false if we are done with this list 461 // 462 return false; 463 } 464 465 466 void 467 RecentsMenu::DoneBuildingItemList() 468 { 469 // 470 // !! note: don't call inherited here 471 // the object list is not built 472 // and this list does not need to be sorted 473 // BNavMenu::DoneBuildingItemList(); 474 // 475 476 if (CountItems() <= 0) { 477 BMenuItem* item = new BMenuItem(B_TRANSLATE("<No recent items>"), 0); 478 item->SetEnabled(false); 479 AddItem(item); 480 } else 481 SetTargetForItems(Target()); 482 } 483 484 485 void 486 RecentsMenu::ClearMenuBuildingState() 487 { 488 fMenuBuilt = false; 489 BNavMenu::ClearMenuBuildingState(); 490 } 491