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