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