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