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