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