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 bind2nd(std::equal_to<entry_ref>(), ref)) 273 != fUniqueRefCheck.end()) { 274 continue; 275 } 276 277 Model model(&ref, true); 278 if (model.InitCheck() != B_OK) 279 return true; 280 281 if (!ShouldShowModel(&model)) 282 return true; 283 284 BMenuItem* item = BNavMenu::NewModelItem(&model, 285 fOpenFolderMessage, fTarget, true); 286 if (item != NULL) { 287 if (!fAddedSeparatorForSection) { 288 fAddedSeparatorForSection = true; 289 AddItem(new TitledSeparatorItem( 290 B_TRANSLATE("Recent folders"))); 291 } 292 AddItem(item); 293 item->SetEnabled(true); 294 // BNavMenu::NewModelItem returns a disabled item here - 295 // need to fix this in BNavMenu::NewModelItem 296 297 return true; 298 } 299 } 300 } 301 302 return false; 303 } 304 305 306 void 307 FavoritesMenu::DoneBuildingItemList() 308 { 309 SetTargetForItems(fTarget); 310 } 311 312 313 void 314 FavoritesMenu::ClearMenuBuildingState() 315 { 316 delete fContainer; 317 fContainer = NULL; 318 fState = kDone; 319 320 // force the menu to get rebuilt each time 321 fMenuBuilt = false; 322 } 323 324 325 bool 326 FavoritesMenu::ShouldShowModel(const Model* model) 327 { 328 if (fIsSavePanel && model->IsFile()) 329 return false; 330 331 if (!fRefFilter || model->Node() == NULL) 332 return true; 333 334 struct stat_beos statBeOS; 335 convert_to_stat_beos(model->StatBuf(), &statBeOS); 336 337 return fRefFilter->Filter(model->EntryRef(), model->Node(), &statBeOS, 338 model->MimeType()); 339 } 340 341 342 // #pragma mark - RecentsMenu 343 344 345 RecentsMenu::RecentsMenu(const char* name, int32 which, uint32 what, 346 BHandler* target) 347 : 348 BNavMenu(name, what, target), 349 fWhich(which), 350 fRecentsCount(0), 351 fItemIndex(0) 352 { 353 int32 applications; 354 int32 documents; 355 int32 folders; 356 TrackerSettings().RecentCounts(&applications,&documents,&folders); 357 358 if (fWhich == 0) 359 fRecentsCount = documents; 360 else if (fWhich == 1) 361 fRecentsCount = applications; 362 else if (fWhich == 2) 363 fRecentsCount = folders; 364 } 365 366 367 void 368 RecentsMenu::DetachedFromWindow() 369 { 370 // 371 // BNavMenu::DetachedFromWindow sets the TypesList to NULL 372 // 373 BMenu::DetachedFromWindow(); 374 } 375 376 377 bool 378 RecentsMenu::StartBuildingItemList() 379 { 380 int32 count = CountItems()-1; 381 for (int32 index = count; index >= 0; index--) { 382 BMenuItem* item = ItemAt(index); 383 ASSERT(item); 384 385 RemoveItem(index); 386 delete item; 387 } 388 // 389 // !! note: don't call inherited from here 390 // the navref is not set for this menu 391 // but it still needs to be a draggable navmenu 392 // simply return true so that AddNextItem is called 393 // 394 // return BNavMenu::StartBuildingItemList(); 395 return true; 396 } 397 398 399 bool 400 RecentsMenu::AddNextItem() 401 { 402 if (fRecentsCount > 0 && AddRecents(fRecentsCount)) 403 return true; 404 405 fItemIndex = 0; 406 return false; 407 } 408 409 410 bool 411 RecentsMenu::AddRecents(int32 count) 412 { 413 if (fItemIndex == 0) { 414 fRecentList.MakeEmpty(); 415 BRoster roster; 416 417 switch(fWhich) { 418 case 0: 419 roster.GetRecentDocuments(&fRecentList, count); 420 break; 421 case 1: 422 roster.GetRecentApps(&fRecentList, count); 423 break; 424 case 2: 425 roster.GetRecentFolders(&fRecentList, count); 426 break; 427 default: 428 return false; 429 break; 430 } 431 } 432 for (;;) { 433 entry_ref ref; 434 if (fRecentList.FindRef("refs", fItemIndex++, &ref) != B_OK) 435 break; 436 437 if (ref.name != NULL && strlen(ref.name) > 0) { 438 Model model(&ref, true); 439 ModelMenuItem* item = BNavMenu::NewModelItem(&model, 440 new BMessage(fMessage.what), 441 Target(), false, NULL, TypesList()); 442 443 if (item != NULL) { 444 AddItem(item); 445 446 // return true so that we know to reenter this list 447 return true; 448 } 449 450 return true; 451 } 452 } 453 454 // 455 // return false if we are done with this list 456 // 457 return false; 458 } 459 460 461 void 462 RecentsMenu::DoneBuildingItemList() 463 { 464 // 465 // !! note: don't call inherited here 466 // the object list is not built 467 // and this list does not need to be sorted 468 // BNavMenu::DoneBuildingItemList(); 469 // 470 471 if (CountItems() <= 0) { 472 BMenuItem* item = new BMenuItem(B_TRANSLATE("<No recent items>"), 0); 473 item->SetEnabled(false); 474 AddItem(item); 475 } else 476 SetTargetForItems(Target()); 477 } 478 479 480 void 481 RecentsMenu::ClearMenuBuildingState() 482 { 483 fMenuBuilt = false; 484 BNavMenu::ClearMenuBuildingState(); 485 } 486