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