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