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