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