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