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