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 2863 AddAttributeControls(fAttrGrid->CountRows()); 2864 2865 // add logic to previous attrview 2866 if (fAttrGrid->CountRows() > 1) 2867 AddLogicMenu(fAttrGrid->CountRows() - 2); 2868 2869 BButton* removeButton = dynamic_cast<BButton*>( 2870 box->FindView("remove button")); 2871 if (removeButton != NULL) 2872 removeButton->SetEnabled(fAttrGrid->CountRows() > 1); 2873 else 2874 SetupAddRemoveButtons(); 2875 } 2876 2877 2878 void 2879 FindPanel::RemoveAttrRow() 2880 { 2881 if (fAttrGrid->CountRows() < 2) 2882 return; 2883 2884 BView* view; 2885 2886 int32 row = fAttrGrid->CountRows() - 1; 2887 for (int32 col = fAttrGrid->CountColumns(); col > 0; col--) { 2888 BLayoutItem* item = fAttrGrid->ItemAt(col - 1, row); 2889 if (item == NULL) 2890 continue; 2891 2892 view = item->View(); 2893 if (view == NULL) 2894 continue; 2895 2896 view->RemoveSelf(); 2897 delete view; 2898 } 2899 2900 BString string = "TextEntry"; 2901 string << (row - 1); 2902 view = FindAttrView(string.String(), row - 1); 2903 if (view != NULL) 2904 view->MakeFocus(); 2905 2906 if (fAttrGrid->CountRows() > 1) { 2907 // remove the And/Or menu field of the previous row 2908 BLayoutItem* item = fAttrGrid->ItemAt(3, row - 1); 2909 if (item == NULL) 2910 return; 2911 2912 view = item->View(); 2913 if (view == NULL) 2914 return; 2915 2916 view->RemoveSelf(); 2917 delete view; 2918 return; 2919 } 2920 2921 // only one row remains 2922 2923 // disable the remove button 2924 BButton* button = dynamic_cast<BButton*>(FindView("remove button")); 2925 if (button != NULL) 2926 button->SetEnabled(false); 2927 2928 // remove the And/Or menu field 2929 BLayoutItem* item = fAttrGrid->RemoveItem(3); 2930 if (item == NULL) 2931 return; 2932 2933 view = item->View(); 2934 if (view == NULL) 2935 return; 2936 2937 view->RemoveSelf(); 2938 delete view; 2939 } 2940 2941 2942 uint32 2943 FindPanel::InitialMode(const BNode* node) 2944 { 2945 if (node == NULL || node->InitCheck() != B_OK) 2946 return kByNameItem; 2947 2948 uint32 result; 2949 if (node->ReadAttr(kAttrQueryInitialMode, B_INT32_TYPE, 0, 2950 (int32*)&result, sizeof(int32)) <= 0) 2951 return kByNameItem; 2952 2953 return result; 2954 } 2955 2956 2957 int32 2958 FindPanel::InitialAttrCount(const BNode* node) 2959 { 2960 if (node == NULL || node->InitCheck() != B_OK) 2961 return 1; 2962 2963 int32 result; 2964 if (node->ReadAttr(kAttrQueryInitialNumAttrs, B_INT32_TYPE, 0, 2965 &result, sizeof(int32)) <= 0) 2966 return 1; 2967 2968 return result; 2969 } 2970 2971 2972 static int32 2973 SelectItemWithLabel(BMenu* menu, const char* label) 2974 { 2975 for (int32 index = menu->CountItems(); index-- > 0;) { 2976 BMenuItem* item = menu->ItemAt(index); 2977 2978 if (strcmp(label, item->Label()) == 0) { 2979 item->SetMarked(true); 2980 return index; 2981 } 2982 } 2983 return -1; 2984 } 2985 2986 2987 void 2988 FindPanel::SaveWindowState(BNode* node, bool editTemplate) 2989 { 2990 ASSERT(node->InitCheck() == B_OK); 2991 2992 BMenuItem* item = CurrentMimeType(); 2993 if (item) { 2994 BString label(item->Label()); 2995 node->WriteAttrString(kAttrQueryInitialMime, &label); 2996 } 2997 2998 uint32 mode = Mode(); 2999 node->WriteAttr(kAttrQueryInitialMode, B_INT32_TYPE, 0, 3000 (int32*)&mode, sizeof(int32)); 3001 3002 if (editTemplate) { 3003 if (UserSpecifiedName()) { 3004 BString name(UserSpecifiedName()); 3005 node->WriteAttrString(kAttrQueryTemplateName, &name); 3006 } 3007 } 3008 3009 switch (Mode()) { 3010 case kByAttributeItem: 3011 { 3012 BMessage message; 3013 int32 count = fAttrGrid->CountRows(); 3014 node->WriteAttr(kAttrQueryInitialNumAttrs, B_INT32_TYPE, 0, 3015 &count, sizeof(int32)); 3016 3017 for (int32 index = 0; index < count; index++) 3018 SaveAttrState(&message, index); 3019 3020 ssize_t size = message.FlattenedSize(); 3021 if (size > 0) { 3022 char* buffer = new char[(size_t)size]; 3023 status_t result = message.Flatten(buffer, size); 3024 if (result == B_OK) { 3025 node->WriteAttr(kAttrQueryInitialAttrs, B_MESSAGE_TYPE, 0, 3026 buffer, (size_t)size); 3027 } 3028 delete[] buffer; 3029 } 3030 break; 3031 } 3032 3033 case kByNameItem: 3034 case kByFormulaItem: 3035 { 3036 BTextControl* textControl = dynamic_cast<BTextControl*>( 3037 FindView("TextControl")); 3038 3039 ASSERT(textControl != NULL); 3040 3041 if (textControl != NULL) { 3042 BString formula(textControl->Text()); 3043 node->WriteAttrString(kAttrQueryInitialString, &formula); 3044 } 3045 break; 3046 } 3047 } 3048 } 3049 3050 3051 void 3052 FindPanel::SwitchToTemplate(const BNode* node) 3053 { 3054 SwitchMode(InitialMode(node)); 3055 // update the menu to correspond to the mode 3056 MarkNamedMenuItem(fSearchModeMenu, InitialMode(node), true); 3057 3058 if (Mode() == (int32)kByAttributeItem) { 3059 RemoveByAttributeItems(); 3060 AddByAttributeItems(node); 3061 } 3062 3063 RestoreWindowState(node); 3064 } 3065 3066 3067 void 3068 FindPanel::RestoreMimeTypeMenuSelection(const BNode* node) 3069 { 3070 if (Mode() == (int32)kByFormulaItem || node == NULL 3071 || node->InitCheck() != B_OK) { 3072 return; 3073 } 3074 3075 BString buffer; 3076 if (node->ReadAttrString(kAttrQueryInitialMime, &buffer) == B_OK) 3077 SetCurrentMimeType(buffer.String()); 3078 } 3079 3080 3081 void 3082 FindPanel::RestoreWindowState(const BNode* node) 3083 { 3084 fMode = InitialMode(node); 3085 if (node == NULL || node->InitCheck() != B_OK) 3086 return; 3087 3088 ShowOrHideMimeTypeMenu(); 3089 RestoreMimeTypeMenuSelection(node); 3090 MoreOptionsStruct saveMoreOptions; 3091 3092 bool storesMoreOptions = ReadAttr(node, kAttrQueryMoreOptions, 3093 kAttrQueryMoreOptionsForeign, B_RAW_TYPE, 0, &saveMoreOptions, 3094 sizeof(saveMoreOptions), &MoreOptionsStruct::EndianSwap) 3095 != kReadAttrFailed; 3096 3097 if (storesMoreOptions) { 3098 // need to sanitize to true or false here, could have picked 3099 // up garbage from attributes 3100 3101 saveMoreOptions.showMoreOptions = true; // Now unused 3102 3103 static_cast<FindWindow*>(Window())->SetOptions(saveMoreOptions.searchTrash); 3104 } 3105 3106 // get volumes to perform query on 3107 3108 int32 selectedVolumes = 0; 3109 3110 attr_info info; 3111 if (node->GetAttrInfo(kAttrQueryVolume, &info) == B_OK) { 3112 char* buffer = new char[info.size]; 3113 if (node->ReadAttr(kAttrQueryVolume, B_MESSAGE_TYPE, 0, buffer, 3114 (size_t)info.size) == info.size) { 3115 BMessage message; 3116 if (message.Unflatten(buffer) == B_OK) { 3117 for (int32 index = 0; ;index++) { 3118 ASSERT(index < 100); 3119 BVolume volume; 3120 // match a volume with the info embedded in 3121 // the message 3122 status_t result 3123 = MatchArchivedVolume(&volume, &message, index); 3124 if (result == B_OK) { 3125 char name[256]; 3126 volume.GetName(name); 3127 if (SelectItemWithLabel(fVolMenu, name) != -1) 3128 ++selectedVolumes; 3129 } else if (result != B_DEV_BAD_DRIVE_NUM) 3130 // if B_DEV_BAD_DRIVE_NUM, the volume just isn't 3131 // mounted this time around, keep looking for more 3132 // if other error, bail 3133 break; 3134 } 3135 } 3136 } 3137 delete[] buffer; 3138 } 3139 3140 LoadDirectoryFiltersFromFile(node); 3141 // mark or unmark "All disks" 3142 if (selectedVolumes == GetNumberOfVolumes()) { 3143 fVolMenu->ItemAt(0)->SetMarked(true); 3144 for (int32 i = 0; i < GetNumberOfVolumes() + 2; ++i) 3145 fVolMenu->ItemAt(i)->SetMarked(false); 3146 } 3147 ShowVolumeMenuLabel(); 3148 3149 switch (Mode()) { 3150 case kByAttributeItem: 3151 { 3152 int32 count = InitialAttrCount(node); 3153 3154 attr_info info; 3155 if (node->GetAttrInfo(kAttrQueryInitialAttrs, &info) != B_OK) 3156 break; 3157 char* buffer = new char[info.size]; 3158 if (node->ReadAttr(kAttrQueryInitialAttrs, B_MESSAGE_TYPE, 0, 3159 buffer, (size_t)info.size) == info.size) { 3160 BMessage message; 3161 if (message.Unflatten(buffer) == B_OK) { 3162 for (int32 index = 0; index < count; index++) 3163 RestoreAttrState(message, index); 3164 } 3165 } 3166 delete[] buffer; 3167 break; 3168 } 3169 3170 case kByNameItem: 3171 case kByFormulaItem: 3172 { 3173 BString buffer; 3174 if (node->ReadAttrString(kAttrQueryInitialString, &buffer) 3175 == B_OK) { 3176 BTextControl* textControl = dynamic_cast<BTextControl*>( 3177 FindView("TextControl")); 3178 3179 ASSERT(textControl != NULL); 3180 3181 if (textControl != NULL) 3182 textControl->SetText(buffer.String()); 3183 } 3184 break; 3185 } 3186 } 3187 3188 // try to restore focus and possibly text selection 3189 BString focusedView; 3190 if (node->ReadAttrString("_trk/focusedView", &focusedView) == B_OK) { 3191 BView* view = FindView(focusedView.String()); 3192 if (view != NULL) { 3193 view->MakeFocus(); 3194 BTextControl* textControl = dynamic_cast<BTextControl*>(view); 3195 if (textControl != NULL && Mode() == kByFormulaItem) { 3196 int32 selStart = 0; 3197 int32 selEnd = INT32_MAX; 3198 node->ReadAttr("_trk/focusedSelStart", B_INT32_TYPE, 0, 3199 &selStart, sizeof(selStart)); 3200 node->ReadAttr("_trk/focusedSelEnd", B_INT32_TYPE, 0, 3201 &selEnd, sizeof(selEnd)); 3202 textControl->TextView()->Select(selStart, selEnd); 3203 } 3204 } 3205 } 3206 } 3207 3208 3209 void 3210 FindPanel::AddByAttributeItems(const BNode* node) 3211 { 3212 int32 numAttributes = InitialAttrCount(node); 3213 if (numAttributes < 1) 3214 numAttributes = 1; 3215 3216 for (int32 index = 0; index < numAttributes; index ++) 3217 AddAttrRow(); 3218 } 3219 3220 3221 void 3222 FindPanel::AddByNameOrFormulaItems() 3223 { 3224 BBox* box = dynamic_cast<BBox*>(FindView("Box")); 3225 3226 ASSERT(box != NULL); 3227 3228 if (box == NULL) 3229 return; 3230 3231 // reset layout 3232 BLayoutBuilder::Group<>(box, B_VERTICAL); 3233 3234 BTextControl* textControl = new BTextControl("TextControl", 3235 "", "", NULL); 3236 textControl->SetDivider(0.0f); 3237 box->SetBorder(B_NO_BORDER); 3238 box->AddChild(textControl); 3239 textControl->MakeFocus(); 3240 } 3241 3242 3243 void 3244 FindPanel::RemoveAttrViewItems(bool removeGrid) 3245 { 3246 if (fAttrGrid == NULL) 3247 return; 3248 3249 BView* view = fAttrGrid->View(); 3250 for (int32 index = view->CountChildren(); index > 0; index--) { 3251 BView* child = view->ChildAt(index - 1); 3252 child->RemoveSelf(); 3253 delete child; 3254 } 3255 3256 if (removeGrid) { 3257 view->RemoveSelf(); 3258 delete view; 3259 fAttrGrid = NULL; 3260 } 3261 } 3262 3263 3264 void 3265 FindPanel::RemoveByAttributeItems() 3266 { 3267 RemoveAttrViewItems(); 3268 BView* view = FindView("add button"); 3269 if (view) { 3270 view->RemoveSelf(); 3271 delete view; 3272 } 3273 3274 view = FindView("remove button"); 3275 if (view) { 3276 view->RemoveSelf(); 3277 delete view; 3278 } 3279 3280 view = FindView("TextControl"); 3281 if (view) { 3282 view->RemoveSelf(); 3283 delete view; 3284 } 3285 } 3286 3287 3288 void 3289 FindPanel::ShowOrHideMimeTypeMenu() 3290 { 3291 BView* menuFieldSpacer = FindView("MimeTypeMenuSpacer"); 3292 BMenuField* menuField = dynamic_cast<BMenuField*>(FindView("MimeTypeMenu")); 3293 if (menuFieldSpacer == NULL || menuField == NULL) 3294 return; 3295 3296 if (Mode() == (int32)kByFormulaItem && !menuField->IsHidden(this)) { 3297 BSize size = menuField->ExplicitMinSize(); 3298 menuField->Hide(); 3299 menuFieldSpacer->SetExplicitMinSize(size); 3300 menuFieldSpacer->SetExplicitMaxSize(size); 3301 if (menuFieldSpacer->IsHidden(this)) 3302 menuFieldSpacer->Show(); 3303 } else if (menuField->IsHidden(this)) { 3304 menuFieldSpacer->Hide(); 3305 menuField->Show(); 3306 } 3307 } 3308 3309 3310 void 3311 FindPanel::AddAttributeControls(int32 gridRow) 3312 { 3313 BPopUpMenu* menu = new BPopUpMenu("PopUp"); 3314 3315 // add NAME attribute to popup 3316 BMenu* submenu = new BMenu(B_TRANSLATE("Name")); 3317 submenu->SetRadioMode(true); 3318 submenu->SetFont(be_plain_font); 3319 BMessage* message = new BMessage(kAttributeItemMain); 3320 message->AddString("name", "name"); 3321 message->AddInt32("type", B_STRING_TYPE); 3322 BMenuItem* item = new BMenuItem(submenu, message); 3323 menu->AddItem(item); 3324 3325 for (int32 i = 0; i < 5; i++) { 3326 message = new BMessage(kAttributeItem); 3327 message->AddInt32("operator", operators[i]); 3328 submenu->AddItem(new BMenuItem( 3329 B_TRANSLATE_NOCOLLECT(operatorLabels[i]), message)); 3330 } 3331 3332 // mark first items initially 3333 menu->ItemAt(0)->SetMarked(true); 3334 submenu->ItemAt(0)->SetMarked(true); 3335 3336 // add SIZE attribute 3337 submenu = new BMenu(B_TRANSLATE("Size")); 3338 submenu->SetRadioMode(true); 3339 submenu->SetFont(be_plain_font); 3340 message = new BMessage(kAttributeItemMain); 3341 message->AddString("name", "size"); 3342 message->AddInt32("type", B_OFF_T_TYPE); 3343 item = new BMenuItem(submenu, message); 3344 menu->AddItem(item); 3345 3346 message = new BMessage(kAttributeItem); 3347 message->AddInt32("operator", B_GT); 3348 submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[5]), 3349 message)); 3350 3351 message = new BMessage(kAttributeItem); 3352 message->AddInt32("operator", B_LT); 3353 submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[6]), 3354 message)); 3355 3356 message = new BMessage(kAttributeItem); 3357 message->AddInt32("operator", B_EQ); 3358 submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[1]), 3359 message)); 3360 3361 // add "modified" field 3362 submenu = new BMenu(B_TRANSLATE("Modified")); 3363 submenu->SetRadioMode(true); 3364 submenu->SetFont(be_plain_font); 3365 message = new BMessage(kAttributeItemMain); 3366 message->AddString("name", "last_modified"); 3367 message->AddInt32("type", B_TIME_TYPE); 3368 item = new BMenuItem(submenu, message); 3369 menu->AddItem(item); 3370 3371 message = new BMessage(kAttributeItem); 3372 message->AddInt32("operator", B_LT); 3373 submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[7]), 3374 message)); 3375 3376 message = new BMessage(kAttributeItem); 3377 message->AddInt32("operator", B_GT); 3378 submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[8]), 3379 message)); 3380 3381 BMenuField* menuField = new BMenuField("MenuField", "", menu); 3382 menuField->SetDivider(0.0f); 3383 fAttrGrid->AddView(menuField, 0, gridRow); 3384 3385 BStringView* stringView = new BStringView("", 3386 menu->FindMarked()->Submenu()->FindMarked()->Label()); 3387 BLayoutItem* layoutItem = fAttrGrid->AddView(stringView, 1, gridRow); 3388 layoutItem->SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT, 3389 B_ALIGN_VERTICAL_UNSET)); 3390 3391 BString title("TextEntry"); 3392 title << gridRow; 3393 BTextControl* textControl = new BTextControl(title.String(), "", "", NULL); 3394 textControl->SetDivider(0.0f); 3395 fAttrGrid->AddView(textControl, 2, gridRow); 3396 textControl->MakeFocus(); 3397 3398 // target everything 3399 menu->SetTargetForItems(this); 3400 for (int32 index = menu->CountItems() - 1; index >= 0; index--) { 3401 BMenu* submenuAtIndex = menu->SubmenuAt(index); 3402 if (submenuAtIndex != NULL) 3403 submenuAtIndex->SetTargetForItems(this); 3404 } 3405 3406 // populate mime popup 3407 AddMimeTypeAttrs(menu); 3408 } 3409 3410 3411 void 3412 FindPanel::RestoreAttrState(const BMessage& message, int32 index) 3413 { 3414 BMenuField* menuField = dynamic_cast<BMenuField*>(FindAttrView("MenuField", index)); 3415 if (menuField != NULL) { 3416 // decode menu selections 3417 BMenu* menu = menuField->Menu(); 3418 3419 ASSERT(menu != NULL); 3420 3421 AddMimeTypeAttrs(menu); 3422 const char* label; 3423 if (message.FindString("menuSelection", index, &label) == B_OK) { 3424 int32 itemIndex = SelectItemWithLabel(menu, label); 3425 if (itemIndex >= 0) { 3426 menu = menu->SubmenuAt(itemIndex); 3427 if (menu != NULL && message.FindString("subMenuSelection", 3428 index, &label) == B_OK) { 3429 SelectItemWithLabel(menu, label); 3430 } 3431 } 3432 } 3433 } 3434 3435 // decode attribute text 3436 BString textEntryString = "TextEntry"; 3437 textEntryString << index; 3438 BTextControl* textControl = dynamic_cast<BTextControl*>( 3439 FindAttrView(textEntryString.String(), index)); 3440 3441 ASSERT(textControl != NULL); 3442 3443 const char* string; 3444 if (textControl != NULL 3445 && message.FindString("attrViewText", index, &string) == B_OK) { 3446 textControl->SetText(string); 3447 } 3448 3449 int32 logicMenuSelectedIndex; 3450 if (message.FindInt32("logicalRelation", index, 3451 &logicMenuSelectedIndex) == B_OK) { 3452 BMenuField* field = dynamic_cast<BMenuField*>( 3453 FindAttrView("Logic", index)); 3454 if (field != NULL) { 3455 BMenu* fieldMenu = field->Menu(); 3456 if (fieldMenu != NULL) { 3457 BMenuItem* logicItem 3458 = fieldMenu->ItemAt(logicMenuSelectedIndex); 3459 if (logicItem != NULL) { 3460 logicItem->SetMarked(true); 3461 return; 3462 } 3463 } 3464 } 3465 3466 AddLogicMenu(index, logicMenuSelectedIndex == 0); 3467 } 3468 } 3469 3470 3471 void 3472 FindPanel::SaveAttrState(BMessage* message, int32 index) 3473 { 3474 BMenu* menu = dynamic_cast<BMenuField*>(FindAttrView("MenuField", index)) 3475 ->Menu(); 3476 3477 // encode main attribute menu selection 3478 BMenuItem* item = menu->FindMarked(); 3479 message->AddString("menuSelection", item ? item->Label() : ""); 3480 3481 // encode submenu selection 3482 const char* label = ""; 3483 if (item) { 3484 BMenu* submenu = menu->SubmenuAt(menu->IndexOf(item)); 3485 if (submenu) { 3486 item = submenu->FindMarked(); 3487 if (item) 3488 label = item->Label(); 3489 } 3490 } 3491 message->AddString("subMenuSelection", label); 3492 3493 // encode attribute text 3494 BString textEntryString = "TextEntry"; 3495 textEntryString << index; 3496 BTextControl* textControl = dynamic_cast<BTextControl*>(FindAttrView( 3497 textEntryString.String(), index)); 3498 3499 ASSERT(textControl != NULL); 3500 3501 if (textControl != NULL) 3502 message->AddString("attrViewText", textControl->Text()); 3503 3504 BMenuField* field = dynamic_cast<BMenuField*>(FindAttrView("Logic", index)); 3505 if (field != NULL) { 3506 BMenu* fieldMenu = field->Menu(); 3507 if (fieldMenu != NULL) { 3508 BMenuItem* item = fieldMenu->FindMarked(); 3509 ASSERT(item != NULL); 3510 message->AddInt32("logicalRelation", 3511 item != NULL ? field->Menu()->IndexOf(item) : 0); 3512 } 3513 } 3514 } 3515 3516 3517 void 3518 FindPanel::AddLogicMenu(int32 index, bool selectAnd) 3519 { 3520 // add "AND/OR" menu 3521 BPopUpMenu* menu = new BPopUpMenu(""); 3522 BMessage* message = new BMessage(); 3523 message->AddInt32("combine", B_AND); 3524 BMenuItem* item = new BMenuItem(B_TRANSLATE("And"), message); 3525 menu->AddItem(item); 3526 if (selectAnd) 3527 item->SetMarked(true); 3528 3529 message = new BMessage(); 3530 message->AddInt32("combine", B_OR); 3531 item = new BMenuItem(B_TRANSLATE("Or"), message); 3532 menu->AddItem(item); 3533 if (!selectAnd) 3534 item->SetMarked(true); 3535 3536 menu->SetTargetForItems(this); 3537 3538 BMenuField* menufield = new BMenuField("Logic", "", menu, B_WILL_DRAW); 3539 menufield->SetDivider(0.0f); 3540 3541 ResizeMenuField(menufield); 3542 3543 fAttrGrid->AddView(menufield, 3, index); 3544 } 3545 3546 3547 void 3548 FindPanel::RemoveLogicMenu(int32 index) 3549 { 3550 BMenuField* menufield = dynamic_cast<BMenuField*>(FindAttrView("Logic", index)); 3551 if (menufield) { 3552 menufield->RemoveSelf(); 3553 delete menufield; 3554 } 3555 } 3556 3557 3558 void 3559 FindPanel::AddAttributes(BMenu* menu, const BMimeType& mimeType) 3560 { 3561 // only add things to menu which have "user-visible" data 3562 BMessage attributeMessage; 3563 if (mimeType.GetAttrInfo(&attributeMessage) != B_OK) 3564 return; 3565 3566 char desc[B_MIME_TYPE_LENGTH]; 3567 mimeType.GetShortDescription(desc); 3568 3569 // go through each field in meta mime and add it to a menu 3570 for (int32 index = 0; ; index++) { 3571 const char* publicName; 3572 if (attributeMessage.FindString("attr:public_name", index, 3573 &publicName) != B_OK) { 3574 break; 3575 } 3576 3577 if (!attributeMessage.FindBool("attr:viewable")) 3578 continue; 3579 3580 const char* attributeName; 3581 if (attributeMessage.FindString("attr:name", index, &attributeName) 3582 != B_OK) { 3583 continue; 3584 } 3585 3586 int32 type; 3587 if (attributeMessage.FindInt32("attr:type", index, &type) != B_OK) 3588 continue; 3589 3590 BMenu* submenu = new BMenu(publicName); 3591 submenu->SetRadioMode(true); 3592 submenu->SetFont(be_plain_font); 3593 BMessage* message = new BMessage(kAttributeItemMain); 3594 message->AddString("name", attributeName); 3595 message->AddInt32("type", type); 3596 BMenuItem* item = new BMenuItem(submenu, message); 3597 menu->AddItem(item); 3598 menu->SetTargetForItems(this); 3599 3600 switch (type) { 3601 case B_STRING_TYPE: 3602 message = new BMessage(kAttributeItem); 3603 message->AddInt32("operator", B_CONTAINS); 3604 submenu->AddItem(new BMenuItem(operatorLabels[0], message)); 3605 3606 message = new BMessage(kAttributeItem); 3607 message->AddInt32("operator", B_EQ); 3608 submenu->AddItem(new BMenuItem(operatorLabels[1], message)); 3609 3610 message = new BMessage(kAttributeItem); 3611 message->AddInt32("operator", B_NE); 3612 submenu->AddItem(new BMenuItem(operatorLabels[2], message)); 3613 submenu->SetTargetForItems(this); 3614 3615 message = new BMessage(kAttributeItem); 3616 message->AddInt32("operator", B_BEGINS_WITH); 3617 submenu->AddItem(new BMenuItem(operatorLabels[3], message)); 3618 submenu->SetTargetForItems(this); 3619 3620 message = new BMessage(kAttributeItem); 3621 message->AddInt32("operator", B_ENDS_WITH); 3622 submenu->AddItem(new BMenuItem(operatorLabels[4], message)); 3623 break; 3624 3625 case B_BOOL_TYPE: 3626 case B_INT16_TYPE: 3627 case B_UINT8_TYPE: 3628 case B_INT8_TYPE: 3629 case B_UINT16_TYPE: 3630 case B_INT32_TYPE: 3631 case B_UINT32_TYPE: 3632 case B_INT64_TYPE: 3633 case B_UINT64_TYPE: 3634 case B_OFF_T_TYPE: 3635 case B_FLOAT_TYPE: 3636 case B_DOUBLE_TYPE: 3637 message = new BMessage(kAttributeItem); 3638 message->AddInt32("operator", B_EQ); 3639 submenu->AddItem(new BMenuItem(operatorLabels[1], message)); 3640 3641 message = new BMessage(kAttributeItem); 3642 message->AddInt32("operator", B_GT); 3643 submenu->AddItem(new BMenuItem(operatorLabels[5], message)); 3644 3645 message = new BMessage(kAttributeItem); 3646 message->AddInt32("operator", B_LT); 3647 submenu->AddItem(new BMenuItem(operatorLabels[6], message)); 3648 break; 3649 3650 case B_TIME_TYPE: 3651 message = new BMessage(kAttributeItem); 3652 message->AddInt32("operator", B_LT); 3653 submenu->AddItem(new BMenuItem(operatorLabels[7], message)); 3654 3655 message = new BMessage(kAttributeItem); 3656 message->AddInt32("operator", B_GT); 3657 submenu->AddItem(new BMenuItem(operatorLabels[8], message)); 3658 break; 3659 } 3660 submenu->SetTargetForItems(this); 3661 } 3662 } 3663 3664 3665 void 3666 FindPanel::AddMimeTypeAttrs(BMenu* menu) 3667 { 3668 const char* typeName; 3669 if (CurrentMimeType(&typeName) == NULL) 3670 return; 3671 3672 BMimeType mimeType(typeName); 3673 if (!mimeType.IsInstalled()) 3674 return; 3675 3676 if (!mimeType.IsSupertypeOnly()) { 3677 // add supertype attributes 3678 BMimeType supertype; 3679 mimeType.GetSupertype(&supertype); 3680 AddAttributes(menu, supertype); 3681 } 3682 3683 AddAttributes(menu, mimeType); 3684 } 3685 3686 3687 void 3688 FindPanel::GetDefaultAttrName(BString& attrName, int32 row) const 3689 { 3690 BMenuItem* item = NULL; 3691 BMenuField* menuField = dynamic_cast<BMenuField*>(fAttrGrid->ItemAt(0, row)->View()); 3692 if (menuField != NULL && menuField->Menu() != NULL) 3693 item = menuField->Menu()->FindMarked(); 3694 3695 if (item != NULL) 3696 attrName << item->Label(); 3697 else 3698 attrName << B_TRANSLATE("Name"); 3699 3700 if (item != NULL && item->Submenu() != NULL) 3701 item = item->Submenu()->FindMarked(); 3702 else 3703 item = NULL; 3704 3705 if (item != NULL) 3706 attrName << " " << item->Label() << " "; 3707 else 3708 attrName << " = "; 3709 3710 BTextControl* textControl 3711 = dynamic_cast<BTextControl*>(fAttrGrid->ItemAt(2, row)->View()); 3712 if (textControl != NULL) 3713 attrName << textControl->Text(); 3714 } 3715 3716 3717 // #pragma mark - 3718 3719 3720 DeleteTransientQueriesTask::DeleteTransientQueriesTask() 3721 : 3722 state(kInitial), 3723 fWalker(NULL) 3724 { 3725 } 3726 3727 3728 DeleteTransientQueriesTask::~DeleteTransientQueriesTask() 3729 { 3730 delete fWalker; 3731 } 3732 3733 3734 bool 3735 DeleteTransientQueriesTask::DoSomeWork() 3736 { 3737 switch (state) { 3738 case kInitial: 3739 Initialize(); 3740 break; 3741 3742 case kAllocatedWalker: 3743 case kTraversing: 3744 if (GetSome()) { 3745 PRINT(("transient query killer done\n")); 3746 return true; 3747 } 3748 break; 3749 3750 case kError: 3751 return true; 3752 3753 } 3754 return false; 3755 } 3756 3757 3758 void 3759 DeleteTransientQueriesTask::Initialize() 3760 { 3761 PRINT(("starting up transient query killer\n")); 3762 BPath path; 3763 status_t result = find_directory(B_USER_DIRECTORY, &path, false); 3764 if (result != B_OK) { 3765 state = kError; 3766 return; 3767 } 3768 fWalker = new BTrackerPrivate::TNodeWalker(path.Path()); 3769 state = kAllocatedWalker; 3770 } 3771 3772 3773 const int32 kBatchCount = 100; 3774 3775 bool 3776 DeleteTransientQueriesTask::GetSome() 3777 { 3778 state = kTraversing; 3779 for (int32 count = kBatchCount; count > 0; count--) { 3780 entry_ref ref; 3781 if (fWalker->GetNextRef(&ref) != B_OK) { 3782 state = kError; 3783 return true; 3784 } 3785 Model model(&ref); 3786 if (model.IsQuery()) 3787 ProcessOneRef(&model); 3788 #if xDEBUG 3789 else 3790 PRINT(("transient query killer: %s not a query\n", model.Name())); 3791 #endif 3792 } 3793 return false; 3794 } 3795 3796 3797 const int32 kDaysToExpire = 7; 3798 3799 static bool 3800 QueryOldEnough(Model* model) 3801 { 3802 // check if it is old and ready to be deleted 3803 time_t now = time(0); 3804 3805 tm nowTimeData; 3806 tm fileModData; 3807 3808 localtime_r(&now, &nowTimeData); 3809 localtime_r(&model->StatBuf()->st_ctime, &fileModData); 3810 3811 if ((nowTimeData.tm_mday - fileModData.tm_mday) < kDaysToExpire 3812 && (nowTimeData.tm_mday - fileModData.tm_mday) > -kDaysToExpire) { 3813 PRINT(("query %s, not old enough\n", model->Name())); 3814 return false; 3815 } 3816 return true; 3817 } 3818 3819 3820 bool 3821 DeleteTransientQueriesTask::ProcessOneRef(Model* model) 3822 { 3823 BModelOpener opener(model); 3824 3825 // is this a temporary query 3826 if (!MoreOptionsStruct::QueryTemporary(model->Node())) { 3827 PRINT(("query %s, not temporary\n", model->Name())); 3828 return false; 3829 } 3830 3831 if (!QueryOldEnough(model)) 3832 return false; 3833 3834 TTracker* tracker = dynamic_cast<TTracker*>(be_app); 3835 ASSERT(tracker != NULL); 3836 3837 // check that it is not showing 3838 if (tracker != NULL && tracker->EntryHasWindowOpen(model->EntryRef())) { 3839 PRINT(("query %s, showing, can't delete\n", model->Name())); 3840 return false; 3841 } 3842 3843 PRINT(("query %s, old, temporary, not shownig - deleting\n", 3844 model->Name())); 3845 3846 BEntry entry(model->EntryRef()); 3847 entry.Remove(); 3848 3849 return true; 3850 } 3851 3852 3853 class DeleteTransientQueriesFunctor : public FunctionObjectWithResult<bool> { 3854 public: 3855 DeleteTransientQueriesFunctor(DeleteTransientQueriesTask* task) 3856 : 3857 task(task) 3858 {} 3859 3860 virtual ~DeleteTransientQueriesFunctor() { delete task; } 3861 3862 virtual void operator()() { result = task->DoSomeWork(); } 3863 3864 private: 3865 DeleteTransientQueriesTask* task; 3866 }; 3867 3868 3869 void 3870 DeleteTransientQueriesTask::StartUpTransientQueryCleaner() 3871 { 3872 TTracker* tracker = dynamic_cast<TTracker*>(be_app); 3873 ASSERT(tracker != NULL); 3874 3875 if (tracker == NULL) 3876 return; 3877 // set up a task that wakes up when the machine is idle and starts 3878 // killing off old transient queries 3879 DeleteTransientQueriesFunctor* worker 3880 = new DeleteTransientQueriesFunctor(new DeleteTransientQueriesTask()); 3881 3882 tracker->MainTaskLoop()->RunWhenIdle(worker, 3883 30 * 60 * 1000000, // half an hour initial delay 3884 5 * 60 * 1000000, // idle for five minutes 3885 10 * 1000000); 3886 } 3887 3888 3889 // #pragma mark - 3890 3891 3892 RecentFindItemsMenu::RecentFindItemsMenu(const char* title, 3893 const BMessenger* target, uint32 what) 3894 : 3895 BMenu(title, B_ITEMS_IN_COLUMN), 3896 fTarget(*target), 3897 fWhat(what) 3898 { 3899 } 3900 3901 3902 void 3903 RecentFindItemsMenu::AttachedToWindow() 3904 { 3905 // re-populate the menu with fresh items 3906 for (int32 index = CountItems() - 1; index >= 0; index--) 3907 delete RemoveItem(index); 3908 3909 FindPanel::AddRecentQueries(this, false, &fTarget, fWhat); 3910 BMenu::AttachedToWindow(); 3911 } 3912 3913 3914 #if !B_BEOS_VERSION_DANO 3915 _IMPEXP_TRACKER 3916 #endif 3917 BMenu* 3918 TrackerBuildRecentFindItemsMenu(const char* title) 3919 { 3920 BMessenger trackerMessenger(kTrackerSignature); 3921 return new RecentFindItemsMenu(title, &trackerMessenger, B_REFS_RECEIVED); 3922 } 3923 3924 3925 // #pragma mark - 3926 3927 void 3928 DraggableQueryIcon::Draw(BRect updateRect) 3929 { 3930 BRect rect(Bounds()); 3931 rgb_color base = ui_color(B_MENU_BACKGROUND_COLOR); 3932 be_control_look->DrawBorder(this, rect, updateRect, base, B_PLAIN_BORDER, 0, 3933 BControlLook::B_BOTTOM_BORDER); 3934 be_control_look->DrawMenuBarBackground(this, rect, updateRect, base, 0, 3935 BControlLook::B_ALL_BORDERS & ~BControlLook::B_LEFT_BORDER); 3936 DraggableIcon::Draw(updateRect); 3937 } 3938 3939 3940 DraggableQueryIcon::DraggableQueryIcon(BRect frame, const char* name, 3941 const BMessage* message, BMessenger messenger, uint32 resizeFlags, 3942 uint32 flags) 3943 : 3944 DraggableIcon(frame, name, B_QUERY_MIMETYPE, B_LARGE_ICON, 3945 message, messenger, resizeFlags, flags) 3946 { 3947 } 3948 3949 3950 bool 3951 DraggableQueryIcon::DragStarted(BMessage* dragMessage) 3952 { 3953 // override to substitute the user-specified query name 3954 dragMessage->RemoveData("be:clip_name"); 3955 3956 FindWindow* window = dynamic_cast<FindWindow*>(Window()); 3957 3958 ASSERT(window != NULL); 3959 3960 return window != NULL && dragMessage->AddString("be:clip_name", 3961 window->BackgroundView()->UserSpecifiedName() != NULL 3962 ? window->BackgroundView()->UserSpecifiedName() 3963 : B_TRANSLATE("New Query")) == B_OK; 3964 } 3965 3966 3967 // #pragma mark - 3968 3969 3970 MostUsedNames::MostUsedNames(const char* fileName, const char* directory, 3971 int32 maxCount) 3972 : 3973 fFileName(fileName), 3974 fDirectory(directory), 3975 fLoaded(false), 3976 fCount(maxCount) 3977 { 3978 } 3979 3980 3981 MostUsedNames::~MostUsedNames() 3982 { 3983 // only write back settings when we've been used 3984 if (!fLoaded) 3985 return; 3986 3987 // write most used list to file 3988 3989 BPath path; 3990 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK 3991 || path.Append(fDirectory) != B_OK || path.Append(fFileName) != B_OK) { 3992 return; 3993 } 3994 3995 BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); 3996 if (file.InitCheck() == B_OK) { 3997 for (int32 i = 0; i < fList.CountItems(); i++) { 3998 list_entry* entry = static_cast<list_entry*>(fList.ItemAt(i)); 3999 4000 char line[B_FILE_NAME_LENGTH + 5]; 4001 4002 // limit upper bound to react more dynamically to changes 4003 if (--entry->count > 20) 4004 entry->count = 20; 4005 4006 // if the item hasn't been chosen in a while, remove it 4007 // (but leave at least one item in the list) 4008 if (entry->count < -10 && i > 0) 4009 continue; 4010 4011 sprintf(line, "%" B_PRId32 " %s\n", entry->count, entry->name); 4012 if (file.Write(line, strlen(line)) < B_OK) 4013 break; 4014 } 4015 } 4016 file.Unset(); 4017 4018 // free data 4019 4020 for (int32 i = fList.CountItems(); i-- > 0;) { 4021 list_entry* entry = static_cast<list_entry*>(fList.ItemAt(i)); 4022 free(entry->name); 4023 delete entry; 4024 } 4025 } 4026 4027 4028 bool 4029 MostUsedNames::ObtainList(BList* list) 4030 { 4031 if (list == NULL) 4032 return false; 4033 4034 if (!fLoaded) 4035 UpdateList(); 4036 4037 fLock.Lock(); 4038 4039 list->MakeEmpty(); 4040 for (int32 i = 0; i < fCount; i++) { 4041 list_entry* entry = static_cast<list_entry*>(fList.ItemAt(i)); 4042 if (entry == NULL) 4043 return true; 4044 4045 list->AddItem(entry->name); 4046 } 4047 return true; 4048 } 4049 4050 4051 void 4052 MostUsedNames::ReleaseList() 4053 { 4054 fLock.Unlock(); 4055 } 4056 4057 4058 void 4059 MostUsedNames::AddName(const char* name) 4060 { 4061 fLock.Lock(); 4062 4063 if (!fLoaded) 4064 LoadList(); 4065 4066 // remove last entry if there are more than 4067 // 2*fCount entries in the list 4068 4069 list_entry* entry = NULL; 4070 4071 if (fList.CountItems() > fCount * 2) { 4072 entry = static_cast<list_entry*>( 4073 fList.RemoveItem(fList.CountItems() - 1)); 4074 4075 // is this the name we want to add here? 4076 if (strcmp(name, entry->name)) { 4077 free(entry->name); 4078 delete entry; 4079 entry = NULL; 4080 } else 4081 fList.AddItem(entry); 4082 } 4083 4084 if (entry == NULL) { 4085 for (int32 i = 0; 4086 (entry = static_cast<list_entry*>(fList.ItemAt(i))) != NULL; i++) { 4087 if (strcmp(entry->name, name) == 0) 4088 break; 4089 } 4090 } 4091 4092 if (entry == NULL) { 4093 entry = new list_entry; 4094 entry->name = strdup(name); 4095 entry->count = 1; 4096 4097 fList.AddItem(entry); 4098 } else if (entry->count < 0) 4099 entry->count = 1; 4100 else 4101 entry->count++; 4102 4103 fLock.Unlock(); 4104 UpdateList(); 4105 } 4106 4107 4108 int 4109 MostUsedNames::CompareNames(const void* a,const void* b) 4110 { 4111 list_entry* entryA = *(list_entry**)a; 4112 list_entry* entryB = *(list_entry**)b; 4113 4114 if (entryA->count == entryB->count) 4115 return strcasecmp(entryA->name,entryB->name); 4116 4117 return entryB->count - entryA->count; 4118 } 4119 4120 4121 void 4122 MostUsedNames::LoadList() 4123 { 4124 if (fLoaded) 4125 return; 4126 fLoaded = true; 4127 4128 // load the most used names list 4129 4130 BPath path; 4131 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK 4132 || path.Append(fDirectory) != B_OK || path.Append(fFileName) != B_OK) { 4133 return; 4134 } 4135 4136 FILE* file = fopen(path.Path(), "r"); 4137 if (file == NULL) 4138 return; 4139 4140 char line[B_FILE_NAME_LENGTH + 5]; 4141 while (fgets(line, sizeof(line), file) != NULL) { 4142 int32 length = (int32)strlen(line) - 1; 4143 if (length >= 0 && line[length] == '\n') 4144 line[length] = '\0'; 4145 4146 int32 count = atoi(line); 4147 4148 char* name = strchr(line, ' '); 4149 if (name == NULL || *(++name) == '\0') 4150 continue; 4151 4152 list_entry* entry = new list_entry; 4153 entry->name = strdup(name); 4154 entry->count = count; 4155 4156 fList.AddItem(entry); 4157 } 4158 fclose(file); 4159 } 4160 4161 4162 void 4163 MostUsedNames::UpdateList() 4164 { 4165 AutoLock<Benaphore> locker(fLock); 4166 4167 if (!fLoaded) 4168 LoadList(); 4169 4170 // sort list items 4171 4172 fList.SortItems(MostUsedNames::CompareNames); 4173 } 4174 4175 } // namespace BPrivate 4176