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