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