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