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 kByFormulaItem: 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(kByFormulaItem))); 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 kByFormulaItem: 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 kByFormulaItem: 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 kByFormulaItem: 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 kByFormulaItem: 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 kByFormulaItem: 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 == kByFormulaItem || 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 kByFormulaItem: 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)kByFormulaItem || 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 kByFormulaItem: 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)kByFormulaItem && !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::AddAttributes(BMenu *menu, const BMimeType &mimeType) 2615 { 2616 // only add things to menu which have "user-visible" data 2617 BMessage attributeMessage; 2618 if (mimeType.GetAttrInfo(&attributeMessage) != B_OK) 2619 return; 2620 2621 char desc[B_MIME_TYPE_LENGTH]; 2622 mimeType.GetShortDescription(desc); 2623 2624 // go through each field in meta mime and add it to a menu 2625 for (int32 index = 0; ; index++) { 2626 const char *publicName; 2627 if (attributeMessage.FindString("attr:public_name", index, &publicName) != B_OK) 2628 break; 2629 2630 if (!attributeMessage.FindBool("attr:viewable")) 2631 continue; 2632 2633 const char *attributeName; 2634 if (attributeMessage.FindString("attr:name", index, &attributeName) != B_OK) 2635 continue; 2636 2637 int32 type; 2638 if (attributeMessage.FindInt32("attr:type", index, &type) != B_OK) 2639 continue; 2640 2641 BMenu *submenu = new BMenu(publicName); 2642 submenu->SetRadioMode(true); 2643 submenu->SetFont(be_plain_font); 2644 BMessage *message = new BMessage(kAttributeItemMain); 2645 message->AddString("name", attributeName); 2646 message->AddInt32("type", type); 2647 BMenuItem *item = new BMenuItem(submenu, message); 2648 menu->AddItem(item); 2649 menu->SetTargetForItems(this); 2650 2651 switch (type) { 2652 case B_STRING_TYPE: 2653 message = new BMessage(kAttributeItem); 2654 message->AddInt32("operator", B_CONTAINS); 2655 submenu->AddItem(new BMenuItem("contains", message)); 2656 2657 message = new BMessage(kAttributeItem); 2658 message->AddInt32("operator", B_EQ); 2659 submenu->AddItem(new BMenuItem("is", message)); 2660 2661 message = new BMessage(kAttributeItem); 2662 message->AddInt32("operator", B_NE); 2663 submenu->AddItem(new BMenuItem("is not", message)); 2664 submenu->SetTargetForItems(this); 2665 2666 message = new BMessage(kAttributeItem); 2667 message->AddInt32("operator", B_BEGINS_WITH); 2668 submenu->AddItem(new BMenuItem("starts with", message)); 2669 submenu->SetTargetForItems(this); 2670 2671 message = new BMessage(kAttributeItem); 2672 message->AddInt32("operator", B_ENDS_WITH); 2673 submenu->AddItem(new BMenuItem("ends with", message)); 2674 break; 2675 2676 case B_BOOL_TYPE: 2677 case B_INT16_TYPE: 2678 case B_UINT8_TYPE: 2679 case B_INT8_TYPE: 2680 case B_UINT16_TYPE: 2681 case B_INT32_TYPE: 2682 case B_UINT32_TYPE: 2683 case B_INT64_TYPE: 2684 case B_UINT64_TYPE: 2685 case B_OFF_T_TYPE: 2686 case B_FLOAT_TYPE: 2687 case B_DOUBLE_TYPE: 2688 message = new BMessage(kAttributeItem); 2689 message->AddInt32("operator", B_EQ); 2690 submenu->AddItem(new BMenuItem("is", message)); 2691 2692 message = new BMessage(kAttributeItem); 2693 message->AddInt32("operator", B_GE); 2694 submenu->AddItem(new BMenuItem("greater than", message)); 2695 2696 message = new BMessage(kAttributeItem); 2697 message->AddInt32("operator", B_LE); 2698 submenu->AddItem(new BMenuItem("less than", message)); 2699 break; 2700 2701 case B_TIME_TYPE: 2702 message = new BMessage(kAttributeItem); 2703 message->AddInt32("operator", B_LE); 2704 submenu->AddItem(new BMenuItem("before", message)); 2705 2706 message = new BMessage(kAttributeItem); 2707 message->AddInt32("operator", B_GE); 2708 submenu->AddItem(new BMenuItem("after", message)); 2709 break; 2710 } 2711 submenu->SetTargetForItems(this); 2712 } 2713 } 2714 2715 2716 void 2717 TAttrView::AddMimeTypeAttrs(BMenu *menu) 2718 { 2719 FindPanel *mainView = dynamic_cast<FindPanel *>(Parent()-> 2720 Parent()->FindView("MainView")); 2721 if (!mainView) 2722 return; 2723 2724 const char *typeName; 2725 if (mainView->CurrentMimeType(&typeName) == NULL) 2726 return; 2727 2728 BMimeType mimeType(typeName); 2729 if (!mimeType.IsInstalled()) 2730 return; 2731 2732 if (!mimeType.IsSupertypeOnly()) { 2733 // add supertype attributes 2734 BMimeType supertype; 2735 mimeType.GetSupertype(&supertype); 2736 AddAttributes(menu, supertype); 2737 } 2738 2739 AddAttributes(menu, mimeType); 2740 } 2741 2742 2743 void 2744 TAttrView::GetDefaultName(BString &result) const 2745 { 2746 BMenuItem *item = NULL; 2747 if (fMenuField->Menu() != NULL) 2748 item = fMenuField->Menu()->FindMarked(); 2749 if (item != NULL) 2750 result << item->Label(); 2751 else 2752 result << "Name"; 2753 2754 if (item->Submenu() != NULL) 2755 item = item->Submenu()->FindMarked(); 2756 else 2757 item = NULL; 2758 2759 if (item != NULL) 2760 result << " " << item->Label() << " "; 2761 else 2762 result << " = "; 2763 2764 result << fTextControl->Text(); 2765 } 2766 2767 2768 // #pragma mark - 2769 2770 2771 DeleteTransientQueriesTask::DeleteTransientQueriesTask() 2772 : state(kInitial), 2773 fWalker(NULL) 2774 { 2775 } 2776 2777 2778 DeleteTransientQueriesTask::~DeleteTransientQueriesTask() 2779 { 2780 delete fWalker; 2781 } 2782 2783 2784 bool 2785 DeleteTransientQueriesTask::DoSomeWork() 2786 { 2787 switch (state) { 2788 case kInitial: 2789 Initialize(); 2790 break; 2791 2792 case kAllocatedWalker: 2793 case kTraversing: 2794 if (GetSome()) { 2795 PRINT(("transient query killer done\n")); 2796 return true; 2797 } 2798 break; 2799 2800 case kError: 2801 return true; 2802 2803 } 2804 return false; 2805 } 2806 2807 2808 void 2809 DeleteTransientQueriesTask::Initialize() 2810 { 2811 PRINT(("starting up transient query killer\n")); 2812 BPath path; 2813 status_t result = find_directory(B_USER_DIRECTORY, &path, false); 2814 if (result != B_OK) { 2815 state = kError; 2816 return; 2817 } 2818 fWalker = new WALKER_NS::TNodeWalker(path.Path()); 2819 state = kAllocatedWalker; 2820 } 2821 2822 2823 const int32 kBatchCount = 100; 2824 2825 bool 2826 DeleteTransientQueriesTask::GetSome() 2827 { 2828 state = kTraversing; 2829 for (int32 count = kBatchCount; count > 0; count--) { 2830 entry_ref ref; 2831 if (fWalker->GetNextRef(&ref) != B_OK) { 2832 state = kError; 2833 return true; 2834 } 2835 Model model(&ref); 2836 if (model.IsQuery()) 2837 ProcessOneRef(&model); 2838 #if xDEBUG 2839 else 2840 PRINT(("transient query killer: %s not a query\n", model.Name())); 2841 #endif 2842 } 2843 return false; 2844 } 2845 2846 2847 const int32 kDaysToExpire = 7; 2848 2849 static bool 2850 QueryOldEnough(Model *model) 2851 { 2852 // check if it is old and ready to be deleted 2853 time_t now = time(0); 2854 2855 tm nowTimeData; 2856 tm fileModData; 2857 2858 localtime_r(&now, &nowTimeData); 2859 localtime_r(&model->StatBuf()->st_ctime, &fileModData); 2860 2861 if ((nowTimeData.tm_mday - fileModData.tm_mday) < kDaysToExpire 2862 && (nowTimeData.tm_mday - fileModData.tm_mday) > -kDaysToExpire) { 2863 PRINT(("query %s, not old enough\n", model->Name())); 2864 return false; 2865 } 2866 return true; 2867 } 2868 2869 2870 bool 2871 DeleteTransientQueriesTask::ProcessOneRef(Model *model) 2872 { 2873 BModelOpener opener(model); 2874 2875 // is this a temporary query 2876 if (!MoreOptionsStruct::QueryTemporary(model->Node())) { 2877 PRINT(("query %s, not temporary\n", model->Name())); 2878 return false; 2879 } 2880 2881 if (!QueryOldEnough(model)) 2882 return false; 2883 2884 ASSERT(dynamic_cast<TTracker *>(be_app)); 2885 2886 // check that it is not showing 2887 if (dynamic_cast<TTracker *>(be_app)->EntryHasWindowOpen(model->EntryRef())) { 2888 PRINT(("query %s, showing, can't delete\n", model->Name())); 2889 return false; 2890 } 2891 2892 PRINT(("query %s, old, temporary, not shownig - deleting\n", model->Name())); 2893 BEntry entry(model->EntryRef()); 2894 entry.Remove(); 2895 2896 return true; 2897 } 2898 2899 2900 class DeleteTransientQueriesFunctor : public FunctionObjectWithResult<bool> { 2901 public: 2902 DeleteTransientQueriesFunctor(DeleteTransientQueriesTask *task) 2903 : task(task) 2904 {} 2905 2906 virtual ~DeleteTransientQueriesFunctor() 2907 { 2908 delete task; 2909 } 2910 2911 virtual void operator()() 2912 { result = task->DoSomeWork(); } 2913 2914 private: 2915 DeleteTransientQueriesTask *task; 2916 }; 2917 2918 2919 void 2920 DeleteTransientQueriesTask::StartUpTransientQueryCleaner() 2921 { 2922 // set up a task that wakes up when the machine is idle and starts 2923 // killing off old transient queries 2924 DeleteTransientQueriesFunctor *worker 2925 = new DeleteTransientQueriesFunctor(new DeleteTransientQueriesTask()); 2926 TTracker *tracker = dynamic_cast<TTracker *>(be_app); 2927 ASSERT(tracker); 2928 tracker->MainTaskLoop()->RunWhenIdle(worker, 2929 30 * 60 * 1000000, // half an hour initial delay 2930 5 * 60 * 1000000, // idle for five minutes 2931 10 * 1000000); 2932 } 2933 2934 2935 // #pragma mark - 2936 2937 2938 RecentFindItemsMenu::RecentFindItemsMenu(const char *title, const BMessenger *target, 2939 uint32 what) 2940 : BMenu(title, B_ITEMS_IN_COLUMN), 2941 fTarget(*target), 2942 fWhat(what) 2943 { 2944 } 2945 2946 2947 void 2948 RecentFindItemsMenu::AttachedToWindow() 2949 { 2950 // re-populate the menu with fresh items 2951 for (int32 index = CountItems() - 1; index >= 0; index--) 2952 delete RemoveItem(index); 2953 2954 FindPanel::AddRecentQueries(this, false, &fTarget, fWhat); 2955 BMenu::AttachedToWindow(); 2956 } 2957 2958 2959 #if !B_BEOS_VERSION_DANO 2960 _IMPEXP_TRACKER 2961 #endif 2962 BMenu * 2963 TrackerBuildRecentFindItemsMenu(const char *title) 2964 { 2965 BMessenger tracker(kTrackerSignature); 2966 return new RecentFindItemsMenu(title, &tracker, B_REFS_RECEIVED); 2967 } 2968 2969 2970 // #pragma mark - 2971 2972 2973 DraggableQueryIcon::DraggableQueryIcon(BRect frame, const char *name, 2974 const BMessage *message, BMessenger messenger, uint32 resizeFlags, uint32 flags) 2975 : DraggableIcon(frame, name, B_QUERY_MIMETYPE, B_LARGE_ICON, 2976 message, messenger, resizeFlags, flags) 2977 { 2978 } 2979 2980 2981 bool 2982 DraggableQueryIcon::DragStarted(BMessage *dragMessage) 2983 { 2984 // override to substitute the user-specified query name 2985 dragMessage->RemoveData("be:clip_name"); 2986 2987 FindWindow *window = dynamic_cast<FindWindow *>(Window()); 2988 ASSERT(window); 2989 dragMessage->AddString("be:clip_name", 2990 window->BackgroundView()->UserSpecifiedName() ? 2991 window->BackgroundView()->UserSpecifiedName() : "New Query"); 2992 2993 return true; 2994 } 2995 2996 2997 // #pragma mark - 2998 2999 3000 MostUsedNames::MostUsedNames(const char *fileName, const char *directory, int32 maxCount) 3001 : 3002 fFileName(fileName), 3003 fDirectory(directory), 3004 fLoaded(false), 3005 fCount(maxCount) 3006 { 3007 } 3008 3009 3010 MostUsedNames::~MostUsedNames() 3011 { 3012 // only write back settings when we've been used 3013 if (!fLoaded) 3014 return; 3015 3016 // write most used list to file 3017 3018 BPath path; 3019 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK) 3020 return; 3021 3022 path.Append(fDirectory); 3023 path.Append(fFileName); 3024 BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); 3025 if (file.InitCheck() == B_OK) { 3026 for (int32 i = 0; i < fList.CountItems(); i++) { 3027 list_entry *entry = static_cast<list_entry *>(fList.ItemAt(i)); 3028 3029 char line[B_FILE_NAME_LENGTH + 5]; 3030 3031 // limit upper bound to react more dynamically to changes 3032 if (--entry->count > 20) 3033 entry->count = 20; 3034 3035 // if the item hasn't been chosen in a while, remove it 3036 // (but leave at least one item in the list) 3037 if (entry->count < -10 && i > 0) 3038 continue; 3039 3040 sprintf(line, "%ld %s\n", entry->count, entry->name); 3041 if (file.Write(line, strlen(line)) < B_OK) 3042 break; 3043 } 3044 } 3045 file.Unset(); 3046 3047 // free data 3048 3049 for (int32 i = fList.CountItems(); i-- > 0;) { 3050 list_entry *entry = static_cast<list_entry *>(fList.ItemAt(i)); 3051 free(entry->name); 3052 delete entry; 3053 } 3054 } 3055 3056 3057 bool 3058 MostUsedNames::ObtainList(BList *list) 3059 { 3060 if (!list) 3061 return false; 3062 3063 if (!fLoaded) 3064 UpdateList(); 3065 3066 fLock.Lock(); 3067 3068 list->MakeEmpty(); 3069 for (int32 i = 0; i < fCount; i++) { 3070 list_entry *entry = static_cast<list_entry *>(fList.ItemAt(i)); 3071 if (entry == NULL) 3072 return true; 3073 3074 list->AddItem(entry->name); 3075 } 3076 return true; 3077 } 3078 3079 3080 void 3081 MostUsedNames::ReleaseList() 3082 { 3083 fLock.Unlock(); 3084 } 3085 3086 3087 void 3088 MostUsedNames::AddName(const char *name) 3089 { 3090 fLock.Lock(); 3091 3092 if (!fLoaded) 3093 LoadList(); 3094 3095 // remove last entry if there are more than 3096 // 2*fCount entries in the list 3097 3098 list_entry *entry = NULL; 3099 3100 if (fList.CountItems() > fCount * 2) { 3101 entry = static_cast<list_entry *>(fList.RemoveItem(fList.CountItems() - 1)); 3102 3103 // is this the name we want to add here? 3104 if (strcmp(name, entry->name)) { 3105 free(entry->name); 3106 delete entry; 3107 entry = NULL; 3108 } else 3109 fList.AddItem(entry); 3110 } 3111 3112 if (entry == NULL) { 3113 for (int32 i = 0; (entry = static_cast<list_entry *>(fList.ItemAt(i))) != NULL; i++) 3114 if (!strcmp(entry->name, name)) 3115 break; 3116 } 3117 3118 if (entry == NULL) { 3119 entry = new list_entry; 3120 entry->name = strdup(name); 3121 entry->count = 1; 3122 3123 fList.AddItem(entry); 3124 } else if (entry->count < 0) 3125 entry->count = 1; 3126 else 3127 entry->count++; 3128 3129 fLock.Unlock(); 3130 UpdateList(); 3131 } 3132 3133 3134 int 3135 MostUsedNames::CompareNames(const void *a,const void *b) 3136 { 3137 list_entry *entryA = *(list_entry **)a; 3138 list_entry *entryB = *(list_entry **)b; 3139 3140 if (entryA->count == entryB->count) 3141 return strcasecmp(entryA->name,entryB->name); 3142 3143 return entryB->count - entryA->count; 3144 } 3145 3146 3147 void 3148 MostUsedNames::LoadList() 3149 { 3150 if (fLoaded) 3151 return; 3152 fLoaded = true; 3153 3154 // load the most used names list 3155 3156 BPath path; 3157 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK) 3158 return; 3159 3160 path.Append(fDirectory); 3161 path.Append(fFileName); 3162 3163 FILE *file = fopen(path.Path(), "r"); 3164 if (file == NULL) 3165 return; 3166 3167 char line[B_FILE_NAME_LENGTH + 5]; 3168 while (fgets(line, sizeof(line), file) != NULL) { 3169 int32 length = (int32)strlen(line) - 1; 3170 if (length >= 0 && line[length] == '\n') 3171 line[length] = '\0'; 3172 3173 int32 count = atoi(line); 3174 3175 char *name = strchr(line, ' '); 3176 if (name == NULL || *(++name) == '\0') 3177 continue; 3178 3179 list_entry *entry = new list_entry; 3180 entry->name = strdup(name); 3181 entry->count = count; 3182 3183 fList.AddItem(entry); 3184 } 3185 fclose(file); 3186 } 3187 3188 3189 void 3190 MostUsedNames::UpdateList() 3191 { 3192 AutoLock<Benaphore> locker(fLock); 3193 3194 if (!fLoaded) 3195 LoadList(); 3196 3197 // sort list items 3198 3199 fList.SortItems(MostUsedNames::CompareNames); 3200 } 3201 3202 } // namespace BPrivate 3203 3204