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 "FindPanel.h" 37 38 #include <utility> 39 40 #include <errno.h> 41 #include <fs_attr.h> 42 #include <parsedate.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <strings.h> 46 47 #include <Alert.h> 48 #include <Application.h> 49 #include <Box.h> 50 #include <Button.h> 51 #include <Catalog.h> 52 #include <CheckBox.h> 53 #include <ControlLook.h> 54 #include <Cursor.h> 55 #include <Debug.h> 56 #include <Directory.h> 57 #include <File.h> 58 #include <FilePanel.h> 59 #include <FindDirectory.h> 60 #include <GroupLayout.h> 61 #include <InterfaceDefs.h> 62 #include <LayoutBuilder.h> 63 #include <Locale.h> 64 #include <MenuBar.h> 65 #include <MenuField.h> 66 #include <MenuItem.h> 67 #include <Mime.h> 68 #include <NodeInfo.h> 69 #include <Path.h> 70 #include <PopUpMenu.h> 71 #include <Query.h> 72 #include <SeparatorView.h> 73 #include <Size.h> 74 #include <SpaceLayoutItem.h> 75 #include <TextControl.h> 76 #include <TextView.h> 77 #include <View.h> 78 #include <Volume.h> 79 #include <VolumeRoster.h> 80 81 #include "Attributes.h" 82 #include "AutoLock.h" 83 #include "Commands.h" 84 #include "ContainerWindow.h" 85 #include "FSUtils.h" 86 #include "FunctionObject.h" 87 #include "IconMenuItem.h" 88 #include "MimeTypes.h" 89 #include "Tracker.h" 90 91 92 #undef B_TRANSLATION_CONTEXT 93 #define B_TRANSLATION_CONTEXT "FindPanel" 94 95 96 const char* kAllMimeTypes = "mime/ALLTYPES"; 97 98 const uint32 kNameModifiedMessage = 'nmmd'; 99 const uint32 kSwitchToQueryTemplate = 'swqt'; 100 const uint32 kRunSaveAsTemplatePanel = 'svtm'; 101 const uint32 kLatchChanged = 'ltch'; 102 103 const char* kDragNDropTypes[] = { 104 B_QUERY_MIMETYPE, 105 B_QUERY_TEMPLATE_MIMETYPE 106 }; 107 static const char* kDragNDropActionSpecifiers[] = { 108 B_TRANSLATE_MARK("Create a Query"), 109 B_TRANSLATE_MARK("Create a Query template") 110 }; 111 112 const uint32 kAttachFile = 'attf'; 113 114 const int32 operators[] = { 115 B_CONTAINS, 116 B_EQ, 117 B_NE, 118 B_BEGINS_WITH, 119 B_ENDS_WITH, 120 B_GT, 121 B_LT 122 }; 123 124 static const char* operatorLabels[] = { 125 B_TRANSLATE_MARK("contains"), 126 B_TRANSLATE_MARK("is"), 127 B_TRANSLATE_MARK("is not"), 128 B_TRANSLATE_MARK("starts with"), 129 B_TRANSLATE_MARK("ends with"), 130 B_TRANSLATE_MARK("greater than"), 131 B_TRANSLATE_MARK("less than"), 132 B_TRANSLATE_MARK("before"), 133 B_TRANSLATE_MARK("after") 134 }; 135 136 namespace BPrivate { 137 138 class MostUsedNames { 139 public: 140 MostUsedNames(const char* fileName, const char* directory, 141 int32 maxCount = 5); 142 ~MostUsedNames(); 143 144 bool ObtainList(BList* list); 145 void ReleaseList(); 146 147 void AddName(const char*); 148 149 protected: 150 struct list_entry { 151 char* name; 152 int32 count; 153 }; 154 155 static int CompareNames(const void* a, const void* b); 156 void LoadList(); 157 void UpdateList(); 158 159 const char* fFileName; 160 const char* fDirectory; 161 bool fLoaded; 162 mutable Benaphore fLock; 163 BList fList; 164 int32 fCount; 165 }; 166 167 168 MostUsedNames gMostUsedMimeTypes("MostUsedMimeTypes", "Tracker"); 169 170 171 // #pragma mark - MoreOptionsStruct 172 173 174 void 175 MoreOptionsStruct::EndianSwap(void*) 176 { 177 // noop for now 178 } 179 180 181 void 182 MoreOptionsStruct::SetQueryTemporary(BNode* node, bool on) 183 { 184 MoreOptionsStruct saveMoreOptions; 185 186 if (ReadAttr(node, kAttrQueryMoreOptions, kAttrQueryMoreOptionsForeign, 187 B_RAW_TYPE, 0, &saveMoreOptions, sizeof(MoreOptionsStruct), 188 &MoreOptionsStruct::EndianSwap) == B_OK) { 189 saveMoreOptions.temporary = on; 190 node->WriteAttr(kAttrQueryMoreOptions, B_RAW_TYPE, 0, &saveMoreOptions, 191 sizeof(saveMoreOptions)); 192 } 193 } 194 195 196 bool 197 MoreOptionsStruct::QueryTemporary(const BNode* node) 198 { 199 MoreOptionsStruct saveMoreOptions; 200 201 if (ReadAttr(node, kAttrQueryMoreOptions, kAttrQueryMoreOptionsForeign, 202 B_RAW_TYPE, 0, &saveMoreOptions, sizeof(MoreOptionsStruct), 203 &MoreOptionsStruct::EndianSwap) == kReadAttrFailed) { 204 return false; 205 } 206 207 return saveMoreOptions.temporary; 208 } 209 210 211 // #pragma mark - FindWindow 212 213 214 int32 215 GetNumberOfVolumes() 216 { 217 static int32 numberOfVolumes = -1; 218 if (numberOfVolumes >= 0) 219 return numberOfVolumes; 220 221 int32 count = 0; 222 BVolumeRoster volumeRoster; 223 BVolume volume; 224 while (volumeRoster.GetNextVolume(&volume) == B_OK) { 225 if (volume.IsPersistent() && volume.KnowsQuery() && volume.KnowsAttr()) 226 ++count; 227 } 228 229 numberOfVolumes = count; 230 return numberOfVolumes; 231 } 232 233 234 FindWindow::FindWindow(const entry_ref* newRef, bool editIfTemplateOnly) 235 : 236 BWindow(BRect(), B_TRANSLATE("Find"), B_TITLED_WINDOW, 237 B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_CLOSE_ON_ESCAPE | B_AUTO_UPDATE_SIZE_LIMITS), 238 fFile(TryOpening(newRef)), 239 fFromTemplate(false), 240 fEditTemplateOnly(false), 241 fSaveAsPanel(NULL), 242 fOpenQueryPanel(NULL), 243 fSaveQueryOrTemplateItem(new BMenuItem(B_TRANSLATE("Save"), 244 new BMessage(kSaveQueryOrTemplate), 'S')) 245 { 246 if (fFile != NULL) { 247 fRef = *newRef; 248 if (editIfTemplateOnly) { 249 char type[B_MIME_TYPE_LENGTH]; 250 if (BNodeInfo(fFile).GetType(type) == B_OK 251 && strcasecmp(type, B_QUERY_TEMPLATE_MIMETYPE) == 0) { 252 fEditTemplateOnly = true; 253 SetTitle(B_TRANSLATE("Edit Query template")); 254 } 255 } 256 } else { 257 // no initial query, fall back on the default query template 258 BEntry entry; 259 GetDefaultQuery(entry); 260 entry.GetRef(&fRef); 261 262 if (entry.Exists()) 263 fFile = TryOpening(&fRef); 264 else { 265 // no default query template yet 266 fFile = new BFile(&entry, O_RDWR | O_CREAT); 267 if (fFile->InitCheck() < B_OK) { 268 delete fFile; 269 fFile = NULL; 270 } else 271 SaveQueryAttributes(fFile, true); 272 } 273 274 fSaveQueryOrTemplateItem->SetEnabled(false); 275 } 276 277 fFromTemplate = IsQueryTemplate(fFile); 278 279 fBackground = new FindPanel(fFile, this, fFromTemplate, 280 fEditTemplateOnly); 281 282 BuildMenuBar(); 283 284 BGroupLayout* layout = new BGroupLayout(B_VERTICAL); 285 SetLayout(layout); 286 layout->SetSpacing(0); 287 layout->SetInsets(0, 0, 0, 0); 288 289 GetLayout()->AddView(fMenuBar); 290 GetLayout()->AddView(fBackground); 291 CenterOnScreen(); 292 } 293 294 295 FindWindow::~FindWindow() 296 { 297 delete fFile; 298 delete fSaveAsPanel; 299 delete fOpenQueryPanel; 300 } 301 302 303 void 304 FindWindow::BuildMenuBar() 305 { 306 fMenuBar = new BMenuBar("Menu Bar"); 307 308 fQueryMenu = new BMenu(B_TRANSLATE("Query")); 309 fOptionsMenu = new BMenu(B_TRANSLATE("Options")); 310 fTemplatesMenu = new BMenu(B_TRANSLATE("Templates")); 311 312 fHistoryMenu = new BMenu(B_TRANSLATE("Recent queries")); 313 BMessenger messenger(fBackground); 314 FindPanel::AddRecentQueries(fHistoryMenu, false, &messenger, kSwitchToQueryTemplate, false); 315 if (fHistoryMenu->CountItems() > 0) { 316 fHistoryMenu->AddSeparatorItem(); 317 fHistoryMenu->AddItem(new BMenuItem(B_TRANSLATE("Clear history"), 318 new BMessage(kClearHistory))); 319 } 320 321 BMenuItem* saveAsQueryItem = new BMenuItem(B_TRANSLATE("Save as query" B_UTF8_ELLIPSIS), NULL); 322 BMessage* saveAsQueryMessage = new BMessage(kOpenSaveAsPanel); 323 saveAsQueryMessage->AddBool("saveastemplate", false); 324 saveAsQueryItem->SetMessage(saveAsQueryMessage); 325 BMenuItem* saveAsQueryTemplateItem = new BMenuItem(B_TRANSLATE("Save as template" 326 B_UTF8_ELLIPSIS), 327 NULL); 328 BMessage* saveAsQueryTemplateMessage = new BMessage(kOpenSaveAsPanel); 329 saveAsQueryTemplateMessage->AddBool("saveastemplate", true); 330 saveAsQueryTemplateItem->SetMessage(saveAsQueryTemplateMessage); 331 332 fQueryMenu->AddItem(fSaveQueryOrTemplateItem); 333 fQueryMenu->AddItem(saveAsQueryItem); 334 fQueryMenu->AddItem(saveAsQueryTemplateItem); 335 fQueryMenu->AddItem(new BMenuItem(B_TRANSLATE("Open" B_UTF8_ELLIPSIS), 336 new BMessage(kOpenLoadQueryPanel), 'O')); 337 fQueryMenu->AddSeparatorItem(); 338 fQueryMenu->AddItem(fHistoryMenu); 339 fSearchInTrash = new BMenuItem(B_TRANSLATE("Include trash"), 340 new BMessage(kSearchInTrashOptionClicked)); 341 fOptionsMenu->AddItem(fSearchInTrash); 342 343 PopulateTemplatesMenu(); 344 345 fMenuBar->AddItem(fQueryMenu); 346 fMenuBar->AddItem(fOptionsMenu); 347 fMenuBar->AddItem(fTemplatesMenu); 348 } 349 350 351 void 352 FindWindow::UpdateFileReferences(const entry_ref* ref) 353 { 354 if (ref == NULL) { 355 fFile->Unset(); 356 fFile = NULL; 357 fFromTemplate = false; 358 fRef = entry_ref(); 359 } 360 361 BEntry entry(ref); 362 if (!entry.Exists()) 363 return; 364 365 fFile->Unset(); 366 fFile = NULL; 367 fFile = TryOpening(ref); 368 369 if (fFile != NULL) { 370 entry.GetRef(&fRef); 371 fFromTemplate = IsQueryTemplate(fFile); 372 } 373 } 374 375 376 void 377 ClearMenu(BMenu* menu) 378 { 379 int32 count = menu->CountItems(); 380 for (int32 i = 0; i < count; i++) { 381 BMenuItem* item = menu->RemoveItem(static_cast<int32>(0)); 382 delete item; 383 } 384 } 385 386 387 status_t 388 FindWindow::DeleteQueryOrTemplate(BEntry* entry) 389 { 390 // params checking 391 if (entry == NULL) 392 return B_BAD_VALUE; 393 394 if (entry->Exists()) { 395 entry_ref ref; 396 entry->GetRef(&ref); 397 if (fRef == ref) { 398 UpdateFileReferences(NULL); 399 fSaveQueryOrTemplateItem->SetEnabled(false); 400 } 401 entry->Remove(); 402 return B_OK; 403 } else { 404 return B_ENTRY_NOT_FOUND; 405 } 406 } 407 408 409 void 410 FindWindow::ClearHistoryOrTemplates(bool clearTemplates, bool temporaryOnly) 411 { 412 BVolumeRoster roster; 413 BVolume volume; 414 while (roster.GetNextVolume(&volume) == B_OK) { 415 if (volume.IsPersistent() && volume.KnowsQuery() && volume.KnowsAttr()) { 416 BQuery query; 417 query.SetVolume(&volume); 418 query.SetPredicate("_trk/recentQuery == 1"); 419 if (query.Fetch() != B_OK) 420 continue; 421 422 BEntry entry; 423 entry_ref ref; 424 while (query.GetNextEntry(&entry) == B_OK) { 425 entry.GetRef(&ref); 426 if (FSInTrashDir(&ref) && !BEntry(&ref).Exists()) 427 continue; 428 char type[B_MIME_TYPE_LENGTH]; 429 BNodeInfo(new BNode(&entry)).GetType(type); 430 if (strcmp(type, B_QUERY_TEMPLATE_MIMETYPE) == 0) { 431 if (clearTemplates) 432 DeleteQueryOrTemplate(&entry); 433 else 434 continue; 435 } 436 437 if (!clearTemplates) { 438 BFile file(&entry, B_READ_ONLY); 439 bool isTemporary; 440 if (file.ReadAttr("_trk/temporary", B_BOOL_TYPE, 0, &isTemporary, 441 sizeof(isTemporary)) 442 == sizeof(isTemporary)) { 443 if (!temporaryOnly) { 444 DeleteQueryOrTemplate(&entry); 445 } else { 446 if (isTemporary) 447 DeleteQueryOrTemplate(&entry); 448 } 449 } 450 } 451 } 452 } 453 } 454 455 if (clearTemplates) { 456 ClearMenu(fTemplatesMenu); 457 } else { 458 ClearMenu(fHistoryMenu); 459 BMessenger messenger(fBackground); 460 FindPanel::AddRecentQueries(fHistoryMenu, false, &messenger, kSwitchToQueryTemplate, false, 461 false, true); 462 if (fHistoryMenu->CountItems() > 0) { 463 fHistoryMenu->AddSeparatorItem(); 464 fHistoryMenu->AddItem(new BMenuItem(B_TRANSLATE("Clear history"), 465 new BMessage(kClearHistory))); 466 } 467 } 468 } 469 470 bool 471 CheckForDuplicates(BObjectList<entry_ref>* list, entry_ref* ref) 472 { 473 // Simple Helper Function To Check For Duplicates Within an Entry List of Templates 474 int32 count = list->CountItems(); 475 BPath comparison(ref); 476 for (int32 i = 0; i < count; i++) { 477 if (BPath(list->ItemAt(i)) == comparison) 478 return true; 479 } 480 return false; 481 } 482 483 484 void 485 FindWindow::PopulateTemplatesMenu() 486 { 487 ClearMenu(fTemplatesMenu); 488 BObjectList<entry_ref> templates(10, true); 489 BVolumeRoster roster; 490 BVolume volume; 491 while (roster.GetNextVolume(&volume) == B_OK) { 492 if (volume.IsPersistent() && volume.KnowsQuery() && volume.KnowsAttr()) { 493 BQuery query; 494 query.SetVolume(&volume); 495 query.SetPredicate("_trk/recentQuery == 1"); 496 497 if (query.Fetch() != B_OK) 498 continue; 499 500 entry_ref ref; 501 while (query.GetNextRef(&ref) == B_OK) { 502 if (FSInTrashDir(&ref)) 503 continue; 504 505 char type[B_MIME_TYPE_LENGTH]; 506 BNode node(&ref); 507 BNodeInfo(&node).GetType(type); 508 if (strcmp(type, B_QUERY_TEMPLATE_MIMETYPE) == 0 && BEntry(&ref).Exists() 509 && CheckForDuplicates(&templates, &ref) == false) { 510 // Checking for duplicates as BQuery returns multiple instances 511 // of the same file if they are deleted at times. 512 513 BMessage* message = new BMessage(kSwitchToQueryTemplate); 514 message->AddRef("refs", &ref); 515 BMenuItem* item = new IconMenuItem(ref.name, message, type); 516 item->SetTarget(BMessenger(fBackground)); 517 fTemplatesMenu->AddItem(item); 518 templates.AddItem(new entry_ref(ref)); 519 } 520 } 521 } 522 } 523 } 524 525 526 void 527 FindWindow::SetOptions(bool searchInTrash) 528 { 529 ASSERT(fSearchInTrash != NULL); 530 fSearchInTrash->SetMarked(searchInTrash); 531 } 532 533 534 BFile* 535 FindWindow::TryOpening(const entry_ref* ref) 536 { 537 if (!ref) 538 return NULL; 539 540 BFile* result = new BFile(ref, O_RDWR); 541 if (result->InitCheck() != B_OK) { 542 delete result; 543 result = NULL; 544 } 545 return result; 546 } 547 548 549 void 550 FindWindow::GetDefaultQuery(BEntry& entry) 551 { 552 BPath path; 553 if (find_directory(B_USER_DIRECTORY, &path, true) == B_OK 554 && path.Append("queries") == B_OK 555 && (mkdir(path.Path(), 0777) == 0 || errno == EEXIST)) { 556 BDirectory directory(path.Path()); 557 entry.SetTo(&directory, "default"); 558 } 559 } 560 561 562 bool 563 FindWindow::IsQueryTemplate(BNode* file) 564 { 565 char type[B_MIME_TYPE_LENGTH]; 566 if (BNodeInfo(file).GetType(type) != B_OK) 567 return false; 568 569 return strcasecmp(type, B_QUERY_TEMPLATE_MIMETYPE) == 0; 570 } 571 572 573 void 574 FindWindow::SwitchToTemplate(const entry_ref* ref) 575 { 576 try { 577 BEntry entry(ref, true); 578 BFile templateFile(&entry, O_RDONLY); 579 580 ThrowOnInitCheckError(&templateFile); 581 fBackground->SwitchToTemplate(&templateFile); 582 } catch (...) { 583 ; 584 } 585 } 586 587 588 const char* 589 FindWindow::QueryName() const 590 { 591 if (fFromTemplate) { 592 if (!fQueryNameFromTemplate.Length() && fFile != NULL) { 593 fFile->ReadAttrString(kAttrQueryTemplateName, 594 &fQueryNameFromTemplate); 595 } 596 597 return fQueryNameFromTemplate.String(); 598 } 599 if (fFile == NULL) 600 return ""; 601 602 return fRef.name; 603 } 604 605 606 static const char* 607 MakeValidFilename(BString& string) 608 { 609 // make a file name that is legal under bfs and hfs - possibly could 610 // add code here to accomodate FAT32 etc. too 611 if (string.Length() > B_FILE_NAME_LENGTH - 1) { 612 string.Truncate(B_FILE_NAME_LENGTH - 4); 613 string += B_UTF8_ELLIPSIS; 614 } 615 616 // replace slashes 617 int32 length = string.Length(); 618 char* buf = string.LockBuffer(length); 619 for (int32 index = length; index-- > 0;) { 620 if (buf[index] == '/' /*|| buf[index] == ':'*/) 621 buf[index] = '_'; 622 } 623 string.UnlockBuffer(length); 624 625 return string.String(); 626 } 627 628 629 void 630 FindWindow::GetPredicateString(BString& predicate, bool& dynamicDate) 631 { 632 BQuery query; 633 switch (fBackground->Mode()) { 634 case kByNameItem: 635 fBackground->GetByNamePredicate(&query); 636 query.GetPredicate(&predicate); 637 break; 638 639 case kByFormulaItem: 640 { 641 BTextControl* textControl 642 = dynamic_cast<BTextControl*>(FindView("TextControl")); 643 if (textControl != NULL) 644 predicate.SetTo(textControl->Text(), 1023); 645 break; 646 } 647 648 case kByAttributeItem: 649 fBackground->GetByAttrPredicate(&query, dynamicDate); 650 query.GetPredicate(&predicate); 651 break; 652 } 653 } 654 655 656 void 657 FindWindow::GetDefaultName(BString& name) 658 { 659 fBackground->GetDefaultName(name); 660 661 time_t timeValue = time(0); 662 char namebuf[B_FILE_NAME_LENGTH]; 663 664 tm timeData; 665 localtime_r(&timeValue, &timeData); 666 667 strftime(namebuf, 32, " - %b %d, %I:%M:%S %p", &timeData); 668 name << namebuf; 669 670 MakeValidFilename(name); 671 } 672 673 674 void 675 FindWindow::SaveQueryAttributes(BNode* file, bool queryTemplate) 676 { 677 ThrowOnError(BNodeInfo(file).SetType( 678 queryTemplate ? B_QUERY_TEMPLATE_MIMETYPE : B_QUERY_MIMETYPE)); 679 680 // save date/time info for recent query support and transient query killer 681 int32 currentTime = (int32)time(0); 682 file->WriteAttr(kAttrQueryLastChange, B_INT32_TYPE, 0, ¤tTime, 683 sizeof(int32)); 684 int32 tmp = 1; 685 file->WriteAttr("_trk/recentQuery", B_INT32_TYPE, 0, &tmp, sizeof(int32)); 686 } 687 688 689 void 690 MarkVolumeAccordingToDirectoryFilter(BMenu* menu, entry_ref* ref) 691 { 692 int32 startingIndex = 2; 693 int32 endingIndex = 2 + GetNumberOfVolumes(); 694 for (int32 index = startingIndex; index < endingIndex; ++index) { 695 BMenuItem* item = menu->ItemAt(index); 696 if (item->IsMarked()) 697 continue; 698 BMessage* message = item->Message(); 699 dev_t device; 700 if (message->FindInt32("device", &device) != B_OK) 701 continue; 702 if (device == ref->device) 703 item->SetMarked(true); 704 } 705 } 706 707 708 status_t 709 FindWindow::SaveQueryAsAttributes(BNode* file, BEntry* entry, bool queryTemplate, 710 const BMessage* oldAttributes, const BPoint* oldLocation, bool temporary) 711 { 712 if (oldAttributes != NULL) { 713 // revive old window settings 714 BContainerWindow::SetLayoutState(file, oldAttributes); 715 } 716 717 if (oldLocation != NULL) { 718 // and the file's location 719 FSSetPoseLocation(entry, *oldLocation); 720 } 721 722 BNodeInfo(file).SetType(queryTemplate 723 ? B_QUERY_TEMPLATE_MIMETYPE : B_QUERY_MIMETYPE); 724 725 BString predicate; 726 bool dynamicDate; 727 GetPredicateString(predicate, dynamicDate); 728 file->WriteAttrString(kAttrQueryString, &predicate); 729 730 if (dynamicDate) { 731 file->WriteAttr(kAttrDynamicDateQuery, B_BOOL_TYPE, 0, &dynamicDate, 732 sizeof(dynamicDate)); 733 } 734 735 int32 tmp = 1; 736 file->WriteAttr("_trk/recentQuery", B_INT32_TYPE, 0, &tmp, sizeof(int32)); 737 738 fBackground->SaveDirectoryFiltersToFile(file); 739 740 BMenu* volMenu = fBackground->VolMenu(); 741 ASSERT(volMenu != NULL); 742 743 int32 numberOfDirectoryFilters = fBackground->fDirectoryFilters.CountItems(); 744 for (int32 i = 0; i < numberOfDirectoryFilters; ++i) 745 MarkVolumeAccordingToDirectoryFilter(volMenu, fBackground->fDirectoryFilters.ItemAt(i)); 746 747 bool addAllVolumes = volMenu->ItemAt(0)->IsMarked(); 748 int32 numberOfVolumes = GetNumberOfVolumes(); 749 BMessage messageContainingVolumeInfo; 750 for (int32 i = 2; i < numberOfVolumes + 2; ++i) { 751 BMenuItem* volumeMenuItem = volMenu->ItemAt(i); 752 BMessage* messageOfVolumeMenuItem = volumeMenuItem->Message(); 753 dev_t device; 754 if (messageOfVolumeMenuItem->FindInt32("device", &device) != B_OK) 755 continue; 756 757 if (volumeMenuItem->IsMarked() || addAllVolumes) { 758 BVolume volume(device); 759 EmbedUniqueVolumeInfo(&messageContainingVolumeInfo, &volume); 760 } 761 } 762 763 ssize_t flattenedSize = messageContainingVolumeInfo.FlattenedSize(); 764 if (flattenedSize > 0) { 765 BString bufferString; 766 char* buffer = bufferString.LockBuffer(flattenedSize); 767 messageContainingVolumeInfo.Flatten(buffer, flattenedSize); 768 if (fFile->WriteAttr(kAttrQueryVolume, B_MESSAGE_TYPE, 0, buffer, 769 static_cast<size_t>(flattenedSize)) 770 != flattenedSize) { 771 return B_ERROR; 772 } 773 } 774 775 file->WriteAttr("_trk/temporary", B_BOOL_TYPE, 0, &temporary, sizeof(temporary)); 776 777 MoreOptionsStruct saveMoreOptions; 778 saveMoreOptions.searchTrash = fSearchInTrash->IsMarked(); 779 saveMoreOptions.temporary = temporary; 780 781 if (file->WriteAttr(kAttrQueryMoreOptions, B_RAW_TYPE, 0, &saveMoreOptions, 782 sizeof(saveMoreOptions)) 783 == sizeof(saveMoreOptions)) { 784 file->RemoveAttr(kAttrQueryMoreOptionsForeign); 785 } 786 787 fBackground->SaveWindowState(file, fEditTemplateOnly); 788 // write out all the dialog items as attributes so that the query can 789 // be reopened and edited later 790 791 BView* focusedItem = CurrentFocus(); 792 if (focusedItem != NULL) { 793 // text controls never get the focus, their internal text views do 794 BView* parent = focusedItem->Parent(); 795 if (dynamic_cast<BTextControl*>(parent) != NULL) 796 focusedItem = parent; 797 798 // write out the current focus and, if text control, selection 799 BString name(focusedItem->Name()); 800 file->WriteAttrString("_trk/focusedView", &name); 801 BTextControl* textControl = dynamic_cast<BTextControl*>(focusedItem); 802 if (textControl != NULL && textControl->TextView() != NULL) { 803 int32 selStart; 804 int32 selEnd; 805 textControl->TextView()->GetSelection(&selStart, &selEnd); 806 file->WriteAttr("_trk/focusedSelStart", B_INT32_TYPE, 0, 807 &selStart, sizeof(selStart)); 808 file->WriteAttr("_trk/focusedSelEnd", B_INT32_TYPE, 0, 809 &selEnd, sizeof(selEnd)); 810 } 811 } 812 813 return B_OK; 814 } 815 816 817 void 818 FindWindow::Save() 819 { 820 FindSaveCommon(false); 821 822 // close the find panel 823 PostMessage(B_QUIT_REQUESTED); 824 } 825 826 827 void 828 FindWindow::Find() 829 { 830 if (!FindSaveCommon(true)) { 831 // have to wait for the node monitor to force old query to close 832 // to avoid a race condition 833 TTracker* tracker = dynamic_cast<TTracker*>(be_app); 834 ASSERT(tracker != NULL); 835 836 for (int32 timeOut = 0; ; timeOut++) { 837 if (tracker != NULL && !tracker->EntryHasWindowOpen(&fRef)) { 838 // window quit, we can post refs received to open a 839 // new copy 840 break; 841 } 842 843 // PRINT(("waiting for query window to quit, %d\n", timeOut)); 844 if (timeOut == 5000) { 845 // the old query window would not quit for some reason 846 TRESPASS(); 847 PostMessage(B_QUIT_REQUESTED); 848 return; 849 } 850 snooze(1000); 851 } 852 } 853 854 // tell the tracker about it 855 BMessage message(B_REFS_RECEIVED); 856 message.AddRef("refs", &fRef); 857 be_app->PostMessage(&message); 858 859 // close the find panel 860 PostMessage(B_QUIT_REQUESTED); 861 } 862 863 864 status_t 865 FindWindow::GetQueryLastChangeTimeFromFile(BMessage* message) 866 { 867 // params checking 868 if (message == NULL) 869 return B_BAD_VALUE; 870 871 struct attr_info info; 872 status_t error = fFile->GetAttrInfo(kAttrQueryLastChange, &info); 873 if (error == B_OK) { 874 if (info.type == B_MESSAGE_TYPE) { 875 char* buffer = new char[info.size]; 876 ssize_t readSize = fFile->ReadAttr(kAttrQueryLastChange, B_MESSAGE_TYPE, 0, buffer, 877 static_cast<size_t>(info.size)); 878 if (readSize == info.size) { 879 if (message->Unflatten(buffer) == B_OK) { 880 delete[] buffer; 881 // Delete the dynamically allocated memory in both situations 882 return B_OK; 883 } else { 884 delete[] buffer; 885 // Delete the dynamically allocated memory in both situations. 886 return B_ERROR; 887 } 888 } 889 } else if (info.type == B_INT32_TYPE) { 890 int32 previousChangedTime; 891 if (fFile->ReadAttr(kAttrQueryLastChange, B_INT32_TYPE, 0, &previousChangedTime, 892 (int32)info.size) 893 == sizeof(int32)) { 894 return message->AddInt32(kAttrQueryLastChange, previousChangedTime); 895 } else { 896 return B_ERROR; 897 } 898 } 899 900 // If it reaches till here, that means the entry type is wrong! 901 return B_BAD_VALUE; 902 } else { 903 return error; 904 } 905 } 906 907 908 bool 909 FindWindow::FindSaveCommon(bool find) 910 { 911 // figure out what we need to do 912 bool readFromOldFile = fFile != NULL; 913 bool replaceOriginal = fFile && (!fFromTemplate || fEditTemplateOnly); 914 bool keepPoseLocation = replaceOriginal; 915 bool newFile = !fFile || (fFromTemplate && !fEditTemplateOnly); 916 917 BEntry entry; 918 BMessage oldAttributes; 919 BPoint location; 920 bool hadLocation = false; 921 const char* userSpecifiedName = fBackground->UserSpecifiedName(); 922 923 if (readFromOldFile) { 924 entry.SetTo(&fRef); 925 BContainerWindow::GetLayoutState(fFile, &oldAttributes); 926 hadLocation = FSGetPoseLocation(fFile, &location); 927 } 928 929 BMessage message; 930 if (replaceOriginal) { 931 if (GetQueryLastChangeTimeFromFile(&message) != B_OK) 932 message.MakeEmpty(); 933 934 fFile->Unset(); 935 entry.Remove(); 936 // remove the current entry - need to do this to quit the 937 // running query and to close the corresponding window 938 939 if (userSpecifiedName != NULL && !fEditTemplateOnly) { 940 // change the name of the old query per users request 941 fRef.set_name(userSpecifiedName); 942 entry.SetTo(&fRef); 943 } 944 } 945 946 if (newFile) { 947 // create query file in the user's directory 948 BPath path; 949 // there might be no queries folder yet, create one 950 if (find_directory(B_USER_DIRECTORY, &path, true) == B_OK 951 && path.Append("queries") == B_OK 952 && (mkdir(path.Path(), 0777) == 0 || errno == EEXIST)) { 953 // either use the user specified name, or go with the name 954 // generated from the predicate, etc. 955 BString name; 956 if (userSpecifiedName == NULL) 957 GetDefaultName(name); 958 else 959 name << userSpecifiedName; 960 961 if (path.Append(name.String()) == B_OK) { 962 entry.SetTo(path.Path()); 963 entry.Remove(); 964 entry.GetRef(&fRef); 965 } 966 } 967 } 968 969 fFile = new BFile(&entry, O_RDWR | O_CREAT); 970 ASSERT(fFile->InitCheck() == B_OK); 971 972 int32 currentTime = (int32)time(0); 973 message.AddInt32(kAttrQueryLastChange, currentTime); 974 ssize_t size = message.FlattenedSize(); 975 char* buffer = new char[size]; 976 if (message.Flatten(buffer, size) == B_OK) 977 fFile->WriteAttr(kAttrQueryLastChange, B_MESSAGE_TYPE, 0, buffer, (int32)size); 978 979 SaveQueryAsAttributes(fFile, &entry, !find, newFile ? 0 : &oldAttributes, 980 (hadLocation && keepPoseLocation) ? &location : 0, newFile); 981 982 return newFile; 983 } 984 985 986 void 987 FindWindow::MessageReceived(BMessage* message) 988 { 989 switch (message->what) { 990 case kFindButton: 991 Find(); 992 break; 993 994 case kSaveButton: 995 Save(); 996 break; 997 998 case kSaveQueryOrTemplate: 999 { 1000 BEntry entry(&fRef); 1001 SaveQueryAsAttributes(fFile, &entry, IsQueryTemplate(fFile), 0, 0, false); 1002 break; 1003 } 1004 1005 case kClearHistory: 1006 { 1007 // BAlert will manage its memory independently 1008 BAlert* alert = new BAlert(B_TRANSLATE("Clear history?"), 1009 B_TRANSLATE("Do you want to clear temporary queries or all queries?" 1010 " This action is irreversible!"), 1011 B_TRANSLATE("Cancel"), 1012 B_TRANSLATE("Clear all"), 1013 B_TRANSLATE("Clear temporary queries only"), B_WIDTH_AS_USUAL, B_OFFSET_SPACING, 1014 B_WARNING_ALERT); 1015 alert->SetShortcut(0, B_ESCAPE); 1016 int32 choice = alert->Go(); 1017 if (choice) 1018 ClearHistoryOrTemplates(false, choice == 2); 1019 break; 1020 } 1021 1022 case kOpenSaveAsPanel: 1023 { 1024 if (fSaveAsPanel == NULL) 1025 fSaveAsPanel = new BFilePanel(B_SAVE_PANEL, new BMessenger(fBackground)); 1026 1027 bool isTemplate; 1028 if (message->FindBool("saveastemplate", &isTemplate) != B_OK) 1029 isTemplate = false; 1030 1031 BMessage* saveMessage = new BMessage(B_SAVE_REQUESTED); 1032 saveMessage->AddBool("includeintemplates", isTemplate); 1033 fSaveAsPanel->SetMessage(saveMessage); 1034 fSaveAsPanel->Window()->SetTitle(isTemplate ? B_TRANSLATE("Save query template:") : 1035 B_TRANSLATE("Save query:")); 1036 fSaveAsPanel->Show(); 1037 break; 1038 } 1039 1040 case kOpenLoadQueryPanel: 1041 { 1042 if (fOpenQueryPanel == NULL) 1043 fOpenQueryPanel = new BFilePanel(B_OPEN_PANEL, new BMessenger(fBackground)); 1044 1045 fOpenQueryPanel->SetMessage(new BMessage(kSwitchToQueryTemplate)); 1046 fOpenQueryPanel->Window()->SetTitle(B_TRANSLATE("Open query:")); 1047 fOpenQueryPanel->Show(); 1048 } 1049 1050 case kSearchInTrashOptionClicked: 1051 { 1052 fSearchInTrash->SetMarked(!fSearchInTrash->IsMarked()); 1053 break; 1054 } 1055 1056 case kAttachFile: 1057 { 1058 entry_ref dir; 1059 const char* name; 1060 bool queryTemplate; 1061 if (message->FindString("name", &name) == B_OK 1062 && message->FindRef("directory", &dir) == B_OK 1063 && message->FindBool("template", &queryTemplate) == B_OK) { 1064 delete fFile; 1065 fFile = NULL; 1066 BDirectory directory(&dir); 1067 BEntry entry(&directory, name); 1068 entry_ref tmpRef; 1069 entry.GetRef(&tmpRef); 1070 fFile = TryOpening(&tmpRef); 1071 if (fFile != NULL) { 1072 fRef = tmpRef; 1073 fFromTemplate = IsQueryTemplate(fFile); 1074 SaveQueryAsAttributes(fFile, &entry, queryTemplate, 0, 0, false); 1075 // try to save whatever state we aleady have 1076 // to the new query so that if the user 1077 // opens it before runing it from the find panel, 1078 // something reasonable happens 1079 } 1080 } 1081 } 1082 // Refresh Template Menu 1083 ClearMenu(fTemplatesMenu); 1084 PopulateTemplatesMenu(); 1085 1086 fSaveQueryOrTemplateItem->SetEnabled(true); 1087 break; 1088 1089 case kSwitchToQueryTemplate: 1090 { 1091 entry_ref ref; 1092 if (message->FindRef("refs", &ref) == B_OK) 1093 SwitchToTemplate(&ref); 1094 1095 UpdateFileReferences(&ref); 1096 fBackground->LoadDirectoryFiltersFromFile(fFile); 1097 fSaveQueryOrTemplateItem->SetEnabled(true); 1098 break; 1099 } 1100 1101 case kRunSaveAsTemplatePanel: 1102 { 1103 if (fSaveAsPanel != NULL) { 1104 fSaveAsPanel->Show(); 1105 } else { 1106 BMessenger panel(BackgroundView()); 1107 fSaveAsPanel = new BFilePanel(B_SAVE_PANEL, &panel); 1108 fSaveAsPanel->SetSaveText(B_TRANSLATE("Query template")); 1109 fSaveAsPanel->Window()->SetTitle(B_TRANSLATE("Save as query template:")); 1110 fSaveAsPanel->Show(); 1111 } 1112 break; 1113 } 1114 1115 default: 1116 _inherited::MessageReceived(message); 1117 break; 1118 } 1119 } 1120 1121 1122 bool 1123 FolderFilter::Filter(const entry_ref* ref, BNode* node, struct stat_beos* stat, 1124 const char* mimeType) 1125 { 1126 ASSERT(node->InitCheck() == B_OK); 1127 if (node->IsDirectory()) { 1128 return true; 1129 } else if (node->IsSymLink()) { 1130 BEntry entry(ref, true); 1131 return entry.IsDirectory(); 1132 } 1133 return false; 1134 } 1135 1136 1137 // #pragma mark - FindPanel 1138 1139 1140 FindPanel::FindPanel(BFile* node, FindWindow* parent, bool fromTemplate, bool editTemplateOnly) 1141 : 1142 BView("MainView", B_WILL_DRAW), 1143 fMode(kByNameItem), 1144 fAttrGrid(NULL), 1145 fDraggableIcon(NULL), 1146 fDirectorySelectPanel(NULL), 1147 fAddSeparatorItemState(true) 1148 { 1149 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 1150 SetLowUIColor(ViewUIColor()); 1151 1152 uint32 initialMode = InitialMode(node); 1153 1154 // add popup for mime types 1155 fMimeTypeMenu = new BPopUpMenu("MimeTypeMenu"); 1156 fMimeTypeMenu->SetRadioMode(false); 1157 AddMimeTypesToMenu(); 1158 1159 fMimeTypeField = new BMenuField("MimeTypeMenu", "", fMimeTypeMenu); 1160 fMimeTypeField->SetDivider(0.0f); 1161 fMimeTypeField->MenuItem()->SetLabel(B_TRANSLATE("All files and folders")); 1162 // add popup for search criteria 1163 fSearchModeMenu = new BPopUpMenu("searchMode"); 1164 fSearchModeMenu->AddItem(new BMenuItem(B_TRANSLATE("by name"), 1165 new BMessage(kByNameItem))); 1166 fSearchModeMenu->AddItem(new BMenuItem(B_TRANSLATE("by attribute"), 1167 new BMessage(kByAttributeItem))); 1168 fSearchModeMenu->AddItem(new BMenuItem(B_TRANSLATE("by formula"), 1169 new BMessage(kByFormulaItem))); 1170 1171 fSearchModeMenu->ItemAt(initialMode == kByNameItem ? 0 : 1172 (initialMode == kByAttributeItem ? 1 : 2))->SetMarked(true); 1173 // mark the appropriate mode 1174 BMenuField* searchModeField = new BMenuField("", "", fSearchModeMenu); 1175 searchModeField->SetDivider(0.0f); 1176 1177 // add popup for volume list 1178 fVolMenu = new BPopUpMenu("", false, false); 1179 BMenuField* volumeField = new BMenuField("", B_TRANSLATE("On"), fVolMenu); 1180 volumeField->SetDivider(volumeField->StringWidth(volumeField->Label()) + 8); 1181 AddVolumes(fVolMenu); 1182 fVolMenu->AddSeparatorItem(); 1183 if (fDirectoryFilters.CountItems() > 0) 1184 fVolMenu->AddSeparatorItem(); 1185 fVolMenu->AddItem(new BMenuItem(B_TRANSLATE("Select folders" B_UTF8_ELLIPSIS), 1186 new BMessage(kSelectDirectoryFilter))); 1187 LoadDirectoryFiltersFromFile(node); 1188 1189 // add Search button 1190 BButton* button; 1191 if (editTemplateOnly) { 1192 button = new BButton("save", B_TRANSLATE("Save"), 1193 new BMessage(kSaveButton), B_FOLLOW_RIGHT + B_FOLLOW_BOTTOM); 1194 } else { 1195 button = new BButton("find", B_TRANSLATE("Search"), 1196 new BMessage(kFindButton), B_FOLLOW_RIGHT + B_FOLLOW_BOTTOM); 1197 } 1198 button->MakeDefault(true); 1199 1200 BView* mimeTypeFieldSpacer = new BBox("MimeTypeMenuSpacer", B_WILL_DRAW, 1201 B_NO_BORDER); 1202 mimeTypeFieldSpacer->SetExplicitMaxSize(BSize(0, 0)); 1203 1204 BBox* queryControls = new BBox("Box"); 1205 queryControls->SetBorder(B_NO_BORDER); 1206 1207 BBox* queryBox = new BBox("Outer Controls"); 1208 1209 BGroupView* queryBoxView = new BGroupView(B_VERTICAL, 1210 B_USE_DEFAULT_SPACING); 1211 queryBoxView->GroupLayout()->SetInsets(B_USE_DEFAULT_SPACING); 1212 queryBox->AddChild(queryBoxView); 1213 1214 button->SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT, B_ALIGN_BOTTOM)); 1215 1216 BLayoutBuilder::Group<>(queryBoxView, B_VERTICAL, B_USE_DEFAULT_SPACING) 1217 .SetInsets(B_USE_DEFAULT_SPACING) 1218 .AddGroup(B_HORIZONTAL, B_USE_SMALL_SPACING) 1219 .Add(fMimeTypeField) 1220 .Add(mimeTypeFieldSpacer) 1221 .Add(searchModeField) 1222 .AddStrut(B_USE_DEFAULT_SPACING) 1223 .Add(volumeField) 1224 .End() 1225 .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER)) 1226 .Add(queryControls); 1227 BLayoutBuilder::Group<>(this, B_VERTICAL, B_USE_DEFAULT_SPACING) 1228 .SetInsets(B_USE_WINDOW_SPACING) 1229 .Add(queryBox) 1230 .AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING) 1231 .AddGlue() 1232 .AddGroup(B_VERTICAL) 1233 .Add(button) 1234 .End(); 1235 1236 if (initialMode != kByAttributeItem) 1237 AddByNameOrFormulaItems(); 1238 else 1239 AddByAttributeItems(node); 1240 1241 ResizeMenuField(fMimeTypeField); 1242 ResizeMenuField(searchModeField); 1243 ResizeMenuField(volumeField); 1244 } 1245 1246 1247 FindPanel::~FindPanel() 1248 { 1249 int32 count = fDirectoryFilters.CountItems(); 1250 for (int32 i = 0; i < count; i++) 1251 delete fDirectoryFilters.RemoveItemAt(i); 1252 } 1253 1254 1255 status_t 1256 FindPanel::AddDirectoryFiltersToMenu(BMenu* menu, BHandler* target) 1257 { 1258 if (menu == NULL) 1259 return B_BAD_VALUE; 1260 1261 int32 count = fDirectoryFilters.CountItems(); 1262 for (int32 i = 0; i < count; i++) { 1263 entry_ref* filter = fDirectoryFilters.ItemAt(i); 1264 if (filter != NULL) 1265 FindPanel::AddDirectoryFilterItemToMenu(menu, filter, target); 1266 } 1267 1268 return B_OK; 1269 } 1270 1271 1272 void 1273 FindPanel::LoadDirectoryFiltersFromFile(const BNode* node) 1274 { 1275 if (node == NULL) 1276 return; 1277 1278 struct attr_info info; 1279 if (node->GetAttrInfo("_trk/directories", &info) != B_OK) 1280 return; 1281 1282 BString bufferString; 1283 char* buffer = bufferString.LockBuffer(info.size); 1284 if (node->ReadAttr("_trk/directories", B_MESSAGE_TYPE, 0, buffer, (size_t)info.size) 1285 != info.size) { 1286 return; 1287 } 1288 1289 BMessage message; 1290 if (message.Unflatten(buffer) != B_OK) 1291 return; 1292 1293 int32 count; 1294 if (message.GetInfo("refs", NULL, &count) != B_OK) 1295 return; 1296 1297 for (int32 i = 0; i < count; i++) { 1298 entry_ref ref; 1299 if (message.FindRef("refs", i, &ref) != B_OK) 1300 continue; 1301 1302 BEntry entry(&ref); 1303 if (entry.InitCheck() == B_OK && entry.Exists() && !entry.IsDirectory()) 1304 continue; 1305 1306 AddDirectoryFilter(&ref); 1307 } 1308 1309 bufferString.UnlockBuffer(); 1310 } 1311 1312 1313 status_t 1314 FindPanel::AddDirectoryFilterItemToMenu(BMenu* menu, const entry_ref* ref, BHandler* target, 1315 int32 index) 1316 { 1317 if (menu == NULL || ref == NULL || target == NULL) 1318 return B_BAD_VALUE; 1319 1320 BEntry entry(ref, true); 1321 if (entry.InitCheck() != B_OK) 1322 return B_ERROR; 1323 1324 if (entry.Exists() && entry.IsDirectory()) { 1325 entry_ref symlinkTraversedDirectory; 1326 entry.GetRef(&symlinkTraversedDirectory); 1327 1328 // Adding the options into the fVolMenu 1329 Model model(&entry); 1330 BMenuItem* item = new ModelMenuItem(&model, model.Name(), NULL); 1331 BMessage* message = new BMessage(kRemoveDirectoryFilter); 1332 message->AddPointer("pointer", item); 1333 message->AddRef("refs", &symlinkTraversedDirectory); 1334 item->SetMessage(message); 1335 item->SetMarked(true); 1336 item->SetTarget(target); 1337 1338 bool status = false; 1339 if (index == -1) 1340 status = menu->AddItem(item); 1341 else 1342 status = menu->AddItem(item, index); 1343 1344 return status ? B_OK : B_ERROR; 1345 1346 } else if (!entry.IsDirectory()) { 1347 return B_NOT_A_DIRECTORY; 1348 } else { 1349 return B_ENTRY_NOT_FOUND; 1350 } 1351 } 1352 1353 1354 status_t 1355 FindPanel::AddDirectoryFilter(const entry_ref* ref, bool addToMenu) 1356 { 1357 if (ref == NULL) 1358 return B_BAD_VALUE; 1359 1360 // Check for Duplicate Entry 1361 int32 count = fDirectoryFilters.CountItems(); 1362 for (int32 i = 0; i < count; i++) { 1363 entry_ref* item = fDirectoryFilters.ItemAt(i); 1364 if (ref != NULL && item != NULL && *item == *ref) 1365 return B_CANCELED; 1366 } 1367 1368 status_t error = B_OK; 1369 1370 if (addToMenu) { 1371 if (fAddSeparatorItemState) { 1372 BMenuItem* addDirectoriesItem = fVolMenu->RemoveItem(fVolMenu->CountItems() - 1); 1373 error = FindPanel::AddDirectoryFilterItemToMenu(fVolMenu, ref, this); 1374 fVolMenu->AddSeparatorItem(); 1375 fVolMenu->AddItem(addDirectoriesItem); 1376 fAddSeparatorItemState = false; 1377 } else { 1378 int32 index = fVolMenu->CountItems() - 2; 1379 error = FindPanel::AddDirectoryFilterItemToMenu(fVolMenu, ref, this, index); 1380 } 1381 1382 UnmarkDisks(); 1383 } 1384 1385 if (error == B_OK) { 1386 fDirectoryFilters.AddItem(new entry_ref(*ref)); 1387 return B_OK; 1388 } else { 1389 return B_ERROR; 1390 } 1391 } 1392 1393 1394 void 1395 FindPanel::RemoveDirectoryFilter(const entry_ref* ref) 1396 { 1397 ASSERT(ref != NULL); 1398 int32 count = fDirectoryFilters.CountItems(); 1399 for (int32 i = 0; i < count; i++) { 1400 entry_ref* item = fDirectoryFilters.ItemAt(i); 1401 if (item != NULL && ref != NULL && (*item) == (*ref)) 1402 fDirectoryFilters.RemoveItemAt(i); 1403 } 1404 } 1405 1406 1407 status_t 1408 FindPanel::SaveDirectoryFiltersToFile(BNode* node) 1409 { 1410 if (node->InitCheck() != B_OK) 1411 return B_NO_INIT; 1412 1413 // Store the entry_refs of the fDirectoryFilters to a BMessage 1414 // So that it can be serialized. 1415 BMessage message; 1416 int32 count = fDirectoryFilters.CountItems(); 1417 for (int32 i = 0; i < count; i++) { 1418 entry_ref* ref = fDirectoryFilters.ItemAt(i); 1419 if (message.AddRef("refs", ref) != B_OK) 1420 return B_ERROR; 1421 } 1422 1423 // Serialize and Write the Attribute 1424 ssize_t size = message.FlattenedSize(); 1425 BString bufferString; 1426 char* buffer = bufferString.LockBuffer(size); 1427 if (message.Flatten(buffer, size) == B_OK) { 1428 if (node->WriteAttr("_trk/directories", B_MESSAGE_TYPE, 0, buffer, (size_t)size) != size) 1429 return B_IO_ERROR; 1430 else 1431 return B_OK; 1432 } 1433 1434 return B_ERROR; 1435 } 1436 1437 1438 void 1439 FindPanel::AttachedToWindow() 1440 { 1441 FindWindow* findWindow = dynamic_cast<FindWindow*>(Window()); 1442 ASSERT(findWindow != NULL); 1443 1444 if (findWindow == NULL) 1445 return; 1446 1447 BNode* node = findWindow->QueryNode(); 1448 fSearchModeMenu->SetTargetForItems(this); 1449 RestoreMimeTypeMenuSelection(node); 1450 // preselect the mime we used the last time have to do it here 1451 // because AddByAttributeItems will build different menus based 1452 // on which mime type is preselected 1453 RestoreWindowState(node); 1454 1455 if (!findWindow->CurrentFocus()) { 1456 // try to pick a good focus if we restore to one already 1457 BTextControl* textControl 1458 = dynamic_cast<BTextControl*>(FindView("TextControl")); 1459 if (textControl == NULL) { 1460 // pick the last text control in the attribute view 1461 BString title("TextEntry"); 1462 title << (fAttrGrid->CountRows() - 1); 1463 textControl = dynamic_cast<BTextControl*>(FindView(title.String())); 1464 } 1465 if (textControl != NULL) 1466 textControl->MakeFocus(); 1467 } 1468 1469 BButton* button = dynamic_cast<BButton*>(FindView("remove button")); 1470 if (button != NULL) 1471 button->SetTarget(this); 1472 1473 button = dynamic_cast<BButton*>(FindView("add button")); 1474 if (button != NULL) 1475 button->SetTarget(this); 1476 1477 fVolMenu->SetTargetForItems(this); 1478 1479 // set target for MIME type items 1480 for (int32 index = MimeTypeMenu()->CountItems(); index-- > 2;) { 1481 BMenu* submenu = MimeTypeMenu()->ItemAt(index)->Submenu(); 1482 if (submenu != NULL) 1483 submenu->SetTargetForItems(this); 1484 } 1485 fMimeTypeMenu->SetTargetForItems(this); 1486 1487 // set the MIME type to the default value, if no value is already selected 1488 if (fMimeTypeMenu->FindMarked() == NULL) { 1489 BMenuItem* firstItem = fMimeTypeMenu->ItemAt(0); 1490 if (firstItem != NULL) 1491 firstItem->SetMarked(true); 1492 } 1493 1494 if (fDraggableIcon != NULL) 1495 fDraggableIcon->SetTarget(BMessenger(this)); 1496 } 1497 1498 1499 void 1500 FindPanel::ResizeMenuField(BMenuField* menuField) 1501 { 1502 BSize size; 1503 menuField->GetPreferredSize(&size.width, &size.height); 1504 1505 BMenu* menu = menuField->Menu(); 1506 1507 float padding = 0.0f; 1508 float width = 0.0f; 1509 1510 BMenuItem* markedItem = menu->FindMarked(); 1511 if (markedItem != NULL) { 1512 if (markedItem->Submenu() != NULL) { 1513 BMenuItem* markedSubItem = markedItem->Submenu()->FindMarked(); 1514 if (markedSubItem != NULL && markedSubItem->Label() != NULL) { 1515 float labelWidth 1516 = menuField->StringWidth(markedSubItem->Label()); 1517 padding = size.width - labelWidth; 1518 } 1519 } else if (markedItem->Label() != NULL) { 1520 float labelWidth = menuField->StringWidth(markedItem->Label()); 1521 padding = size.width - labelWidth; 1522 } 1523 } 1524 1525 for (int32 index = menu->CountItems(); index-- > 0; ) { 1526 BMenuItem* item = menu->ItemAt(index); 1527 if (item->Label() != NULL) 1528 width = std::max(width, menuField->StringWidth(item->Label())); 1529 1530 BMenu* submenu = item->Submenu(); 1531 if (submenu != NULL) { 1532 for (int32 subIndex = submenu->CountItems(); subIndex-- > 0; ) { 1533 BMenuItem* subItem = submenu->ItemAt(subIndex); 1534 if (subItem->Label() == NULL) 1535 continue; 1536 1537 width = std::max(width, 1538 menuField->StringWidth(subItem->Label())); 1539 } 1540 } 1541 } 1542 1543 width = std::max(width, menuField->StringWidth(B_TRANSLATE("Multiple selections"))); 1544 1545 float maxWidth = be_control_look->DefaultItemSpacing() * 20; 1546 size.width = std::min(width + padding, maxWidth); 1547 menuField->SetExplicitSize(size); 1548 } 1549 1550 1551 static void 1552 PopUpMenuSetTitle(BMenu* menu, const char* title) 1553 { 1554 // This should really be in BMenuField 1555 BMenu* bar = menu->Supermenu(); 1556 1557 ASSERT(bar); 1558 ASSERT(bar->ItemAt(0)); 1559 if (bar == NULL || !bar->ItemAt(0)) 1560 return; 1561 1562 bar->ItemAt(0)->SetLabel(title); 1563 } 1564 1565 1566 void 1567 FindPanel::ShowVolumeMenuLabel() 1568 { 1569 // find out if more than one items are marked 1570 int32 totalVolumes = GetNumberOfVolumes(); 1571 int32 selectedVolumesCount = 0; 1572 1573 BMenuItem* lastSelectedVolumeItem = NULL; 1574 1575 for (int32 i = 2; i < totalVolumes + 2; ++i) { 1576 BMenuItem* volumeItem = fVolMenu->ItemAt(i); 1577 if (volumeItem->IsMarked()) { 1578 ++selectedVolumesCount; 1579 lastSelectedVolumeItem = volumeItem; 1580 } 1581 } 1582 1583 if (fDirectoryFilters.CountItems() > 1) { 1584 PopUpMenuSetTitle(fVolMenu, B_TRANSLATE_COMMENT("multiple selections", 1585 "The user has selected multiple menu items")); 1586 } else if (fDirectoryFilters.CountItems() == 1) { 1587 PopUpMenuSetTitle(fVolMenu, fDirectoryFilters.ItemAt(0)->name); 1588 } else if (selectedVolumesCount == 0 || selectedVolumesCount == totalVolumes) { 1589 fVolMenu->ItemAt(0)->SetMarked(true); 1590 PopUpMenuSetTitle(fVolMenu, fVolMenu->ItemAt(0)->Label()); 1591 } else if (selectedVolumesCount == 1) { 1592 fVolMenu->ItemAt(0)->SetMarked(false); 1593 PopUpMenuSetTitle(fVolMenu, lastSelectedVolumeItem->Label()); 1594 } else { 1595 fVolMenu->ItemAt(0)->SetMarked(false); 1596 PopUpMenuSetTitle(fVolMenu, B_TRANSLATE_COMMENT("multiple selections", 1597 "The user has selected multiple menu items")); 1598 } 1599 } 1600 1601 1602 void 1603 FindPanel::Draw(BRect) 1604 { 1605 if (fAttrGrid == NULL) 1606 return; 1607 1608 for (int32 index = 0; index < fAttrGrid->CountRows(); index++) { 1609 BMenuField* menuField 1610 = dynamic_cast<BMenuField*>(FindAttrView("MenuField", index)); 1611 if (menuField == NULL) 1612 continue; 1613 1614 BLayoutItem* stringViewLayoutItem = fAttrGrid->ItemAt(1, index); 1615 if (stringViewLayoutItem == NULL) 1616 continue; 1617 1618 BMenu* menuFieldMenu = menuField->Menu(); 1619 if (menuFieldMenu == NULL) 1620 continue; 1621 1622 BMenuItem* item = menuFieldMenu->FindMarked(); 1623 if (item == NULL || item->Submenu() == NULL 1624 || item->Submenu()->FindMarked() == NULL) { 1625 continue; 1626 } 1627 1628 if (stringViewLayoutItem == NULL) { 1629 stringViewLayoutItem = fAttrGrid->AddView(new BStringView("", 1630 item->Submenu()->FindMarked()->Label()), 1, index); 1631 stringViewLayoutItem->SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT, 1632 B_ALIGN_VERTICAL_UNSET)); 1633 } 1634 1635 if (stringViewLayoutItem != NULL) { 1636 BStringView* stringView 1637 = dynamic_cast<BStringView*>(stringViewLayoutItem->View()); 1638 if (stringView != NULL) { 1639 BMenu* submenu = item->Submenu(); 1640 if (submenu != NULL) { 1641 BMenuItem* selected = submenu->FindMarked(); 1642 if (selected != NULL) 1643 stringView->SetText(selected->Label()); 1644 } 1645 } 1646 } 1647 } 1648 } 1649 1650 1651 void 1652 FindPanel::UnmarkDisks() 1653 { 1654 int32 startingIndex = 2; 1655 int32 endingIndex = 2 + GetNumberOfVolumes(); 1656 for (int32 i = startingIndex; i < endingIndex; ++i) 1657 fVolMenu->ItemAt(i)->SetMarked(false); 1658 1659 fVolMenu->ItemAt(0)->SetMarked(false); 1660 } 1661 1662 1663 void 1664 FindPanel::MessageReceived(BMessage* message) 1665 { 1666 entry_ref dir; 1667 const char* name; 1668 BMenuItem* item; 1669 1670 switch (message->what) { 1671 case kVolumeItem: 1672 { 1673 // volume changed 1674 BMenuItem* invokedItem; 1675 dev_t dev; 1676 if (message->FindPointer("source", (void**)&invokedItem) != B_OK) 1677 return; 1678 1679 if (message->FindInt32("device", &dev) != B_OK) 1680 break; 1681 1682 BMenu* menu = invokedItem->Menu(); 1683 ASSERT(menu); 1684 1685 if (dev == -1) { 1686 // all disks selected, uncheck everything else 1687 int32 count = 0; 1688 BVolumeRoster roster; 1689 BVolume volume; 1690 while (roster.GetNextVolume(&volume) == B_OK) { 1691 if (volume.IsPersistent() && volume.KnowsQuery()) { 1692 BDirectory root; 1693 if (volume.GetRootDirectory(&root) != B_OK) 1694 continue; 1695 count++; 1696 } 1697 } 1698 for (int32 index = 2; index < count + 2; index++) 1699 menu->ItemAt(index)->SetMarked(false); 1700 1701 // make all disks the title and check it 1702 PopUpMenuSetTitle(menu, menu->ItemAt(0)->Label()); 1703 menu->ItemAt(0)->SetMarked(true); 1704 } else { 1705 // a specific volume selected, unmark "all disks" 1706 menu->ItemAt(0)->SetMarked(false); 1707 1708 // toggle mark on invoked item 1709 int32 count = menu->CountItems(); 1710 for (int32 index = 2; index < count; index++) { 1711 BMenuItem* item = menu->ItemAt(index); 1712 1713 if (invokedItem == item) { 1714 // we just selected this 1715 bool wasMarked = item->IsMarked(); 1716 item->SetMarked(!wasMarked); 1717 } 1718 } 1719 } 1720 1721 int32 count = fVolMenu->CountItems(); 1722 int32 startingIndex = 3 + GetNumberOfVolumes(); 1723 int32 endingIndex = count - 2; 1724 for (int32 i = startingIndex; i < endingIndex; ++i) { 1725 BMenuItem* menuItem = fVolMenu->ItemAt(i); 1726 BMessage* message = menuItem->Message(); 1727 entry_ref ref; 1728 if (!message || message->FindRef("refs", &ref) != B_OK) 1729 continue; 1730 RemoveDirectoryFilter(&ref); 1731 menuItem->SetMarked(false); 1732 } 1733 1734 // make sure the right label is showing 1735 ShowVolumeMenuLabel(); 1736 1737 break; 1738 } 1739 1740 case kSelectDirectoryFilter: 1741 { 1742 if (fDirectorySelectPanel == NULL) { 1743 BRefFilter* filter = new FolderFilter(); 1744 fDirectorySelectPanel = new BFilePanel(B_OPEN_PANEL, new BMessenger(this), NULL, 1745 B_DIRECTORY_NODE, true, new BMessage(kAddDirectoryFilters), filter); 1746 } 1747 1748 fDirectorySelectPanel->Window()->SetTitle(B_TRANSLATE("Select folders")); 1749 fDirectorySelectPanel->Show(); 1750 break; 1751 } 1752 1753 case kAddDirectoryFilters: 1754 { 1755 int32 count; 1756 message->GetInfo("refs", NULL, &count); 1757 for (int32 i = 0; i < count; i++) { 1758 entry_ref ref; 1759 status_t error = message->FindRef("refs", i, &ref); 1760 if (error == B_OK) 1761 AddDirectoryFilter(&ref); 1762 } 1763 ShowVolumeMenuLabel(); 1764 break; 1765 } 1766 1767 case kRemoveDirectoryFilter: 1768 { 1769 BMenuItem* item; 1770 entry_ref ref; 1771 if (message->FindPointer("pointer", (void**)&item) == B_OK 1772 && message->FindRef("refs", &ref) == B_OK) { 1773 1774 if (item->IsMarked()) { 1775 RemoveDirectoryFilter(&ref); 1776 item->SetMarked(false); 1777 } else { 1778 AddDirectoryFilter(&ref, false); 1779 item->SetMarked(true); 1780 UnmarkDisks(); 1781 } 1782 1783 ShowVolumeMenuLabel(); 1784 } 1785 break; 1786 } 1787 1788 case kByAttributeItem: 1789 case kByNameItem: 1790 case kByFormulaItem: 1791 SwitchMode(message->what); 1792 break; 1793 1794 case kAddItem: 1795 AddAttrRow(); 1796 break; 1797 1798 case kRemoveItem: 1799 RemoveAttrRow(); 1800 break; 1801 1802 case kMIMETypeItem: 1803 { 1804 BMenuItem* item; 1805 if (message->FindPointer("source", (void**)&item) == B_OK) { 1806 // don't add the "All files and folders" to the list 1807 if (fMimeTypeMenu->IndexOf(item) != 0) 1808 gMostUsedMimeTypes.AddName(item->Label()); 1809 1810 SetCurrentMimeType(item); 1811 } 1812 if (fMode == kByAttributeItem) { 1813 // the attributes for this type may be different 1814 RemoveAttrViewItems(false); 1815 AddAttrRow(); 1816 } 1817 1818 break; 1819 } 1820 1821 case kAttributeItem: 1822 if (message->FindPointer("source", (void**)&item) != B_OK) 1823 return; 1824 1825 item->Menu()->Superitem()->SetMarked(true); 1826 Invalidate(); 1827 break; 1828 1829 case kAttributeItemMain: 1830 // in case someone selected just an attribute without the 1831 // comparator 1832 if (message->FindPointer("source", (void**)&item) != B_OK) 1833 return; 1834 1835 if (item->Submenu()->ItemAt(0) != NULL) 1836 item->Submenu()->ItemAt(0)->SetMarked(true); 1837 1838 Invalidate(); 1839 break; 1840 1841 case B_SAVE_REQUESTED: 1842 { 1843 // finish saving query template from a SaveAs panel 1844 entry_ref ref; 1845 status_t error = message->FindRef("refs", &ref); 1846 1847 if (error == B_OK) { 1848 // direct entry selected, convert to parent dir and name 1849 BEntry entry(&ref); 1850 error = entry.GetParent(&entry); 1851 if (error == B_OK) { 1852 entry.GetRef(&dir); 1853 name = ref.name; 1854 } 1855 } else { 1856 // parent dir and name selected 1857 error = message->FindRef("directory", &dir); 1858 if (error == B_OK) 1859 error = message->FindString("name", &name); 1860 } 1861 1862 bool includeInTemplates; 1863 1864 if (error == B_OK 1865 && message->FindBool("includeintemplates", &includeInTemplates) == B_OK) { 1866 SaveAsQueryOrTemplate(&dir, name, includeInTemplates); 1867 } 1868 break; 1869 } 1870 1871 case B_COPY_TARGET: 1872 { 1873 // finish drag&drop 1874 const char* str; 1875 const char* mimeType = NULL; 1876 const char* actionSpecifier = NULL; 1877 1878 if (message->FindString("be:types", &str) == B_OK 1879 && strcasecmp(str, B_FILE_MIME_TYPE) == 0 1880 && (message->FindString("be:actionspecifier", 1881 &actionSpecifier) == B_OK 1882 || message->FindString("be:filetypes", &mimeType) == B_OK) 1883 && message->FindString("name", &name) == B_OK 1884 && message->FindRef("directory", &dir) == B_OK) { 1885 1886 bool query = false; 1887 bool queryTemplate = false; 1888 1889 if (actionSpecifier 1890 && strcasecmp(actionSpecifier, 1891 B_TRANSLATE_NOCOLLECT( 1892 kDragNDropActionSpecifiers[0])) == 0) { 1893 query = true; 1894 } else if (actionSpecifier 1895 && strcasecmp(actionSpecifier, 1896 B_TRANSLATE_NOCOLLECT( 1897 kDragNDropActionSpecifiers[1])) == 0) { 1898 queryTemplate = true; 1899 } else if (mimeType && strcasecmp(mimeType, 1900 kDragNDropTypes[0]) == 0) { 1901 query = true; 1902 } else if (mimeType && strcasecmp(mimeType, 1903 kDragNDropTypes[1]) == 0) { 1904 queryTemplate = true; 1905 } 1906 1907 if (query || queryTemplate) 1908 SaveAsQueryOrTemplate(&dir, name, queryTemplate); 1909 } 1910 1911 break; 1912 } 1913 1914 default: 1915 _inherited::MessageReceived(message); 1916 break; 1917 } 1918 } 1919 1920 1921 void 1922 FindPanel::SaveAsQueryOrTemplate(const entry_ref* dir, const char* name, 1923 bool queryTemplate) 1924 { 1925 BDirectory directory(dir); 1926 BFile file(&directory, name, O_RDWR | O_CREAT | O_TRUNC); 1927 BNodeInfo(&file).SetType(queryTemplate 1928 ? B_QUERY_TEMPLATE_MIMETYPE : B_QUERY_MIMETYPE); 1929 1930 BMessage attach(kAttachFile); 1931 attach.AddRef("directory", dir); 1932 attach.AddString("name", name); 1933 attach.AddBool("template", queryTemplate); 1934 Window()->PostMessage(&attach, 0); 1935 } 1936 1937 1938 BView* 1939 FindPanel::FindAttrView(const char* name, int row) const 1940 { 1941 for (int32 index = 0; index < fAttrGrid->CountColumns(); index++) { 1942 1943 BLayoutItem* item = fAttrGrid->ItemAt(index, row); 1944 if (item == NULL) 1945 continue; 1946 1947 BView* view = item->View(); 1948 if (view == NULL) 1949 continue; 1950 1951 view = view->FindView(name); 1952 if (view != NULL) 1953 return view; 1954 1955 } 1956 1957 return NULL; 1958 } 1959 1960 1961 void 1962 FindPanel::BuildAttrQuery(BQuery* query, bool& dynamicDate) const 1963 { 1964 dynamicDate = false; 1965 1966 // go through each attrview and add the attr and comparison info 1967 for (int32 index = 0; index < fAttrGrid->CountRows(); index++) { 1968 1969 BString title; 1970 title << "TextEntry" << index; 1971 1972 BTextControl* textControl = dynamic_cast<BTextControl*>( 1973 FindAttrView(title, index)); 1974 if (textControl == NULL) 1975 return; 1976 1977 BMenuField* menuField = dynamic_cast<BMenuField*>( 1978 FindAttrView("MenuField", index)); 1979 if (menuField == NULL) 1980 return; 1981 1982 BMenuItem* item = menuField->Menu()->FindMarked(); 1983 if (item == NULL) 1984 continue; 1985 1986 BMessage* message = item->Message(); 1987 int32 type; 1988 if (message->FindInt32("type", &type) == B_OK) { 1989 1990 const char* str; 1991 if (message->FindString("name", &str) == B_OK) 1992 query->PushAttr(str); 1993 else 1994 query->PushAttr(item->Label()); 1995 1996 switch (type) { 1997 case B_STRING_TYPE: 1998 query->PushString(textControl->Text(), true); 1999 break; 2000 2001 case B_TIME_TYPE: 2002 { 2003 int flags = 0; 2004 DEBUG_ONLY(time_t result =) 2005 parsedate_etc(textControl->Text(), -1, 2006 &flags); 2007 dynamicDate = (flags & PARSEDATE_RELATIVE_TIME) != 0; 2008 PRINT(("parsedate_etc - date is %srelative, %" 2009 B_PRIdTIME "\n", 2010 dynamicDate ? "" : "not ", result)); 2011 2012 query->PushDate(textControl->Text()); 2013 break; 2014 } 2015 2016 case B_BOOL_TYPE: 2017 { 2018 uint32 value; 2019 if (strcasecmp(textControl->Text(), 2020 "true") == 0) { 2021 value = 1; 2022 } else if (strcasecmp(textControl->Text(), 2023 "false") == 0) { 2024 value = 0; 2025 } else 2026 value = (uint32)atoi(textControl->Text()); 2027 2028 value %= 2; 2029 query->PushUInt32(value); 2030 break; 2031 } 2032 2033 case B_UINT8_TYPE: 2034 case B_UINT16_TYPE: 2035 case B_UINT32_TYPE: 2036 query->PushUInt32((uint32)StringToScalar( 2037 textControl->Text())); 2038 break; 2039 2040 case B_INT8_TYPE: 2041 case B_INT16_TYPE: 2042 case B_INT32_TYPE: 2043 query->PushInt32((int32)StringToScalar( 2044 textControl->Text())); 2045 break; 2046 2047 case B_UINT64_TYPE: 2048 query->PushUInt64((uint64)StringToScalar( 2049 textControl->Text())); 2050 break; 2051 2052 case B_OFF_T_TYPE: 2053 case B_INT64_TYPE: 2054 query->PushInt64(StringToScalar( 2055 textControl->Text())); 2056 break; 2057 2058 case B_FLOAT_TYPE: 2059 { 2060 float floatVal; 2061 sscanf(textControl->Text(), "%f", 2062 &floatVal); 2063 query->PushFloat(floatVal); 2064 break; 2065 } 2066 2067 case B_DOUBLE_TYPE: 2068 { 2069 double doubleVal; 2070 sscanf(textControl->Text(), "%lf", 2071 &doubleVal); 2072 query->PushDouble(doubleVal); 2073 break; 2074 } 2075 } 2076 } 2077 2078 query_op theOperator; 2079 BMenuItem* operatorItem = item->Submenu()->FindMarked(); 2080 if (operatorItem && operatorItem->Message() != NULL) { 2081 operatorItem->Message()->FindInt32("operator", 2082 (int32*)&theOperator); 2083 query->PushOp(theOperator); 2084 } else 2085 query->PushOp(B_EQ); 2086 2087 // add logic based on selection in Logic menufield 2088 if (index > 0) { 2089 menuField = dynamic_cast<BMenuField*>( 2090 FindAttrView("Logic", index - 1)); 2091 if (menuField) { 2092 item = menuField->Menu()->FindMarked(); 2093 if (item) { 2094 message = item->Message(); 2095 message->FindInt32("combine", (int32*)&theOperator); 2096 query->PushOp(theOperator); 2097 } 2098 } else 2099 query->PushOp(B_AND); 2100 } 2101 } 2102 } 2103 2104 2105 void 2106 FindPanel::PushMimeType(BQuery* query) const 2107 { 2108 const char* type; 2109 if (CurrentMimeType(&type) == NULL) 2110 return; 2111 2112 if (strcmp(kAllMimeTypes, type)) { 2113 // add an asterisk if we are searching for a supertype 2114 char buffer[B_FILE_NAME_LENGTH]; 2115 if (strchr(type, '/') == NULL) { 2116 strlcpy(buffer, type, sizeof(buffer)); 2117 strlcat(buffer, "/*", sizeof(buffer)); 2118 type = buffer; 2119 } 2120 2121 query->PushAttr(kAttrMIMEType); 2122 query->PushString(type); 2123 query->PushOp(B_EQ); 2124 query->PushOp(B_AND); 2125 } 2126 } 2127 2128 2129 void 2130 FindPanel::GetByAttrPredicate(BQuery* query, bool& dynamicDate) const 2131 { 2132 ASSERT(Mode() == (int32)kByAttributeItem); 2133 BuildAttrQuery(query, dynamicDate); 2134 PushMimeType(query); 2135 } 2136 2137 2138 void 2139 FindPanel::GetDefaultName(BString& name) const 2140 { 2141 BTextControl* textControl = dynamic_cast<BTextControl*>( 2142 FindView("TextControl")); 2143 2144 switch (Mode()) { 2145 case kByNameItem: 2146 if (textControl != NULL) { 2147 name.SetTo(B_TRANSLATE_COMMENT("Name = %name", 2148 "FindResultTitle")); 2149 name.ReplaceFirst("%name", textControl->Text()); 2150 } 2151 break; 2152 2153 case kByFormulaItem: 2154 if (textControl != NULL) { 2155 name.SetTo(B_TRANSLATE_COMMENT("Formula %formula", 2156 "FindResultTitle")); 2157 name.ReplaceFirst("%formula", textControl->Text()); 2158 } 2159 break; 2160 2161 case kByAttributeItem: 2162 { 2163 BMenuItem* item = fMimeTypeMenu->FindMarked(); 2164 if (item != NULL) 2165 name << item->Label() << ": "; 2166 2167 for (int32 i = 0; i < fAttrGrid->CountRows(); i++) { 2168 GetDefaultAttrName(name, i); 2169 if (i + 1 < fAttrGrid->CountRows()) 2170 name << ", "; 2171 } 2172 break; 2173 } 2174 } 2175 } 2176 2177 2178 const char* 2179 FindPanel::UserSpecifiedName() const 2180 { 2181 return NULL; 2182 } 2183 2184 2185 void 2186 FindPanel::GetByNamePredicate(BQuery* query) const 2187 { 2188 ASSERT(Mode() == (int32)kByNameItem); 2189 2190 BTextControl* textControl 2191 = dynamic_cast<BTextControl*>(FindView("TextControl")); 2192 2193 ASSERT(textControl != NULL); 2194 2195 if (textControl == NULL) 2196 return; 2197 2198 query->PushAttr("name"); 2199 query->PushString(textControl->Text(), true); 2200 2201 if (strstr(textControl->Text(), "*") != NULL) { 2202 // assume pattern is a regular expression, try doing an exact match 2203 query->PushOp(B_EQ); 2204 } else 2205 query->PushOp(B_CONTAINS); 2206 2207 PushMimeType(query); 2208 } 2209 2210 2211 void 2212 FindPanel::SwitchMode(uint32 mode) 2213 { 2214 if (fMode == mode) 2215 // no work, bail 2216 return; 2217 2218 uint32 oldMode = fMode; 2219 BString buffer; 2220 2221 switch (mode) { 2222 case kByFormulaItem: 2223 { 2224 if (oldMode == kByAttributeItem || oldMode == kByNameItem) { 2225 BQuery query; 2226 if (oldMode == kByAttributeItem) { 2227 bool dummy; 2228 GetByAttrPredicate(&query, dummy); 2229 } else 2230 GetByNamePredicate(&query); 2231 2232 query.GetPredicate(&buffer); 2233 } 2234 } 2235 // fall-through 2236 case kByNameItem: 2237 { 2238 fMode = mode; 2239 RemoveByAttributeItems(); 2240 ShowOrHideMimeTypeMenu(); 2241 AddByNameOrFormulaItems(); 2242 2243 if (buffer.Length() > 0) { 2244 ASSERT(mode == kByFormulaItem 2245 || oldMode == kByAttributeItem); 2246 BTextControl* textControl 2247 = dynamic_cast<BTextControl*>(FindView("TextControl")); 2248 if (textControl != NULL) 2249 textControl->SetText(buffer.String()); 2250 } 2251 break; 2252 } 2253 2254 case kByAttributeItem: 2255 { 2256 fMode = mode; 2257 BTextControl* textControl 2258 = dynamic_cast<BTextControl*>(FindView("TextControl")); 2259 if (textControl != NULL) { 2260 textControl->RemoveSelf(); 2261 delete textControl; 2262 } 2263 2264 ShowOrHideMimeTypeMenu(); 2265 AddAttrRow(); 2266 break; 2267 } 2268 } 2269 } 2270 2271 2272 BMenuItem* 2273 FindPanel::CurrentMimeType(const char** type) const 2274 { 2275 // search for marked item in the list 2276 BMenuItem* item = MimeTypeMenu()->FindMarked(); 2277 2278 if (item != NULL && MimeTypeMenu()->IndexOf(item) != 0 2279 && item->Submenu() == NULL) { 2280 // if it's one of the most used items, ignore it 2281 item = NULL; 2282 } 2283 2284 if (item == NULL) { 2285 for (int32 index = MimeTypeMenu()->CountItems(); index-- > 0;) { 2286 BMenu* submenu = MimeTypeMenu()->ItemAt(index)->Submenu(); 2287 if (submenu != NULL && (item = submenu->FindMarked()) != NULL) 2288 break; 2289 } 2290 } 2291 2292 if (type != NULL && item != NULL) { 2293 BMessage* message = item->Message(); 2294 if (message == NULL) 2295 return NULL; 2296 2297 if (message->FindString("mimetype", type) != B_OK) 2298 return NULL; 2299 } 2300 return item; 2301 } 2302 2303 2304 status_t 2305 FindPanel::SetCurrentMimeType(BMenuItem* item) 2306 { 2307 // unmark old MIME type (in most used list, and the tree) 2308 2309 BMenuItem* marked = CurrentMimeType(); 2310 if (marked != NULL) { 2311 marked->SetMarked(false); 2312 2313 if ((marked = MimeTypeMenu()->FindMarked()) != NULL) 2314 marked->SetMarked(false); 2315 } 2316 2317 // mark new MIME type (in most used list, and the tree) 2318 2319 if (item != NULL) { 2320 item->SetMarked(true); 2321 fMimeTypeField->MenuItem()->SetLabel(item->Label()); 2322 2323 BMenuItem* search; 2324 for (int32 i = 2; (search = MimeTypeMenu()->ItemAt(i)) != NULL; i++) { 2325 if (item == search || search->Label() == NULL) 2326 continue; 2327 2328 if (strcmp(item->Label(), search->Label()) == 0) { 2329 search->SetMarked(true); 2330 break; 2331 } 2332 2333 BMenu* submenu = search->Submenu(); 2334 if (submenu == NULL) 2335 continue; 2336 2337 for (int32 j = submenu->CountItems(); j-- > 0;) { 2338 BMenuItem* sub = submenu->ItemAt(j); 2339 if (strcmp(item->Label(), sub->Label()) == 0) { 2340 sub->SetMarked(true); 2341 break; 2342 } 2343 } 2344 } 2345 } 2346 2347 return B_OK; 2348 } 2349 2350 2351 status_t 2352 FindPanel::SetCurrentMimeType(const char* label) 2353 { 2354 // unmark old MIME type (in most used list, and the tree) 2355 2356 BMenuItem* marked = CurrentMimeType(); 2357 if (marked != NULL) { 2358 marked->SetMarked(false); 2359 2360 if ((marked = MimeTypeMenu()->FindMarked()) != NULL) 2361 marked->SetMarked(false); 2362 } 2363 2364 // mark new MIME type (in most used list, and the tree) 2365 2366 fMimeTypeField->MenuItem()->SetLabel(label); 2367 bool found = false; 2368 2369 for (int32 index = MimeTypeMenu()->CountItems(); index-- > 0;) { 2370 BMenuItem* item = MimeTypeMenu()->ItemAt(index); 2371 BMenu* submenu = item->Submenu(); 2372 if (submenu != NULL && !found) { 2373 for (int32 subIndex = submenu->CountItems(); subIndex-- > 0;) { 2374 BMenuItem* subItem = submenu->ItemAt(subIndex); 2375 if (subItem->Label() != NULL 2376 && strcmp(label, subItem->Label()) == 0) { 2377 subItem->SetMarked(true); 2378 found = true; 2379 } 2380 } 2381 } 2382 2383 if (item->Label() != NULL && strcmp(label, item->Label()) == 0) { 2384 item->SetMarked(true); 2385 return B_OK; 2386 } 2387 } 2388 2389 return found ? B_OK : B_ENTRY_NOT_FOUND; 2390 } 2391 2392 2393 static 2394 void AddSubtype(BString& text, const BMimeType& type) 2395 { 2396 text.Append(" ("); 2397 text.Append(strchr(type.Type(), '/') + 1); 2398 // omit the slash 2399 text.Append(")"); 2400 } 2401 2402 2403 bool 2404 FindPanel::AddOneMimeTypeToMenu(const ShortMimeInfo* info, void* castToMenu) 2405 { 2406 BPopUpMenu* menu = static_cast<BPopUpMenu*>(castToMenu); 2407 2408 BMimeType type(info->InternalName()); 2409 BMimeType super; 2410 type.GetSupertype(&super); 2411 if (super.InitCheck() < B_OK) 2412 return false; 2413 2414 BMenuItem* superItem = menu->FindItem(super.Type()); 2415 if (superItem != NULL) { 2416 BMessage* message = new BMessage(kMIMETypeItem); 2417 message->AddString("mimetype", info->InternalName()); 2418 2419 // check to ensure previous item's name differs 2420 BMenu* menu = superItem->Submenu(); 2421 BMenuItem* previous = menu->ItemAt(menu->CountItems() - 1); 2422 BString text = info->ShortDescription(); 2423 if (previous != NULL 2424 && strcasecmp(previous->Label(), info->ShortDescription()) == 0) { 2425 AddSubtype(text, type); 2426 2427 // update the previous item as well 2428 BMimeType type(previous->Message()->GetString("mimetype", NULL)); 2429 BString label = ShortMimeInfo(type).ShortDescription(); 2430 AddSubtype(label, type); 2431 previous->SetLabel(label.String()); 2432 } 2433 2434 menu->AddItem(new IconMenuItem(text.String(), message, 2435 info->InternalName())); 2436 } 2437 2438 return false; 2439 } 2440 2441 2442 void 2443 FindPanel::AddMimeTypesToMenu() 2444 { 2445 BMessage* itemMessage = new BMessage(kMIMETypeItem); 2446 itemMessage->AddString("mimetype", kAllMimeTypes); 2447 2448 IconMenuItem* firstItem = new IconMenuItem( 2449 B_TRANSLATE("All files and folders"), itemMessage, 2450 static_cast<BBitmap*>(NULL)); 2451 MimeTypeMenu()->AddItem(firstItem); 2452 MimeTypeMenu()->AddSeparatorItem(); 2453 2454 // add recent MIME types 2455 2456 TTracker* tracker = dynamic_cast<TTracker*>(be_app); 2457 ASSERT(tracker != NULL); 2458 2459 BList list; 2460 if (tracker != NULL && gMostUsedMimeTypes.ObtainList(&list)) { 2461 int32 count = 0; 2462 for (int32 index = 0; index < list.CountItems(); index++) { 2463 const char* name = (const char*)list.ItemAt(index); 2464 2465 MimeTypeList* mimeTypes = tracker->MimeTypes(); 2466 if (mimeTypes != NULL) { 2467 const ShortMimeInfo* info = mimeTypes->FindMimeType(name); 2468 if (info == NULL) 2469 continue; 2470 2471 BMessage* message = new BMessage(kMIMETypeItem); 2472 message->AddString("mimetype", info->InternalName()); 2473 2474 MimeTypeMenu()->AddItem(new BMenuItem(name, message)); 2475 count++; 2476 } 2477 } 2478 if (count != 0) 2479 MimeTypeMenu()->AddSeparatorItem(); 2480 2481 gMostUsedMimeTypes.ReleaseList(); 2482 } 2483 2484 // add MIME type tree list 2485 2486 BMessage types; 2487 if (BMimeType::GetInstalledSupertypes(&types) == B_OK) { 2488 const char* superType; 2489 int32 index = 0; 2490 2491 while (types.FindString("super_types", index++, &superType) == B_OK) { 2492 BMenu* superMenu = new BMenu(superType); 2493 2494 BMessage* message = new BMessage(kMIMETypeItem); 2495 message->AddString("mimetype", superType); 2496 2497 MimeTypeMenu()->AddItem(new IconMenuItem(superMenu, message, 2498 superType)); 2499 2500 // the MimeTypeMenu's font is not correct at this time 2501 superMenu->SetFont(be_plain_font); 2502 } 2503 } 2504 2505 if (tracker != NULL) { 2506 tracker->MimeTypes()->EachCommonType( 2507 &FindPanel::AddOneMimeTypeToMenu, MimeTypeMenu()); 2508 } 2509 2510 // remove empty super type menus (and set target) 2511 2512 for (int32 index = MimeTypeMenu()->CountItems(); index-- > 2;) { 2513 BMenuItem* item = MimeTypeMenu()->ItemAt(index); 2514 BMenu* submenu = item->Submenu(); 2515 if (submenu == NULL) 2516 continue; 2517 2518 if (submenu->CountItems() == 0) { 2519 MimeTypeMenu()->RemoveItem(item); 2520 delete item; 2521 } else 2522 submenu->SetTargetForItems(this); 2523 } 2524 } 2525 2526 2527 void 2528 FindPanel::AddVolumes(BMenu* menu) 2529 { 2530 // ToDo: add calls to this to rebuild the menu when a volume gets mounted 2531 2532 BMessage* message = new BMessage(kVolumeItem); 2533 message->AddInt32("device", -1); 2534 menu->AddItem(new BMenuItem(B_TRANSLATE("All disks"), message)); 2535 menu->AddSeparatorItem(); 2536 PopUpMenuSetTitle(menu, B_TRANSLATE("All disks")); 2537 2538 BVolumeRoster roster; 2539 BVolume volume; 2540 roster.Rewind(); 2541 while (roster.GetNextVolume(&volume) == B_OK) { 2542 if (volume.IsPersistent() && volume.KnowsQuery()) { 2543 BDirectory root; 2544 if (volume.GetRootDirectory(&root) != B_OK) 2545 continue; 2546 2547 BEntry entry; 2548 root.GetEntry(&entry); 2549 2550 Model model(&entry, true); 2551 if (model.InitCheck() != B_OK) 2552 continue; 2553 2554 message = new BMessage(kVolumeItem); 2555 message->AddInt32("device", volume.Device()); 2556 menu->AddItem(new ModelMenuItem(&model, model.Name(), message)); 2557 } 2558 } 2559 2560 if (menu->ItemAt(0)) 2561 menu->ItemAt(0)->SetMarked(true); 2562 2563 menu->SetTargetForItems(this); 2564 } 2565 2566 2567 typedef std::pair<entry_ref, uint32> EntryWithDate; 2568 2569 static int 2570 SortByDatePredicate(const EntryWithDate* entry1, const EntryWithDate* entry2) 2571 { 2572 return entry1->second > entry2->second ? 2573 -1 : (entry1->second == entry2->second ? 0 : 1); 2574 } 2575 2576 2577 struct AddOneRecentParams { 2578 BMenu* menu; 2579 const BMessenger* target; 2580 uint32 what; 2581 }; 2582 2583 2584 static const entry_ref* 2585 AddOneRecentItem(const entry_ref* ref, void* castToParams) 2586 { 2587 AddOneRecentParams* params = (AddOneRecentParams*)castToParams; 2588 2589 BMessage* message = new BMessage(params->what); 2590 message->AddRef("refs", ref); 2591 2592 char type[B_MIME_TYPE_LENGTH]; 2593 BNode node(ref); 2594 BNodeInfo(&node).GetType(type); 2595 BMenuItem* item = new IconMenuItem(ref->name, message, type); 2596 item->SetTarget(*params->target); 2597 params->menu->AddItem(item); 2598 2599 return NULL; 2600 } 2601 2602 // Helper Function To Catch Entries caused from duplicate files received through BQuery 2603 bool 2604 CheckForDuplicates(BObjectList<EntryWithDate>* list, EntryWithDate* entry) 2605 { 2606 // params checking 2607 if (list == NULL || entry == NULL) 2608 return false; 2609 2610 int32 count = list->CountItems(); 2611 for (int32 i = 0; i < count; i++) { 2612 EntryWithDate* item = list->ItemAt(i); 2613 if (entry != NULL && item != NULL && item->first == entry->first 2614 && entry->second == item->second) { 2615 return true; 2616 } 2617 } 2618 2619 return false; 2620 } 2621 2622 2623 void 2624 FindPanel::AddRecentQueries(BMenu* menu, bool addSaveAsItem, const BMessenger* target, uint32 what, 2625 bool includeTemplates, bool includeTemporaryQueries, bool includePersistedQueries) 2626 { 2627 BObjectList<entry_ref> templates(10, true); 2628 BObjectList<EntryWithDate> recentQueries(10, true); 2629 2630 // find all the queries on all volumes 2631 BVolumeRoster roster; 2632 BVolume volume; 2633 roster.Rewind(); 2634 while (roster.GetNextVolume(&volume) == B_OK) { 2635 if (volume.IsPersistent() && volume.KnowsQuery() 2636 && volume.KnowsAttr()) { 2637 BQuery query; 2638 query.SetVolume(&volume); 2639 query.SetPredicate("_trk/recentQuery == 1"); 2640 if (query.Fetch() != B_OK) 2641 continue; 2642 2643 entry_ref ref; 2644 while (query.GetNextRef(&ref) == B_OK) { 2645 // ignore queries in the Trash 2646 BEntry entry(&ref); 2647 if (FSInTrashDir(&ref) || !entry.Exists()) 2648 continue; 2649 2650 char type[B_MIME_TYPE_LENGTH]; 2651 BNode node(&ref); 2652 BNodeInfo(&node).GetType(type); 2653 2654 if (strcasecmp(type, B_QUERY_TEMPLATE_MIMETYPE) == 0 && includeTemplates) { 2655 templates.AddItem(new entry_ref(ref)); 2656 } else if (strcasecmp(type, B_QUERY_MIMETYPE) == 0) { 2657 bool isTemporary = true; 2658 node.ReadAttr("_trk/temporary", B_BOOL_TYPE, 0, &isTemporary, sizeof(bool)); 2659 2660 struct attr_info info; 2661 if (node.GetAttrInfo(kAttrQueryLastChange, &info) != B_OK) 2662 continue; 2663 2664 if (info.type == B_MESSAGE_TYPE) { 2665 BString bufferString; 2666 char* buffer = bufferString.LockBuffer(info.size); 2667 BMessage message; 2668 if (node.ReadAttr(kAttrQueryLastChange, B_MESSAGE_TYPE, 0, buffer, 2669 static_cast<size_t>(info.size)) 2670 != info.size || message.Unflatten(buffer) != B_OK) 2671 continue; 2672 bufferString.UnlockBuffer(); 2673 2674 int32 count; 2675 if (message.GetInfo(kAttrQueryLastChange, NULL, &count) != B_OK) 2676 continue; 2677 2678 for (int32 i = 0; i < count; i++) { 2679 int32 time; 2680 if (message.FindInt32(kAttrQueryLastChange, i, &time) 2681 == B_OK) { 2682 EntryWithDate* item = new EntryWithDate(ref, time); 2683 if (((isTemporary && includeTemporaryQueries) 2684 || (!isTemporary 2685 && includePersistedQueries)) 2686 && !CheckForDuplicates(&recentQueries, item)) { 2687 recentQueries.AddItem(item); 2688 } else { 2689 delete item; 2690 } 2691 } 2692 } 2693 } 2694 if (info.type == B_INT32_TYPE) { 2695 int32 changeTime; 2696 if (node.ReadAttr(kAttrQueryLastChange, B_INT32_TYPE, 0, &changeTime, 2697 sizeof(int32)) 2698 == sizeof(int32)) { 2699 EntryWithDate* item = new EntryWithDate(ref, changeTime); 2700 if (((isTemporary && includeTemporaryQueries) 2701 || (!isTemporary && includePersistedQueries)) 2702 && !CheckForDuplicates(&recentQueries, item)) { 2703 recentQueries.AddItem(item); 2704 } else { 2705 delete item; 2706 } 2707 } 2708 } 2709 } 2710 } 2711 } 2712 } 2713 2714 // we are only adding last ten queries 2715 recentQueries.SortItems(SortByDatePredicate); 2716 2717 // but all templates 2718 AddOneRecentParams params; 2719 params.menu = menu; 2720 params.target = target; 2721 params.what = what; 2722 templates.EachElement(AddOneRecentItem, ¶ms); 2723 2724 int32 count = recentQueries.CountItems(); 2725 if (count > 10) { 2726 // show only up to 10 recent queries 2727 count = 10; 2728 } else if (count < 0) 2729 count = 0; 2730 2731 if (templates.CountItems() > 0 && count > 0) 2732 menu->AddSeparatorItem(); 2733 2734 for (int32 index = 0; index < count; index++) 2735 AddOneRecentItem(&recentQueries.ItemAt(index)->first, ¶ms); 2736 2737 if (addSaveAsItem) { 2738 // add a Save as template item 2739 if (count > 0 || templates.CountItems() > 0) 2740 menu->AddSeparatorItem(); 2741 2742 BMessage* message = new BMessage(kRunSaveAsTemplatePanel); 2743 BMenuItem* item = new BMenuItem( 2744 B_TRANSLATE("Save query as template" B_UTF8_ELLIPSIS), message); 2745 menu->AddItem(item); 2746 } 2747 } 2748 2749 2750 void 2751 FindPanel::SetupAddRemoveButtons() 2752 { 2753 BBox* box = dynamic_cast<BBox*>(FindView("Box")); 2754 2755 ASSERT(box != NULL); 2756 2757 if (box == NULL) 2758 return; 2759 2760 BButton* removeButton = new BButton("remove button", B_TRANSLATE("Remove"), 2761 new BMessage(kRemoveItem)); 2762 removeButton->SetEnabled(false); 2763 removeButton->SetTarget(this); 2764 2765 BButton* addButton = new BButton("add button", B_TRANSLATE("Add"), 2766 new BMessage(kAddItem)); 2767 addButton->SetTarget(this); 2768 2769 BGroupLayout* layout = dynamic_cast<BGroupLayout*>(box->GetLayout()); 2770 2771 ASSERT(layout != NULL); 2772 2773 if (layout == NULL) 2774 return; 2775 2776 BLayoutBuilder::Group<>(layout) 2777 .AddGroup(B_HORIZONTAL) 2778 .AddGlue() 2779 .Add(removeButton) 2780 .Add(addButton) 2781 .End() 2782 .End(); 2783 } 2784 2785 2786 void 2787 FindPanel::FillCurrentQueryName(BTextControl* queryName, FindWindow* window) 2788 { 2789 ASSERT(window); 2790 queryName->SetText(window->QueryName()); 2791 } 2792 2793 2794 void 2795 FindPanel::AddAttrRow() 2796 { 2797 BBox* box = dynamic_cast<BBox*>(FindView("Box")); 2798 2799 ASSERT(box != NULL); 2800 2801 if (box == NULL) 2802 return; 2803 2804 BGridView* grid = dynamic_cast<BGridView*>(box->FindView("AttrFields")); 2805 if (grid == NULL) { 2806 // reset layout 2807 BLayoutBuilder::Group<>(box, B_VERTICAL); 2808 2809 grid = new BGridView("AttrFields"); 2810 box->AddChild(grid); 2811 } 2812 2813 fAttrGrid = grid->GridLayout(); 2814 2815 AddAttributeControls(fAttrGrid->CountRows()); 2816 2817 // add logic to previous attrview 2818 if (fAttrGrid->CountRows() > 1) 2819 AddLogicMenu(fAttrGrid->CountRows() - 2); 2820 2821 BButton* removeButton = dynamic_cast<BButton*>( 2822 box->FindView("remove button")); 2823 if (removeButton != NULL) 2824 removeButton->SetEnabled(fAttrGrid->CountRows() > 1); 2825 else 2826 SetupAddRemoveButtons(); 2827 } 2828 2829 2830 void 2831 FindPanel::RemoveAttrRow() 2832 { 2833 if (fAttrGrid->CountRows() < 2) 2834 return; 2835 2836 BView* view; 2837 2838 int32 row = fAttrGrid->CountRows() - 1; 2839 for (int32 col = fAttrGrid->CountColumns(); col > 0; col--) { 2840 BLayoutItem* item = fAttrGrid->ItemAt(col - 1, row); 2841 if (item == NULL) 2842 continue; 2843 2844 view = item->View(); 2845 if (view == NULL) 2846 continue; 2847 2848 view->RemoveSelf(); 2849 delete view; 2850 } 2851 2852 BString string = "TextEntry"; 2853 string << (row - 1); 2854 view = FindAttrView(string.String(), row - 1); 2855 if (view != NULL) 2856 view->MakeFocus(); 2857 2858 if (fAttrGrid->CountRows() > 1) { 2859 // remove the And/Or menu field of the previous row 2860 BLayoutItem* item = fAttrGrid->ItemAt(3, row - 1); 2861 if (item == NULL) 2862 return; 2863 2864 view = item->View(); 2865 if (view == NULL) 2866 return; 2867 2868 view->RemoveSelf(); 2869 delete view; 2870 return; 2871 } 2872 2873 // only one row remains 2874 2875 // disable the remove button 2876 BButton* button = dynamic_cast<BButton*>(FindView("remove button")); 2877 if (button != NULL) 2878 button->SetEnabled(false); 2879 2880 // remove the And/Or menu field 2881 BLayoutItem* item = fAttrGrid->RemoveItem(3); 2882 if (item == NULL) 2883 return; 2884 2885 view = item->View(); 2886 if (view == NULL) 2887 return; 2888 2889 view->RemoveSelf(); 2890 delete view; 2891 } 2892 2893 2894 uint32 2895 FindPanel::InitialMode(const BNode* node) 2896 { 2897 if (node == NULL || node->InitCheck() != B_OK) 2898 return kByNameItem; 2899 2900 uint32 result; 2901 if (node->ReadAttr(kAttrQueryInitialMode, B_INT32_TYPE, 0, 2902 (int32*)&result, sizeof(int32)) <= 0) 2903 return kByNameItem; 2904 2905 return result; 2906 } 2907 2908 2909 int32 2910 FindPanel::InitialAttrCount(const BNode* node) 2911 { 2912 if (node == NULL || node->InitCheck() != B_OK) 2913 return 1; 2914 2915 int32 result; 2916 if (node->ReadAttr(kAttrQueryInitialNumAttrs, B_INT32_TYPE, 0, 2917 &result, sizeof(int32)) <= 0) 2918 return 1; 2919 2920 return result; 2921 } 2922 2923 2924 static int32 2925 SelectItemWithLabel(BMenu* menu, const char* label) 2926 { 2927 for (int32 index = menu->CountItems(); index-- > 0;) { 2928 BMenuItem* item = menu->ItemAt(index); 2929 2930 if (strcmp(label, item->Label()) == 0) { 2931 item->SetMarked(true); 2932 return index; 2933 } 2934 } 2935 return -1; 2936 } 2937 2938 2939 void 2940 FindPanel::SaveWindowState(BNode* node, bool editTemplate) 2941 { 2942 ASSERT(node->InitCheck() == B_OK); 2943 2944 BMenuItem* item = CurrentMimeType(); 2945 if (item) { 2946 BString label(item->Label()); 2947 node->WriteAttrString(kAttrQueryInitialMime, &label); 2948 } 2949 2950 uint32 mode = Mode(); 2951 node->WriteAttr(kAttrQueryInitialMode, B_INT32_TYPE, 0, 2952 (int32*)&mode, sizeof(int32)); 2953 2954 if (editTemplate) { 2955 if (UserSpecifiedName()) { 2956 BString name(UserSpecifiedName()); 2957 node->WriteAttrString(kAttrQueryTemplateName, &name); 2958 } 2959 } 2960 2961 switch (Mode()) { 2962 case kByAttributeItem: 2963 { 2964 BMessage message; 2965 int32 count = fAttrGrid->CountRows(); 2966 node->WriteAttr(kAttrQueryInitialNumAttrs, B_INT32_TYPE, 0, 2967 &count, sizeof(int32)); 2968 2969 for (int32 index = 0; index < count; index++) 2970 SaveAttrState(&message, index); 2971 2972 ssize_t size = message.FlattenedSize(); 2973 if (size > 0) { 2974 char* buffer = new char[(size_t)size]; 2975 status_t result = message.Flatten(buffer, size); 2976 if (result == B_OK) { 2977 node->WriteAttr(kAttrQueryInitialAttrs, B_MESSAGE_TYPE, 0, 2978 buffer, (size_t)size); 2979 } 2980 delete[] buffer; 2981 } 2982 break; 2983 } 2984 2985 case kByNameItem: 2986 case kByFormulaItem: 2987 { 2988 BTextControl* textControl = dynamic_cast<BTextControl*>( 2989 FindView("TextControl")); 2990 2991 ASSERT(textControl != NULL); 2992 2993 if (textControl != NULL) { 2994 BString formula(textControl->Text()); 2995 node->WriteAttrString(kAttrQueryInitialString, &formula); 2996 } 2997 break; 2998 } 2999 } 3000 } 3001 3002 3003 void 3004 FindPanel::SwitchToTemplate(const BNode* node) 3005 { 3006 SwitchMode(InitialMode(node)); 3007 // update the menu to correspond to the mode 3008 MarkNamedMenuItem(fSearchModeMenu, InitialMode(node), true); 3009 3010 if (Mode() == (int32)kByAttributeItem) { 3011 RemoveByAttributeItems(); 3012 AddByAttributeItems(node); 3013 } 3014 3015 RestoreWindowState(node); 3016 } 3017 3018 3019 void 3020 FindPanel::RestoreMimeTypeMenuSelection(const BNode* node) 3021 { 3022 if (Mode() == (int32)kByFormulaItem || node == NULL 3023 || node->InitCheck() != B_OK) { 3024 return; 3025 } 3026 3027 BString buffer; 3028 if (node->ReadAttrString(kAttrQueryInitialMime, &buffer) == B_OK) 3029 SetCurrentMimeType(buffer.String()); 3030 } 3031 3032 3033 void 3034 FindPanel::RestoreWindowState(const BNode* node) 3035 { 3036 fMode = InitialMode(node); 3037 if (node == NULL || node->InitCheck() != B_OK) 3038 return; 3039 3040 ShowOrHideMimeTypeMenu(); 3041 RestoreMimeTypeMenuSelection(node); 3042 MoreOptionsStruct saveMoreOptions; 3043 3044 bool storesMoreOptions = ReadAttr(node, kAttrQueryMoreOptions, 3045 kAttrQueryMoreOptionsForeign, B_RAW_TYPE, 0, &saveMoreOptions, 3046 sizeof(saveMoreOptions), &MoreOptionsStruct::EndianSwap) 3047 != kReadAttrFailed; 3048 3049 if (storesMoreOptions) { 3050 // need to sanitize to true or false here, could have picked 3051 // up garbage from attributes 3052 3053 saveMoreOptions.showMoreOptions = true; // Now unused 3054 3055 static_cast<FindWindow*>(Window())->SetOptions(saveMoreOptions.searchTrash); 3056 } 3057 3058 // get volumes to perform query on 3059 3060 int32 selectedVolumes = 0; 3061 3062 attr_info info; 3063 if (node->GetAttrInfo(kAttrQueryVolume, &info) == B_OK) { 3064 char* buffer = new char[info.size]; 3065 if (node->ReadAttr(kAttrQueryVolume, B_MESSAGE_TYPE, 0, buffer, 3066 (size_t)info.size) == info.size) { 3067 BMessage message; 3068 if (message.Unflatten(buffer) == B_OK) { 3069 for (int32 index = 0; ;index++) { 3070 ASSERT(index < 100); 3071 BVolume volume; 3072 // match a volume with the info embedded in 3073 // the message 3074 status_t result 3075 = MatchArchivedVolume(&volume, &message, index); 3076 if (result == B_OK) { 3077 char name[256]; 3078 volume.GetName(name); 3079 if (SelectItemWithLabel(fVolMenu, name) != -1) 3080 ++selectedVolumes; 3081 } else if (result != B_DEV_BAD_DRIVE_NUM) 3082 // if B_DEV_BAD_DRIVE_NUM, the volume just isn't 3083 // mounted this time around, keep looking for more 3084 // if other error, bail 3085 break; 3086 } 3087 } 3088 } 3089 delete[] buffer; 3090 } 3091 3092 LoadDirectoryFiltersFromFile(node); 3093 // mark or unmark "All disks" 3094 if (selectedVolumes == GetNumberOfVolumes()) { 3095 fVolMenu->ItemAt(0)->SetMarked(true); 3096 for (int32 i = 0; i < GetNumberOfVolumes() + 2; ++i) 3097 fVolMenu->ItemAt(i)->SetMarked(false); 3098 } 3099 ShowVolumeMenuLabel(); 3100 3101 switch (Mode()) { 3102 case kByAttributeItem: 3103 { 3104 int32 count = InitialAttrCount(node); 3105 3106 attr_info info; 3107 if (node->GetAttrInfo(kAttrQueryInitialAttrs, &info) != B_OK) 3108 break; 3109 char* buffer = new char[info.size]; 3110 if (node->ReadAttr(kAttrQueryInitialAttrs, B_MESSAGE_TYPE, 0, 3111 buffer, (size_t)info.size) == info.size) { 3112 BMessage message; 3113 if (message.Unflatten(buffer) == B_OK) { 3114 for (int32 index = 0; index < count; index++) 3115 RestoreAttrState(message, index); 3116 } 3117 } 3118 delete[] buffer; 3119 break; 3120 } 3121 3122 case kByNameItem: 3123 case kByFormulaItem: 3124 { 3125 BString buffer; 3126 if (node->ReadAttrString(kAttrQueryInitialString, &buffer) 3127 == B_OK) { 3128 BTextControl* textControl = dynamic_cast<BTextControl*>( 3129 FindView("TextControl")); 3130 3131 ASSERT(textControl != NULL); 3132 3133 if (textControl != NULL) 3134 textControl->SetText(buffer.String()); 3135 } 3136 break; 3137 } 3138 } 3139 3140 // try to restore focus and possibly text selection 3141 BString focusedView; 3142 if (node->ReadAttrString("_trk/focusedView", &focusedView) == B_OK) { 3143 BView* view = FindView(focusedView.String()); 3144 if (view != NULL) { 3145 view->MakeFocus(); 3146 BTextControl* textControl = dynamic_cast<BTextControl*>(view); 3147 if (textControl != NULL && Mode() == kByFormulaItem) { 3148 int32 selStart = 0; 3149 int32 selEnd = INT32_MAX; 3150 node->ReadAttr("_trk/focusedSelStart", B_INT32_TYPE, 0, 3151 &selStart, sizeof(selStart)); 3152 node->ReadAttr("_trk/focusedSelEnd", B_INT32_TYPE, 0, 3153 &selEnd, sizeof(selEnd)); 3154 textControl->TextView()->Select(selStart, selEnd); 3155 } 3156 } 3157 } 3158 } 3159 3160 3161 void 3162 FindPanel::AddByAttributeItems(const BNode* node) 3163 { 3164 int32 numAttributes = InitialAttrCount(node); 3165 if (numAttributes < 1) 3166 numAttributes = 1; 3167 3168 for (int32 index = 0; index < numAttributes; index ++) 3169 AddAttrRow(); 3170 } 3171 3172 3173 void 3174 FindPanel::AddByNameOrFormulaItems() 3175 { 3176 BBox* box = dynamic_cast<BBox*>(FindView("Box")); 3177 3178 ASSERT(box != NULL); 3179 3180 if (box == NULL) 3181 return; 3182 3183 // reset layout 3184 BLayoutBuilder::Group<>(box, B_VERTICAL); 3185 3186 BTextControl* textControl = new BTextControl("TextControl", 3187 "", "", NULL); 3188 textControl->SetDivider(0.0f); 3189 box->SetBorder(B_NO_BORDER); 3190 box->AddChild(textControl); 3191 textControl->MakeFocus(); 3192 } 3193 3194 3195 void 3196 FindPanel::RemoveAttrViewItems(bool removeGrid) 3197 { 3198 if (fAttrGrid == NULL) 3199 return; 3200 3201 BView* view = fAttrGrid->View(); 3202 for (int32 index = view->CountChildren(); index > 0; index--) { 3203 BView* child = view->ChildAt(index - 1); 3204 child->RemoveSelf(); 3205 delete child; 3206 } 3207 3208 if (removeGrid) { 3209 view->RemoveSelf(); 3210 delete view; 3211 fAttrGrid = NULL; 3212 } 3213 } 3214 3215 3216 void 3217 FindPanel::RemoveByAttributeItems() 3218 { 3219 RemoveAttrViewItems(); 3220 BView* view = FindView("add button"); 3221 if (view) { 3222 view->RemoveSelf(); 3223 delete view; 3224 } 3225 3226 view = FindView("remove button"); 3227 if (view) { 3228 view->RemoveSelf(); 3229 delete view; 3230 } 3231 3232 view = FindView("TextControl"); 3233 if (view) { 3234 view->RemoveSelf(); 3235 delete view; 3236 } 3237 } 3238 3239 3240 void 3241 FindPanel::ShowOrHideMimeTypeMenu() 3242 { 3243 BView* menuFieldSpacer = FindView("MimeTypeMenuSpacer"); 3244 BMenuField* menuField 3245 = dynamic_cast<BMenuField*>(FindView("MimeTypeMenu")); 3246 if (menuFieldSpacer == NULL || menuField == NULL) 3247 return; 3248 3249 if (Mode() == (int32)kByFormulaItem && !menuField->IsHidden(this)) { 3250 BSize size = menuField->ExplicitMinSize(); 3251 menuField->Hide(); 3252 menuFieldSpacer->SetExplicitMinSize(size); 3253 menuFieldSpacer->SetExplicitMaxSize(size); 3254 if (menuFieldSpacer->IsHidden(this)) 3255 menuFieldSpacer->Show(); 3256 } else if (menuField->IsHidden(this)) { 3257 menuFieldSpacer->Hide(); 3258 menuField->Show(); 3259 } 3260 } 3261 3262 3263 void 3264 FindPanel::AddAttributeControls(int32 gridRow) 3265 { 3266 BPopUpMenu* menu = new BPopUpMenu("PopUp"); 3267 3268 // add NAME attribute to popup 3269 BMenu* submenu = new BMenu(B_TRANSLATE("Name")); 3270 submenu->SetRadioMode(true); 3271 submenu->SetFont(be_plain_font); 3272 BMessage* message = new BMessage(kAttributeItemMain); 3273 message->AddString("name", "name"); 3274 message->AddInt32("type", B_STRING_TYPE); 3275 BMenuItem* item = new BMenuItem(submenu, message); 3276 menu->AddItem(item); 3277 3278 for (int32 i = 0; i < 5; i++) { 3279 message = new BMessage(kAttributeItem); 3280 message->AddInt32("operator", operators[i]); 3281 submenu->AddItem(new BMenuItem( 3282 B_TRANSLATE_NOCOLLECT(operatorLabels[i]), message)); 3283 } 3284 3285 // mark first items initially 3286 menu->ItemAt(0)->SetMarked(true); 3287 submenu->ItemAt(0)->SetMarked(true); 3288 3289 // add SIZE attribute 3290 submenu = new BMenu(B_TRANSLATE("Size")); 3291 submenu->SetRadioMode(true); 3292 submenu->SetFont(be_plain_font); 3293 message = new BMessage(kAttributeItemMain); 3294 message->AddString("name", "size"); 3295 message->AddInt32("type", B_OFF_T_TYPE); 3296 item = new BMenuItem(submenu, message); 3297 menu->AddItem(item); 3298 3299 message = new BMessage(kAttributeItem); 3300 message->AddInt32("operator", B_GT); 3301 submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[5]), 3302 message)); 3303 3304 message = new BMessage(kAttributeItem); 3305 message->AddInt32("operator", B_LT); 3306 submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[6]), 3307 message)); 3308 3309 message = new BMessage(kAttributeItem); 3310 message->AddInt32("operator", B_EQ); 3311 submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[1]), 3312 message)); 3313 3314 // add "modified" field 3315 submenu = new BMenu(B_TRANSLATE("Modified")); 3316 submenu->SetRadioMode(true); 3317 submenu->SetFont(be_plain_font); 3318 message = new BMessage(kAttributeItemMain); 3319 message->AddString("name", "last_modified"); 3320 message->AddInt32("type", B_TIME_TYPE); 3321 item = new BMenuItem(submenu, message); 3322 menu->AddItem(item); 3323 3324 message = new BMessage(kAttributeItem); 3325 message->AddInt32("operator", B_LT); 3326 submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[7]), 3327 message)); 3328 3329 message = new BMessage(kAttributeItem); 3330 message->AddInt32("operator", B_GT); 3331 submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[8]), 3332 message)); 3333 3334 BMenuField* menuField = new BMenuField("MenuField", "", menu); 3335 menuField->SetDivider(0.0f); 3336 fAttrGrid->AddView(menuField, 0, gridRow); 3337 3338 BStringView* stringView = new BStringView("", 3339 menu->FindMarked()->Submenu()->FindMarked()->Label()); 3340 BLayoutItem* layoutItem = fAttrGrid->AddView(stringView, 1, gridRow); 3341 layoutItem->SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT, 3342 B_ALIGN_VERTICAL_UNSET)); 3343 3344 BString title("TextEntry"); 3345 title << gridRow; 3346 BTextControl* textControl = new BTextControl(title.String(), "", "", NULL); 3347 textControl->SetDivider(0.0f); 3348 fAttrGrid->AddView(textControl, 2, gridRow); 3349 textControl->MakeFocus(); 3350 3351 // target everything 3352 menu->SetTargetForItems(this); 3353 for (int32 index = menu->CountItems() - 1; index >= 0; index--) { 3354 BMenu* submenuAtIndex = menu->SubmenuAt(index); 3355 if (submenuAtIndex != NULL) 3356 submenuAtIndex->SetTargetForItems(this); 3357 } 3358 3359 // populate mime popup 3360 AddMimeTypeAttrs(menu); 3361 } 3362 3363 3364 void 3365 FindPanel::RestoreAttrState(const BMessage& message, int32 index) 3366 { 3367 BMenuField* menuField 3368 = dynamic_cast<BMenuField*>(FindAttrView("MenuField", index)); 3369 if (menuField != NULL) { 3370 // decode menu selections 3371 BMenu* menu = menuField->Menu(); 3372 3373 ASSERT(menu != NULL); 3374 3375 AddMimeTypeAttrs(menu); 3376 const char* label; 3377 if (message.FindString("menuSelection", index, &label) == B_OK) { 3378 int32 itemIndex = SelectItemWithLabel(menu, label); 3379 if (itemIndex >= 0) { 3380 menu = menu->SubmenuAt(itemIndex); 3381 if (menu != NULL && message.FindString("subMenuSelection", 3382 index, &label) == B_OK) { 3383 SelectItemWithLabel(menu, label); 3384 } 3385 } 3386 } 3387 } 3388 3389 // decode attribute text 3390 BString textEntryString = "TextEntry"; 3391 textEntryString << index; 3392 BTextControl* textControl = dynamic_cast<BTextControl*>( 3393 FindAttrView(textEntryString.String(), index)); 3394 3395 ASSERT(textControl != NULL); 3396 3397 const char* string; 3398 if (textControl != NULL 3399 && message.FindString("attrViewText", index, &string) == B_OK) { 3400 textControl->SetText(string); 3401 } 3402 3403 int32 logicMenuSelectedIndex; 3404 if (message.FindInt32("logicalRelation", index, 3405 &logicMenuSelectedIndex) == B_OK) { 3406 BMenuField* field = dynamic_cast<BMenuField*>( 3407 FindAttrView("Logic", index)); 3408 if (field != NULL) { 3409 BMenu* fieldMenu = field->Menu(); 3410 if (fieldMenu != NULL) { 3411 BMenuItem* logicItem 3412 = fieldMenu->ItemAt(logicMenuSelectedIndex); 3413 if (logicItem != NULL) { 3414 logicItem->SetMarked(true); 3415 return; 3416 } 3417 } 3418 } 3419 3420 AddLogicMenu(index, logicMenuSelectedIndex == 0); 3421 } 3422 } 3423 3424 3425 void 3426 FindPanel::SaveAttrState(BMessage* message, int32 index) 3427 { 3428 BMenu* menu = dynamic_cast<BMenuField*>(FindAttrView("MenuField", index)) 3429 ->Menu(); 3430 3431 // encode main attribute menu selection 3432 BMenuItem* item = menu->FindMarked(); 3433 message->AddString("menuSelection", item ? item->Label() : ""); 3434 3435 // encode submenu selection 3436 const char* label = ""; 3437 if (item) { 3438 BMenu* submenu = menu->SubmenuAt(menu->IndexOf(item)); 3439 if (submenu) { 3440 item = submenu->FindMarked(); 3441 if (item) 3442 label = item->Label(); 3443 } 3444 } 3445 message->AddString("subMenuSelection", label); 3446 3447 // encode attribute text 3448 BString textEntryString = "TextEntry"; 3449 textEntryString << index; 3450 BTextControl* textControl = dynamic_cast<BTextControl*>(FindAttrView( 3451 textEntryString.String(), index)); 3452 3453 ASSERT(textControl != NULL); 3454 3455 if (textControl != NULL) 3456 message->AddString("attrViewText", textControl->Text()); 3457 3458 BMenuField* field = dynamic_cast<BMenuField*>(FindAttrView("Logic", index)); 3459 if (field != NULL) { 3460 BMenu* fieldMenu = field->Menu(); 3461 if (fieldMenu != NULL) { 3462 BMenuItem* item = fieldMenu->FindMarked(); 3463 ASSERT(item != NULL); 3464 message->AddInt32("logicalRelation", 3465 item != NULL ? field->Menu()->IndexOf(item) : 0); 3466 } 3467 } 3468 } 3469 3470 3471 void 3472 FindPanel::AddLogicMenu(int32 index, bool selectAnd) 3473 { 3474 // add "AND/OR" menu 3475 BPopUpMenu* menu = new BPopUpMenu(""); 3476 BMessage* message = new BMessage(); 3477 message->AddInt32("combine", B_AND); 3478 BMenuItem* item = new BMenuItem(B_TRANSLATE("And"), message); 3479 menu->AddItem(item); 3480 if (selectAnd) 3481 item->SetMarked(true); 3482 3483 message = new BMessage(); 3484 message->AddInt32("combine", B_OR); 3485 item = new BMenuItem(B_TRANSLATE("Or"), message); 3486 menu->AddItem(item); 3487 if (!selectAnd) 3488 item->SetMarked(true); 3489 3490 menu->SetTargetForItems(this); 3491 3492 BMenuField* menufield = new BMenuField("Logic", "", menu, B_WILL_DRAW); 3493 menufield->SetDivider(0.0f); 3494 3495 ResizeMenuField(menufield); 3496 3497 fAttrGrid->AddView(menufield, 3, index); 3498 } 3499 3500 3501 void 3502 FindPanel::RemoveLogicMenu(int32 index) 3503 { 3504 BMenuField* menufield = dynamic_cast<BMenuField*>(FindAttrView("Logic", index)); 3505 if (menufield) { 3506 menufield->RemoveSelf(); 3507 delete menufield; 3508 } 3509 } 3510 3511 3512 void 3513 FindPanel::AddAttributes(BMenu* menu, const BMimeType& mimeType) 3514 { 3515 // only add things to menu which have "user-visible" data 3516 BMessage attributeMessage; 3517 if (mimeType.GetAttrInfo(&attributeMessage) != B_OK) 3518 return; 3519 3520 char desc[B_MIME_TYPE_LENGTH]; 3521 mimeType.GetShortDescription(desc); 3522 3523 // go through each field in meta mime and add it to a menu 3524 for (int32 index = 0; ; index++) { 3525 const char* publicName; 3526 if (attributeMessage.FindString("attr:public_name", index, 3527 &publicName) != B_OK) { 3528 break; 3529 } 3530 3531 if (!attributeMessage.FindBool("attr:viewable")) 3532 continue; 3533 3534 const char* attributeName; 3535 if (attributeMessage.FindString("attr:name", index, &attributeName) 3536 != B_OK) { 3537 continue; 3538 } 3539 3540 int32 type; 3541 if (attributeMessage.FindInt32("attr:type", index, &type) != B_OK) 3542 continue; 3543 3544 BMenu* submenu = new BMenu(publicName); 3545 submenu->SetRadioMode(true); 3546 submenu->SetFont(be_plain_font); 3547 BMessage* message = new BMessage(kAttributeItemMain); 3548 message->AddString("name", attributeName); 3549 message->AddInt32("type", type); 3550 BMenuItem* item = new BMenuItem(submenu, message); 3551 menu->AddItem(item); 3552 menu->SetTargetForItems(this); 3553 3554 switch (type) { 3555 case B_STRING_TYPE: 3556 message = new BMessage(kAttributeItem); 3557 message->AddInt32("operator", B_CONTAINS); 3558 submenu->AddItem(new BMenuItem(operatorLabels[0], message)); 3559 3560 message = new BMessage(kAttributeItem); 3561 message->AddInt32("operator", B_EQ); 3562 submenu->AddItem(new BMenuItem(operatorLabels[1], message)); 3563 3564 message = new BMessage(kAttributeItem); 3565 message->AddInt32("operator", B_NE); 3566 submenu->AddItem(new BMenuItem(operatorLabels[2], message)); 3567 submenu->SetTargetForItems(this); 3568 3569 message = new BMessage(kAttributeItem); 3570 message->AddInt32("operator", B_BEGINS_WITH); 3571 submenu->AddItem(new BMenuItem(operatorLabels[3], message)); 3572 submenu->SetTargetForItems(this); 3573 3574 message = new BMessage(kAttributeItem); 3575 message->AddInt32("operator", B_ENDS_WITH); 3576 submenu->AddItem(new BMenuItem(operatorLabels[4], message)); 3577 break; 3578 3579 case B_BOOL_TYPE: 3580 case B_INT16_TYPE: 3581 case B_UINT8_TYPE: 3582 case B_INT8_TYPE: 3583 case B_UINT16_TYPE: 3584 case B_INT32_TYPE: 3585 case B_UINT32_TYPE: 3586 case B_INT64_TYPE: 3587 case B_UINT64_TYPE: 3588 case B_OFF_T_TYPE: 3589 case B_FLOAT_TYPE: 3590 case B_DOUBLE_TYPE: 3591 message = new BMessage(kAttributeItem); 3592 message->AddInt32("operator", B_EQ); 3593 submenu->AddItem(new BMenuItem(operatorLabels[1], message)); 3594 3595 message = new BMessage(kAttributeItem); 3596 message->AddInt32("operator", B_GT); 3597 submenu->AddItem(new BMenuItem(operatorLabels[5], message)); 3598 3599 message = new BMessage(kAttributeItem); 3600 message->AddInt32("operator", B_LT); 3601 submenu->AddItem(new BMenuItem(operatorLabels[6], message)); 3602 break; 3603 3604 case B_TIME_TYPE: 3605 message = new BMessage(kAttributeItem); 3606 message->AddInt32("operator", B_LT); 3607 submenu->AddItem(new BMenuItem(operatorLabels[7], message)); 3608 3609 message = new BMessage(kAttributeItem); 3610 message->AddInt32("operator", B_GT); 3611 submenu->AddItem(new BMenuItem(operatorLabels[8], message)); 3612 break; 3613 } 3614 submenu->SetTargetForItems(this); 3615 } 3616 } 3617 3618 3619 void 3620 FindPanel::AddMimeTypeAttrs(BMenu* menu) 3621 { 3622 const char* typeName; 3623 if (CurrentMimeType(&typeName) == NULL) 3624 return; 3625 3626 BMimeType mimeType(typeName); 3627 if (!mimeType.IsInstalled()) 3628 return; 3629 3630 if (!mimeType.IsSupertypeOnly()) { 3631 // add supertype attributes 3632 BMimeType supertype; 3633 mimeType.GetSupertype(&supertype); 3634 AddAttributes(menu, supertype); 3635 } 3636 3637 AddAttributes(menu, mimeType); 3638 } 3639 3640 3641 void 3642 FindPanel::GetDefaultAttrName(BString& attrName, int32 row) const 3643 { 3644 BMenuItem* item = NULL; 3645 BMenuField* menuField 3646 = dynamic_cast<BMenuField*>(fAttrGrid->ItemAt(0, row)->View()); 3647 if (menuField != NULL && menuField->Menu() != NULL) 3648 item = menuField->Menu()->FindMarked(); 3649 3650 if (item != NULL) 3651 attrName << item->Label(); 3652 else 3653 attrName << B_TRANSLATE("Name"); 3654 3655 if (item != NULL && item->Submenu() != NULL) 3656 item = item->Submenu()->FindMarked(); 3657 else 3658 item = NULL; 3659 3660 if (item != NULL) 3661 attrName << " " << item->Label() << " "; 3662 else 3663 attrName << " = "; 3664 3665 BTextControl* textControl 3666 = dynamic_cast<BTextControl*>(fAttrGrid->ItemAt(2, row)->View()); 3667 if (textControl != NULL) 3668 attrName << textControl->Text(); 3669 } 3670 3671 3672 // #pragma mark - 3673 3674 3675 DeleteTransientQueriesTask::DeleteTransientQueriesTask() 3676 : 3677 state(kInitial), 3678 fWalker(NULL) 3679 { 3680 } 3681 3682 3683 DeleteTransientQueriesTask::~DeleteTransientQueriesTask() 3684 { 3685 delete fWalker; 3686 } 3687 3688 3689 bool 3690 DeleteTransientQueriesTask::DoSomeWork() 3691 { 3692 switch (state) { 3693 case kInitial: 3694 Initialize(); 3695 break; 3696 3697 case kAllocatedWalker: 3698 case kTraversing: 3699 if (GetSome()) { 3700 PRINT(("transient query killer done\n")); 3701 return true; 3702 } 3703 break; 3704 3705 case kError: 3706 return true; 3707 3708 } 3709 return false; 3710 } 3711 3712 3713 void 3714 DeleteTransientQueriesTask::Initialize() 3715 { 3716 PRINT(("starting up transient query killer\n")); 3717 BPath path; 3718 status_t result = find_directory(B_USER_DIRECTORY, &path, false); 3719 if (result != B_OK) { 3720 state = kError; 3721 return; 3722 } 3723 fWalker = new BTrackerPrivate::TNodeWalker(path.Path()); 3724 state = kAllocatedWalker; 3725 } 3726 3727 3728 const int32 kBatchCount = 100; 3729 3730 bool 3731 DeleteTransientQueriesTask::GetSome() 3732 { 3733 state = kTraversing; 3734 for (int32 count = kBatchCount; count > 0; count--) { 3735 entry_ref ref; 3736 if (fWalker->GetNextRef(&ref) != B_OK) { 3737 state = kError; 3738 return true; 3739 } 3740 Model model(&ref); 3741 if (model.IsQuery()) 3742 ProcessOneRef(&model); 3743 #if xDEBUG 3744 else 3745 PRINT(("transient query killer: %s not a query\n", model.Name())); 3746 #endif 3747 } 3748 return false; 3749 } 3750 3751 3752 const int32 kDaysToExpire = 7; 3753 3754 static bool 3755 QueryOldEnough(Model* model) 3756 { 3757 // check if it is old and ready to be deleted 3758 time_t now = time(0); 3759 3760 tm nowTimeData; 3761 tm fileModData; 3762 3763 localtime_r(&now, &nowTimeData); 3764 localtime_r(&model->StatBuf()->st_ctime, &fileModData); 3765 3766 if ((nowTimeData.tm_mday - fileModData.tm_mday) < kDaysToExpire 3767 && (nowTimeData.tm_mday - fileModData.tm_mday) > -kDaysToExpire) { 3768 PRINT(("query %s, not old enough\n", model->Name())); 3769 return false; 3770 } 3771 return true; 3772 } 3773 3774 3775 bool 3776 DeleteTransientQueriesTask::ProcessOneRef(Model* model) 3777 { 3778 BModelOpener opener(model); 3779 3780 // is this a temporary query 3781 if (!MoreOptionsStruct::QueryTemporary(model->Node())) { 3782 PRINT(("query %s, not temporary\n", model->Name())); 3783 return false; 3784 } 3785 3786 if (!QueryOldEnough(model)) 3787 return false; 3788 3789 TTracker* tracker = dynamic_cast<TTracker*>(be_app); 3790 ASSERT(tracker != NULL); 3791 3792 // check that it is not showing 3793 if (tracker != NULL && tracker->EntryHasWindowOpen(model->EntryRef())) { 3794 PRINT(("query %s, showing, can't delete\n", model->Name())); 3795 return false; 3796 } 3797 3798 PRINT(("query %s, old, temporary, not shownig - deleting\n", 3799 model->Name())); 3800 3801 BEntry entry(model->EntryRef()); 3802 entry.Remove(); 3803 3804 return true; 3805 } 3806 3807 3808 class DeleteTransientQueriesFunctor : public FunctionObjectWithResult<bool> { 3809 public: 3810 DeleteTransientQueriesFunctor(DeleteTransientQueriesTask* task) 3811 : 3812 task(task) 3813 {} 3814 3815 virtual ~DeleteTransientQueriesFunctor() { delete task; } 3816 3817 virtual void operator()() { result = task->DoSomeWork(); } 3818 3819 private: 3820 DeleteTransientQueriesTask* task; 3821 }; 3822 3823 3824 void 3825 DeleteTransientQueriesTask::StartUpTransientQueryCleaner() 3826 { 3827 TTracker* tracker = dynamic_cast<TTracker*>(be_app); 3828 ASSERT(tracker != NULL); 3829 3830 if (tracker == NULL) 3831 return; 3832 // set up a task that wakes up when the machine is idle and starts 3833 // killing off old transient queries 3834 DeleteTransientQueriesFunctor* worker 3835 = new DeleteTransientQueriesFunctor(new DeleteTransientQueriesTask()); 3836 3837 tracker->MainTaskLoop()->RunWhenIdle(worker, 3838 30 * 60 * 1000000, // half an hour initial delay 3839 5 * 60 * 1000000, // idle for five minutes 3840 10 * 1000000); 3841 } 3842 3843 3844 // #pragma mark - 3845 3846 3847 RecentFindItemsMenu::RecentFindItemsMenu(const char* title, 3848 const BMessenger* target, uint32 what) 3849 : 3850 BMenu(title, B_ITEMS_IN_COLUMN), 3851 fTarget(*target), 3852 fWhat(what) 3853 { 3854 } 3855 3856 3857 void 3858 RecentFindItemsMenu::AttachedToWindow() 3859 { 3860 // re-populate the menu with fresh items 3861 for (int32 index = CountItems() - 1; index >= 0; index--) 3862 delete RemoveItem(index); 3863 3864 FindPanel::AddRecentQueries(this, false, &fTarget, fWhat); 3865 BMenu::AttachedToWindow(); 3866 } 3867 3868 3869 #if !B_BEOS_VERSION_DANO 3870 _IMPEXP_TRACKER 3871 #endif 3872 BMenu* 3873 TrackerBuildRecentFindItemsMenu(const char* title) 3874 { 3875 BMessenger trackerMessenger(kTrackerSignature); 3876 return new RecentFindItemsMenu(title, &trackerMessenger, B_REFS_RECEIVED); 3877 } 3878 3879 3880 // #pragma mark - 3881 3882 void 3883 DraggableQueryIcon::Draw(BRect updateRect) 3884 { 3885 BRect rect(Bounds()); 3886 rgb_color base = ui_color(B_MENU_BACKGROUND_COLOR); 3887 be_control_look->DrawBorder(this, rect, updateRect, base, B_PLAIN_BORDER, 0, 3888 BControlLook::B_BOTTOM_BORDER); 3889 be_control_look->DrawMenuBarBackground(this, rect, updateRect, base, 0, 3890 BControlLook::B_ALL_BORDERS & ~BControlLook::B_LEFT_BORDER); 3891 DraggableIcon::Draw(updateRect); 3892 } 3893 3894 3895 DraggableQueryIcon::DraggableQueryIcon(BRect frame, const char* name, 3896 const BMessage* message, BMessenger messenger, uint32 resizeFlags, 3897 uint32 flags) 3898 : 3899 DraggableIcon(frame, name, B_QUERY_MIMETYPE, B_LARGE_ICON, 3900 message, messenger, resizeFlags, flags) 3901 { 3902 } 3903 3904 3905 bool 3906 DraggableQueryIcon::DragStarted(BMessage* dragMessage) 3907 { 3908 // override to substitute the user-specified query name 3909 dragMessage->RemoveData("be:clip_name"); 3910 3911 FindWindow* window = dynamic_cast<FindWindow*>(Window()); 3912 3913 ASSERT(window != NULL); 3914 3915 return window != NULL && dragMessage->AddString("be:clip_name", 3916 window->BackgroundView()->UserSpecifiedName() != NULL 3917 ? window->BackgroundView()->UserSpecifiedName() 3918 : B_TRANSLATE("New Query")) == B_OK; 3919 } 3920 3921 3922 // #pragma mark - 3923 3924 3925 MostUsedNames::MostUsedNames(const char* fileName, const char* directory, 3926 int32 maxCount) 3927 : 3928 fFileName(fileName), 3929 fDirectory(directory), 3930 fLoaded(false), 3931 fCount(maxCount) 3932 { 3933 } 3934 3935 3936 MostUsedNames::~MostUsedNames() 3937 { 3938 // only write back settings when we've been used 3939 if (!fLoaded) 3940 return; 3941 3942 // write most used list to file 3943 3944 BPath path; 3945 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK 3946 || path.Append(fDirectory) != B_OK || path.Append(fFileName) != B_OK) { 3947 return; 3948 } 3949 3950 BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); 3951 if (file.InitCheck() == B_OK) { 3952 for (int32 i = 0; i < fList.CountItems(); i++) { 3953 list_entry* entry = static_cast<list_entry*>(fList.ItemAt(i)); 3954 3955 char line[B_FILE_NAME_LENGTH + 5]; 3956 3957 // limit upper bound to react more dynamically to changes 3958 if (--entry->count > 20) 3959 entry->count = 20; 3960 3961 // if the item hasn't been chosen in a while, remove it 3962 // (but leave at least one item in the list) 3963 if (entry->count < -10 && i > 0) 3964 continue; 3965 3966 sprintf(line, "%" B_PRId32 " %s\n", entry->count, entry->name); 3967 if (file.Write(line, strlen(line)) < B_OK) 3968 break; 3969 } 3970 } 3971 file.Unset(); 3972 3973 // free data 3974 3975 for (int32 i = fList.CountItems(); i-- > 0;) { 3976 list_entry* entry = static_cast<list_entry*>(fList.ItemAt(i)); 3977 free(entry->name); 3978 delete entry; 3979 } 3980 } 3981 3982 3983 bool 3984 MostUsedNames::ObtainList(BList* list) 3985 { 3986 if (list == NULL) 3987 return false; 3988 3989 if (!fLoaded) 3990 UpdateList(); 3991 3992 fLock.Lock(); 3993 3994 list->MakeEmpty(); 3995 for (int32 i = 0; i < fCount; i++) { 3996 list_entry* entry = static_cast<list_entry*>(fList.ItemAt(i)); 3997 if (entry == NULL) 3998 return true; 3999 4000 list->AddItem(entry->name); 4001 } 4002 return true; 4003 } 4004 4005 4006 void 4007 MostUsedNames::ReleaseList() 4008 { 4009 fLock.Unlock(); 4010 } 4011 4012 4013 void 4014 MostUsedNames::AddName(const char* name) 4015 { 4016 fLock.Lock(); 4017 4018 if (!fLoaded) 4019 LoadList(); 4020 4021 // remove last entry if there are more than 4022 // 2*fCount entries in the list 4023 4024 list_entry* entry = NULL; 4025 4026 if (fList.CountItems() > fCount * 2) { 4027 entry = static_cast<list_entry*>( 4028 fList.RemoveItem(fList.CountItems() - 1)); 4029 4030 // is this the name we want to add here? 4031 if (strcmp(name, entry->name)) { 4032 free(entry->name); 4033 delete entry; 4034 entry = NULL; 4035 } else 4036 fList.AddItem(entry); 4037 } 4038 4039 if (entry == NULL) { 4040 for (int32 i = 0; 4041 (entry = static_cast<list_entry*>(fList.ItemAt(i))) != NULL; i++) { 4042 if (strcmp(entry->name, name) == 0) 4043 break; 4044 } 4045 } 4046 4047 if (entry == NULL) { 4048 entry = new list_entry; 4049 entry->name = strdup(name); 4050 entry->count = 1; 4051 4052 fList.AddItem(entry); 4053 } else if (entry->count < 0) 4054 entry->count = 1; 4055 else 4056 entry->count++; 4057 4058 fLock.Unlock(); 4059 UpdateList(); 4060 } 4061 4062 4063 int 4064 MostUsedNames::CompareNames(const void* a,const void* b) 4065 { 4066 list_entry* entryA = *(list_entry**)a; 4067 list_entry* entryB = *(list_entry**)b; 4068 4069 if (entryA->count == entryB->count) 4070 return strcasecmp(entryA->name,entryB->name); 4071 4072 return entryB->count - entryA->count; 4073 } 4074 4075 4076 void 4077 MostUsedNames::LoadList() 4078 { 4079 if (fLoaded) 4080 return; 4081 fLoaded = true; 4082 4083 // load the most used names list 4084 4085 BPath path; 4086 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK 4087 || path.Append(fDirectory) != B_OK || path.Append(fFileName) != B_OK) { 4088 return; 4089 } 4090 4091 FILE* file = fopen(path.Path(), "r"); 4092 if (file == NULL) 4093 return; 4094 4095 char line[B_FILE_NAME_LENGTH + 5]; 4096 while (fgets(line, sizeof(line), file) != NULL) { 4097 int32 length = (int32)strlen(line) - 1; 4098 if (length >= 0 && line[length] == '\n') 4099 line[length] = '\0'; 4100 4101 int32 count = atoi(line); 4102 4103 char* name = strchr(line, ' '); 4104 if (name == NULL || *(++name) == '\0') 4105 continue; 4106 4107 list_entry* entry = new list_entry; 4108 entry->name = strdup(name); 4109 entry->count = count; 4110 4111 fList.AddItem(entry); 4112 } 4113 fclose(file); 4114 } 4115 4116 4117 void 4118 MostUsedNames::UpdateList() 4119 { 4120 AutoLock<Benaphore> locker(fLock); 4121 4122 if (!fLoaded) 4123 LoadList(); 4124 4125 // sort list items 4126 4127 fList.SortItems(MostUsedNames::CompareNames); 4128 } 4129 4130 } // namespace BPrivate 4131