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