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