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