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