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 ResizeMenuField(fMimeTypeField); 866 ResizeMenuField(searchModeField); 867 ResizeMenuField(volumeField); 868 } 869 870 871 FindPanel::~FindPanel() 872 { 873 } 874 875 876 void 877 FindPanel::AttachedToWindow() 878 { 879 BNode* node = dynamic_cast<FindWindow*>(Window())->QueryNode(); 880 fSearchModeMenu->SetTargetForItems(this); 881 fQueryName->SetTarget(this); 882 fLatch->SetTarget(this); 883 RestoreMimeTypeMenuSelection(node); 884 // preselect the mime we used the last time have to do it here 885 // because AddByAttributeItems will build different menus based 886 // on which mime type is preselected 887 RestoreWindowState(node); 888 889 if (!Window()->CurrentFocus()) { 890 // try to pick a good focus if we restore to one already 891 BTextControl* textControl 892 = dynamic_cast<BTextControl*>(FindView("TextControl")); 893 if (textControl == NULL) { 894 // pick the last text control in the attribute view 895 BString title("TextEntry"); 896 title << (fAttrGrid->CountRows() - 1); 897 textControl 898 = dynamic_cast<BTextControl*>(FindView(title.String())); 899 } 900 if (textControl) 901 textControl->MakeFocus(); 902 } 903 904 BButton* button = dynamic_cast<BButton*>(FindView("remove button")); 905 if (button) 906 button->SetTarget(this); 907 908 button = dynamic_cast<BButton*>(FindView("add button")); 909 if (button) 910 button->SetTarget(this); 911 912 fVolMenu->SetTargetForItems(this); 913 914 // set target for MIME type items 915 for (int32 index = MimeTypeMenu()->CountItems();index-- > 2;) { 916 BMenu* submenu = MimeTypeMenu()->ItemAt(index)->Submenu(); 917 if (submenu != NULL) 918 submenu->SetTargetForItems(this); 919 } 920 fMimeTypeMenu->SetTargetForItems(this); 921 922 if (fDraggableIcon) 923 fDraggableIcon->SetTarget(BMessenger(this)); 924 925 fRecentQueries->SetTargetForItems(Window()); 926 } 927 928 929 void 930 FindPanel::ResizeMenuField(BMenuField* menuField) 931 { 932 BSize size; 933 menuField->GetPreferredSize(&size.width, &size.height); 934 935 BMenu* menu = menuField->Menu(); 936 937 float padding = 0.0f; 938 float width = 0.0f; 939 940 BMenuItem* markedItem = menu->FindMarked(); 941 if (markedItem != NULL) { 942 if (markedItem->Submenu() != NULL) { 943 BMenuItem* markedSubItem = markedItem->Submenu()->FindMarked(); 944 if (markedSubItem != NULL && markedSubItem->Label() != NULL) { 945 float labelWidth 946 = menuField->StringWidth(markedSubItem->Label()); 947 padding = size.width - labelWidth; 948 } 949 } else if (markedItem->Label() != NULL) { 950 float labelWidth = menuField->StringWidth(markedItem->Label()); 951 padding = size.width - labelWidth; 952 } 953 } 954 955 for (int32 index = menu->CountItems(); index-- > 0; ) { 956 BMenuItem* item = menu->ItemAt(index); 957 if (item->Label() != NULL) 958 width = std::max(width, StringWidth(item->Label())); 959 960 BMenu* submenu = item->Submenu(); 961 if (submenu != NULL) { 962 for (int32 subIndex = submenu->CountItems(); subIndex-- > 0; ) { 963 BMenuItem* subItem = submenu->ItemAt(subIndex); 964 if (subItem->Label() == NULL) 965 continue; 966 967 width = std::max(width, 968 menuField->StringWidth(subItem->Label())); 969 } 970 } 971 } 972 973 float maxWidth = be_control_look->DefaultItemSpacing() * 20; 974 size.width = std::min(width + padding, maxWidth); 975 menuField->SetExplicitSize(size); 976 } 977 978 static void 979 PopUpMenuSetTitle(BMenu* menu, const char* title) 980 { 981 // This should really be in BMenuField 982 BMenu* bar = menu->Supermenu(); 983 984 ASSERT(bar); 985 ASSERT(bar->ItemAt(0)); 986 if (bar == NULL || !bar->ItemAt(0)) 987 return; 988 989 bar->ItemAt(0)->SetLabel(title); 990 } 991 992 993 void 994 FindPanel::ShowVolumeMenuLabel() 995 { 996 if (fVolMenu->ItemAt(0)->IsMarked()) { 997 // "all disks" selected 998 PopUpMenuSetTitle(fVolMenu, fVolMenu->ItemAt(0)->Label()); 999 return; 1000 } 1001 1002 // find out if more than one items are marked 1003 int32 count = fVolMenu->CountItems(); 1004 int32 countSelected = 0; 1005 BMenuItem* tmpItem = NULL; 1006 for (int32 index = 2; index < count; index++) { 1007 BMenuItem* item = fVolMenu->ItemAt(index); 1008 if (item->IsMarked()) { 1009 countSelected++; 1010 tmpItem = item; 1011 } 1012 } 1013 1014 if (countSelected == 0) { 1015 // no disk selected, for now revert to search all disks 1016 // ToDo: 1017 // show no disks here and add a check that will not let the 1018 // query go if the user doesn't pick at least one 1019 fVolMenu->ItemAt(0)->SetMarked(true); 1020 PopUpMenuSetTitle(fVolMenu, fVolMenu->ItemAt(0)->Label()); 1021 } else if (countSelected > 1) 1022 // if more than two disks selected, don't use the disk name 1023 // as a label 1024 PopUpMenuSetTitle(fVolMenu, B_TRANSLATE("multiple disks")); 1025 else { 1026 ASSERT(tmpItem); 1027 PopUpMenuSetTitle(fVolMenu, tmpItem->Label()); 1028 } 1029 } 1030 1031 1032 void 1033 FindPanel::Draw(BRect) 1034 { 1035 if (fAttrGrid == NULL) 1036 return; 1037 1038 for (int32 index = 0; index < fAttrGrid->CountRows(); index++) { 1039 BMenuField* menuField 1040 = dynamic_cast<BMenuField*>(FindAttrView("MenuField", index)); 1041 if (menuField == NULL) 1042 continue; 1043 1044 BLayoutItem* stringView = fAttrGrid->ItemAt(1, index); 1045 BMenuItem* item = menuField->Menu()->FindMarked(); 1046 if (item == NULL || item->Submenu() == NULL 1047 || item->Submenu()->FindMarked() == NULL) { 1048 continue; 1049 } 1050 1051 if (stringView == NULL) { 1052 stringView = fAttrGrid->AddView(new BStringView("", 1053 item->Submenu()->FindMarked()->Label()), 1, index); 1054 stringView->SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT, 1055 B_ALIGN_VERTICAL_UNSET)); 1056 } else { 1057 dynamic_cast<BStringView*>(stringView->View())->SetText( 1058 item->Submenu()->FindMarked()->Label()); 1059 } 1060 } 1061 } 1062 1063 1064 void 1065 FindPanel::MessageReceived(BMessage* message) 1066 { 1067 entry_ref dir; 1068 const char* name; 1069 BMenuItem* item; 1070 1071 switch (message->what) { 1072 case kVolumeItem: 1073 { 1074 // volume changed 1075 BMenuItem* invokedItem; 1076 dev_t dev; 1077 if (message->FindPointer("source", (void**)&invokedItem) != B_OK) 1078 return; 1079 1080 if (message->FindInt32("device", &dev) != B_OK) 1081 break; 1082 1083 BMenu* menu = invokedItem->Menu(); 1084 ASSERT(menu); 1085 1086 if (dev == -1) { 1087 // all disks selected, uncheck everything else 1088 int32 count = menu->CountItems(); 1089 for (int32 index = 2; index < count; index++) 1090 menu->ItemAt(index)->SetMarked(false); 1091 1092 // make all disks the title and check it 1093 PopUpMenuSetTitle(menu, menu->ItemAt(0)->Label()); 1094 menu->ItemAt(0)->SetMarked(true); 1095 } else { 1096 // a specific volume selected, unmark "all disks" 1097 menu->ItemAt(0)->SetMarked(false); 1098 1099 // toggle mark on invoked item 1100 int32 count = menu->CountItems(); 1101 for (int32 index = 2; index < count; index++) { 1102 BMenuItem* item = menu->ItemAt(index); 1103 1104 if (invokedItem == item) { 1105 // we just selected this 1106 bool wasMarked = item->IsMarked(); 1107 item->SetMarked(!wasMarked); 1108 } 1109 } 1110 } 1111 // make sure the right label is showing 1112 ShowVolumeMenuLabel(); 1113 1114 break; 1115 } 1116 1117 case kByAttributeItem: 1118 case kByNameItem: 1119 case kByFormulaItem: 1120 SwitchMode(message->what); 1121 break; 1122 1123 case kAddItem: 1124 AddAttrRow(); 1125 break; 1126 1127 case kRemoveItem: 1128 RemoveAttrRow(); 1129 break; 1130 1131 case kMIMETypeItem: 1132 { 1133 if (fMode == kByAttributeItem) { 1134 // the attributes for this type may be different 1135 RemoveAttrViewItems(false); 1136 AddAttrRow(); 1137 } 1138 1139 BMenuItem* item; 1140 if (message->FindPointer("source", (void**)&item) == B_OK) { 1141 // don't add the "All files and folders" to the list 1142 if (fMimeTypeMenu->IndexOf(item) != 0) 1143 gMostUsedMimeTypes.AddName(item->Label()); 1144 1145 SetCurrentMimeType(item); 1146 } 1147 1148 break; 1149 } 1150 1151 case kNameModifiedMessage: 1152 // the query name was edited, make the query permanent 1153 fTemporaryCheck->SetValue(0); 1154 break; 1155 1156 case kAttributeItem: 1157 if (message->FindPointer("source", (void**)&item) != B_OK) 1158 return; 1159 1160 item->Menu()->Superitem()->SetMarked(true); 1161 Invalidate(); 1162 break; 1163 1164 case kAttributeItemMain: 1165 // in case someone selected just an attribute without the 1166 // comparator 1167 if (message->FindPointer("source", (void**)&item) != B_OK) 1168 return; 1169 1170 if (item->Submenu()->ItemAt(0) != NULL) 1171 item->Submenu()->ItemAt(0)->SetMarked(true); 1172 1173 Invalidate(); 1174 break; 1175 1176 case kLatchChanged: 1177 { 1178 int32 value; 1179 if (message->FindInt32("be:value", &value) != B_OK) 1180 break; 1181 1182 if (value == 0 && !fMoreOptions->IsHidden(this)) 1183 fMoreOptions->Hide(); 1184 else if (value == 1 && fMoreOptions->IsHidden(this)) 1185 fMoreOptions->Show(); 1186 1187 break; 1188 } 1189 1190 case B_SAVE_REQUESTED: 1191 { 1192 // finish saving query template from a SaveAs panel 1193 entry_ref ref; 1194 status_t error = message->FindRef("refs", &ref); 1195 1196 if (error == B_OK) { 1197 // direct entry selected, convert to parent dir and name 1198 BEntry entry(&ref); 1199 error = entry.GetParent(&entry); 1200 if (error == B_OK) { 1201 entry.GetRef(&dir); 1202 name = ref.name; 1203 } 1204 } else { 1205 // parent dir and name selected 1206 error = message->FindRef("directory", &dir); 1207 if (error == B_OK) 1208 error = message->FindString("name", &name); 1209 } 1210 1211 if (error == B_OK) 1212 SaveAsQueryOrTemplate(&dir, name, true); 1213 1214 break; 1215 } 1216 1217 case B_COPY_TARGET: 1218 { 1219 // finish drag&drop 1220 const char* str; 1221 const char* mimeType = NULL; 1222 const char* actionSpecifier = NULL; 1223 1224 if (message->FindString("be:types", &str) == B_OK 1225 && strcasecmp(str, B_FILE_MIME_TYPE) == 0 1226 && (message->FindString("be:actionspecifier", 1227 &actionSpecifier) == B_OK 1228 || message->FindString("be:filetypes", &mimeType) == B_OK) 1229 && message->FindString("name", &name) == B_OK 1230 && message->FindRef("directory", &dir) == B_OK) { 1231 1232 bool query = false; 1233 bool queryTemplate = false; 1234 1235 if (actionSpecifier 1236 && strcasecmp(actionSpecifier, 1237 B_TRANSLATE_NOCOLLECT( 1238 kDragNDropActionSpecifiers[0])) == 0) { 1239 query = true; 1240 } else if (actionSpecifier 1241 && strcasecmp(actionSpecifier, 1242 B_TRANSLATE_NOCOLLECT( 1243 kDragNDropActionSpecifiers[1])) == 0) { 1244 queryTemplate = true; 1245 } else if (mimeType && strcasecmp(mimeType, 1246 kDragNDropTypes[0]) == 0) { 1247 query = true; 1248 } else if (mimeType && strcasecmp(mimeType, 1249 kDragNDropTypes[1]) == 0) { 1250 queryTemplate = true; 1251 } 1252 1253 if (query || queryTemplate) 1254 SaveAsQueryOrTemplate(&dir, name, queryTemplate); 1255 } 1256 1257 break; 1258 } 1259 1260 default: 1261 _inherited::MessageReceived(message); 1262 break; 1263 } 1264 } 1265 1266 1267 void 1268 FindPanel::SaveAsQueryOrTemplate(const entry_ref* dir, const char* name, 1269 bool queryTemplate) 1270 { 1271 BDirectory directory(dir); 1272 BFile file(&directory, name, O_RDWR | O_CREAT | O_TRUNC); 1273 BNodeInfo(&file).SetType(queryTemplate 1274 ? B_QUERY_TEMPLATE_MIMETYPE : B_QUERY_MIMETYPE); 1275 1276 BMessage attach(kAttachFile); 1277 attach.AddRef("directory", dir); 1278 attach.AddString("name", name); 1279 attach.AddBool("template", queryTemplate); 1280 Window()->PostMessage(&attach, 0); 1281 } 1282 1283 1284 BView* 1285 FindPanel::FindAttrView(const char* name, int row) const 1286 { 1287 for (int32 index = 0; index < fAttrGrid->CountColumns(); index++) { 1288 1289 BLayoutItem* item = fAttrGrid->ItemAt(index, row); 1290 if (item == NULL) 1291 continue; 1292 1293 BView* view = item->View(); 1294 if (view == NULL) 1295 continue; 1296 1297 view = view->FindView(name); 1298 if (view != NULL) 1299 return view; 1300 1301 } 1302 1303 return NULL; 1304 } 1305 1306 void 1307 FindPanel::BuildAttrQuery(BQuery* query, bool &dynamicDate) const 1308 { 1309 dynamicDate = false; 1310 1311 // go through each attrview and add the attr and comparison info 1312 for (int32 index = 0; index < fAttrGrid->CountRows(); index++) { 1313 1314 BString title; 1315 title << "TextEntry" << index; 1316 1317 BTextControl* textControl = dynamic_cast<BTextControl*> 1318 (FindAttrView(title, index)); 1319 if (textControl == NULL) 1320 return; 1321 1322 BMenuField* menuField 1323 = dynamic_cast<BMenuField*>(FindAttrView("MenuField", index)); 1324 if (menuField == NULL) 1325 return; 1326 1327 BMenuItem* item = menuField->Menu()->FindMarked(); 1328 if (item == NULL) 1329 continue; 1330 1331 BMessage* message = item->Message(); 1332 int32 type; 1333 if (message->FindInt32("type", &type) == B_OK) { 1334 1335 const char* str; 1336 if (message->FindString("name", &str) == B_OK) 1337 query->PushAttr(str); 1338 else 1339 query->PushAttr(item->Label()); 1340 1341 switch (type) { 1342 case B_STRING_TYPE: 1343 query->PushString(textControl->TextView()->Text(), true); 1344 break; 1345 1346 case B_TIME_TYPE: 1347 { 1348 int flags = 0; 1349 DEBUG_ONLY(time_t result =) 1350 parsedate_etc(textControl->TextView()->Text(), -1, 1351 &flags); 1352 dynamicDate = (flags & PARSEDATE_RELATIVE_TIME) != 0; 1353 PRINT(("parsedate_etc - date is %srelative, %" 1354 B_PRIdTIME "\n", 1355 dynamicDate ? "" : "not ", result)); 1356 1357 query->PushDate(textControl->TextView()->Text()); 1358 break; 1359 } 1360 1361 case B_BOOL_TYPE: 1362 { 1363 uint32 value; 1364 if (strcasecmp(textControl->TextView()->Text(), 1365 "true") == 0) { 1366 value = 1; 1367 } else if (strcasecmp(textControl->TextView()->Text(), 1368 "true") == 0) { 1369 value = 1; 1370 } else 1371 value = (uint32)atoi(textControl->TextView()->Text()); 1372 1373 value %= 2; 1374 query->PushUInt32(value); 1375 break; 1376 } 1377 1378 case B_UINT8_TYPE: 1379 case B_UINT16_TYPE: 1380 case B_UINT32_TYPE: 1381 query->PushUInt32((uint32)StringToScalar( 1382 textControl->TextView()->Text())); 1383 break; 1384 1385 case B_INT8_TYPE: 1386 case B_INT16_TYPE: 1387 case B_INT32_TYPE: 1388 query->PushInt32((int32)StringToScalar( 1389 textControl->TextView()->Text())); 1390 break; 1391 1392 case B_UINT64_TYPE: 1393 query->PushUInt64((uint64)StringToScalar( 1394 textControl->TextView()->Text())); 1395 break; 1396 1397 case B_OFF_T_TYPE: 1398 case B_INT64_TYPE: 1399 query->PushInt64(StringToScalar( 1400 textControl->TextView()->Text())); 1401 break; 1402 1403 case B_FLOAT_TYPE: 1404 { 1405 float floatVal; 1406 sscanf(textControl->TextView()->Text(), "%f", 1407 &floatVal); 1408 query->PushFloat(floatVal); 1409 break; 1410 } 1411 1412 case B_DOUBLE_TYPE: 1413 { 1414 double doubleVal; 1415 sscanf(textControl->TextView()->Text(), "%lf", 1416 &doubleVal); 1417 query->PushDouble(doubleVal); 1418 break; 1419 } 1420 } 1421 } 1422 1423 query_op theOperator; 1424 BMenuItem* operatorItem = item->Submenu()->FindMarked(); 1425 if (operatorItem && operatorItem->Message() != NULL) { 1426 operatorItem->Message()->FindInt32("operator", 1427 (int32*)&theOperator); 1428 query->PushOp(theOperator); 1429 } else 1430 query->PushOp(B_EQ); 1431 1432 // add logic based on selection in Logic menufield 1433 if (index > 0) { 1434 menuField 1435 = dynamic_cast<BMenuField*>(FindAttrView("Logic", index - 1)); 1436 if (menuField) { 1437 item = menuField->Menu()->FindMarked(); 1438 if (item) { 1439 message = item->Message(); 1440 message->FindInt32("combine", (int32*)&theOperator); 1441 query->PushOp(theOperator); 1442 } 1443 } else 1444 query->PushOp(B_AND); 1445 } 1446 } 1447 } 1448 1449 1450 void 1451 FindPanel::PushMimeType(BQuery* query) const 1452 { 1453 const char* type; 1454 if (CurrentMimeType(&type) == NULL) 1455 return; 1456 1457 if (strcmp(kAllMimeTypes, type)) { 1458 // add an asterisk if we are searching for a supertype 1459 char buffer[B_FILE_NAME_LENGTH]; 1460 if (strchr(type, '/') == NULL) { 1461 strlcpy(buffer, type, sizeof(buffer)); 1462 strlcat(buffer, "/*", sizeof(buffer)); 1463 type = buffer; 1464 } 1465 1466 query->PushAttr(kAttrMIMEType); 1467 query->PushString(type); 1468 query->PushOp(B_EQ); 1469 query->PushOp(B_AND); 1470 } 1471 } 1472 1473 1474 void 1475 FindPanel::GetByAttrPredicate(BQuery* query, bool &dynamicDate) const 1476 { 1477 ASSERT(Mode() == (int32)kByAttributeItem); 1478 BuildAttrQuery(query, dynamicDate); 1479 PushMimeType(query); 1480 } 1481 1482 1483 void 1484 FindPanel::GetDefaultName(BString &result) const 1485 { 1486 BTextControl* textControl 1487 = dynamic_cast<BTextControl*>(FindView("TextControl")); 1488 switch (Mode()) { 1489 case kByNameItem: 1490 result.SetTo(B_TRANSLATE_COMMENT("Name = %name", 1491 "FindResultTitle")); 1492 result.ReplaceFirst("%name", textControl->TextView()->Text()); 1493 break; 1494 1495 case kByFormulaItem: 1496 result.SetTo(B_TRANSLATE_COMMENT("Formula %formula", 1497 "FindResultTitle")); 1498 result.ReplaceFirst("%formula", textControl->TextView()->Text()); 1499 break; 1500 1501 case kByAttributeItem: 1502 { 1503 BMenuItem* item = fMimeTypeMenu->FindMarked(); 1504 if (item != NULL) 1505 result << item->Label() << ": "; 1506 for (int32 i = 0; i < fAttrGrid->CountRows(); i++) { 1507 GetDefaultAttrName(result, i); 1508 if (i + 1 < fAttrGrid->CountRows()) 1509 result << ", "; 1510 } 1511 break; 1512 } 1513 } 1514 } 1515 1516 1517 const char* 1518 FindPanel::UserSpecifiedName() const 1519 { 1520 if (fQueryName->Text()[0] == '\0') 1521 return NULL; 1522 1523 return fQueryName->Text(); 1524 } 1525 1526 1527 void 1528 FindPanel::GetByNamePredicate(BQuery* query) const 1529 { 1530 ASSERT(Mode() == (int32)kByNameItem); 1531 BTextControl* textControl 1532 = dynamic_cast<BTextControl*>(FindView("TextControl")); 1533 ASSERT(textControl); 1534 1535 query->PushAttr("name"); 1536 query->PushString(textControl->TextView()->Text(), true); 1537 1538 if (strstr(textControl->TextView()->Text(), "*")) { 1539 // assume pattern is a regular expression, try doing an exact match 1540 query->PushOp(B_EQ); 1541 } else 1542 query->PushOp(B_CONTAINS); 1543 1544 PushMimeType(query); 1545 } 1546 1547 1548 void 1549 FindPanel::SwitchMode(uint32 mode) 1550 { 1551 if (fMode == mode) 1552 // no work, bail 1553 return; 1554 1555 uint32 oldMode = fMode; 1556 BString buffer; 1557 1558 switch (mode) { 1559 case kByFormulaItem: 1560 { 1561 if (oldMode == kByAttributeItem || oldMode == kByNameItem) { 1562 BQuery query; 1563 if (oldMode == kByAttributeItem) { 1564 bool dummy; 1565 GetByAttrPredicate(&query, dummy); 1566 } else 1567 GetByNamePredicate(&query); 1568 1569 query.GetPredicate(&buffer); 1570 } 1571 } // fall thru 1572 case kByNameItem: 1573 { 1574 fMode = mode; 1575 RemoveByAttributeItems(); 1576 ShowOrHideMimeTypeMenu(); 1577 AddByNameOrFormulaItems(); 1578 1579 if (buffer.Length()) { 1580 ASSERT(mode == kByFormulaItem 1581 || oldMode == kByAttributeItem); 1582 BTextControl* textControl 1583 = dynamic_cast<BTextControl*>(FindView("TextControl")); 1584 textControl->SetText(buffer.String()); 1585 } 1586 break; 1587 } 1588 1589 case kByAttributeItem: 1590 { 1591 fMode = mode; 1592 BTextControl* textControl 1593 = dynamic_cast<BTextControl*>(FindView("TextControl")); 1594 if (textControl) { 1595 textControl->RemoveSelf(); 1596 delete textControl; 1597 } 1598 1599 ShowOrHideMimeTypeMenu(); 1600 AddAttrRow(); 1601 break; 1602 } 1603 } 1604 } 1605 1606 1607 BMenuItem* 1608 FindPanel::CurrentMimeType(const char** type) const 1609 { 1610 // search for marked item in the list 1611 BMenuItem* item = MimeTypeMenu()->FindMarked(); 1612 1613 if (item != NULL && MimeTypeMenu()->IndexOf(item) != 0 1614 && item->Submenu() == NULL) { 1615 // if it's one of the most used items, ignore it 1616 item = NULL; 1617 } 1618 1619 if (item == NULL) { 1620 for (int32 index = MimeTypeMenu()->CountItems(); index-- > 0;) { 1621 BMenu* submenu = MimeTypeMenu()->ItemAt(index)->Submenu(); 1622 if (submenu != NULL && (item = submenu->FindMarked()) != NULL) 1623 break; 1624 } 1625 } 1626 1627 if (type != NULL && item != NULL) { 1628 BMessage* message = item->Message(); 1629 if (message == NULL) 1630 return NULL; 1631 1632 if (message->FindString("mimetype", type) != B_OK) 1633 return NULL; 1634 } 1635 return item; 1636 } 1637 1638 1639 status_t 1640 FindPanel::SetCurrentMimeType(BMenuItem* item) 1641 { 1642 // unmark old MIME type (in most used list, and the tree) 1643 1644 BMenuItem* marked = CurrentMimeType(); 1645 if (marked != NULL) { 1646 marked->SetMarked(false); 1647 1648 if ((marked = MimeTypeMenu()->FindMarked()) != NULL) 1649 marked->SetMarked(false); 1650 } 1651 1652 // mark new MIME type (in most used list, and the tree) 1653 1654 if (item != NULL) { 1655 item->SetMarked(true); 1656 fMimeTypeField->MenuItem()->SetLabel(item->Label()); 1657 1658 BMenuItem* search; 1659 for (int32 i = 2; (search = MimeTypeMenu()->ItemAt(i)) != NULL; i++) { 1660 if (item == search || search->Label() == NULL) 1661 continue; 1662 1663 if (strcmp(item->Label(), search->Label()) == 0) { 1664 search->SetMarked(true); 1665 break; 1666 } 1667 1668 BMenu* submenu = search->Submenu(); 1669 if (submenu == NULL) 1670 continue; 1671 1672 for (int32 j = submenu->CountItems(); j-- > 0;) { 1673 BMenuItem* sub = submenu->ItemAt(j); 1674 if (strcmp(item->Label(), sub->Label()) == 0) { 1675 sub->SetMarked(true); 1676 break; 1677 } 1678 } 1679 } 1680 } 1681 1682 return B_OK; 1683 } 1684 1685 1686 status_t 1687 FindPanel::SetCurrentMimeType(const char* label) 1688 { 1689 // unmark old MIME type (in most used list, and the tree) 1690 1691 BMenuItem* marked = CurrentMimeType(); 1692 if (marked != NULL) { 1693 marked->SetMarked(false); 1694 1695 if ((marked = MimeTypeMenu()->FindMarked()) != NULL) 1696 marked->SetMarked(false); 1697 } 1698 1699 // mark new MIME type (in most used list, and the tree) 1700 1701 fMimeTypeField->MenuItem()->SetLabel(label); 1702 bool found = false; 1703 1704 for (int32 index = MimeTypeMenu()->CountItems(); index-- > 0;) { 1705 BMenuItem* item = MimeTypeMenu()->ItemAt(index); 1706 BMenu* submenu = item->Submenu(); 1707 if (submenu != NULL && !found) { 1708 for (int32 subIndex = submenu->CountItems(); subIndex-- > 0;) { 1709 BMenuItem* subItem = submenu->ItemAt(subIndex); 1710 if (subItem->Label() != NULL 1711 && strcmp(label, subItem->Label()) == 0) { 1712 subItem->SetMarked(true); 1713 found = true; 1714 } 1715 } 1716 } 1717 1718 if (item->Label() != NULL && strcmp(label, item->Label()) == 0) { 1719 item->SetMarked(true); 1720 return B_OK; 1721 } 1722 } 1723 1724 return found ? B_OK : B_ENTRY_NOT_FOUND; 1725 } 1726 1727 1728 bool 1729 FindPanel::AddOneMimeTypeToMenu(const ShortMimeInfo* info, void* castToMenu) 1730 { 1731 BPopUpMenu* menu = static_cast<BPopUpMenu*>(castToMenu); 1732 1733 BMimeType type(info->InternalName()); 1734 BMimeType super; 1735 type.GetSupertype(&super); 1736 if (super.InitCheck() < B_OK) 1737 return false; 1738 1739 BMenuItem* superItem = menu->FindItem(super.Type()); 1740 if (superItem != NULL) { 1741 BMessage* msg = new BMessage(kMIMETypeItem); 1742 msg->AddString("mimetype", info->InternalName()); 1743 1744 superItem->Submenu()->AddItem(new IconMenuItem( 1745 info->ShortDescription(), msg, info->InternalName(), 1746 B_MINI_ICON)); 1747 } 1748 1749 return false; 1750 } 1751 1752 1753 void 1754 FindPanel::AddMimeTypesToMenu() 1755 { 1756 BMessage* itemMessage = new BMessage(kMIMETypeItem); 1757 itemMessage->AddString("mimetype", kAllMimeTypes); 1758 MimeTypeMenu()->AddItem( 1759 new BMenuItem(B_TRANSLATE("All files and folders"), itemMessage)); 1760 MimeTypeMenu()->AddSeparatorItem(); 1761 MimeTypeMenu()->ItemAt(0)->SetMarked(true); 1762 1763 // add recent MIME types 1764 1765 TTracker* tracker = dynamic_cast<TTracker*>(be_app); 1766 1767 BList list; 1768 if (gMostUsedMimeTypes.ObtainList(&list) && tracker) { 1769 int32 count = 0; 1770 for (int32 index = 0; index < list.CountItems(); index++) { 1771 const char* name = (const char*)list.ItemAt(index); 1772 1773 const ShortMimeInfo* info; 1774 if ((info = tracker->MimeTypes()->FindMimeType(name)) == NULL) 1775 continue; 1776 1777 BMessage* message = new BMessage(kMIMETypeItem); 1778 message->AddString("mimetype", info->InternalName()); 1779 1780 MimeTypeMenu()->AddItem(new BMenuItem(name, message)); 1781 count++; 1782 } 1783 if (count != 0) 1784 MimeTypeMenu()->AddSeparatorItem(); 1785 1786 gMostUsedMimeTypes.ReleaseList(); 1787 } 1788 1789 // add MIME type tree list 1790 1791 BMessage types; 1792 if (BMimeType::GetInstalledSupertypes(&types) == B_OK) { 1793 const char* superType; 1794 int32 index = 0; 1795 1796 while (types.FindString("super_types", index++, &superType) == B_OK) { 1797 BMenu* superMenu = new BMenu(superType); 1798 1799 BMessage* message = new BMessage(kMIMETypeItem); 1800 message->AddString("mimetype", superType); 1801 1802 MimeTypeMenu()->AddItem(new IconMenuItem(superMenu, message, 1803 superType, B_MINI_ICON)); 1804 1805 // the MimeTypeMenu's font is not correct at this time 1806 superMenu->SetFont(be_plain_font); 1807 } 1808 } 1809 1810 if (tracker != NULL) { 1811 tracker->MimeTypes()->EachCommonType(&FindPanel::AddOneMimeTypeToMenu, 1812 MimeTypeMenu()); 1813 } 1814 1815 // remove empty super type menus (and set target) 1816 1817 for (int32 index = MimeTypeMenu()->CountItems(); index-- > 2;) { 1818 BMenuItem* item = MimeTypeMenu()->ItemAt(index); 1819 BMenu* submenu = item->Submenu(); 1820 if (submenu == NULL) 1821 continue; 1822 1823 if (submenu->CountItems() == 0) { 1824 MimeTypeMenu()->RemoveItem(item); 1825 delete item; 1826 } else 1827 submenu->SetTargetForItems(this); 1828 } 1829 1830 MimeTypeMenu()->SetTargetForItems(this); 1831 } 1832 1833 1834 void 1835 FindPanel::AddVolumes(BMenu* menu) 1836 { 1837 // ToDo: add calls to this to rebuild the menu when a volume gets mounted 1838 1839 BMessage* message = new BMessage(kVolumeItem); 1840 message->AddInt32("device", -1); 1841 menu->AddItem(new BMenuItem(B_TRANSLATE("All disks"), message)); 1842 menu->AddSeparatorItem(); 1843 PopUpMenuSetTitle(menu, B_TRANSLATE("All disks")); 1844 1845 BVolumeRoster roster; 1846 BVolume volume; 1847 roster.Rewind(); 1848 while (roster.GetNextVolume(&volume) == B_OK) { 1849 if (volume.IsPersistent() && volume.KnowsQuery()) { 1850 BDirectory root; 1851 if (volume.GetRootDirectory(&root) != B_OK) 1852 continue; 1853 1854 BEntry entry; 1855 root.GetEntry(&entry); 1856 1857 Model model(&entry, true); 1858 if (model.InitCheck() != B_OK) 1859 continue; 1860 1861 message = new BMessage(kVolumeItem); 1862 message->AddInt32("device", volume.Device()); 1863 menu->AddItem(new ModelMenuItem(&model, model.Name(), message)); 1864 } 1865 } 1866 1867 if (menu->ItemAt(0)) 1868 menu->ItemAt(0)->SetMarked(true); 1869 1870 menu->SetTargetForItems(this); 1871 } 1872 1873 1874 typedef std::pair<entry_ref, uint32> EntryWithDate; 1875 1876 static int 1877 SortByDatePredicate(const EntryWithDate* entry1, const EntryWithDate* entry2) 1878 { 1879 return entry1->second > entry2->second ? 1880 -1 : (entry1->second == entry2->second ? 0 : 1); 1881 } 1882 1883 struct AddOneRecentParams { 1884 BMenu* menu; 1885 const BMessenger* target; 1886 uint32 what; 1887 }; 1888 1889 static const entry_ref* 1890 AddOneRecentItem(const entry_ref* ref, void* castToParams) 1891 { 1892 AddOneRecentParams* params = (AddOneRecentParams*)castToParams; 1893 1894 BMessage* message = new BMessage(params->what); 1895 message->AddRef("refs", ref); 1896 1897 char type[B_MIME_TYPE_LENGTH]; 1898 BNode node(ref); 1899 BNodeInfo(&node).GetType(type); 1900 BMenuItem* item = new IconMenuItem(ref->name, message, type, B_MINI_ICON); 1901 item->SetTarget(*params->target); 1902 params->menu->AddItem(item); 1903 1904 return NULL; 1905 } 1906 1907 1908 void 1909 FindPanel::AddRecentQueries(BMenu* menu, bool addSaveAsItem, 1910 const BMessenger* target, uint32 what) 1911 { 1912 BObjectList<entry_ref> templates(10, true); 1913 BObjectList<EntryWithDate> recentQueries(10, true); 1914 1915 // find all the queries on all volumes 1916 BVolumeRoster roster; 1917 BVolume volume; 1918 roster.Rewind(); 1919 while (roster.GetNextVolume(&volume) == B_OK) { 1920 if (volume.IsPersistent() && volume.KnowsQuery() 1921 && volume.KnowsAttr()) { 1922 BQuery query; 1923 query.SetVolume(&volume); 1924 query.SetPredicate("_trk/recentQuery == 1"); 1925 if (query.Fetch() != B_OK) 1926 continue; 1927 1928 entry_ref ref; 1929 while (query.GetNextRef(&ref) == B_OK) { 1930 // ignore queries in the Trash 1931 if (FSInTrashDir(&ref)) 1932 continue; 1933 1934 char type[B_MIME_TYPE_LENGTH]; 1935 BNode node(&ref); 1936 BNodeInfo(&node).GetType(type); 1937 1938 if (strcasecmp(type, B_QUERY_TEMPLATE_MIMETYPE) == 0) 1939 templates.AddItem(new entry_ref(ref)); 1940 else { 1941 uint32 changeTime; 1942 if (node.ReadAttr(kAttrQueryLastChange, B_INT32_TYPE, 0, 1943 &changeTime, sizeof(uint32)) != sizeof(uint32)) 1944 continue; 1945 1946 recentQueries.AddItem(new EntryWithDate(ref, changeTime)); 1947 } 1948 } 1949 } 1950 } 1951 1952 // we are only adding last ten queries 1953 recentQueries.SortItems(SortByDatePredicate); 1954 1955 // but all templates 1956 AddOneRecentParams params; 1957 params.menu = menu; 1958 params.target = target; 1959 params.what = what; 1960 templates.EachElement(AddOneRecentItem, ¶ms); 1961 1962 int32 count = recentQueries.CountItems(); 1963 if (count > 10) { 1964 // show only up to 10 recent queries 1965 count = 10; 1966 } else if (count < 0) 1967 count = 0; 1968 1969 if (templates.CountItems() > 0 && count > 0) 1970 menu->AddSeparatorItem(); 1971 1972 for (int32 index = 0; index < count; index++) 1973 AddOneRecentItem(&recentQueries.ItemAt(index)->first, ¶ms); 1974 1975 if (addSaveAsItem) { 1976 // add a Save as template item 1977 if (count > 0 || templates.CountItems() > 0) 1978 menu->AddSeparatorItem(); 1979 1980 BMessage* message = new BMessage(kRunSaveAsTemplatePanel); 1981 BMenuItem* item = new BMenuItem( 1982 B_TRANSLATE("Save query as template" B_UTF8_ELLIPSIS), message); 1983 menu->AddItem(item); 1984 } 1985 } 1986 1987 1988 void 1989 FindPanel::SetUpAddRemoveButtons(BBox* box) 1990 { 1991 BButton* removeButton = dynamic_cast<BButton*>(box->FindView("remove button")); 1992 if (removeButton != NULL) { 1993 removeButton->SetEnabled(fAttrGrid->CountRows() > 1); 1994 return; 1995 } 1996 1997 removeButton = new BButton("remove button", B_TRANSLATE("Remove"), 1998 new BMessage(kRemoveItem)); 1999 removeButton->SetEnabled(false); 2000 removeButton->SetTarget(this); 2001 2002 BButton* addButton = new BButton("add button", B_TRANSLATE("Add"), 2003 new BMessage(kAddItem)); 2004 addButton->SetTarget(this); 2005 2006 BGroupLayout* layout = dynamic_cast<BGroupLayout*>(box->GetLayout()); 2007 BLayoutBuilder::Group<>(layout) 2008 .AddGroup(B_HORIZONTAL) 2009 .AddGlue() 2010 .Add(removeButton) 2011 .Add(addButton) 2012 .End(); 2013 } 2014 2015 2016 void 2017 FindPanel::FillCurrentQueryName(BTextControl* queryName, FindWindow* window) 2018 { 2019 ASSERT(window); 2020 queryName->SetText(window->QueryName()); 2021 } 2022 2023 2024 void 2025 FindPanel::AddAttrRow() 2026 { 2027 BBox* box = dynamic_cast<BBox*>(FindView("Box")); 2028 BGridView* grid = dynamic_cast<BGridView*>(box->FindView("AttrFields")); 2029 2030 if (grid == NULL) { 2031 // reset layout 2032 BLayoutBuilder::Group<>(box, B_VERTICAL); 2033 2034 grid = new BGridView("AttrFields"); 2035 box->AddChild(grid); 2036 } 2037 2038 fAttrGrid = grid->GridLayout(); 2039 2040 AddAttributeControls(fAttrGrid->CountRows()); 2041 2042 // add logic to previous attrview 2043 if (fAttrGrid->CountRows() > 1) 2044 AddLogicMenu(fAttrGrid->CountRows() - 2); 2045 2046 SetUpAddRemoveButtons(box); 2047 } 2048 2049 2050 void 2051 FindPanel::RemoveAttrRow() 2052 { 2053 if (fAttrGrid->CountRows() < 2) 2054 return; 2055 2056 BView* view; 2057 2058 int32 row = fAttrGrid->CountRows() - 1; 2059 for (int32 col = fAttrGrid->CountColumns(); col > 0; col--) { 2060 BLayoutItem* item = fAttrGrid->ItemAt(col - 1, row); 2061 if (item == NULL) 2062 continue; 2063 2064 view = item->View(); 2065 if (view == NULL) 2066 continue; 2067 2068 view->RemoveSelf(); 2069 delete view; 2070 } 2071 2072 BString string = "TextEntry"; 2073 string << (row - 1); 2074 view = FindAttrView(string.String(), row - 1); 2075 if (view != NULL) 2076 view->MakeFocus(); 2077 2078 if (fAttrGrid->CountRows() > 1) { 2079 // remove the And/Or menu field of the previous row 2080 BLayoutItem* item = fAttrGrid->ItemAt(3, row - 1); 2081 if (item == NULL) 2082 return; 2083 2084 view = item->View(); 2085 if (view == NULL) 2086 return; 2087 2088 view->RemoveSelf(); 2089 delete view; 2090 return; 2091 } 2092 2093 // only one row remains 2094 2095 // disable the remove button 2096 BButton* button = dynamic_cast<BButton*>(FindView("remove button")); 2097 if (button != NULL) 2098 button->SetEnabled(false); 2099 2100 // remove the And/Or menu field 2101 BLayoutItem* item = fAttrGrid->RemoveItem(3); 2102 if (item == NULL) 2103 return; 2104 2105 view = item->View(); 2106 if (view == NULL) 2107 return; 2108 2109 view->RemoveSelf(); 2110 delete view; 2111 } 2112 2113 2114 uint32 2115 FindPanel::InitialMode(const BNode* node) 2116 { 2117 if (node == NULL || node->InitCheck() != B_OK) 2118 return kByNameItem; 2119 2120 uint32 result; 2121 if (node->ReadAttr(kAttrQueryInitialMode, B_INT32_TYPE, 0, 2122 (int32*)&result, sizeof(int32)) <= 0) 2123 return kByNameItem; 2124 2125 return result; 2126 } 2127 2128 2129 int32 2130 FindPanel::InitialAttrCount(const BNode* node) 2131 { 2132 if (node == NULL || node->InitCheck() != B_OK) 2133 return 1; 2134 2135 int32 result; 2136 if (node->ReadAttr(kAttrQueryInitialNumAttrs, B_INT32_TYPE, 0, 2137 &result, sizeof(int32)) <= 0) 2138 return 1; 2139 2140 return result; 2141 } 2142 2143 2144 static int32 2145 SelectItemWithLabel(BMenu* menu, const char* label) 2146 { 2147 for (int32 index = menu->CountItems(); index-- > 0;) { 2148 BMenuItem* item = menu->ItemAt(index); 2149 2150 if (strcmp(label, item->Label()) == 0) { 2151 item->SetMarked(true); 2152 return index; 2153 } 2154 } 2155 return -1; 2156 } 2157 2158 2159 void 2160 FindPanel::SaveWindowState(BNode* node, bool editTemplate) 2161 { 2162 ASSERT(node->InitCheck() == B_OK); 2163 2164 BMenuItem* item = CurrentMimeType(); 2165 if (item) { 2166 BString label(item->Label()); 2167 node->WriteAttrString(kAttrQueryInitialMime, &label); 2168 } 2169 2170 uint32 mode = Mode(); 2171 node->WriteAttr(kAttrQueryInitialMode, B_INT32_TYPE, 0, 2172 (int32*)&mode, sizeof(int32)); 2173 2174 MoreOptionsStruct saveMoreOptions; 2175 saveMoreOptions.showMoreOptions = fLatch->Value() != 0; 2176 2177 saveMoreOptions.searchTrash = fSearchTrashCheck->Value() != 0; 2178 saveMoreOptions.temporary = fTemporaryCheck->Value() != 0; 2179 2180 if (node->WriteAttr(kAttrQueryMoreOptions, B_RAW_TYPE, 0, 2181 &saveMoreOptions, 2182 sizeof(saveMoreOptions)) == sizeof(saveMoreOptions)) { 2183 node->RemoveAttr(kAttrQueryMoreOptionsForeign); 2184 } 2185 2186 if (editTemplate) { 2187 if (UserSpecifiedName()) { 2188 BString name(UserSpecifiedName()); 2189 node->WriteAttrString(kAttrQueryTemplateName, &name); 2190 } 2191 } 2192 2193 switch (Mode()) { 2194 case kByAttributeItem: 2195 { 2196 BMessage message; 2197 int32 count = fAttrGrid->CountRows(); 2198 node->WriteAttr(kAttrQueryInitialNumAttrs, B_INT32_TYPE, 0, 2199 &count, sizeof(int32)); 2200 2201 for (int32 index = 0; index < count; index++) 2202 SaveAttrState(&message, index); 2203 2204 ssize_t size = message.FlattenedSize(); 2205 char* buffer = new char[size]; 2206 status_t result = message.Flatten(buffer, size); 2207 if (result == B_OK) { 2208 node->WriteAttr(kAttrQueryInitialAttrs, B_MESSAGE_TYPE, 0, 2209 buffer, (size_t)size); 2210 } 2211 2212 delete[] buffer; 2213 break; 2214 } 2215 2216 case kByNameItem: 2217 case kByFormulaItem: 2218 { 2219 BTextControl* textControl = dynamic_cast<BTextControl*> 2220 (FindView("TextControl")); 2221 ASSERT(textControl); 2222 BString formula(textControl->TextView()->Text()); 2223 node->WriteAttrString(kAttrQueryInitialString, &formula); 2224 break; 2225 } 2226 } 2227 } 2228 2229 2230 void 2231 FindPanel::SwitchToTemplate(const BNode* node) 2232 { 2233 SwitchMode(InitialMode(node)); 2234 // update the menu to correspond to the mode 2235 MarkNamedMenuItem(fSearchModeMenu, InitialMode(node), true); 2236 2237 if (Mode() == (int32)kByAttributeItem) { 2238 RemoveByAttributeItems(); 2239 AddByAttributeItems(node); 2240 } 2241 2242 RestoreWindowState(node); 2243 } 2244 2245 2246 void 2247 FindPanel::RestoreMimeTypeMenuSelection(const BNode* node) 2248 { 2249 if (Mode() == (int32)kByFormulaItem || node == NULL 2250 || node->InitCheck() != B_OK) { 2251 return; 2252 } 2253 2254 BString buffer; 2255 if (node->ReadAttrString(kAttrQueryInitialMime, &buffer) == B_OK) 2256 SetCurrentMimeType(buffer.String()); 2257 } 2258 2259 2260 void 2261 FindPanel::RestoreWindowState(const BNode* node) 2262 { 2263 fMode = InitialMode(node); 2264 if (node == NULL || node->InitCheck() != B_OK) 2265 return; 2266 2267 ShowOrHideMimeTypeMenu(); 2268 RestoreMimeTypeMenuSelection(node); 2269 MoreOptionsStruct saveMoreOptions; 2270 2271 bool storesMoreOptions = ReadAttr(node, kAttrQueryMoreOptions, 2272 kAttrQueryMoreOptionsForeign, B_RAW_TYPE, 0, &saveMoreOptions, 2273 sizeof(saveMoreOptions), &MoreOptionsStruct::EndianSwap) 2274 != kReadAttrFailed; 2275 2276 if (storesMoreOptions) { 2277 // need to sanitize to true or false here, could have picked 2278 // up garbage from attributes 2279 2280 saveMoreOptions.showMoreOptions = 2281 (saveMoreOptions.showMoreOptions != 0); 2282 2283 fLatch->SetValue(saveMoreOptions.showMoreOptions); 2284 if (saveMoreOptions.showMoreOptions == 1 && fMoreOptions->IsHidden()) 2285 fMoreOptions->Show(); 2286 else if (saveMoreOptions.showMoreOptions == 0 && !fMoreOptions->IsHidden()) 2287 fMoreOptions->Hide(); 2288 2289 fSearchTrashCheck->SetValue(saveMoreOptions.searchTrash); 2290 fTemporaryCheck->SetValue(saveMoreOptions.temporary); 2291 2292 fQueryName->SetModificationMessage(NULL); 2293 FillCurrentQueryName(fQueryName, dynamic_cast<FindWindow*>(Window())); 2294 2295 // set modification message after checking the temporary check box, 2296 // and filling out the text control so that we do not always trigger 2297 // clearing of the temporary check box. 2298 fQueryName->SetModificationMessage( 2299 new BMessage(kNameModifiedMessage)); 2300 } 2301 2302 // get volumes to perform query on 2303 bool searchAllVolumes = true; 2304 2305 attr_info info; 2306 if (node->GetAttrInfo(kAttrQueryVolume, &info) == B_OK) { 2307 char* buffer = new char[info.size]; 2308 if (node->ReadAttr(kAttrQueryVolume, B_MESSAGE_TYPE, 0, buffer, 2309 (size_t)info.size) == info.size) { 2310 BMessage message; 2311 if (message.Unflatten(buffer) == B_OK) { 2312 for (int32 index = 0; ;index++) { 2313 ASSERT(index < 100); 2314 BVolume volume; 2315 // match a volume with the info embedded in 2316 // the message 2317 status_t result 2318 = MatchArchivedVolume(&volume, &message, index); 2319 if (result == B_OK) { 2320 char name[256]; 2321 volume.GetName(name); 2322 SelectItemWithLabel(fVolMenu, name); 2323 searchAllVolumes = false; 2324 } else if (result != B_DEV_BAD_DRIVE_NUM) 2325 // if B_DEV_BAD_DRIVE_NUM, the volume just isn't 2326 // mounted this time around, keep looking for more 2327 // if other error, bail 2328 break; 2329 } 2330 } 2331 } 2332 delete [] buffer; 2333 } 2334 // mark or unmark "All disks" 2335 fVolMenu->ItemAt(0)->SetMarked(searchAllVolumes); 2336 ShowVolumeMenuLabel(); 2337 2338 switch (Mode()) { 2339 case kByAttributeItem: 2340 { 2341 int32 count = InitialAttrCount(node); 2342 2343 attr_info info; 2344 if (node->GetAttrInfo(kAttrQueryInitialAttrs, &info) != B_OK) 2345 break; 2346 char* buffer = new char[info.size]; 2347 if (node->ReadAttr(kAttrQueryInitialAttrs, B_MESSAGE_TYPE, 0, 2348 buffer, (size_t)info.size) == info.size) { 2349 BMessage message; 2350 if (message.Unflatten(buffer) == B_OK) 2351 for (int32 index = 0; index < count; index++) { 2352 RestoreAttrState(message, index); 2353 } 2354 } 2355 delete[] buffer; 2356 break; 2357 } 2358 2359 case kByNameItem: 2360 case kByFormulaItem: 2361 { 2362 BString buffer; 2363 if (node->ReadAttrString(kAttrQueryInitialString, &buffer) 2364 == B_OK) { 2365 BTextControl* textControl = dynamic_cast<BTextControl*> 2366 (FindView("TextControl")); 2367 ASSERT(textControl); 2368 2369 textControl->TextView()->SetText(buffer.String()); 2370 } 2371 break; 2372 } 2373 } 2374 2375 // try to restore focus and possibly text selection 2376 BString focusedView; 2377 if (node->ReadAttrString("_trk/focusedView", &focusedView) == B_OK) { 2378 BView* view = FindView(focusedView.String()); 2379 if (view != NULL) { 2380 view->MakeFocus(); 2381 BTextControl* textControl = dynamic_cast<BTextControl*>(view); 2382 if (textControl != NULL && Mode() == kByFormulaItem) { 2383 int32 selStart = 0; 2384 int32 selEnd = INT32_MAX; 2385 node->ReadAttr("_trk/focusedSelStart", B_INT32_TYPE, 0, 2386 &selStart, sizeof(selStart)); 2387 node->ReadAttr("_trk/focusedSelEnd", B_INT32_TYPE, 0, 2388 &selEnd, sizeof(selEnd)); 2389 textControl->TextView()->Select(selStart, selEnd); 2390 } 2391 } 2392 } 2393 } 2394 2395 2396 void 2397 FindPanel::AddByAttributeItems(const BNode* node) 2398 { 2399 int32 numAttributes = InitialAttrCount(node); 2400 if (numAttributes < 1) 2401 numAttributes = 1; 2402 2403 for (int32 index = 0; index < numAttributes; index ++) 2404 AddAttrRow(); 2405 } 2406 2407 2408 void 2409 FindPanel::AddByNameOrFormulaItems() 2410 { 2411 BBox* box = dynamic_cast<BBox*>(FindView("Box")); 2412 // reset layout 2413 BLayoutBuilder::Group<>(box, B_VERTICAL); 2414 2415 BTextControl* textControl = new BTextControl("TextControl", 2416 "", "", NULL); 2417 textControl->SetDivider(0.0f); 2418 box->SetBorder(B_NO_BORDER); 2419 box->AddChild(textControl); 2420 textControl->MakeFocus(); 2421 } 2422 2423 2424 void 2425 FindPanel::RemoveAttrViewItems(bool removeGrid) 2426 { 2427 if (fAttrGrid == NULL) 2428 return; 2429 2430 BView* view = fAttrGrid->View(); 2431 for (int32 index = view->CountChildren(); index > 0; index--) { 2432 BView* child = view->ChildAt(index - 1); 2433 child->RemoveSelf(); 2434 delete child; 2435 } 2436 2437 if (removeGrid) { 2438 view->RemoveSelf(); 2439 delete view; 2440 fAttrGrid = NULL; 2441 } 2442 } 2443 2444 2445 void 2446 FindPanel::RemoveByAttributeItems() 2447 { 2448 RemoveAttrViewItems(); 2449 BView* view = FindView("add button"); 2450 if (view) { 2451 view->RemoveSelf(); 2452 delete view; 2453 } 2454 2455 view = FindView("remove button"); 2456 if (view) { 2457 view->RemoveSelf(); 2458 delete view; 2459 } 2460 2461 view = FindView("TextControl"); 2462 if (view) { 2463 view->RemoveSelf(); 2464 delete view; 2465 } 2466 } 2467 2468 2469 void 2470 FindPanel::ShowOrHideMimeTypeMenu() 2471 { 2472 BView* menuFieldSpacer = FindView("MimeTypeMenuSpacer"); 2473 BMenuField* menuField 2474 = dynamic_cast<BMenuField*>(FindView("MimeTypeMenu")); 2475 if (Mode() == (int32)kByFormulaItem && !menuField->IsHidden(this)) { 2476 BSize size = menuField->ExplicitMinSize(); 2477 menuField->Hide(); 2478 menuFieldSpacer->SetExplicitMinSize(size); 2479 menuFieldSpacer->SetExplicitMaxSize(size); 2480 if (menuFieldSpacer->IsHidden(this)) 2481 menuFieldSpacer->Show(); 2482 } else if (menuField->IsHidden(this)) { 2483 menuFieldSpacer->Hide(); 2484 menuField->Show(); 2485 } 2486 } 2487 2488 2489 void 2490 FindPanel::AddAttributeControls(int32 gridRow) 2491 { 2492 BPopUpMenu* menu = new BPopUpMenu("PopUp"); 2493 2494 // add NAME attribute to popup 2495 BMenu* submenu = new BMenu(B_TRANSLATE("Name")); 2496 submenu->SetRadioMode(true); 2497 submenu->SetFont(be_plain_font); 2498 BMessage* message = new BMessage(kAttributeItemMain); 2499 message->AddString("name", "name"); 2500 message->AddInt32("type", B_STRING_TYPE); 2501 BMenuItem* item = new BMenuItem(submenu, message); 2502 menu->AddItem(item); 2503 2504 for (int32 i = 0; i < 5; i++) { 2505 message = new BMessage(kAttributeItem); 2506 message->AddInt32("operator", operators[i]); 2507 submenu->AddItem(new BMenuItem( 2508 B_TRANSLATE_NOCOLLECT(operatorLabels[i]), message)); 2509 } 2510 2511 // mark first items initially 2512 menu->ItemAt(0)->SetMarked(true); 2513 submenu->ItemAt(0)->SetMarked(true); 2514 2515 // add SIZE attribute 2516 submenu = new BMenu(B_TRANSLATE("Size")); 2517 submenu->SetRadioMode(true); 2518 submenu->SetFont(be_plain_font); 2519 message = new BMessage(kAttributeItemMain); 2520 message->AddString("name", "size"); 2521 message->AddInt32("type", B_OFF_T_TYPE); 2522 item = new BMenuItem(submenu, message); 2523 menu->AddItem(item); 2524 2525 message = new BMessage(kAttributeItem); 2526 message->AddInt32("operator", B_GE); 2527 submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[5]), 2528 message)); 2529 2530 message = new BMessage(kAttributeItem); 2531 message->AddInt32("operator", B_LE); 2532 submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[6]), 2533 message)); 2534 2535 message = new BMessage(kAttributeItem); 2536 message->AddInt32("operator", B_EQ); 2537 submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[1]), 2538 message)); 2539 2540 // add "modified" field 2541 submenu = new BMenu(B_TRANSLATE("Modified")); 2542 submenu->SetRadioMode(true); 2543 submenu->SetFont(be_plain_font); 2544 message = new BMessage(kAttributeItemMain); 2545 message->AddString("name", "last_modified"); 2546 message->AddInt32("type", B_TIME_TYPE); 2547 item = new BMenuItem(submenu, message); 2548 menu->AddItem(item); 2549 2550 message = new BMessage(kAttributeItem); 2551 message->AddInt32("operator", B_LE); 2552 submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[7]), 2553 message)); 2554 2555 message = new BMessage(kAttributeItem); 2556 message->AddInt32("operator", B_GE); 2557 submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[8]), 2558 message)); 2559 2560 BMenuField* menuField = new BMenuField("MenuField", "", menu); 2561 menuField->SetDivider(0.0f); 2562 fAttrGrid->AddView(menuField, 0, gridRow); 2563 2564 BStringView* stringView = new BStringView("", 2565 menu->FindMarked()->Submenu()->FindMarked()->Label()); 2566 BLayoutItem* layoutItem = fAttrGrid->AddView(stringView, 1, gridRow); 2567 layoutItem->SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT, 2568 B_ALIGN_VERTICAL_UNSET)); 2569 2570 BString title("TextEntry"); 2571 title << gridRow; 2572 BTextControl* textControl = new BTextControl(title.String(), "", "", NULL); 2573 textControl->SetDivider(0.0f); 2574 fAttrGrid->AddView(textControl, 2, gridRow); 2575 textControl->MakeFocus(); 2576 2577 // target everything 2578 menu->SetTargetForItems(this); 2579 for (int32 index = menu->CountItems() - 1; index >= 0; index--) 2580 menu->SubmenuAt(index)->SetTargetForItems(this); 2581 2582 // populate mime popup 2583 AddMimeTypeAttrs(menu); 2584 } 2585 2586 2587 void 2588 FindPanel::RestoreAttrState(const BMessage &message, int32 index) 2589 { 2590 BMenu* menu = dynamic_cast<BMenuField*>(FindAttrView("MenuField", index)) 2591 ->Menu(); 2592 // decode menu selections 2593 2594 AddMimeTypeAttrs(menu); 2595 2596 const char* label; 2597 if (message.FindString("menuSelection", index, &label) == B_OK) { 2598 int32 itemIndex = SelectItemWithLabel(menu, label); 2599 if (itemIndex >=0) { 2600 menu = menu->SubmenuAt(itemIndex); 2601 if (menu && message.FindString("subMenuSelection", index, &label) 2602 == B_OK) 2603 SelectItemWithLabel(menu, label); 2604 } 2605 } 2606 2607 // decode attribute text 2608 BString bstring = "TextEntry"; 2609 bstring << index; 2610 BTextControl* textControl 2611 = dynamic_cast<BTextControl*>(FindAttrView(bstring.String(), index)); 2612 ASSERT(textControl); 2613 const char* string; 2614 if (message.FindString("attrViewText", index, &string) == B_OK) 2615 textControl->TextView()->SetText(string); 2616 2617 int32 logicMenuSelectedIndex; 2618 BMenuField* field = dynamic_cast<BMenuField*>(FindAttrView("Logic", index)); 2619 if (message.FindInt32("logicalRelation", index, 2620 &logicMenuSelectedIndex) == B_OK) { 2621 if (field) 2622 field->Menu()->ItemAt(logicMenuSelectedIndex)->SetMarked(true); 2623 else 2624 AddLogicMenu(index, logicMenuSelectedIndex == 0); 2625 } 2626 } 2627 2628 2629 void 2630 FindPanel::SaveAttrState(BMessage* message, int32 index) 2631 { 2632 BMenu* menu = dynamic_cast<BMenuField*>(FindAttrView("MenuField", index)) 2633 ->Menu(); 2634 2635 // encode main attribute menu selection 2636 BMenuItem* item = menu->FindMarked(); 2637 message->AddString("menuSelection", item ? item->Label() : ""); 2638 2639 // encode submenu selection 2640 const char* label = ""; 2641 if (item) { 2642 BMenu* submenu = menu->SubmenuAt(menu->IndexOf(item)); 2643 if (submenu) { 2644 item = submenu->FindMarked(); 2645 if (item) 2646 label = item->Label(); 2647 } 2648 } 2649 message->AddString("subMenuSelection", label); 2650 2651 // encode attribute text 2652 BString string = "TextEntry"; 2653 string << index; 2654 BTextControl* textControl 2655 = dynamic_cast<BTextControl*>(FindAttrView(string.String(), index)); 2656 ASSERT(textControl); 2657 message->AddString("attrViewText", textControl->TextView()->Text()); 2658 2659 BMenuField* field = dynamic_cast<BMenuField*>(FindAttrView("Logic", index)); 2660 if (field != NULL) { 2661 BMenuItem* item = field->Menu()->FindMarked(); 2662 ASSERT(item); 2663 message->AddInt32("logicalRelation", 2664 item ? field->Menu()->IndexOf(item) : 0); 2665 } 2666 } 2667 2668 2669 void 2670 FindPanel::AddLogicMenu(int32 index, bool selectAnd) 2671 { 2672 // add "AND/OR" menu 2673 BPopUpMenu* menu = new BPopUpMenu(""); 2674 BMessage* message = new BMessage(); 2675 message->AddInt32("combine", B_AND); 2676 BMenuItem* item = new BMenuItem(B_TRANSLATE("And"), message); 2677 menu->AddItem(item); 2678 if (selectAnd) 2679 item->SetMarked(true); 2680 2681 message = new BMessage(); 2682 message->AddInt32("combine", B_OR); 2683 item = new BMenuItem(B_TRANSLATE("Or"), message); 2684 menu->AddItem(item); 2685 if (!selectAnd) 2686 item->SetMarked(true); 2687 2688 menu->SetTargetForItems(this); 2689 2690 BMenuField* menufield = new BMenuField("Logic", "", menu, B_WILL_DRAW); 2691 menufield->SetDivider(0.0f); 2692 2693 ResizeMenuField(menufield); 2694 2695 fAttrGrid->AddView(menufield, 3, index); 2696 } 2697 2698 2699 void 2700 FindPanel::RemoveLogicMenu(int32 index) 2701 { 2702 BMenuField* menufield = dynamic_cast<BMenuField*>(FindAttrView("Logic", index)); 2703 if (menufield) { 2704 menufield->RemoveSelf(); 2705 delete menufield; 2706 } 2707 } 2708 2709 2710 void 2711 FindPanel::AddAttributes(BMenu* menu, const BMimeType &mimeType) 2712 { 2713 // only add things to menu which have "user-visible" data 2714 BMessage attributeMessage; 2715 if (mimeType.GetAttrInfo(&attributeMessage) != B_OK) 2716 return; 2717 2718 char desc[B_MIME_TYPE_LENGTH]; 2719 mimeType.GetShortDescription(desc); 2720 2721 // go through each field in meta mime and add it to a menu 2722 for (int32 index = 0; ; index++) { 2723 const char* publicName; 2724 if (attributeMessage.FindString("attr:public_name", index, 2725 &publicName) != B_OK) { 2726 break; 2727 } 2728 2729 if (!attributeMessage.FindBool("attr:viewable")) 2730 continue; 2731 2732 const char* attributeName; 2733 if (attributeMessage.FindString("attr:name", index, &attributeName) 2734 != B_OK) { 2735 continue; 2736 } 2737 2738 int32 type; 2739 if (attributeMessage.FindInt32("attr:type", index, &type) != B_OK) 2740 continue; 2741 2742 BMenu* submenu = new BMenu(publicName); 2743 submenu->SetRadioMode(true); 2744 submenu->SetFont(be_plain_font); 2745 BMessage* message = new BMessage(kAttributeItemMain); 2746 message->AddString("name", attributeName); 2747 message->AddInt32("type", type); 2748 BMenuItem* item = new BMenuItem(submenu, message); 2749 menu->AddItem(item); 2750 menu->SetTargetForItems(this); 2751 2752 switch (type) { 2753 case B_STRING_TYPE: 2754 message = new BMessage(kAttributeItem); 2755 message->AddInt32("operator", B_CONTAINS); 2756 submenu->AddItem(new BMenuItem(operatorLabels[0], message)); 2757 2758 message = new BMessage(kAttributeItem); 2759 message->AddInt32("operator", B_EQ); 2760 submenu->AddItem(new BMenuItem(operatorLabels[1], message)); 2761 2762 message = new BMessage(kAttributeItem); 2763 message->AddInt32("operator", B_NE); 2764 submenu->AddItem(new BMenuItem(operatorLabels[2], message)); 2765 submenu->SetTargetForItems(this); 2766 2767 message = new BMessage(kAttributeItem); 2768 message->AddInt32("operator", B_BEGINS_WITH); 2769 submenu->AddItem(new BMenuItem(operatorLabels[3], message)); 2770 submenu->SetTargetForItems(this); 2771 2772 message = new BMessage(kAttributeItem); 2773 message->AddInt32("operator", B_ENDS_WITH); 2774 submenu->AddItem(new BMenuItem(operatorLabels[4], message)); 2775 break; 2776 2777 case B_BOOL_TYPE: 2778 case B_INT16_TYPE: 2779 case B_UINT8_TYPE: 2780 case B_INT8_TYPE: 2781 case B_UINT16_TYPE: 2782 case B_INT32_TYPE: 2783 case B_UINT32_TYPE: 2784 case B_INT64_TYPE: 2785 case B_UINT64_TYPE: 2786 case B_OFF_T_TYPE: 2787 case B_FLOAT_TYPE: 2788 case B_DOUBLE_TYPE: 2789 message = new BMessage(kAttributeItem); 2790 message->AddInt32("operator", B_EQ); 2791 submenu->AddItem(new BMenuItem(operatorLabels[1], message)); 2792 2793 message = new BMessage(kAttributeItem); 2794 message->AddInt32("operator", B_GE); 2795 submenu->AddItem(new BMenuItem(operatorLabels[5], message)); 2796 2797 message = new BMessage(kAttributeItem); 2798 message->AddInt32("operator", B_LE); 2799 submenu->AddItem(new BMenuItem(operatorLabels[6], message)); 2800 break; 2801 2802 case B_TIME_TYPE: 2803 message = new BMessage(kAttributeItem); 2804 message->AddInt32("operator", B_LE); 2805 submenu->AddItem(new BMenuItem(operatorLabels[7], message)); 2806 2807 message = new BMessage(kAttributeItem); 2808 message->AddInt32("operator", B_GE); 2809 submenu->AddItem(new BMenuItem(operatorLabels[8], message)); 2810 break; 2811 } 2812 submenu->SetTargetForItems(this); 2813 } 2814 } 2815 2816 2817 void 2818 FindPanel::AddMimeTypeAttrs(BMenu* menu) 2819 { 2820 const char* typeName; 2821 if (CurrentMimeType(&typeName) == NULL) 2822 return; 2823 2824 BMimeType mimeType(typeName); 2825 if (!mimeType.IsInstalled()) 2826 return; 2827 2828 if (!mimeType.IsSupertypeOnly()) { 2829 // add supertype attributes 2830 BMimeType supertype; 2831 mimeType.GetSupertype(&supertype); 2832 AddAttributes(menu, supertype); 2833 } 2834 2835 AddAttributes(menu, mimeType); 2836 } 2837 2838 2839 void 2840 FindPanel::GetDefaultAttrName(BString &result, int32 row) const 2841 { 2842 BMenuItem* item = NULL; 2843 BMenuField* menuField 2844 = dynamic_cast<BMenuField*>(fAttrGrid->ItemAt(0, row)->View()); 2845 if (menuField->Menu() != NULL) 2846 item = menuField->Menu()->FindMarked(); 2847 if (item != NULL) 2848 result << item->Label(); 2849 else 2850 result << B_TRANSLATE("Name"); 2851 2852 if (item && item->Submenu() != NULL) 2853 item = item->Submenu()->FindMarked(); 2854 else 2855 item = NULL; 2856 2857 if (item != NULL) 2858 result << " " << item->Label() << " "; 2859 else 2860 result << " = "; 2861 2862 BTextControl* textControl 2863 = dynamic_cast<BTextControl*>(fAttrGrid->ItemAt(2, row)->View()); 2864 result << textControl->Text(); 2865 } 2866 2867 2868 // #pragma mark - 2869 2870 2871 DeleteTransientQueriesTask::DeleteTransientQueriesTask() 2872 : state(kInitial), 2873 fWalker(NULL) 2874 { 2875 } 2876 2877 2878 DeleteTransientQueriesTask::~DeleteTransientQueriesTask() 2879 { 2880 delete fWalker; 2881 } 2882 2883 2884 bool 2885 DeleteTransientQueriesTask::DoSomeWork() 2886 { 2887 switch (state) { 2888 case kInitial: 2889 Initialize(); 2890 break; 2891 2892 case kAllocatedWalker: 2893 case kTraversing: 2894 if (GetSome()) { 2895 PRINT(("transient query killer done\n")); 2896 return true; 2897 } 2898 break; 2899 2900 case kError: 2901 return true; 2902 2903 } 2904 return false; 2905 } 2906 2907 2908 void 2909 DeleteTransientQueriesTask::Initialize() 2910 { 2911 PRINT(("starting up transient query killer\n")); 2912 BPath path; 2913 status_t result = find_directory(B_USER_DIRECTORY, &path, false); 2914 if (result != B_OK) { 2915 state = kError; 2916 return; 2917 } 2918 fWalker = new BTrackerPrivate::TNodeWalker(path.Path()); 2919 state = kAllocatedWalker; 2920 } 2921 2922 2923 const int32 kBatchCount = 100; 2924 2925 bool 2926 DeleteTransientQueriesTask::GetSome() 2927 { 2928 state = kTraversing; 2929 for (int32 count = kBatchCount; count > 0; count--) { 2930 entry_ref ref; 2931 if (fWalker->GetNextRef(&ref) != B_OK) { 2932 state = kError; 2933 return true; 2934 } 2935 Model model(&ref); 2936 if (model.IsQuery()) 2937 ProcessOneRef(&model); 2938 #if xDEBUG 2939 else 2940 PRINT(("transient query killer: %s not a query\n", model.Name())); 2941 #endif 2942 } 2943 return false; 2944 } 2945 2946 2947 const int32 kDaysToExpire = 7; 2948 2949 static bool 2950 QueryOldEnough(Model* model) 2951 { 2952 // check if it is old and ready to be deleted 2953 time_t now = time(0); 2954 2955 tm nowTimeData; 2956 tm fileModData; 2957 2958 localtime_r(&now, &nowTimeData); 2959 localtime_r(&model->StatBuf()->st_ctime, &fileModData); 2960 2961 if ((nowTimeData.tm_mday - fileModData.tm_mday) < kDaysToExpire 2962 && (nowTimeData.tm_mday - fileModData.tm_mday) > -kDaysToExpire) { 2963 PRINT(("query %s, not old enough\n", model->Name())); 2964 return false; 2965 } 2966 return true; 2967 } 2968 2969 2970 bool 2971 DeleteTransientQueriesTask::ProcessOneRef(Model* model) 2972 { 2973 BModelOpener opener(model); 2974 2975 // is this a temporary query 2976 if (!MoreOptionsStruct::QueryTemporary(model->Node())) { 2977 PRINT(("query %s, not temporary\n", model->Name())); 2978 return false; 2979 } 2980 2981 if (!QueryOldEnough(model)) 2982 return false; 2983 2984 ASSERT(dynamic_cast<TTracker*>(be_app)); 2985 2986 // check that it is not showing 2987 if (dynamic_cast<TTracker*>(be_app)->EntryHasWindowOpen( 2988 model->EntryRef())) { 2989 PRINT(("query %s, showing, can't delete\n", model->Name())); 2990 return false; 2991 } 2992 2993 PRINT(("query %s, old, temporary, not shownig - deleting\n", 2994 model->Name())); 2995 BEntry entry(model->EntryRef()); 2996 entry.Remove(); 2997 2998 return true; 2999 } 3000 3001 3002 class DeleteTransientQueriesFunctor : public FunctionObjectWithResult<bool> { 3003 public: 3004 DeleteTransientQueriesFunctor(DeleteTransientQueriesTask* task) 3005 : task(task) 3006 {} 3007 3008 virtual ~DeleteTransientQueriesFunctor() 3009 { 3010 delete task; 3011 } 3012 3013 virtual void operator()() 3014 { result = task->DoSomeWork(); } 3015 3016 private: 3017 DeleteTransientQueriesTask* task; 3018 }; 3019 3020 3021 void 3022 DeleteTransientQueriesTask::StartUpTransientQueryCleaner() 3023 { 3024 // set up a task that wakes up when the machine is idle and starts 3025 // killing off old transient queries 3026 DeleteTransientQueriesFunctor* worker 3027 = new DeleteTransientQueriesFunctor(new DeleteTransientQueriesTask()); 3028 TTracker* tracker = dynamic_cast<TTracker*>(be_app); 3029 ASSERT(tracker); 3030 tracker->MainTaskLoop()->RunWhenIdle(worker, 3031 30 * 60 * 1000000, // half an hour initial delay 3032 5 * 60 * 1000000, // idle for five minutes 3033 10 * 1000000); 3034 } 3035 3036 3037 // #pragma mark - 3038 3039 3040 RecentFindItemsMenu::RecentFindItemsMenu(const char* title, 3041 const BMessenger* target, uint32 what) 3042 : 3043 BMenu(title, B_ITEMS_IN_COLUMN), 3044 fTarget(*target), 3045 fWhat(what) 3046 { 3047 } 3048 3049 3050 void 3051 RecentFindItemsMenu::AttachedToWindow() 3052 { 3053 // re-populate the menu with fresh items 3054 for (int32 index = CountItems() - 1; index >= 0; index--) 3055 delete RemoveItem(index); 3056 3057 FindPanel::AddRecentQueries(this, false, &fTarget, fWhat); 3058 BMenu::AttachedToWindow(); 3059 } 3060 3061 3062 #if !B_BEOS_VERSION_DANO 3063 _IMPEXP_TRACKER 3064 #endif 3065 BMenu* 3066 TrackerBuildRecentFindItemsMenu(const char* title) 3067 { 3068 BMessenger tracker(kTrackerSignature); 3069 return new RecentFindItemsMenu(title, &tracker, B_REFS_RECEIVED); 3070 } 3071 3072 3073 // #pragma mark - 3074 3075 3076 DraggableQueryIcon::DraggableQueryIcon(BRect frame, const char* name, 3077 const BMessage* message, BMessenger messenger, uint32 resizeFlags, 3078 uint32 flags) 3079 : 3080 DraggableIcon(frame, name, B_QUERY_MIMETYPE, B_LARGE_ICON, 3081 message, messenger, resizeFlags, flags) 3082 { 3083 } 3084 3085 3086 bool 3087 DraggableQueryIcon::DragStarted(BMessage* dragMessage) 3088 { 3089 // override to substitute the user-specified query name 3090 dragMessage->RemoveData("be:clip_name"); 3091 3092 FindWindow* window = dynamic_cast<FindWindow*>(Window()); 3093 ASSERT(window); 3094 dragMessage->AddString("be:clip_name", 3095 window->BackgroundView()->UserSpecifiedName() ? 3096 window->BackgroundView()->UserSpecifiedName() 3097 : B_TRANSLATE("New Query")); 3098 3099 return true; 3100 } 3101 3102 3103 // #pragma mark - 3104 3105 3106 MostUsedNames::MostUsedNames(const char* fileName, const char* directory, 3107 int32 maxCount) 3108 : 3109 fFileName(fileName), 3110 fDirectory(directory), 3111 fLoaded(false), 3112 fCount(maxCount) 3113 { 3114 } 3115 3116 3117 MostUsedNames::~MostUsedNames() 3118 { 3119 // only write back settings when we've been used 3120 if (!fLoaded) 3121 return; 3122 3123 // write most used list to file 3124 3125 BPath path; 3126 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK) 3127 return; 3128 3129 path.Append(fDirectory); 3130 path.Append(fFileName); 3131 BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); 3132 if (file.InitCheck() == B_OK) { 3133 for (int32 i = 0; i < fList.CountItems(); i++) { 3134 list_entry* entry = static_cast<list_entry*>(fList.ItemAt(i)); 3135 3136 char line[B_FILE_NAME_LENGTH + 5]; 3137 3138 // limit upper bound to react more dynamically to changes 3139 if (--entry->count > 20) 3140 entry->count = 20; 3141 3142 // if the item hasn't been chosen in a while, remove it 3143 // (but leave at least one item in the list) 3144 if (entry->count < -10 && i > 0) 3145 continue; 3146 3147 sprintf(line, "%" B_PRId32 " %s\n", entry->count, entry->name); 3148 if (file.Write(line, strlen(line)) < B_OK) 3149 break; 3150 } 3151 } 3152 file.Unset(); 3153 3154 // free data 3155 3156 for (int32 i = fList.CountItems(); i-- > 0;) { 3157 list_entry* entry = static_cast<list_entry*>(fList.ItemAt(i)); 3158 free(entry->name); 3159 delete entry; 3160 } 3161 } 3162 3163 3164 bool 3165 MostUsedNames::ObtainList(BList* list) 3166 { 3167 if (list == NULL) 3168 return false; 3169 3170 if (!fLoaded) 3171 UpdateList(); 3172 3173 fLock.Lock(); 3174 3175 list->MakeEmpty(); 3176 for (int32 i = 0; i < fCount; i++) { 3177 list_entry* entry = static_cast<list_entry*>(fList.ItemAt(i)); 3178 if (entry == NULL) 3179 return true; 3180 3181 list->AddItem(entry->name); 3182 } 3183 return true; 3184 } 3185 3186 3187 void 3188 MostUsedNames::ReleaseList() 3189 { 3190 fLock.Unlock(); 3191 } 3192 3193 3194 void 3195 MostUsedNames::AddName(const char* name) 3196 { 3197 fLock.Lock(); 3198 3199 if (!fLoaded) 3200 LoadList(); 3201 3202 // remove last entry if there are more than 3203 // 2*fCount entries in the list 3204 3205 list_entry* entry = NULL; 3206 3207 if (fList.CountItems() > fCount * 2) { 3208 entry = static_cast<list_entry*>( 3209 fList.RemoveItem(fList.CountItems() - 1)); 3210 3211 // is this the name we want to add here? 3212 if (strcmp(name, entry->name)) { 3213 free(entry->name); 3214 delete entry; 3215 entry = NULL; 3216 } else 3217 fList.AddItem(entry); 3218 } 3219 3220 if (entry == NULL) { 3221 for (int32 i = 0; 3222 (entry = static_cast<list_entry*>(fList.ItemAt(i))) != NULL; i++) { 3223 if (strcmp(entry->name, name) == 0) 3224 break; 3225 } 3226 } 3227 3228 if (entry == NULL) { 3229 entry = new list_entry; 3230 entry->name = strdup(name); 3231 entry->count = 1; 3232 3233 fList.AddItem(entry); 3234 } else if (entry->count < 0) 3235 entry->count = 1; 3236 else 3237 entry->count++; 3238 3239 fLock.Unlock(); 3240 UpdateList(); 3241 } 3242 3243 3244 int 3245 MostUsedNames::CompareNames(const void* a,const void* b) 3246 { 3247 list_entry* entryA = *(list_entry**)a; 3248 list_entry* entryB = *(list_entry**)b; 3249 3250 if (entryA->count == entryB->count) 3251 return strcasecmp(entryA->name,entryB->name); 3252 3253 return entryB->count - entryA->count; 3254 } 3255 3256 3257 void 3258 MostUsedNames::LoadList() 3259 { 3260 if (fLoaded) 3261 return; 3262 fLoaded = true; 3263 3264 // load the most used names list 3265 3266 BPath path; 3267 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK) 3268 return; 3269 3270 path.Append(fDirectory); 3271 path.Append(fFileName); 3272 3273 FILE* file = fopen(path.Path(), "r"); 3274 if (file == NULL) 3275 return; 3276 3277 char line[B_FILE_NAME_LENGTH + 5]; 3278 while (fgets(line, sizeof(line), file) != NULL) { 3279 int32 length = (int32)strlen(line) - 1; 3280 if (length >= 0 && line[length] == '\n') 3281 line[length] = '\0'; 3282 3283 int32 count = atoi(line); 3284 3285 char* name = strchr(line, ' '); 3286 if (name == NULL || *(++name) == '\0') 3287 continue; 3288 3289 list_entry* entry = new list_entry; 3290 entry->name = strdup(name); 3291 entry->count = count; 3292 3293 fList.AddItem(entry); 3294 } 3295 fclose(file); 3296 } 3297 3298 3299 void 3300 MostUsedNames::UpdateList() 3301 { 3302 AutoLock<Benaphore> locker(fLock); 3303 3304 if (!fLoaded) 3305 LoadList(); 3306 3307 // sort list items 3308 3309 fList.SortItems(MostUsedNames::CompareNames); 3310 } 3311 3312 } // namespace BPrivate 3313