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