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 fSearchTrashCheck->ResizeToPreferred(); 760 fMoreOptionsPane->AddItem(fSearchTrashCheck, 1); 761 762 rect.OffsetBy(fSearchTrashCheck->Bounds().Width() + 8, 0); 763 fTemporaryCheck = new BCheckBox(rect, "temporary", "Temporary", 0); 764 fMoreOptionsPane->AddItem(fTemporaryCheck, 1); 765 fTemporaryCheck->SetValue(1); 766 767 BRect latchRect(paneInitialRect); 768 latchRect.left -= 20; 769 latchRect.right = latchRect.left + 10; 770 latchRect.top = paneInitialRect.top + paneInitialRect.Height() / 2 - 5; 771 772 latchRect.bottom = latchRect.top + 12; 773 774 fLatch = new PaneSwitch(latchRect, "moreOptionsLatch", true, 775 B_FOLLOW_BOTTOM | B_FOLLOW_LEFT); 776 AddChild(fLatch); 777 fMoreOptionsPane->SetSwitch(fLatch); 778 779 if (initialMode != kByAttributeItem) 780 AddByNameOrFormulaItems(); 781 else 782 AddByAttributeItems(node); 783 784 // add Search button 785 rect = bounds; 786 rect.left = rect.right - 80; 787 rect.top = rect.bottom - 30; 788 rect.right = rect.left + 60; 789 rect.bottom = rect.top + 20; 790 BButton *button; 791 if (editTemplateOnly) 792 button = new BButton(rect, "save", "Save", 793 new BMessage(kSaveButton), B_FOLLOW_RIGHT + B_FOLLOW_BOTTOM); 794 else 795 button = new BButton(rect, "find", "Search", 796 new BMessage(kFindButton), B_FOLLOW_RIGHT + B_FOLLOW_BOTTOM); 797 798 AddChild(button); 799 button->MakeDefault(true); 800 } 801 802 803 FindPanel::~FindPanel() 804 { 805 } 806 807 808 void 809 FindPanel::AttachedToWindow() 810 { 811 BNode *node = dynamic_cast<FindWindow *>(Window())->QueryNode(); 812 fSearchModeMenu->SetTargetForItems(this); 813 fQueryName->SetTarget(this); 814 fLatch->SetTarget(fMoreOptionsPane); 815 RestoreMimeTypeMenuSelection(node); 816 // preselect the mime we used the last time 817 // have to do it here because AddByAttributeItems will build different 818 // menus based on which mime type is preselected 819 RestoreWindowState(node); 820 821 if (!Window()->CurrentFocus()) { 822 // try to pick a good focus if we restore to one already 823 BTextControl *textControl = dynamic_cast<BTextControl *>(FindView("TextControl")); 824 if (!textControl) { 825 // pick the last text control in the attribute view 826 BString title("TextEntry"); 827 title << (fAttrViewList.CountItems() - 1); 828 textControl = dynamic_cast<BTextControl *>(FindView(title.String())); 829 } 830 if (textControl) 831 textControl->MakeFocus(); 832 } 833 834 BButton *button = dynamic_cast<BButton *>(FindView("remove")); 835 if (button) 836 button->SetTarget(this); 837 838 button = dynamic_cast<BButton *>(FindView("add")); 839 if (button) 840 button->SetTarget(this); 841 842 fVolMenu->SetTargetForItems(this); 843 844 // set target for MIME type items 845 for (int32 index = MimeTypeMenu()->CountItems();index-- > 2;) { 846 BMenu *submenu = MimeTypeMenu()->ItemAt(index)->Submenu(); 847 if (submenu != NULL) 848 submenu->SetTargetForItems(this); 849 } 850 fMimeTypeMenu->SetTargetForItems(this); 851 852 853 if (fDraggableIcon) 854 fDraggableIcon->SetTarget(BMessenger(this)); 855 856 fRecentQueries->SetTargetForItems(Window()); 857 } 858 859 const float kAttrViewDelta = 30; 860 861 BRect 862 FindPanel::InitialViewSize(const BNode *node) 863 { 864 if (!node || InitialMode(node) != (int32)kByAttributeItem) 865 return kInitialRect; 866 867 int32 numAttributes = InitialAttrCount(node); 868 if (numAttributes < 1) 869 numAttributes = 1; 870 871 BRect result = kInitialRect; 872 result.bottom = result.top + kInitialAttrModeWindowHeight 873 + (numAttributes - 1) * kIncrementPerAttribute; 874 875 return result; 876 } 877 878 879 float 880 FindPanel::ViewHeightForMode(uint32 mode, bool moreOptions) 881 { 882 float result = moreOptions ? kMoreOptionsDelta : 0; 883 switch (mode) { 884 case kByFormulaItem: 885 case kByNameItem: 886 return 110 + result; 887 888 case kByAttributeItem: 889 return 110 + result + kAttrViewDelta; 890 891 } 892 TRESPASS(); 893 return 0; 894 } 895 896 897 float 898 FindPanel::BoxHeightForMode(uint32 mode, bool /*moreOptions*/) 899 { 900 switch (mode) { 901 case kByFormulaItem: 902 case kByNameItem: 903 return 40; 904 905 case kByAttributeItem: 906 return 40 + kAttrViewDelta; 907 908 } 909 TRESPASS(); 910 return 0; 911 } 912 913 914 static void 915 PopUpMenuSetTitle(BMenu *menu, const char *title) 916 { 917 // This should really be in BMenuField 918 BMenu *bar = menu->Supermenu(); 919 920 ASSERT(bar); 921 ASSERT(bar->ItemAt(0)); 922 if (!bar || !bar->ItemAt(0)) 923 return; 924 925 bar->ItemAt(0)->SetLabel(title); 926 } 927 928 929 void 930 FindPanel::ShowVolumeMenuLabel() 931 { 932 if (fVolMenu->ItemAt(0)->IsMarked()) { 933 // "all disks" selected 934 PopUpMenuSetTitle(fVolMenu, fVolMenu->ItemAt(0)->Label()); 935 return; 936 } 937 938 // find out if more than one items are marked 939 int32 count = fVolMenu->CountItems(); 940 int32 countSelected = 0; 941 BMenuItem *tmpItem = NULL; 942 for (int32 index = 2; index < count; index++) { 943 BMenuItem *item = fVolMenu->ItemAt(index); 944 if (item->IsMarked()) { 945 countSelected++; 946 tmpItem = item; 947 } 948 } 949 if (countSelected == 0) { 950 // no disk selected, for now revert to search all disks 951 // ToDo: 952 // show no disks here and add a check that will not let the 953 // query go if the user doesn't pick at least one 954 fVolMenu->ItemAt(0)->SetMarked(true); 955 PopUpMenuSetTitle(fVolMenu, fVolMenu->ItemAt(0)->Label()); 956 } else if (countSelected > 1) 957 // if more than two disks selected, don't use the disk name 958 // as a label 959 PopUpMenuSetTitle(fVolMenu, "multiple disks"); 960 else { 961 ASSERT(tmpItem); 962 PopUpMenuSetTitle(fVolMenu, tmpItem->Label()); 963 } 964 } 965 966 967 void 968 FindPanel::MessageReceived(BMessage *message) 969 { 970 entry_ref dir; 971 const char *name; 972 973 switch (message->what) { 974 case kVolumeItem: 975 { 976 // volume changed 977 BMenuItem *invokedItem; 978 dev_t dev; 979 if (message->FindPointer("source", (void **)&invokedItem) != B_OK) 980 return; 981 982 if (message->FindInt32("device", &dev) != B_OK) 983 break; 984 985 BMenu *menu = invokedItem->Menu(); 986 ASSERT(menu); 987 988 if (dev == -1) { 989 990 // all disks selected, uncheck everything else 991 int32 count = menu->CountItems(); 992 for (int32 index = 2; index < count; index++) 993 menu->ItemAt(index)->SetMarked(false); 994 995 // make all disks the title and check it 996 PopUpMenuSetTitle(menu, menu->ItemAt(0)->Label()); 997 menu->ItemAt(0)->SetMarked(true); 998 999 } else { 1000 // a specific volume selected, unmark "all disks" 1001 menu->ItemAt(0)->SetMarked(false); 1002 1003 // toggle mark on invoked item 1004 int32 count = menu->CountItems(); 1005 for (int32 index = 2; index < count; index++) { 1006 BMenuItem *item = menu->ItemAt(index); 1007 1008 if (invokedItem == item) { 1009 // we just selected this 1010 bool wasMarked = item->IsMarked(); 1011 item->SetMarked(!wasMarked); 1012 } 1013 } 1014 } 1015 // make sure the right label is showing 1016 ShowVolumeMenuLabel(); 1017 1018 break; 1019 } 1020 1021 case kByAttributeItem: 1022 case kByNameItem: 1023 case kByFormulaItem: 1024 SwitchMode(message->what); 1025 break; 1026 1027 case kAddItem: 1028 AddAttrView(); 1029 break; 1030 1031 case kRemoveItem: 1032 RemoveAttrView(); 1033 break; 1034 1035 case kMIMETypeItem: 1036 { 1037 BMenuItem *item; 1038 if (message->FindPointer("source", (void **)&item) == B_OK) { 1039 // don't add the "All files and folders" to the list 1040 if (fMimeTypeMenu->IndexOf(item) != 0) 1041 gMostUsedMimeTypes.AddName(item->Label()); 1042 1043 SetCurrentMimeType(item); 1044 } 1045 1046 // mime type switched 1047 if (fMode != kByAttributeItem) 1048 break; 1049 1050 // the attributes for this type may be different, rip out the existing ones 1051 RemoveAttrViewItems(); 1052 1053 Window()->ResizeTo(Window()->Frame().Width(), 1054 ViewHeightForMode(kByAttributeItem, fLatch->Value() != 0)); 1055 1056 BBox *box = dynamic_cast<BBox *>(FindView("Box")); 1057 ASSERT(box); 1058 box->ResizeTo(box->Bounds().Width(), 1059 BoxHeightForMode(kByAttributeItem, fLatch->Value() != 0)); 1060 1061 AddAttrView(); 1062 break; 1063 } 1064 1065 case kNameModifiedMessage: 1066 // the query name was edited, make the query permanent 1067 fTemporaryCheck->SetValue(0); 1068 break; 1069 1070 case B_SAVE_REQUESTED: 1071 { 1072 // finish saving query template from a SaveAs panel 1073 entry_ref ref; 1074 status_t error = message->FindRef("refs", &ref); 1075 1076 if (error == B_OK) { 1077 // direct entry selected, convert to parent dir and name 1078 BEntry entry(&ref); 1079 error = entry.GetParent(&entry); 1080 if (error == B_OK) { 1081 entry.GetRef(&dir); 1082 name = ref.name; 1083 } 1084 } else { 1085 // parent dir and name selected 1086 error = message->FindRef("directory", &dir); 1087 if (error == B_OK) 1088 error = message->FindString("name", &name); 1089 } 1090 if (error == B_OK) 1091 SaveAsQueryOrTemplate(&dir, name, true); 1092 } 1093 break; 1094 1095 case B_COPY_TARGET: 1096 { 1097 // finish drag&drop 1098 const char *str; 1099 const char *mimeType = NULL; 1100 const char *actionSpecifier = NULL; 1101 if (message->FindString("be:types", &str) == B_OK 1102 && strcasecmp(str, B_FILE_MIME_TYPE) == 0 1103 && (message->FindString("be:actionspecifier", &actionSpecifier) == B_OK 1104 || message->FindString("be:filetypes", &mimeType) == B_OK) 1105 && message->FindString("name", &name) == B_OK 1106 && message->FindRef("directory", &dir) == B_OK) { 1107 1108 bool query = false; 1109 bool queryTemplate = false; 1110 1111 if (actionSpecifier 1112 && strcasecmp(actionSpecifier, kDragNDropActionSpecifiers[0]) == 0) 1113 query = true; 1114 else if (actionSpecifier 1115 && strcasecmp(actionSpecifier, kDragNDropActionSpecifiers[1]) == 0) 1116 queryTemplate = true; 1117 else if (mimeType && strcasecmp(mimeType, kDragNDropTypes[0]) == 0) 1118 query = true; 1119 else if (mimeType && strcasecmp(mimeType, kDragNDropTypes[1]) == 0) 1120 queryTemplate = true; 1121 1122 if (query || queryTemplate) 1123 SaveAsQueryOrTemplate(&dir, name, queryTemplate); 1124 } 1125 } 1126 break; 1127 1128 default: 1129 _inherited::MessageReceived(message); 1130 break; 1131 } 1132 } 1133 1134 1135 void 1136 FindPanel::SaveAsQueryOrTemplate(const entry_ref *dir, const char *name, bool queryTemplate) 1137 { 1138 BDirectory directory(dir); 1139 BFile file(&directory, name, O_RDWR | O_CREAT | O_TRUNC); 1140 BNodeInfo(&file).SetType(queryTemplate ? B_QUERY_TEMPLATE_MIMETYPE : B_QUERY_MIMETYPE); 1141 1142 BMessage attach(kAttachFile); 1143 attach.AddRef("directory", dir); 1144 attach.AddString("name", name); 1145 attach.AddBool("template", queryTemplate); 1146 Window()->PostMessage(&attach, 0); 1147 } 1148 1149 1150 void 1151 FindPanel::BuildAttrQuery(BQuery *query, bool &dynamicDate) const 1152 { 1153 dynamicDate = false; 1154 1155 // go through each attrview and add the attr and comparison info 1156 for (int32 index = 0; index < fAttrViewList.CountItems(); index++) { 1157 1158 TAttrView *view = fAttrViewList.ItemAt(index); 1159 BString title; 1160 title << "TextEntry" << index; 1161 1162 BTextControl *textControl = dynamic_cast<BTextControl *> 1163 (view->FindView(title.String())); 1164 if (!textControl) 1165 return; 1166 1167 BMenuField *menuField = dynamic_cast<BMenuField *>(view->FindView("MenuField")); 1168 if (!menuField) 1169 return; 1170 1171 BMenuItem *item = menuField->Menu()->FindMarked(); 1172 if (!item) 1173 continue; 1174 1175 BMessage *message = item->Message(); 1176 int32 type; 1177 if (message->FindInt32("type", &type) == B_OK) { 1178 1179 const char *str; 1180 if (message->FindString("name", &str) == B_OK) 1181 query->PushAttr(str); 1182 else 1183 query->PushAttr(item->Label()); 1184 1185 switch (type) { 1186 case B_STRING_TYPE: 1187 query->PushString(textControl->TextView()->Text(), true); 1188 break; 1189 1190 case B_TIME_TYPE: 1191 { 1192 int flags = 0; 1193 DEBUG_ONLY(time_t result =) 1194 parsedate_etc(textControl->TextView()->Text(), -1, &flags); 1195 dynamicDate = (flags & PARSEDATE_RELATIVE_TIME) != 0; 1196 PRINT(("parsedate_etc - date is %srelative, %l\n", 1197 dynamicDate ? "" : "not ", result)); 1198 1199 query->PushDate(textControl->TextView()->Text()); 1200 } 1201 break; 1202 1203 case B_BOOL_TYPE: 1204 { 1205 uint32 value; 1206 if (strcasecmp(textControl->TextView()->Text(), "true") == 0) 1207 value = 1; 1208 else if (strcasecmp(textControl->TextView()->Text(), "true") == 0) 1209 value = 1; 1210 else 1211 value = (uint32)atoi(textControl->TextView()->Text()); 1212 1213 value %= 2; 1214 query->PushUInt32(value); 1215 } 1216 break; 1217 1218 case B_UINT8_TYPE: 1219 case B_UINT16_TYPE: 1220 case B_UINT32_TYPE: 1221 query->PushUInt32((uint32)StringToScalar(textControl->TextView()->Text())); 1222 break; 1223 1224 case B_INT8_TYPE: 1225 case B_INT16_TYPE: 1226 case B_INT32_TYPE: 1227 query->PushInt32((int32)StringToScalar(textControl->TextView()->Text())); 1228 break; 1229 1230 case B_UINT64_TYPE: 1231 query->PushUInt64((uint64)StringToScalar(textControl->TextView()->Text())); 1232 break; 1233 1234 case B_OFF_T_TYPE: 1235 case B_INT64_TYPE: 1236 query->PushInt64(StringToScalar(textControl->TextView()->Text())); 1237 break; 1238 1239 case B_FLOAT_TYPE: 1240 { 1241 float floatVal; 1242 sscanf(textControl->TextView()->Text(), "%f", &floatVal); 1243 query->PushFloat(floatVal); 1244 } 1245 break; 1246 1247 case B_DOUBLE_TYPE: 1248 { 1249 double doubleVal; 1250 sscanf(textControl->TextView()->Text(), "%lf", &doubleVal); 1251 query->PushDouble(doubleVal); 1252 } 1253 break; 1254 1255 } 1256 } 1257 1258 query_op theOperator; 1259 BMenuItem *operatorItem = item->Submenu()->FindMarked(); 1260 if (operatorItem && operatorItem->Message() != NULL) { 1261 operatorItem->Message()->FindInt32("operator", (int32 *)&theOperator); 1262 query->PushOp(theOperator); 1263 } else 1264 query->PushOp(B_EQ); 1265 1266 // add logic based on selection in Logic menufield 1267 if (index > 0) { 1268 TAttrView *prevView = fAttrViewList.ItemAt(index - 1); 1269 menuField = dynamic_cast<BMenuField *>(prevView->FindView("Logic")); 1270 if (menuField) { 1271 item = menuField->Menu()->FindMarked(); 1272 if (item) { 1273 message = item->Message(); 1274 message->FindInt32("combine", (int32 *)&theOperator); 1275 query->PushOp(theOperator); 1276 } 1277 } else 1278 query->PushOp(B_AND); 1279 } 1280 } 1281 } 1282 1283 1284 void 1285 FindPanel::PushMimeType(BQuery *query) const 1286 { 1287 const char *type; 1288 if (CurrentMimeType(&type) == NULL) 1289 return; 1290 1291 if (strcmp(kAllMimeTypes, type)) { 1292 // add an asterisk if we are searching for a supertype 1293 char buffer[B_FILE_NAME_LENGTH]; 1294 if (strchr(type,'/') == NULL) { 1295 strcpy(buffer,type); 1296 strcat(buffer,"/*"); 1297 type = buffer; 1298 } 1299 1300 query->PushAttr(kAttrMIMEType); 1301 query->PushString(type); 1302 query->PushOp(B_EQ); 1303 query->PushOp(B_AND); 1304 } 1305 } 1306 1307 1308 void 1309 FindPanel::GetByAttrPredicate(BQuery *query, bool &dynamicDate) const 1310 { 1311 ASSERT(Mode() == (int32)kByAttributeItem); 1312 BuildAttrQuery(query, dynamicDate); 1313 PushMimeType(query); 1314 } 1315 1316 1317 void 1318 FindPanel::GetDefaultName(BString &result) const 1319 { 1320 BTextControl *textControl = dynamic_cast<BTextControl *>(FindView("TextControl")); 1321 switch (Mode()) { 1322 case kByNameItem: 1323 result << "Name = " << textControl->TextView()->Text(); 1324 break; 1325 1326 case kByFormulaItem: 1327 result << "Formula " << textControl->TextView()->Text(); 1328 break; 1329 1330 case kByAttributeItem: 1331 { 1332 BMenuItem *item = fMimeTypeMenu->FindMarked(); 1333 if (item != NULL) 1334 result << item->Label() << ": "; 1335 1336 for (int32 i = 0; i < fAttrViewList.CountItems(); i++) { 1337 fAttrViewList.ItemAt(i)->GetDefaultName(result); 1338 if (i + 1 < fAttrViewList.CountItems()) 1339 result << ", "; 1340 } 1341 break; 1342 } 1343 } 1344 } 1345 1346 1347 const char * 1348 FindPanel::UserSpecifiedName() const 1349 { 1350 if (fQueryName->Text()[0] == '\0') 1351 return NULL; 1352 1353 return fQueryName->Text(); 1354 } 1355 1356 1357 void 1358 FindPanel::GetByNamePredicate(BQuery *query) const 1359 { 1360 ASSERT(Mode() == (int32)kByNameItem); 1361 BTextControl *textControl = dynamic_cast<BTextControl *>(FindView("TextControl")); 1362 ASSERT(textControl); 1363 1364 query->PushAttr("name"); 1365 query->PushString(textControl->TextView()->Text(), true); 1366 1367 if (strstr(textControl->TextView()->Text(), "*")) 1368 // assume pattern is a regular expression and try doing an exact match 1369 query->PushOp(B_EQ); 1370 else 1371 query->PushOp(B_CONTAINS); 1372 1373 PushMimeType(query); 1374 } 1375 1376 1377 void 1378 FindPanel::SwitchMode(uint32 mode) 1379 { 1380 if (fMode == mode) 1381 // no work, bail 1382 return; 1383 1384 BBox *box = dynamic_cast<BBox *>(FindView("Box")); 1385 ASSERT(box); 1386 1387 uint32 oldMode = fMode; 1388 BString buffer; 1389 1390 switch (mode) { 1391 case kByFormulaItem: 1392 if (oldMode == kByAttributeItem || oldMode == kByNameItem) { 1393 BQuery query; 1394 if (oldMode == kByAttributeItem) { 1395 bool dummy; 1396 GetByAttrPredicate(&query, dummy); 1397 } else 1398 GetByNamePredicate(&query); 1399 1400 query.GetPredicate(&buffer); 1401 } 1402 // fall thru 1403 1404 case kByNameItem: 1405 { 1406 fMode = mode; 1407 Window()->ResizeTo(Window()->Frame().Width(), 1408 ViewHeightForMode(mode, fLatch->Value() != 0)); 1409 BRect bounds(Bounds()); 1410 bounds.InsetBy(15, 30); 1411 bounds.bottom -= 10; 1412 if (fLatch->Value()) 1413 bounds.bottom -= kMoreOptionsDelta; 1414 box->ResizeTo(bounds.Width(), BoxHeightForMode(mode, fLatch->Value() != 0)); 1415 1416 RemoveByAttributeItems(); 1417 ShowOrHideMimeTypeMenu(); 1418 AddByNameOrFormulaItems(); 1419 1420 if (buffer.Length()) { 1421 ASSERT(mode == kByFormulaItem || oldMode == kByAttributeItem); 1422 BTextControl *textControl = dynamic_cast<BTextControl *> 1423 (FindView("TextControl")); 1424 textControl->SetText(buffer.String()); 1425 } 1426 break; 1427 } 1428 1429 case kByAttributeItem: 1430 { 1431 fMode = mode; 1432 box->ResizeTo(box->Bounds().Width(), 1433 BoxHeightForMode(mode, fLatch->Value() != 0)); 1434 1435 Window()->ResizeTo(Window()->Frame().Width(), 1436 ViewHeightForMode(mode, fLatch->Value() != 0)); 1437 1438 BTextControl *textControl = dynamic_cast<BTextControl *> 1439 (FindView("TextControl")); 1440 1441 if (textControl) { 1442 textControl->RemoveSelf(); 1443 delete textControl; 1444 } 1445 1446 ShowOrHideMimeTypeMenu(); 1447 AddAttrView(); 1448 break; 1449 } 1450 } 1451 } 1452 1453 1454 BMenuItem * 1455 FindPanel::CurrentMimeType(const char **type) const 1456 { 1457 // search for marked item in the list 1458 BMenuItem *item = MimeTypeMenu()->FindMarked(); 1459 1460 // if it's one of the most used items, ignore it 1461 if (item != NULL && MimeTypeMenu()->IndexOf(item) != 0 && item->Submenu() == NULL) 1462 item = NULL; 1463 1464 if (item == NULL) { 1465 for (int32 index = MimeTypeMenu()->CountItems(); index-- > 0;) { 1466 BMenu *submenu = MimeTypeMenu()->ItemAt(index)->Submenu(); 1467 if (submenu != NULL && (item = submenu->FindMarked()) != NULL) 1468 break; 1469 } 1470 } 1471 1472 if (type && item != NULL) { 1473 BMessage *message = item->Message(); 1474 if (!message) 1475 return NULL; 1476 1477 if (message->FindString("mimetype", type) != B_OK) 1478 return NULL; 1479 } 1480 return item; 1481 } 1482 1483 1484 status_t 1485 FindPanel::SetCurrentMimeType(BMenuItem *item) 1486 { 1487 // unmark old MIME type (in most used list, and the tree) 1488 1489 BMenuItem *marked = CurrentMimeType(); 1490 if (marked != NULL) { 1491 marked->SetMarked(false); 1492 1493 if ((marked = MimeTypeMenu()->FindMarked()) != NULL) 1494 marked->SetMarked(false); 1495 } 1496 1497 // mark new MIME type (in most used list, and the tree) 1498 1499 if (item != NULL) { 1500 item->SetMarked(true); 1501 fMimeTypeField->MenuItem()->SetLabel(item->Label()); 1502 1503 BMenuItem *search; 1504 for (int32 i = 2;(search = MimeTypeMenu()->ItemAt(i)) != NULL;i++) { 1505 if (item == search || !search->Label()) 1506 continue; 1507 if (!strcmp(item->Label(),search->Label())) { 1508 search->SetMarked(true); 1509 break; 1510 } 1511 BMenu *submenu = search->Submenu(); 1512 if (submenu) { 1513 for (int32 j = submenu->CountItems();j-- > 0;) { 1514 BMenuItem *sub = submenu->ItemAt(j); 1515 if (!strcmp(item->Label(),sub->Label())) { 1516 sub->SetMarked(true); 1517 break; 1518 } 1519 } 1520 } 1521 } 1522 } 1523 return B_OK; 1524 } 1525 1526 1527 status_t 1528 FindPanel::SetCurrentMimeType(const char *label) 1529 { 1530 // unmark old MIME type (in most used list, and the tree) 1531 1532 BMenuItem *marked = CurrentMimeType(); 1533 if (marked != NULL) { 1534 marked->SetMarked(false); 1535 1536 if ((marked = MimeTypeMenu()->FindMarked()) != NULL) 1537 marked->SetMarked(false); 1538 } 1539 1540 // mark new MIME type (in most used list, and the tree) 1541 1542 fMimeTypeField->MenuItem()->SetLabel(label); 1543 bool found = false; 1544 1545 for (int32 index = MimeTypeMenu()->CountItems(); index-- > 0;) { 1546 BMenuItem *item = MimeTypeMenu()->ItemAt(index); 1547 BMenu *submenu = item->Submenu(); 1548 if (submenu != NULL && !found) { 1549 for (int32 subIndex = submenu->CountItems(); subIndex-- > 0;) { 1550 BMenuItem *subItem = submenu->ItemAt(subIndex); 1551 if (subItem->Label() != NULL && !strcmp(label, subItem->Label())) { 1552 subItem->SetMarked(true); 1553 found = true; 1554 } 1555 } 1556 } 1557 if (item->Label() != NULL && !strcmp(label, item->Label())) { 1558 item->SetMarked(true); 1559 return B_OK; 1560 } 1561 } 1562 1563 return found ? B_OK : B_ENTRY_NOT_FOUND; 1564 } 1565 1566 1567 bool 1568 FindPanel::AddOneMimeTypeToMenu(const ShortMimeInfo *info, void *castToMenu) 1569 { 1570 BPopUpMenu *menu = static_cast<BPopUpMenu *>(castToMenu); 1571 1572 BMimeType type(info->InternalName()); 1573 BMimeType super; 1574 type.GetSupertype(&super); 1575 if (super.InitCheck() < B_OK) 1576 return false; 1577 1578 BMenuItem *superItem = menu->FindItem(super.Type()); 1579 if (superItem != NULL) { 1580 BMessage *msg = new BMessage(kMIMETypeItem); 1581 msg->AddString("mimetype", info->InternalName()); 1582 1583 superItem->Submenu()->AddItem(new IconMenuItem(info->ShortDescription(), 1584 msg, info->InternalName(), B_MINI_ICON)); 1585 } 1586 1587 return false; 1588 } 1589 1590 1591 void 1592 FindPanel::AddMimeTypesToMenu() 1593 { 1594 BMessage *itemMessage = new BMessage(kMIMETypeItem); 1595 itemMessage->AddString("mimetype", kAllMimeTypes); 1596 MimeTypeMenu()->AddItem(new BMenuItem("All files and folders", itemMessage)); 1597 MimeTypeMenu()->AddSeparatorItem(); 1598 MimeTypeMenu()->ItemAt(0)->SetMarked(true); 1599 1600 // add recent MIME types 1601 1602 TTracker *tracker = dynamic_cast<TTracker *>(be_app); 1603 1604 BList list; 1605 if (gMostUsedMimeTypes.ObtainList(&list) && tracker) { 1606 int32 count = 0; 1607 for (int32 index = 0; index < list.CountItems(); index++) { 1608 const char *name = (const char *)list.ItemAt(index); 1609 1610 const ShortMimeInfo *info; 1611 if ((info = tracker->MimeTypes()->FindMimeType(name)) == NULL) 1612 continue; 1613 1614 BMessage *message = new BMessage(kMIMETypeItem); 1615 message->AddString("mimetype", info->InternalName()); 1616 1617 MimeTypeMenu()->AddItem(new BMenuItem(name, message)); 1618 count++; 1619 } 1620 if (count != 0) 1621 MimeTypeMenu()->AddSeparatorItem(); 1622 1623 gMostUsedMimeTypes.ReleaseList(); 1624 } 1625 1626 // add MIME type tree list 1627 1628 BMessage types; 1629 if (BMimeType::GetInstalledSupertypes(&types) == B_OK) { 1630 const char *superType; 1631 int32 index = 0; 1632 1633 while (types.FindString("super_types",index++,&superType) == B_OK) { 1634 BMenu *superMenu = new BMenu(superType); 1635 1636 BMessage *message = new BMessage(kMIMETypeItem); 1637 message->AddString("mimetype", superType); 1638 1639 MimeTypeMenu()->AddItem(new IconMenuItem(superMenu, message, 1640 superType, B_MINI_ICON)); 1641 1642 // the MimeTypeMenu's font is not correct at this time 1643 superMenu->SetFont(be_plain_font); 1644 } 1645 } 1646 1647 if (tracker) 1648 tracker->MimeTypes()->EachCommonType(&FindPanel::AddOneMimeTypeToMenu, 1649 MimeTypeMenu()); 1650 1651 // remove empty super type menus (and set target) 1652 1653 for (int32 index = MimeTypeMenu()->CountItems();index-- > 2;) { 1654 BMenuItem *item = MimeTypeMenu()->ItemAt(index); 1655 BMenu *submenu = item->Submenu(); 1656 if (submenu != NULL) { 1657 if (submenu->CountItems() == 0) { 1658 MimeTypeMenu()->RemoveItem(item); 1659 delete item; 1660 } else 1661 submenu->SetTargetForItems(this); 1662 } 1663 } 1664 1665 MimeTypeMenu()->SetTargetForItems(this); 1666 } 1667 1668 1669 void 1670 FindPanel::AddVolumes(BMenu *menu) 1671 { 1672 // ToDo: add calls to this to rebuild the menu when a volume gets mounted 1673 1674 BMessage *message = new BMessage(kVolumeItem); 1675 message->AddInt32("device", -1); 1676 menu->AddItem(new BMenuItem("All disks", message)); 1677 menu->AddSeparatorItem(); 1678 PopUpMenuSetTitle(menu, "All disks"); 1679 1680 BVolumeRoster roster; 1681 BVolume volume; 1682 roster.Rewind(); 1683 while (roster.GetNextVolume(&volume) == B_OK) { 1684 if (volume.IsPersistent() && volume.KnowsQuery()) { 1685 BDirectory root; 1686 if (volume.GetRootDirectory(&root) != B_OK) 1687 continue; 1688 1689 BEntry entry; 1690 root.GetEntry(&entry); 1691 1692 Model model(&entry, true); 1693 if (model.InitCheck() != B_OK) 1694 continue; 1695 1696 message = new BMessage(kVolumeItem); 1697 message->AddInt32("device", volume.Device()); 1698 menu->AddItem(new ModelMenuItem(&model, model.Name(), message)); 1699 } 1700 } 1701 1702 if (menu->ItemAt(0)) 1703 menu->ItemAt(0)->SetMarked(true); 1704 1705 menu->SetTargetForItems(this); 1706 } 1707 1708 1709 typedef std::pair<entry_ref, uint32> EntryWithDate; 1710 1711 static int 1712 SortByDatePredicate(const EntryWithDate *entry1, const EntryWithDate *entry2) 1713 { 1714 return entry1->second > entry2->second ? 1715 -1 : (entry1->second == entry2->second ? 0 : 1); 1716 } 1717 1718 struct AddOneRecentParams { 1719 BMenu *menu; 1720 const BMessenger *target; 1721 uint32 what; 1722 }; 1723 1724 static const entry_ref * 1725 AddOneRecentItem(const entry_ref *ref, void *castToParams) 1726 { 1727 AddOneRecentParams *params = (AddOneRecentParams *)castToParams; 1728 1729 BMessage *message = new BMessage(params->what); 1730 message->AddRef("refs", ref); 1731 1732 char type[B_MIME_TYPE_LENGTH]; 1733 BNode node(ref); 1734 BNodeInfo(&node).GetType(type); 1735 BMenuItem *item = new IconMenuItem(ref->name, message, type, B_MINI_ICON); 1736 item->SetTarget(*params->target); 1737 params->menu->AddItem(item); 1738 1739 return NULL; 1740 } 1741 1742 1743 void 1744 FindPanel::AddRecentQueries(BMenu *menu, bool addSaveAsItem, const BMessenger *target, 1745 uint32 what) 1746 { 1747 BObjectList<entry_ref> templates(10, true); 1748 BObjectList<EntryWithDate> recentQueries(10, true); 1749 1750 // find all the queries on all volumes 1751 BVolumeRoster roster; 1752 BVolume volume; 1753 roster.Rewind(); 1754 while (roster.GetNextVolume(&volume) == B_OK) { 1755 if (volume.IsPersistent() && volume.KnowsQuery() && volume.KnowsAttr()) { 1756 1757 BQuery query; 1758 query.SetVolume(&volume); 1759 query.SetPredicate("_trk/recentQuery == 1"); 1760 if (query.Fetch() != B_OK) 1761 continue; 1762 1763 entry_ref ref; 1764 while (query.GetNextRef(&ref) == B_OK) { 1765 // ignore queries in the Trash 1766 if (FSInTrashDir(&ref)) 1767 continue; 1768 1769 char type[B_MIME_TYPE_LENGTH]; 1770 BNode node(&ref); 1771 BNodeInfo(&node).GetType(type); 1772 1773 if (strcasecmp(type, B_QUERY_TEMPLATE_MIMETYPE) == 0) 1774 templates.AddItem(new entry_ref(ref)); 1775 else { 1776 uint32 changeTime; 1777 if (node.ReadAttr(kAttrQueryLastChange, B_INT32_TYPE, 0, 1778 &changeTime, sizeof(uint32)) != sizeof(uint32)) 1779 continue; 1780 1781 recentQueries.AddItem(new EntryWithDate(ref, changeTime)); 1782 } 1783 1784 } 1785 } 1786 } 1787 1788 // we are only adding last ten queries 1789 recentQueries.SortItems(SortByDatePredicate); 1790 1791 // but all templates 1792 AddOneRecentParams params; 1793 params.menu = menu; 1794 params.target = target; 1795 params.what = what; 1796 templates.EachElement(AddOneRecentItem, ¶ms); 1797 1798 int32 count = recentQueries.CountItems(); 1799 // show only up to 10 recent queries 1800 if (count > 10) 1801 count = 10; 1802 1803 if (templates.CountItems() && count) 1804 menu->AddSeparatorItem(); 1805 1806 for (int32 index = 0; index < count; index++) 1807 AddOneRecentItem(&recentQueries.ItemAt(index)->first, ¶ms); 1808 1809 1810 if (addSaveAsItem) { 1811 // add a Save as template item 1812 if (count || templates.CountItems()) 1813 menu->AddSeparatorItem(); 1814 1815 BMessage *message = new BMessage(kRunSaveAsTemplatePanel); 1816 BMenuItem *item = new BMenuItem("Save Query as template"B_UTF8_ELLIPSIS, message); 1817 menu->AddItem(item); 1818 } 1819 } 1820 1821 1822 void 1823 FindPanel::AddOneAttributeItem(BBox *box, BRect rect) 1824 { 1825 TAttrView *attrView = new TAttrView(rect, fAttrViewList.CountItems()); 1826 fAttrViewList.AddItem(attrView); 1827 1828 box->AddChild(attrView); 1829 attrView->MakeTextViewFocus(); 1830 } 1831 1832 1833 void 1834 FindPanel::SetUpAddRemoveButtons(BBox *box) 1835 { 1836 BButton *button = Window() != NULL 1837 ? dynamic_cast<BButton *>(Window()->FindView("remove")) 1838 : NULL; 1839 if (button == NULL) { 1840 BRect rect = box->Bounds(); 1841 rect.InsetBy(5, 10); 1842 rect.top = rect.bottom - 20; 1843 rect.right = rect.left + 22 + be_plain_font->StringWidth("Add"); 1844 1845 button = new BButton(rect, "add", "Add", new BMessage(kAddItem), 1846 B_FOLLOW_RIGHT + B_FOLLOW_BOTTOM); 1847 button->SetTarget(this); 1848 box->AddChild(button); 1849 1850 rect.OffsetBy(rect.Width() + 6, 0); 1851 rect.right = rect.left + 22 + be_plain_font->StringWidth("Remove"); 1852 button = new BButton(rect, "remove", "Remove", new BMessage(kRemoveItem), 1853 B_FOLLOW_RIGHT + B_FOLLOW_BOTTOM); 1854 1855 button->SetEnabled(false); 1856 button->SetTarget(this); 1857 box->AddChild(button); 1858 } 1859 // enable remove button as needed 1860 button->SetEnabled(fAttrViewList.CountItems() > 1); 1861 } 1862 1863 1864 void 1865 FindPanel::FillCurrentQueryName(BTextControl *queryName, FindWindow *window) 1866 { 1867 ASSERT(window); 1868 queryName->SetText(window->QueryName()); 1869 } 1870 1871 1872 void 1873 FindPanel::AddAttrView() 1874 { 1875 BBox *box = dynamic_cast<BBox *>(FindView("Box")); 1876 BRect bounds(Bounds()); 1877 1878 TAttrView *previous = fAttrViewList.LastItem(); 1879 1880 if (previous) 1881 Window()->ResizeBy(0, 30); 1882 1883 bounds = Bounds(); 1884 bounds.InsetBy(15, 30); 1885 bounds.bottom -= 10 + (fLatch->Value() ? kAttrViewDelta : 0); 1886 1887 if (previous) { 1888 box->ResizeTo(bounds.Width(), bounds.Height()); 1889 bounds = previous->Frame(); 1890 bounds.OffsetBy(0, 30); 1891 } else { 1892 bounds = box->Bounds(); 1893 bounds.InsetBy(5, 5); 1894 bounds.bottom = bounds.top + 25; 1895 } 1896 AddOneAttributeItem(box, bounds); 1897 1898 // add logic to previous attrview 1899 if (previous) 1900 previous->AddLogicMenu(); 1901 1902 SetUpAddRemoveButtons(box); 1903 1904 // populate mime popup 1905 TAttrView *last = fAttrViewList.LastItem(); 1906 last->AddMimeTypeAttrs(); 1907 } 1908 1909 1910 void 1911 FindPanel::RemoveAttrView() 1912 { 1913 if (fAttrViewList.CountItems() < 2) 1914 return; 1915 1916 BBox *box = dynamic_cast<BBox *>(FindView("Box")); 1917 TAttrView *attrView = fAttrViewList.LastItem(); 1918 if (!box || !attrView) 1919 return; 1920 1921 Window()->ResizeBy(0, -30); 1922 BRect bounds(Bounds()); 1923 bounds.InsetBy(15, 30); 1924 bounds.bottom -= 10 + (fLatch->Value() ? kAttrViewDelta : 0); 1925 box->ResizeTo(bounds.Width(), bounds.Height()); 1926 1927 fAttrViewList.RemoveItem(attrView); 1928 attrView->RemoveSelf(); 1929 delete attrView; 1930 1931 attrView = fAttrViewList.LastItem(); 1932 attrView->RemoveLogicMenu(); 1933 attrView->MakeTextViewFocus(); 1934 1935 if (fAttrViewList.CountItems() != 1) 1936 return; 1937 1938 BButton *button = dynamic_cast<BButton *>(Window()->FindView("remove")); 1939 if (button) 1940 button->SetEnabled(false); 1941 } 1942 1943 1944 uint32 1945 FindPanel::InitialMode(const BNode *node) 1946 { 1947 if (!node || node->InitCheck() != B_OK) 1948 return kByNameItem; 1949 1950 uint32 result; 1951 if (node->ReadAttr(kAttrQueryInitialMode, B_INT32_TYPE, 0, 1952 (int32 *)&result, sizeof(int32)) <= 0) 1953 return kByNameItem; 1954 1955 return result; 1956 } 1957 1958 1959 int32 1960 FindPanel::InitialAttrCount(const BNode *node) 1961 { 1962 if (!node || node->InitCheck() != B_OK) 1963 return 1; 1964 1965 int32 result; 1966 if (node->ReadAttr(kAttrQueryInitialNumAttrs, B_INT32_TYPE, 0, 1967 &result, sizeof(int32)) <= 0) 1968 return 1; 1969 1970 return result; 1971 } 1972 1973 1974 static int32 1975 SelectItemWithLabel(BMenu *menu, const char *label) 1976 { 1977 for (int32 index = menu->CountItems(); index-- > 0;) { 1978 BMenuItem *item = menu->ItemAt(index); 1979 1980 if (strcmp(label, item->Label()) == 0) { 1981 item->SetMarked(true); 1982 return index; 1983 } 1984 } 1985 return -1; 1986 } 1987 1988 1989 void 1990 FindPanel::SaveWindowState(BNode *node, bool editTemplate) 1991 { 1992 ASSERT(node->InitCheck() == B_OK); 1993 1994 BMenuItem *item = CurrentMimeType(); 1995 if (item) { 1996 BString label(item->Label()); 1997 node->WriteAttrString(kAttrQueryInitialMime, &label); 1998 } 1999 2000 uint32 mode = Mode(); 2001 node->WriteAttr(kAttrQueryInitialMode, B_INT32_TYPE, 0, 2002 (int32 *)&mode, sizeof(int32)); 2003 2004 MoreOptionsStruct saveMoreOptions; 2005 saveMoreOptions.showMoreOptions = fLatch->Value() != 0; 2006 2007 saveMoreOptions.searchTrash = fSearchTrashCheck->Value() != 0; 2008 saveMoreOptions.temporary = fTemporaryCheck->Value() != 0; 2009 2010 if (node->WriteAttr(kAttrQueryMoreOptions, B_RAW_TYPE, 0, &saveMoreOptions, 2011 sizeof(saveMoreOptions)) == sizeof(saveMoreOptions)) 2012 node->RemoveAttr(kAttrQueryMoreOptionsForeign); 2013 2014 if (editTemplate) { 2015 if (UserSpecifiedName()) { 2016 BString name(UserSpecifiedName()); 2017 node->WriteAttrString(kAttrQueryTemplateName, &name); 2018 } 2019 } 2020 2021 switch (Mode()) { 2022 case kByAttributeItem: 2023 { 2024 BMessage message; 2025 int32 count = fAttrViewList.CountItems(); 2026 node->WriteAttr(kAttrQueryInitialNumAttrs, B_INT32_TYPE, 0, 2027 &count, sizeof(int32)); 2028 2029 for (int32 index = 0; index < count; index++) 2030 fAttrViewList.ItemAt(index)->SaveState(&message, index); 2031 2032 ssize_t size = message.FlattenedSize(); 2033 char *buffer = new char[size]; 2034 status_t result = message.Flatten(buffer, size); 2035 ASSERT(result == B_OK); 2036 result = node->WriteAttr(kAttrQueryInitialAttrs, B_MESSAGE_TYPE, 0, 2037 buffer, (size_t)size); 2038 ASSERT(result == size); 2039 delete [] buffer; 2040 } 2041 break; 2042 2043 case kByNameItem: 2044 case kByFormulaItem: 2045 { 2046 BTextControl *textControl = dynamic_cast<BTextControl *> 2047 (FindView("TextControl")); 2048 ASSERT(textControl); 2049 BString formula(textControl->TextView()->Text()); 2050 node->WriteAttrString(kAttrQueryInitialString, &formula); 2051 break; 2052 } 2053 } 2054 } 2055 2056 2057 void 2058 FindPanel::SwitchToTemplate(const BNode *node) 2059 { 2060 if (fLatch->Value()) { 2061 // this is kind of a hack - the following code up to 2062 // RestoreWindowState assumes the latch is closed 2063 // Would be nicer if all the size of the window were set once 2064 // and correctly - this is not easy thought because the latch 2065 // controls the window size in relative increments 2066 fLatch->SetValue(0); 2067 fMoreOptionsPane->SetMode(0); 2068 } 2069 2070 SwitchMode(InitialMode(node)); 2071 // update the menu to correspond to the mode 2072 MarkNamedMenuItem(fSearchModeMenu, InitialMode(node), true); 2073 2074 BRect initialRect(InitialViewSize(node)); 2075 Window()->ResizeTo(initialRect.Width(), initialRect.Height()); 2076 if (Mode() == (int32)kByAttributeItem) { 2077 RemoveByAttributeItems(); 2078 ResizeAttributeBox(node); 2079 AddByAttributeItems(node); 2080 } 2081 2082 RestoreWindowState(node); 2083 } 2084 2085 2086 void 2087 FindPanel::RestoreMimeTypeMenuSelection(const BNode *node) 2088 { 2089 if (Mode() == (int32)kByFormulaItem || node == NULL || node->InitCheck() != B_OK) 2090 return; 2091 2092 BString buffer; 2093 if (node->ReadAttrString(kAttrQueryInitialMime, &buffer) == B_OK) 2094 SetCurrentMimeType(buffer.String()); 2095 } 2096 2097 2098 void 2099 FindPanel::RestoreWindowState(const BNode *node) 2100 { 2101 fMode = InitialMode(node); 2102 if (!node || node->InitCheck() != B_OK) 2103 return; 2104 2105 ShowOrHideMimeTypeMenu(); 2106 RestoreMimeTypeMenuSelection(node); 2107 MoreOptionsStruct saveMoreOptions; 2108 2109 bool storesMoreOptions = ReadAttr(node, kAttrQueryMoreOptions, 2110 kAttrQueryMoreOptionsForeign, B_RAW_TYPE, 0, &saveMoreOptions, 2111 sizeof(saveMoreOptions), &MoreOptionsStruct::EndianSwap) 2112 != kReadAttrFailed; 2113 2114 if (storesMoreOptions) { 2115 // need to sanitize to true or false here, could have picked 2116 // up garbage from attributes 2117 2118 saveMoreOptions.showMoreOptions = 2119 (saveMoreOptions.showMoreOptions != 0); 2120 2121 fLatch->SetValue(saveMoreOptions.showMoreOptions); 2122 fMoreOptionsPane->SetMode(saveMoreOptions.showMoreOptions); 2123 2124 fSearchTrashCheck->SetValue(saveMoreOptions.searchTrash); 2125 fTemporaryCheck->SetValue(saveMoreOptions.temporary); 2126 2127 fQueryName->SetModificationMessage(NULL); 2128 FillCurrentQueryName(fQueryName, dynamic_cast<FindWindow *>(Window())); 2129 2130 // set modification message after checking the temporary check box, 2131 // and filling out the text control so that we do not 2132 // always trigger clearing of the temporary check box. 2133 fQueryName->SetModificationMessage(new BMessage(kNameModifiedMessage)); 2134 } 2135 2136 // get volumes to perform query on 2137 bool searchAllVolumes = true; 2138 2139 attr_info info; 2140 if (node->GetAttrInfo(kAttrQueryVolume, &info) == B_OK) { 2141 char *buffer = new char[info.size]; 2142 if (node->ReadAttr(kAttrQueryVolume, B_MESSAGE_TYPE, 0, buffer, (size_t)info.size) 2143 == info.size) { 2144 BMessage message; 2145 if (message.Unflatten(buffer) == B_OK) { 2146 for (int32 index = 0; ;index++) { 2147 ASSERT(index < 100); 2148 BVolume volume; 2149 // match a volume with the info embedded in the message 2150 status_t result = MatchArchivedVolume(&volume, &message, index); 2151 if (result == B_OK) { 2152 char name[256]; 2153 volume.GetName(name); 2154 SelectItemWithLabel(fVolMenu, name); 2155 searchAllVolumes = false; 2156 } else if (result != B_DEV_BAD_DRIVE_NUM) 2157 // if B_DEV_BAD_DRIVE_NUM, the volume just isn't mounted this 2158 // time around, keep looking for more 2159 // if other error, bail 2160 break; 2161 } 2162 } 2163 } 2164 delete [] buffer; 2165 } 2166 // mark or unmark "All disks" 2167 fVolMenu->ItemAt(0)->SetMarked(searchAllVolumes); 2168 ShowVolumeMenuLabel(); 2169 2170 switch (Mode()) { 2171 case kByAttributeItem: 2172 { 2173 int32 count = InitialAttrCount(node); 2174 2175 attr_info info; 2176 if (node->GetAttrInfo(kAttrQueryInitialAttrs, &info) != B_OK) 2177 break; 2178 char *buffer = new char[info.size]; 2179 if (node->ReadAttr(kAttrQueryInitialAttrs, B_MESSAGE_TYPE, 0, buffer, (size_t)info.size) 2180 == info.size) { 2181 BMessage message; 2182 if (message.Unflatten(buffer) == B_OK) 2183 for (int32 index = 0; index < count; index++) 2184 fAttrViewList.ItemAt(index)->RestoreState(message, index); 2185 } 2186 delete [] buffer; 2187 break; 2188 } 2189 2190 case kByNameItem: 2191 case kByFormulaItem: 2192 { 2193 BString buffer; 2194 if (node->ReadAttrString(kAttrQueryInitialString, &buffer) == B_OK) { 2195 BTextControl *textControl = dynamic_cast<BTextControl *> 2196 (FindView("TextControl")); 2197 ASSERT(textControl); 2198 2199 textControl->TextView()->SetText(buffer.String()); 2200 } 2201 } 2202 break; 2203 } 2204 2205 // try to restore focus and possibly text selection 2206 BString focusedView; 2207 if (node->ReadAttrString("_trk/focusedView", &focusedView) == B_OK) { 2208 BView *view = FindView(focusedView.String()); 2209 if (view) { 2210 view->MakeFocus(); 2211 BTextControl *textControl = dynamic_cast<BTextControl *>(view); 2212 if (textControl) { 2213 int32 selStart = 0, selEnd = LONG_MAX; 2214 node->ReadAttr("_trk/focusedSelStart", B_INT32_TYPE, 0, 2215 &selStart, sizeof(selStart)); 2216 node->ReadAttr("_trk/focusedSelEnd", B_INT32_TYPE, 0, 2217 &selEnd, sizeof(selEnd)); 2218 textControl->TextView()->Select(selStart, selEnd); 2219 } 2220 } 2221 } 2222 } 2223 2224 2225 void 2226 FindPanel::ResizeAttributeBox(const BNode *node) 2227 { 2228 BBox *box = dynamic_cast<BBox *>(FindView("Box")); 2229 BRect bounds(box->Bounds()); 2230 int32 count = InitialAttrCount(node); 2231 2232 bounds.bottom = count * 30 + 40; 2233 box->ResizeTo(bounds.Width(), bounds.Height()); 2234 } 2235 2236 2237 void 2238 FindPanel::AddByAttributeItems(const BNode *node) 2239 { 2240 BBox *box = dynamic_cast<BBox *>(FindView("Box")); 2241 ASSERT(box); 2242 BRect bounds(box->Bounds()); 2243 2244 int32 numAttributes = InitialAttrCount(node); 2245 if (numAttributes < 1) 2246 numAttributes = 1; 2247 2248 BRect rect(bounds); 2249 rect.InsetBy(5, 5); 2250 rect.bottom = rect.top + 25; 2251 2252 for (int32 index = 0; index < numAttributes; index ++) { 2253 AddOneAttributeItem(box, rect); 2254 rect.OffsetBy(0, 30); 2255 } 2256 SetUpAddRemoveButtons(box); 2257 } 2258 2259 2260 void 2261 FindPanel::AddByNameOrFormulaItems() 2262 { 2263 BBox *box = dynamic_cast<BBox *>(FindView("Box")); 2264 2265 BRect bounds(box->Bounds()); 2266 bounds.InsetBy(10, 10); 2267 BTextControl *textControl = new BTextControl(bounds, "TextControl", "", "", NULL); 2268 textControl->SetDivider(0.0f); 2269 box->AddChild(textControl); 2270 textControl->MakeFocus(); 2271 } 2272 2273 2274 void 2275 FindPanel::RemoveAttrViewItems() 2276 { 2277 for (;;) { 2278 BView *view = FindView("AttrView"); 2279 if (view == NULL) 2280 break; 2281 view->RemoveSelf(); 2282 delete view; 2283 } 2284 2285 fAttrViewList.MakeEmpty(); 2286 } 2287 2288 2289 void 2290 FindPanel::RemoveByAttributeItems() 2291 { 2292 RemoveAttrViewItems(); 2293 BView *view = FindView("add"); 2294 if (view) { 2295 view->RemoveSelf(); 2296 delete view; 2297 } 2298 2299 view = FindView("remove"); 2300 if (view) { 2301 view->RemoveSelf(); 2302 delete view; 2303 } 2304 2305 view = dynamic_cast<BTextControl *>(FindView("TextControl")); 2306 if (view) { 2307 view->RemoveSelf(); 2308 delete view; 2309 } 2310 } 2311 2312 2313 void 2314 FindPanel::ShowOrHideMimeTypeMenu() 2315 { 2316 BMenuField *menuField = dynamic_cast<BMenuField *>(FindView("MimeTypeMenu")); 2317 if (Mode() == (int32)kByFormulaItem && !menuField->IsHidden()) 2318 menuField->Hide(); 2319 else if (menuField->IsHidden()) 2320 menuField->Show(); 2321 } 2322 2323 2324 // #pragma mark - 2325 2326 2327 TAttrView::TAttrView(BRect frame, int32 index) 2328 : BView(frame, "AttrView", B_FOLLOW_NONE, B_WILL_DRAW) 2329 { 2330 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 2331 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 2332 2333 BPopUpMenu *menu = new BPopUpMenu("PopUp"); 2334 2335 // add NAME attribute to popup 2336 BMenu *submenu = new BMenu("Name"); 2337 submenu->SetRadioMode(true); 2338 submenu->SetFont(be_plain_font); 2339 BMessage *message = new BMessage(kAttributeItemMain); 2340 message->AddString("name", "name"); 2341 message->AddInt32("type", B_STRING_TYPE); 2342 BMenuItem *item = new BMenuItem(submenu, message); 2343 menu->AddItem(item); 2344 2345 const int32 operators[] = {B_CONTAINS, B_EQ, B_NE, B_BEGINS_WITH, B_ENDS_WITH}; 2346 const char *operatorLabels[] = {"contains", "is", "is not", "starts with", "ends with"}; 2347 2348 for (int32 i = 0;i < 5;i++) { 2349 message = new BMessage(kAttributeItem); 2350 message->AddInt32("operator", operators[i]); 2351 submenu->AddItem(new BMenuItem(operatorLabels[i], message)); 2352 } 2353 2354 // mark first item 2355 menu->ItemAt(0)->SetMarked(true); 2356 submenu->ItemAt(0)->SetMarked(true); 2357 2358 // add SIZE attribute 2359 submenu = new BMenu("Size"); 2360 submenu->SetRadioMode(true); 2361 submenu->SetFont(be_plain_font); 2362 message = new BMessage(kAttributeItemMain); 2363 message->AddString("name", "size"); 2364 message->AddInt32("type", B_OFF_T_TYPE); 2365 item = new BMenuItem(submenu, message); 2366 menu->AddItem(item); 2367 2368 message = new BMessage(kAttributeItem); 2369 message->AddInt32("operator", B_GE); 2370 submenu->AddItem(new BMenuItem("greater than", message)); 2371 2372 message = new BMessage(kAttributeItem); 2373 message->AddInt32("operator", B_LE); 2374 submenu->AddItem(new BMenuItem("less than", message)); 2375 2376 message = new BMessage(kAttributeItem); 2377 message->AddInt32("operator", B_EQ); 2378 submenu->AddItem(new BMenuItem("is", message)); 2379 2380 // add "modified" field 2381 submenu = new BMenu("Modified"); 2382 submenu->SetRadioMode(true); 2383 submenu->SetFont(be_plain_font); 2384 message = new BMessage(kAttributeItemMain); 2385 message->AddString("name", "last_modified"); 2386 message->AddInt32("type", B_TIME_TYPE); 2387 item = new BMenuItem(submenu, message); 2388 menu->AddItem(item); 2389 2390 message = new BMessage(kAttributeItem); 2391 message->AddInt32("operator", B_LE); 2392 submenu->AddItem(new BMenuItem("before", message)); 2393 2394 message = new BMessage(kAttributeItem); 2395 message->AddInt32("operator", B_GE); 2396 submenu->AddItem(new BMenuItem("after", message)); 2397 2398 BRect bounds(Bounds()); 2399 bounds.right = bounds.left + 100; 2400 bounds.bottom = bounds.top + 15; 2401 fMenuField = new BMenuField(bounds, "MenuField", "", menu); 2402 fMenuField->SetDivider(0.0f); 2403 2404 // add text entry box 2405 bounds = Bounds(); 2406 bounds.left += bounds.right - 180; 2407 bounds.top += 2; 2408 bounds.right -= 42; 2409 BString title("TextEntry"); 2410 title << index; 2411 fTextControl = new BTextControl(bounds, title.String(), "", "", NULL); 2412 fTextControl->SetDivider(0.0f); 2413 AddChild(fTextControl); 2414 2415 AddChild(fMenuField); 2416 // add attributes from currently selected mimetype 2417 } 2418 2419 2420 TAttrView::~TAttrView() 2421 { 2422 } 2423 2424 2425 void 2426 TAttrView::AttachedToWindow() 2427 { 2428 BMenu *menu = fMenuField->Menu(); 2429 // target everything 2430 menu->SetTargetForItems(this); 2431 2432 for (int32 index = menu->CountItems() - 1; index >= 0; index--) 2433 menu->SubmenuAt(index)->SetTargetForItems(this); 2434 } 2435 2436 2437 void 2438 TAttrView::MakeTextViewFocus() 2439 { 2440 fTextControl->MakeFocus(); 2441 } 2442 2443 2444 void 2445 TAttrView::RestoreState(const BMessage &message, int32 index) 2446 { 2447 BMenu *menu = fMenuField->Menu(); 2448 // decode menu selections 2449 2450 AddMimeTypeAttrs(menu); 2451 2452 const char *label; 2453 if (message.FindString("menuSelection", index, &label) == B_OK) { 2454 int32 itemIndex = SelectItemWithLabel(menu, label); 2455 if (itemIndex >=0) { 2456 menu = menu->SubmenuAt(itemIndex); 2457 if (menu && message.FindString("subMenuSelection", index, &label) 2458 == B_OK) 2459 SelectItemWithLabel(menu, label); 2460 } 2461 } 2462 2463 // decode attribute text 2464 ASSERT(fTextControl); 2465 const char *string; 2466 if (message.FindString("attrViewText", index, &string) == B_OK) 2467 fTextControl->TextView()->SetText(string); 2468 2469 int32 logicMenuSelectedIndex; 2470 BMenuField *field = dynamic_cast<BMenuField *>(FindView("Logic")); 2471 if (message.FindInt32("logicalRelation", index, 2472 &logicMenuSelectedIndex) == B_OK) { 2473 if (field) 2474 field->Menu()->ItemAt(logicMenuSelectedIndex)->SetMarked(true); 2475 else 2476 AddLogicMenu(logicMenuSelectedIndex == 0); 2477 } 2478 } 2479 2480 2481 void 2482 TAttrView::SaveState(BMessage *message, int32) 2483 { 2484 BMenu *menu = fMenuField->Menu(); 2485 2486 // encode main attribute menu selection 2487 BMenuItem *item = menu->FindMarked(); 2488 message->AddString("menuSelection", item ? item->Label() : ""); 2489 2490 // encode submenu selection 2491 const char *label = ""; 2492 if (item) { 2493 BMenu *submenu = menu->SubmenuAt(menu->IndexOf(item)); 2494 if (submenu) { 2495 item = submenu->FindMarked(); 2496 if (item) 2497 label = item->Label(); 2498 } 2499 } 2500 message->AddString("subMenuSelection", label); 2501 2502 // encode attribute text 2503 ASSERT(fTextControl); 2504 message->AddString("attrViewText", fTextControl->TextView()->Text()); 2505 2506 BMenuField *field = dynamic_cast<BMenuField *>(FindView("Logic")); 2507 if (field) { 2508 BMenuItem *item = field->Menu()->FindMarked(); 2509 ASSERT(item); 2510 message->AddInt32("logicalRelation", item ? field->Menu()->IndexOf(item) : 0); 2511 } 2512 } 2513 2514 void 2515 TAttrView::AddLogicMenu(bool selectAnd) 2516 { 2517 // add "AND/OR" menu 2518 BPopUpMenu *menu = new BPopUpMenu(""); 2519 BMessage *message = new BMessage(); 2520 message->AddInt32("combine", B_AND); 2521 BMenuItem *item = new BMenuItem("And", message); 2522 menu->AddItem(item); 2523 if (selectAnd) 2524 item->SetMarked(true); 2525 2526 message = new BMessage(); 2527 message->AddInt32("combine", B_OR); 2528 item = new BMenuItem("Or", message); 2529 menu->AddItem(item); 2530 if (!selectAnd) 2531 item->SetMarked(true); 2532 2533 menu->SetTargetForItems(this); 2534 2535 BRect bounds(Bounds()); 2536 bounds.left = bounds.right - 40; 2537 bounds.bottom = bounds.top + 15; 2538 BMenuField *menufield = new BMenuField(bounds, "Logic", "", menu); 2539 menufield->SetDivider(0.0f); 2540 menufield->HidePopUpMarker(); 2541 AddChild(menufield); 2542 } 2543 2544 2545 void 2546 TAttrView::RemoveLogicMenu() 2547 { 2548 BMenuField *menufield = dynamic_cast<BMenuField *>(FindView("Logic")); 2549 if (menufield) { 2550 menufield->RemoveSelf(); 2551 delete menufield; 2552 } 2553 } 2554 2555 2556 void 2557 TAttrView::Draw(BRect) 2558 { 2559 BMenuItem *item = fMenuField->Menu()->FindMarked(); 2560 if (!item) 2561 return; 2562 2563 if (item->Submenu()->FindMarked()) { 2564 float width = StringWidth(item->Submenu()->FindMarked()->Label()); 2565 BRect bounds(fTextControl->Frame()); 2566 2567 // draws the is/contains, etc. string 2568 bounds.left -= (width + 10); 2569 bounds.bottom -= 6; 2570 DrawString(item->Submenu()->FindMarked()->Label(), bounds.LeftBottom()); 2571 } 2572 } 2573 2574 2575 void 2576 TAttrView::MessageReceived(BMessage *message) 2577 { 2578 BMenuItem *item; 2579 2580 switch (message->what) { 2581 case kAttributeItem: 2582 if (message->FindPointer("source", (void **)&item) != B_OK) 2583 return; 2584 2585 item->Menu()->Superitem()->SetMarked(true); 2586 Invalidate(); 2587 break; 2588 2589 case kAttributeItemMain: 2590 // in case someone selected just and attribute without the 2591 // comparator 2592 if (message->FindPointer("source", (void **)&item) != B_OK) 2593 return; 2594 2595 if (item->Submenu()->ItemAt(0)) 2596 item->Submenu()->ItemAt(0)->SetMarked(true); 2597 Invalidate(); 2598 break; 2599 2600 default: 2601 _inherited::MessageReceived(message); 2602 break; 2603 } 2604 } 2605 2606 2607 void 2608 TAttrView::AddMimeTypeAttrs() 2609 { 2610 BMenu *menu = fMenuField->Menu(); 2611 AddMimeTypeAttrs(menu); 2612 } 2613 2614 2615 void 2616 TAttrView::AddAttributes(BMenu *menu, const BMimeType &mimeType) 2617 { 2618 // only add things to menu which have "user-visible" data 2619 BMessage attributeMessage; 2620 if (mimeType.GetAttrInfo(&attributeMessage) != B_OK) 2621 return; 2622 2623 char desc[B_MIME_TYPE_LENGTH]; 2624 mimeType.GetShortDescription(desc); 2625 2626 // go through each field in meta mime and add it to a menu 2627 for (int32 index = 0; ; index++) { 2628 const char *publicName; 2629 if (attributeMessage.FindString("attr:public_name", index, &publicName) != B_OK) 2630 break; 2631 2632 if (!attributeMessage.FindBool("attr:viewable")) 2633 continue; 2634 2635 const char *attributeName; 2636 if (attributeMessage.FindString("attr:name", index, &attributeName) != B_OK) 2637 continue; 2638 2639 int32 type; 2640 if (attributeMessage.FindInt32("attr:type", index, &type) != B_OK) 2641 continue; 2642 2643 BMenu *submenu = new BMenu(publicName); 2644 submenu->SetRadioMode(true); 2645 submenu->SetFont(be_plain_font); 2646 BMessage *message = new BMessage(kAttributeItemMain); 2647 message->AddString("name", attributeName); 2648 message->AddInt32("type", type); 2649 BMenuItem *item = new BMenuItem(submenu, message); 2650 menu->AddItem(item); 2651 menu->SetTargetForItems(this); 2652 2653 switch (type) { 2654 case B_STRING_TYPE: 2655 message = new BMessage(kAttributeItem); 2656 message->AddInt32("operator", B_CONTAINS); 2657 submenu->AddItem(new BMenuItem("contains", message)); 2658 2659 message = new BMessage(kAttributeItem); 2660 message->AddInt32("operator", B_EQ); 2661 submenu->AddItem(new BMenuItem("is", message)); 2662 2663 message = new BMessage(kAttributeItem); 2664 message->AddInt32("operator", B_NE); 2665 submenu->AddItem(new BMenuItem("is not", message)); 2666 submenu->SetTargetForItems(this); 2667 2668 message = new BMessage(kAttributeItem); 2669 message->AddInt32("operator", B_BEGINS_WITH); 2670 submenu->AddItem(new BMenuItem("starts with", message)); 2671 submenu->SetTargetForItems(this); 2672 2673 message = new BMessage(kAttributeItem); 2674 message->AddInt32("operator", B_ENDS_WITH); 2675 submenu->AddItem(new BMenuItem("ends with", message)); 2676 break; 2677 2678 case B_BOOL_TYPE: 2679 case B_INT16_TYPE: 2680 case B_UINT8_TYPE: 2681 case B_INT8_TYPE: 2682 case B_UINT16_TYPE: 2683 case B_INT32_TYPE: 2684 case B_UINT32_TYPE: 2685 case B_INT64_TYPE: 2686 case B_UINT64_TYPE: 2687 case B_OFF_T_TYPE: 2688 case B_FLOAT_TYPE: 2689 case B_DOUBLE_TYPE: 2690 message = new BMessage(kAttributeItem); 2691 message->AddInt32("operator", B_EQ); 2692 submenu->AddItem(new BMenuItem("is", message)); 2693 2694 message = new BMessage(kAttributeItem); 2695 message->AddInt32("operator", B_GE); 2696 submenu->AddItem(new BMenuItem("greater than", message)); 2697 2698 message = new BMessage(kAttributeItem); 2699 message->AddInt32("operator", B_LE); 2700 submenu->AddItem(new BMenuItem("less than", message)); 2701 break; 2702 2703 case B_TIME_TYPE: 2704 message = new BMessage(kAttributeItem); 2705 message->AddInt32("operator", B_LE); 2706 submenu->AddItem(new BMenuItem("before", message)); 2707 2708 message = new BMessage(kAttributeItem); 2709 message->AddInt32("operator", B_GE); 2710 submenu->AddItem(new BMenuItem("after", message)); 2711 break; 2712 } 2713 submenu->SetTargetForItems(this); 2714 } 2715 } 2716 2717 2718 void 2719 TAttrView::AddMimeTypeAttrs(BMenu *menu) 2720 { 2721 FindPanel *mainView = dynamic_cast<FindPanel *>(Parent()-> 2722 Parent()->FindView("MainView")); 2723 if (!mainView) 2724 return; 2725 2726 const char *typeName; 2727 if (mainView->CurrentMimeType(&typeName) == NULL) 2728 return; 2729 2730 BMimeType mimeType(typeName); 2731 if (!mimeType.IsInstalled()) 2732 return; 2733 2734 if (!mimeType.IsSupertypeOnly()) { 2735 // add supertype attributes 2736 BMimeType supertype; 2737 mimeType.GetSupertype(&supertype); 2738 AddAttributes(menu, supertype); 2739 } 2740 2741 AddAttributes(menu, mimeType); 2742 } 2743 2744 2745 void 2746 TAttrView::GetDefaultName(BString &result) const 2747 { 2748 BMenuItem *item = NULL; 2749 if (fMenuField->Menu() != NULL) 2750 item = fMenuField->Menu()->FindMarked(); 2751 if (item != NULL) 2752 result << item->Label(); 2753 else 2754 result << "Name"; 2755 2756 if (item->Submenu() != NULL) 2757 item = item->Submenu()->FindMarked(); 2758 else 2759 item = NULL; 2760 2761 if (item != NULL) 2762 result << " " << item->Label() << " "; 2763 else 2764 result << " = "; 2765 2766 result << fTextControl->Text(); 2767 } 2768 2769 2770 // #pragma mark - 2771 2772 2773 DeleteTransientQueriesTask::DeleteTransientQueriesTask() 2774 : state(kInitial), 2775 fWalker(NULL) 2776 { 2777 } 2778 2779 2780 DeleteTransientQueriesTask::~DeleteTransientQueriesTask() 2781 { 2782 delete fWalker; 2783 } 2784 2785 2786 bool 2787 DeleteTransientQueriesTask::DoSomeWork() 2788 { 2789 switch (state) { 2790 case kInitial: 2791 Initialize(); 2792 break; 2793 2794 case kAllocatedWalker: 2795 case kTraversing: 2796 if (GetSome()) { 2797 PRINT(("transient query killer done\n")); 2798 return true; 2799 } 2800 break; 2801 2802 case kError: 2803 return true; 2804 2805 } 2806 return false; 2807 } 2808 2809 2810 void 2811 DeleteTransientQueriesTask::Initialize() 2812 { 2813 PRINT(("starting up transient query killer\n")); 2814 BPath path; 2815 status_t result = find_directory(B_USER_DIRECTORY, &path, false); 2816 if (result != B_OK) { 2817 state = kError; 2818 return; 2819 } 2820 fWalker = new WALKER_NS::TNodeWalker(path.Path()); 2821 state = kAllocatedWalker; 2822 } 2823 2824 2825 const int32 kBatchCount = 100; 2826 2827 bool 2828 DeleteTransientQueriesTask::GetSome() 2829 { 2830 state = kTraversing; 2831 for (int32 count = kBatchCount; count > 0; count--) { 2832 entry_ref ref; 2833 if (fWalker->GetNextRef(&ref) != B_OK) { 2834 state = kError; 2835 return true; 2836 } 2837 Model model(&ref); 2838 if (model.IsQuery()) 2839 ProcessOneRef(&model); 2840 #if xDEBUG 2841 else 2842 PRINT(("transient query killer: %s not a query\n", model.Name())); 2843 #endif 2844 } 2845 return false; 2846 } 2847 2848 2849 const int32 kDaysToExpire = 7; 2850 2851 static bool 2852 QueryOldEnough(Model *model) 2853 { 2854 // check if it is old and ready to be deleted 2855 time_t now = time(0); 2856 2857 tm nowTimeData; 2858 tm fileModData; 2859 2860 localtime_r(&now, &nowTimeData); 2861 localtime_r(&model->StatBuf()->st_ctime, &fileModData); 2862 2863 if ((nowTimeData.tm_mday - fileModData.tm_mday) < kDaysToExpire 2864 && (nowTimeData.tm_mday - fileModData.tm_mday) > -kDaysToExpire) { 2865 PRINT(("query %s, not old enough\n", model->Name())); 2866 return false; 2867 } 2868 return true; 2869 } 2870 2871 2872 bool 2873 DeleteTransientQueriesTask::ProcessOneRef(Model *model) 2874 { 2875 BModelOpener opener(model); 2876 2877 // is this a temporary query 2878 if (!MoreOptionsStruct::QueryTemporary(model->Node())) { 2879 PRINT(("query %s, not temporary\n", model->Name())); 2880 return false; 2881 } 2882 2883 if (!QueryOldEnough(model)) 2884 return false; 2885 2886 ASSERT(dynamic_cast<TTracker *>(be_app)); 2887 2888 // check that it is not showing 2889 if (dynamic_cast<TTracker *>(be_app)->EntryHasWindowOpen(model->EntryRef())) { 2890 PRINT(("query %s, showing, can't delete\n", model->Name())); 2891 return false; 2892 } 2893 2894 PRINT(("query %s, old, temporary, not shownig - deleting\n", model->Name())); 2895 BEntry entry(model->EntryRef()); 2896 entry.Remove(); 2897 2898 return true; 2899 } 2900 2901 2902 class DeleteTransientQueriesFunctor : public FunctionObjectWithResult<bool> { 2903 public: 2904 DeleteTransientQueriesFunctor(DeleteTransientQueriesTask *task) 2905 : task(task) 2906 {} 2907 2908 virtual ~DeleteTransientQueriesFunctor() 2909 { 2910 delete task; 2911 } 2912 2913 virtual void operator()() 2914 { result = task->DoSomeWork(); } 2915 2916 private: 2917 DeleteTransientQueriesTask *task; 2918 }; 2919 2920 2921 void 2922 DeleteTransientQueriesTask::StartUpTransientQueryCleaner() 2923 { 2924 // set up a task that wakes up when the machine is idle and starts 2925 // killing off old transient queries 2926 DeleteTransientQueriesFunctor *worker 2927 = new DeleteTransientQueriesFunctor(new DeleteTransientQueriesTask()); 2928 TTracker *tracker = dynamic_cast<TTracker *>(be_app); 2929 ASSERT(tracker); 2930 tracker->MainTaskLoop()->RunWhenIdle(worker, 2931 30 * 60 * 1000000, // half an hour initial delay 2932 5 * 60 * 1000000, // idle for five minutes 2933 10 * 1000000); 2934 } 2935 2936 2937 // #pragma mark - 2938 2939 2940 RecentFindItemsMenu::RecentFindItemsMenu(const char *title, const BMessenger *target, 2941 uint32 what) 2942 : BMenu(title, B_ITEMS_IN_COLUMN), 2943 fTarget(*target), 2944 fWhat(what) 2945 { 2946 } 2947 2948 2949 void 2950 RecentFindItemsMenu::AttachedToWindow() 2951 { 2952 // re-populate the menu with fresh items 2953 for (int32 index = CountItems() - 1; index >= 0; index--) 2954 delete RemoveItem(index); 2955 2956 FindPanel::AddRecentQueries(this, false, &fTarget, fWhat); 2957 BMenu::AttachedToWindow(); 2958 } 2959 2960 2961 #if !B_BEOS_VERSION_DANO 2962 _IMPEXP_TRACKER 2963 #endif 2964 BMenu * 2965 TrackerBuildRecentFindItemsMenu(const char *title) 2966 { 2967 BMessenger tracker(kTrackerSignature); 2968 return new RecentFindItemsMenu(title, &tracker, B_REFS_RECEIVED); 2969 } 2970 2971 2972 // #pragma mark - 2973 2974 2975 DraggableQueryIcon::DraggableQueryIcon(BRect frame, const char *name, 2976 const BMessage *message, BMessenger messenger, uint32 resizeFlags, uint32 flags) 2977 : DraggableIcon(frame, name, B_QUERY_MIMETYPE, B_LARGE_ICON, 2978 message, messenger, resizeFlags, flags) 2979 { 2980 } 2981 2982 2983 bool 2984 DraggableQueryIcon::DragStarted(BMessage *dragMessage) 2985 { 2986 // override to substitute the user-specified query name 2987 dragMessage->RemoveData("be:clip_name"); 2988 2989 FindWindow *window = dynamic_cast<FindWindow *>(Window()); 2990 ASSERT(window); 2991 dragMessage->AddString("be:clip_name", 2992 window->BackgroundView()->UserSpecifiedName() ? 2993 window->BackgroundView()->UserSpecifiedName() : "New Query"); 2994 2995 return true; 2996 } 2997 2998 2999 // #pragma mark - 3000 3001 3002 MostUsedNames::MostUsedNames(const char *fileName, const char *directory, int32 maxCount) 3003 : 3004 fFileName(fileName), 3005 fDirectory(directory), 3006 fLoaded(false), 3007 fCount(maxCount) 3008 { 3009 } 3010 3011 3012 MostUsedNames::~MostUsedNames() 3013 { 3014 // only write back settings when we've been used 3015 if (!fLoaded) 3016 return; 3017 3018 // write most used list to file 3019 3020 BPath path; 3021 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK) 3022 return; 3023 3024 path.Append(fDirectory); 3025 path.Append(fFileName); 3026 BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); 3027 if (file.InitCheck() == B_OK) { 3028 for (int32 i = 0; i < fList.CountItems(); i++) { 3029 list_entry *entry = static_cast<list_entry *>(fList.ItemAt(i)); 3030 3031 char line[B_FILE_NAME_LENGTH + 5]; 3032 3033 // limit upper bound to react more dynamically to changes 3034 if (--entry->count > 20) 3035 entry->count = 20; 3036 3037 // if the item hasn't been chosen in a while, remove it 3038 // (but leave at least one item in the list) 3039 if (entry->count < -10 && i > 0) 3040 continue; 3041 3042 sprintf(line, "%ld %s\n", entry->count, entry->name); 3043 if (file.Write(line, strlen(line)) < B_OK) 3044 break; 3045 } 3046 } 3047 file.Unset(); 3048 3049 // free data 3050 3051 for (int32 i = fList.CountItems(); i-- > 0;) { 3052 list_entry *entry = static_cast<list_entry *>(fList.ItemAt(i)); 3053 free(entry->name); 3054 delete entry; 3055 } 3056 } 3057 3058 3059 bool 3060 MostUsedNames::ObtainList(BList *list) 3061 { 3062 if (!list) 3063 return false; 3064 3065 if (!fLoaded) 3066 UpdateList(); 3067 3068 fLock.Lock(); 3069 3070 list->MakeEmpty(); 3071 for (int32 i = 0; i < fCount; i++) { 3072 list_entry *entry = static_cast<list_entry *>(fList.ItemAt(i)); 3073 if (entry == NULL) 3074 return true; 3075 3076 list->AddItem(entry->name); 3077 } 3078 return true; 3079 } 3080 3081 3082 void 3083 MostUsedNames::ReleaseList() 3084 { 3085 fLock.Unlock(); 3086 } 3087 3088 3089 void 3090 MostUsedNames::AddName(const char *name) 3091 { 3092 fLock.Lock(); 3093 3094 if (!fLoaded) 3095 LoadList(); 3096 3097 // remove last entry if there are more than 3098 // 2*fCount entries in the list 3099 3100 list_entry *entry = NULL; 3101 3102 if (fList.CountItems() > fCount * 2) { 3103 entry = static_cast<list_entry *>(fList.RemoveItem(fList.CountItems() - 1)); 3104 3105 // is this the name we want to add here? 3106 if (strcmp(name, entry->name)) { 3107 free(entry->name); 3108 delete entry; 3109 entry = NULL; 3110 } else 3111 fList.AddItem(entry); 3112 } 3113 3114 if (entry == NULL) { 3115 for (int32 i = 0; (entry = static_cast<list_entry *>(fList.ItemAt(i))) != NULL; i++) 3116 if (!strcmp(entry->name, name)) 3117 break; 3118 } 3119 3120 if (entry == NULL) { 3121 entry = new list_entry; 3122 entry->name = strdup(name); 3123 entry->count = 1; 3124 3125 fList.AddItem(entry); 3126 } else if (entry->count < 0) 3127 entry->count = 1; 3128 else 3129 entry->count++; 3130 3131 fLock.Unlock(); 3132 UpdateList(); 3133 } 3134 3135 3136 int 3137 MostUsedNames::CompareNames(const void *a,const void *b) 3138 { 3139 list_entry *entryA = *(list_entry **)a; 3140 list_entry *entryB = *(list_entry **)b; 3141 3142 if (entryA->count == entryB->count) 3143 return strcasecmp(entryA->name,entryB->name); 3144 3145 return entryB->count - entryA->count; 3146 } 3147 3148 3149 void 3150 MostUsedNames::LoadList() 3151 { 3152 if (fLoaded) 3153 return; 3154 fLoaded = true; 3155 3156 // load the most used names list 3157 3158 BPath path; 3159 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK) 3160 return; 3161 3162 path.Append(fDirectory); 3163 path.Append(fFileName); 3164 3165 FILE *file = fopen(path.Path(), "r"); 3166 if (file == NULL) 3167 return; 3168 3169 char line[B_FILE_NAME_LENGTH + 5]; 3170 while (fgets(line, sizeof(line), file) != NULL) { 3171 int32 length = (int32)strlen(line) - 1; 3172 if (length >= 0 && line[length] == '\n') 3173 line[length] = '\0'; 3174 3175 int32 count = atoi(line); 3176 3177 char *name = strchr(line, ' '); 3178 if (name == NULL || *(++name) == '\0') 3179 continue; 3180 3181 list_entry *entry = new list_entry; 3182 entry->name = strdup(name); 3183 entry->count = count; 3184 3185 fList.AddItem(entry); 3186 } 3187 fclose(file); 3188 } 3189 3190 3191 void 3192 MostUsedNames::UpdateList() 3193 { 3194 AutoLock<Benaphore> locker(fLock); 3195 3196 if (!fLoaded) 3197 LoadList(); 3198 3199 // sort list items 3200 3201 fList.SortItems(MostUsedNames::CompareNames); 3202 } 3203 3204 } // namespace BPrivate 3205 3206