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