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