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 82 const uint32 kMoreOptionsMessage = 'mrop'; 83 const uint32 kNameModifiedMessage = 'nmmd'; 84 const uint32 kSwitchToQueryTemplate = 'swqt'; 85 const uint32 kRunSaveAsTemplatePanel = 'svtm'; 86 87 const char *kDragNDropTypes [] = { B_QUERY_MIMETYPE, B_QUERY_TEMPLATE_MIMETYPE }; 88 const char *kDragNDropActionSpecifiers [] = { "Create a Query", "Create a Query template" }; 89 90 const uint32 kAttachFile = 'attf'; 91 92 93 namespace BPrivate { 94 95 class MostUsedNames { 96 public: 97 MostUsedNames(const char *fileName, const char *directory, int32 maxCount = 5); 98 ~MostUsedNames(); 99 100 bool ObtainList(BList *list); 101 void ReleaseList(); 102 103 void AddName(const char *); 104 105 protected: 106 struct list_entry { 107 char *name; 108 int32 count; 109 }; 110 111 static int CompareNames(const void *a, const void *b); 112 void LoadList(); 113 void UpdateList(); 114 115 const char *fFileName; 116 const char *fDirectory; 117 bool fLoaded; 118 mutable Benaphore fLock; 119 BList fList; 120 int32 fCount; 121 }; 122 123 MostUsedNames gMostUsedMimeTypes("MostUsedMimeTypes", "Tracker"); 124 125 126 void 127 MoreOptionsStruct::EndianSwap(void *) 128 { 129 // noop for now 130 } 131 132 133 void 134 MoreOptionsStruct::SetQueryTemporary(BNode *node, bool on) 135 { 136 MoreOptionsStruct saveMoreOptions; 137 138 ReadAttr(node, kAttrQueryMoreOptions, kAttrQueryMoreOptionsForeign, 139 B_RAW_TYPE, 0, &saveMoreOptions, sizeof(MoreOptionsStruct), 140 &MoreOptionsStruct::EndianSwap); 141 saveMoreOptions.temporary = on; 142 node->WriteAttr(kAttrQueryMoreOptions, B_RAW_TYPE, 0, &saveMoreOptions, 143 sizeof(saveMoreOptions)); 144 } 145 146 147 bool 148 MoreOptionsStruct::QueryTemporary(const BNode *node) 149 { 150 MoreOptionsStruct saveMoreOptions; 151 152 if (ReadAttr(node, kAttrQueryMoreOptions, kAttrQueryMoreOptionsForeign, 153 B_RAW_TYPE, 0, &saveMoreOptions, sizeof(MoreOptionsStruct), 154 &MoreOptionsStruct::EndianSwap) == kReadAttrFailed) 155 return false; 156 157 return saveMoreOptions.temporary; 158 } 159 160 161 // #pragma mark - 162 163 164 FindWindow::FindWindow(const entry_ref *newRef, bool editIfTemplateOnly) 165 : BWindow(kInitialRect, "Find", B_TITLED_WINDOW, B_NOT_RESIZABLE | B_NOT_ZOOMABLE), 166 fFile(TryOpening(newRef)), 167 fFromTemplate(false), 168 fEditTemplateOnly(false), 169 fSaveAsTemplatePanel(NULL) 170 { 171 if (fFile) { 172 fRef = *newRef; 173 if (editIfTemplateOnly) { 174 char type[B_MIME_TYPE_LENGTH]; 175 if (BNodeInfo(fFile).GetType(type) == B_OK 176 && strcasecmp(type, B_QUERY_TEMPLATE_MIMETYPE) == 0) { 177 fEditTemplateOnly = true; 178 SetTitle("Edit Query Template"); 179 } 180 } 181 } else { 182 // no initial query, fall back on the default query template 183 BEntry entry; 184 GetDefaultQuery(entry); 185 entry.GetRef(&fRef); 186 187 if (entry.Exists()) 188 fFile = TryOpening(&fRef); 189 else { 190 // no default query template yet 191 fFile = new BFile(&entry, O_RDWR | O_CREAT); 192 if (fFile->InitCheck() < B_OK) { 193 delete fFile; 194 fFile = NULL; 195 } else 196 SaveQueryAttributes(fFile, true); 197 } 198 } 199 200 if (fFile) { 201 BRect initialRect(FindPanel::InitialViewSize(fFile)); 202 ResizeTo(initialRect.Width(), initialRect.Height()); 203 } 204 205 fFromTemplate = IsQueryTemplate(fFile); 206 207 fBackground = new FindPanel(Bounds(), fFile, this, fFromTemplate, fEditTemplateOnly); 208 AddChild(fBackground); 209 } 210 211 212 FindWindow::~FindWindow() 213 { 214 delete fFile; 215 delete fSaveAsTemplatePanel; 216 } 217 218 219 BFile * 220 FindWindow::TryOpening(const entry_ref *ref) 221 { 222 if (!ref) 223 return NULL; 224 225 BFile *result = new BFile(ref, O_RDWR); 226 if (result->InitCheck() != B_OK) { 227 delete result; 228 result = NULL; 229 } 230 return result; 231 } 232 233 234 void 235 FindWindow::GetDefaultQuery(BEntry &entry) 236 { 237 BPath path; 238 find_directory(B_USER_DIRECTORY, &path, true); 239 path.Append("queries"); 240 mkdir(path.Path(), 0777); 241 BDirectory directory(path.Path()); 242 243 entry.SetTo(&directory, "default"); 244 } 245 246 247 bool 248 FindWindow::IsQueryTemplate(BNode *file) 249 { 250 char type[B_MIME_TYPE_LENGTH]; 251 if (BNodeInfo(file).GetType(type) != B_OK) 252 return false; 253 254 return strcasecmp(type, B_QUERY_TEMPLATE_MIMETYPE) == 0; 255 } 256 257 258 void 259 FindWindow::SwitchToTemplate(const entry_ref *ref) 260 { 261 try { 262 BEntry entry(ref, true); 263 BFile templateFile(&entry, O_RDONLY); 264 265 ThrowOnInitCheckError(&templateFile); 266 DisableUpdates(); 267 // turn off updates to reduce flicker while re-populating the 268 // window 269 fBackground->SwitchToTemplate(&templateFile); 270 EnableUpdates(); 271 272 } catch (...) { 273 } 274 } 275 276 277 const char * 278 FindWindow::QueryName() const 279 { 280 if (fFromTemplate) { 281 if (!fQueryNameFromTemplate.Length()) 282 fFile->ReadAttrString(kAttrQueryTemplateName, &fQueryNameFromTemplate); 283 284 return fQueryNameFromTemplate.String(); 285 } 286 if (!fFile) 287 return ""; 288 289 return fRef.name; 290 } 291 292 293 static const char * 294 MakeValidFilename(BString &string) 295 { 296 // make a file name that is legal under bfs and hfs - possibly could 297 // add code here to accomodate FAT32 etc. too 298 if (string.Length() > B_FILE_NAME_LENGTH - 1) { 299 string.Truncate(B_FILE_NAME_LENGTH - 4); 300 string += B_UTF8_ELLIPSIS; 301 } 302 303 // replace slashes 304 int32 length = string.Length(); 305 char *buf = string.LockBuffer(length); 306 for (int32 index = length; index-- > 0;) 307 if (buf[index] == '/' /*|| buf[index] == ':'*/) 308 buf[index] = '_'; 309 string.UnlockBuffer(length); 310 311 return string.String(); 312 } 313 314 315 void 316 FindWindow::GetPredicateString(BString &predicate, bool &dynamicDate) 317 { 318 BQuery query; 319 BTextControl *textControl = dynamic_cast<BTextControl *>(FindView("TextControl")); 320 switch (fBackground->Mode()) { 321 case kByNameItem: 322 fBackground->GetByNamePredicate(&query); 323 query.GetPredicate(&predicate); 324 break; 325 326 case kByForumlaItem: 327 predicate.SetTo(textControl->TextView()->Text(), 1023); 328 break; 329 330 case kByAttributeItem: 331 fBackground->GetByAttrPredicate(&query, dynamicDate); 332 query.GetPredicate(&predicate); 333 break; 334 } 335 } 336 337 338 void 339 FindWindow::GetDefaultName(BString &result) 340 { 341 fBackground->GetDefaultName(result); 342 343 time_t timeValue = time(0); 344 char namebuf[B_FILE_NAME_LENGTH]; 345 346 tm timeData; 347 localtime_r(&timeValue, &timeData); 348 349 strftime(namebuf, 32, " - %b %d, %I:%M:%S %p", &timeData); 350 result << namebuf; 351 352 MakeValidFilename(result); 353 } 354 355 356 void 357 FindWindow::SaveQueryAttributes(BNode *file, bool queryTemplate) 358 { 359 ThrowOnError( BNodeInfo(file).SetType( 360 queryTemplate ? B_QUERY_TEMPLATE_MIMETYPE : B_QUERY_MIMETYPE) ); 361 362 // save date/time info for recent query support and transient query killer 363 int32 currentTime = (int32)time(0); 364 file->WriteAttr(kAttrQueryLastChange, B_INT32_TYPE, 0, ¤tTime, sizeof(int32)); 365 int32 tmp = 1; 366 file->WriteAttr("_trk/recentQuery", B_INT32_TYPE, 0, &tmp, sizeof(int32)); 367 } 368 369 370 status_t 371 FindWindow::SaveQueryAsAttributes(BNode *file, BEntry *entry, bool queryTemplate, 372 const BMessage *oldAttributes, const BPoint *oldLocation) 373 { 374 if (oldAttributes) 375 // revive old window settings 376 BContainerWindow::SetLayoutState(file, oldAttributes); 377 378 if (oldLocation) 379 // and the file's location 380 FSSetPoseLocation(entry, *oldLocation); 381 382 BNodeInfo(file).SetType(queryTemplate ? B_QUERY_TEMPLATE_MIMETYPE : B_QUERY_MIMETYPE); 383 384 BString predicate; 385 bool dynamicDate; 386 GetPredicateString(predicate, dynamicDate); 387 file->WriteAttrString(kAttrQueryString, &predicate); 388 389 if (dynamicDate) 390 file->WriteAttr(kAttrDynamicDateQuery, B_BOOL_TYPE, 0, &dynamicDate, 391 sizeof(dynamicDate)); 392 393 int32 tmp = 1; 394 file->WriteAttr("_trk/recentQuery", B_INT32_TYPE, 0, &tmp, sizeof(int32)); 395 396 // write some useful info to help locate the volume to query 397 BMenuItem *item = fBackground->VolMenu()->FindMarked(); 398 if (item) { 399 dev_t dev; 400 BMessage message; 401 int32 count = 0; 402 403 int32 itemCount = fBackground->VolMenu()->CountItems(); 404 for (int32 index = 2; index < itemCount; index++) { 405 BMenuItem *item = fBackground->VolMenu()->ItemAt(index); 406 407 if (!item->IsMarked()) 408 continue; 409 410 if (item->Message()->FindInt32("device", &dev) != B_OK) 411 continue; 412 413 count++; 414 BVolume volume(dev); 415 EmbedUniqueVolumeInfo(&message, &volume); 416 } 417 418 if (count) { 419 // do we need to embed any volumes 420 ssize_t size = message.FlattenedSize(); 421 BString buffer; 422 status_t result = message.Flatten(buffer.LockBuffer(size), size); 423 ASSERT(result == B_OK); 424 result = file->WriteAttr(kAttrQueryVolume, B_MESSAGE_TYPE, 0, 425 buffer.String(), (size_t)size); 426 ASSERT(result == size); 427 buffer.UnlockBuffer(); 428 } 429 // default to query for everything 430 } 431 432 fBackground->SaveWindowState(file, fEditTemplateOnly); 433 // write out all the dialog items as attributes so that the query can 434 // be reopened and edited later 435 436 BView *focusedItem = CurrentFocus(); 437 if (focusedItem) { 438 // text controls never get the focus, their internal text views do 439 BView *parent = focusedItem->Parent(); 440 if (dynamic_cast<BTextControl *>(parent)) 441 focusedItem = parent; 442 443 // write out the current focus and, if text control, selection 444 BString name(focusedItem->Name()); 445 file->WriteAttrString("_trk/focusedView", &name); 446 BTextControl *textControl = dynamic_cast<BTextControl *>(focusedItem); 447 if (textControl) { 448 int32 selStart, selEnd; 449 textControl->TextView()->GetSelection(&selStart, &selEnd); 450 file->WriteAttr("_trk/focusedSelStart", B_INT32_TYPE, 0, 451 &selStart, sizeof(selStart)); 452 file->WriteAttr("_trk/focusedSelEnd", B_INT32_TYPE, 0, 453 &selEnd, sizeof(selEnd)); 454 } 455 } 456 return B_OK; 457 } 458 459 460 void 461 FindWindow::Save() 462 { 463 FindSaveCommon(false); 464 465 // close the find panel 466 PostMessage(B_QUIT_REQUESTED); 467 } 468 469 470 void 471 FindWindow::Find() 472 { 473 if (!FindSaveCommon(true)) { 474 // have to wait for the node monitor to force old query to close 475 // to avoid a race condition 476 TTracker *tracker = dynamic_cast<TTracker *>(be_app); 477 ASSERT(tracker); 478 for (int32 timeOut = 0; ; timeOut++) { 479 if (!tracker->EntryHasWindowOpen(&fRef)) 480 // window quit, we can post refs received to open a 481 // new copy 482 break; 483 484 // PRINT(("waiting for query window to quit, %d\n", timeOut)); 485 if (timeOut == 5000) { 486 // the old query window would not quit for some reason 487 TRESPASS(); 488 PostMessage(B_QUIT_REQUESTED); 489 return; 490 } 491 snooze(1000); 492 } 493 } 494 495 int32 currentTime = (int32)time(0); 496 fFile->WriteAttr(kAttrQueryLastChange, B_INT32_TYPE, 0, ¤tTime, sizeof(int32)); 497 498 // tell the tracker about it 499 BMessage message(B_REFS_RECEIVED); 500 message.AddRef("refs", &fRef); 501 be_app->PostMessage(&message); 502 503 // close the find panel 504 PostMessage(B_QUIT_REQUESTED); 505 } 506 507 508 bool 509 FindWindow::FindSaveCommon(bool find) 510 { 511 // figure out what we need to do 512 bool readFromOldFile = fFile != NULL; 513 bool replaceOriginal = fFile && (!fFromTemplate || fEditTemplateOnly); 514 bool keepPoseLocation = replaceOriginal; 515 bool newFile = !fFile || (fFromTemplate && !fEditTemplateOnly); 516 517 BEntry entry; 518 BMessage oldAttributes; 519 BPoint location; 520 bool hadLocation = false; 521 const char *userSpecifiedName = fBackground->UserSpecifiedName(); 522 523 if (readFromOldFile) { 524 entry.SetTo(&fRef); 525 BContainerWindow::GetLayoutState(fFile, &oldAttributes); 526 hadLocation = FSGetPoseLocation(fFile, &location); 527 } 528 529 if (replaceOriginal) { 530 fFile->Unset(); 531 entry.Remove(); 532 // remove the current entry - need to do this to quit the 533 // running query and to close the corresponding window 534 535 if (userSpecifiedName && !fEditTemplateOnly) { 536 // change the name of the old query per users request 537 fRef.set_name(userSpecifiedName); 538 entry.SetTo(&fRef); 539 } 540 } 541 542 if (newFile) { 543 // create query file in the user's directory 544 BPath path; 545 find_directory(B_USER_DIRECTORY, &path, true); 546 path.Append("queries"); 547 // there might be no queries folder yet, create one 548 mkdir(path.Path(), 0777); 549 550 // either use the user specified name, or go with the name 551 // generated from the predicate, etc. 552 if (!userSpecifiedName) { 553 BString text; 554 GetDefaultName(text); 555 path.Append(text.String()); 556 } else 557 path.Append(userSpecifiedName); 558 559 entry.SetTo(path.Path()); 560 entry.Remove(); 561 entry.GetRef(&fRef); 562 } 563 564 fFile = new BFile(&entry, O_RDWR | O_CREAT); 565 ASSERT(fFile->InitCheck() == B_OK); 566 567 SaveQueryAsAttributes(fFile, &entry, !find, newFile ? 0 : &oldAttributes, 568 (hadLocation && keepPoseLocation) ? &location : 0); 569 570 return newFile; 571 } 572 573 574 void 575 FindWindow::MessageReceived(BMessage *message) 576 { 577 switch (message->what) { 578 case kFindButton: 579 Find(); 580 break; 581 582 case kSaveButton: 583 Save(); 584 break; 585 586 case kAttachFile: 587 { 588 entry_ref dir; 589 const char *name; 590 bool queryTemplate; 591 if (message->FindString("name", &name) == B_OK 592 && message->FindRef("directory", &dir) == B_OK 593 && message->FindBool("template", &queryTemplate) == B_OK) { 594 delete fFile; 595 fFile = NULL; 596 BDirectory directory(&dir); 597 BEntry entry(&directory, name); 598 entry_ref tmpRef; 599 entry.GetRef(&tmpRef); 600 fFile = TryOpening(&tmpRef); 601 if (fFile) { 602 fRef = tmpRef; 603 SaveQueryAsAttributes(fFile, &entry, queryTemplate, 0, 0); 604 // try to save whatever state we aleady have 605 // to the new query so that if the user 606 // opens it before runing it from the find panel, 607 // something reasonable happens 608 } 609 } 610 } 611 break; 612 613 case kSwitchToQueryTemplate: 614 { 615 entry_ref ref; 616 if (message->FindRef("refs", &ref) == B_OK) 617 SwitchToTemplate(&ref); 618 } 619 break; 620 621 case kRunSaveAsTemplatePanel: 622 if (fSaveAsTemplatePanel) 623 fSaveAsTemplatePanel->Show(); 624 else { 625 BMessenger panel(BackgroundView()); 626 fSaveAsTemplatePanel = new BFilePanel(B_SAVE_PANEL, &panel); 627 fSaveAsTemplatePanel->SetSaveText("Query template"); 628 fSaveAsTemplatePanel->Window()->SetTitle("Save As Query Template:"); 629 fSaveAsTemplatePanel->Show(); 630 } 631 break; 632 633 default: 634 _inherited::MessageReceived(message); 635 break; 636 } 637 } 638 639 640 // #pragma mark - 641 642 const float kMoreOptionsDelta = 20; 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 = dynamic_cast<BButton *>(Window()->FindView("remove")); 1834 if (!button) { 1835 BRect rect = box->Bounds(); 1836 rect.InsetBy(5, 10); 1837 rect.top = rect.bottom - 20; 1838 rect.right = rect.left + 40; 1839 1840 button = new BButton(rect, "add", "Add", new BMessage(kAddItem), 1841 B_FOLLOW_RIGHT + B_FOLLOW_BOTTOM); 1842 button->SetTarget(this); 1843 box->AddChild(button); 1844 1845 rect.OffsetBy(50, 0); 1846 rect.right = rect.left + 55; 1847 button = new BButton(rect, "remove", "Remove", new BMessage(kRemoveItem), 1848 B_FOLLOW_RIGHT + B_FOLLOW_BOTTOM); 1849 1850 button->SetEnabled(false); 1851 button->SetTarget(this); 1852 box->AddChild(button); 1853 } 1854 // enable remove button as needed 1855 button->SetEnabled(fAttrViewList.CountItems() > 1); 1856 } 1857 1858 1859 void 1860 FindPanel::FillCurrentQueryName(BTextControl *queryName, FindWindow *window) 1861 { 1862 ASSERT(window); 1863 queryName->SetText(window->QueryName()); 1864 } 1865 1866 1867 void 1868 FindPanel::AddAttrView() 1869 { 1870 BBox *box = dynamic_cast<BBox *>(FindView("Box")); 1871 BRect bounds(Bounds()); 1872 1873 TAttrView *previous = fAttrViewList.LastItem(); 1874 1875 if (previous) 1876 Window()->ResizeBy(0, 30); 1877 1878 bounds = Bounds(); 1879 bounds.InsetBy(15, 30); 1880 bounds.bottom -= 10 + (fLatch->Value() ? kAttrViewDelta : 0); 1881 1882 if (previous) { 1883 box->ResizeTo(bounds.Width(), bounds.Height()); 1884 bounds = previous->Frame(); 1885 bounds.OffsetBy(0, 30); 1886 } else { 1887 bounds = box->Bounds(); 1888 bounds.InsetBy(5, 5); 1889 bounds.bottom = bounds.top + 25; 1890 } 1891 AddOneAttributeItem(box, bounds); 1892 1893 // add logic to previous attrview 1894 if (previous) 1895 previous->AddLogicMenu(); 1896 1897 SetUpAddRemoveButtons(box); 1898 1899 // populate mime popup 1900 TAttrView *last = fAttrViewList.LastItem(); 1901 last->AddMimeTypeAttrs(); 1902 } 1903 1904 1905 void 1906 FindPanel::RemoveAttrView() 1907 { 1908 if (fAttrViewList.CountItems() < 2) 1909 return; 1910 1911 BBox *box = dynamic_cast<BBox *>(FindView("Box")); 1912 TAttrView *attrView = fAttrViewList.LastItem(); 1913 if (!box || !attrView) 1914 return; 1915 1916 Window()->ResizeBy(0, -30); 1917 BRect bounds(Bounds()); 1918 bounds.InsetBy(15, 30); 1919 bounds.bottom -= 10 + (fLatch->Value() ? kAttrViewDelta : 0); 1920 box->ResizeTo(bounds.Width(), bounds.Height()); 1921 1922 fAttrViewList.RemoveItem(attrView); 1923 attrView->RemoveSelf(); 1924 delete attrView; 1925 1926 attrView = fAttrViewList.LastItem(); 1927 attrView->RemoveLogicMenu(); 1928 attrView->MakeTextViewFocus(); 1929 1930 if (fAttrViewList.CountItems() != 1) 1931 return; 1932 1933 BButton *button = dynamic_cast<BButton *>(Window()->FindView("remove")); 1934 if (button) 1935 button->SetEnabled(false); 1936 } 1937 1938 1939 uint32 1940 FindPanel::InitialMode(const BNode *node) 1941 { 1942 if (!node || node->InitCheck() != B_OK) 1943 return kByNameItem; 1944 1945 uint32 result; 1946 if (node->ReadAttr(kAttrQueryInitialMode, B_INT32_TYPE, 0, 1947 (int32 *)&result, sizeof(int32)) <= 0) 1948 return kByNameItem; 1949 1950 return result; 1951 } 1952 1953 1954 int32 1955 FindPanel::InitialAttrCount(const BNode *node) 1956 { 1957 if (!node || node->InitCheck() != B_OK) 1958 return 1; 1959 1960 int32 result; 1961 if (node->ReadAttr(kAttrQueryInitialNumAttrs, B_INT32_TYPE, 0, 1962 &result, sizeof(int32)) <= 0) 1963 return 1; 1964 1965 return result; 1966 } 1967 1968 1969 static int32 1970 SelectItemWithLabel(BMenu *menu, const char *label) 1971 { 1972 for (int32 index = menu->CountItems(); index-- > 0;) { 1973 BMenuItem *item = menu->ItemAt(index); 1974 1975 if (strcmp(label, item->Label()) == 0) { 1976 item->SetMarked(true); 1977 return index; 1978 } 1979 } 1980 return -1; 1981 } 1982 1983 1984 void 1985 FindPanel::SaveWindowState(BNode *node, bool editTemplate) 1986 { 1987 ASSERT(node->InitCheck() == B_OK); 1988 1989 BMenuItem *item = CurrentMimeType(); 1990 if (item) { 1991 BString label(item->Label()); 1992 node->WriteAttrString(kAttrQueryInitialMime, &label); 1993 } 1994 1995 uint32 mode = Mode(); 1996 node->WriteAttr(kAttrQueryInitialMode, B_INT32_TYPE, 0, 1997 (int32 *)&mode, sizeof(int32)); 1998 1999 MoreOptionsStruct saveMoreOptions; 2000 saveMoreOptions.showMoreOptions = fLatch->Value() != 0; 2001 2002 saveMoreOptions.searchTrash = fSearchTrashCheck->Value() != 0; 2003 saveMoreOptions.temporary = fTemporaryCheck->Value() != 0; 2004 2005 if (node->WriteAttr(kAttrQueryMoreOptions, B_RAW_TYPE, 0, &saveMoreOptions, 2006 sizeof(saveMoreOptions)) == sizeof(saveMoreOptions)) 2007 node->RemoveAttr(kAttrQueryMoreOptionsForeign); 2008 2009 if (editTemplate) { 2010 if (UserSpecifiedName()) { 2011 BString name(UserSpecifiedName()); 2012 node->WriteAttrString(kAttrQueryTemplateName, &name); 2013 } 2014 } 2015 2016 switch (Mode()) { 2017 case kByAttributeItem: 2018 { 2019 BMessage message; 2020 int32 count = fAttrViewList.CountItems(); 2021 node->WriteAttr(kAttrQueryInitialNumAttrs, B_INT32_TYPE, 0, 2022 &count, sizeof(int32)); 2023 2024 for (int32 index = 0; index < count; index++) 2025 fAttrViewList.ItemAt(index)->SaveState(&message, index); 2026 2027 ssize_t size = message.FlattenedSize(); 2028 char *buffer = new char[size]; 2029 status_t result = message.Flatten(buffer, size); 2030 ASSERT(result == B_OK); 2031 result = node->WriteAttr(kAttrQueryInitialAttrs, B_MESSAGE_TYPE, 0, 2032 buffer, (size_t)size); 2033 ASSERT(result == size); 2034 delete [] buffer; 2035 } 2036 break; 2037 2038 case kByNameItem: 2039 case kByForumlaItem: 2040 { 2041 BTextControl *textControl = dynamic_cast<BTextControl *> 2042 (FindView("TextControl")); 2043 ASSERT(textControl); 2044 BString formula(textControl->TextView()->Text()); 2045 node->WriteAttrString(kAttrQueryInitialString, &formula); 2046 break; 2047 } 2048 } 2049 } 2050 2051 2052 void 2053 FindPanel::SwitchToTemplate(const BNode *node) 2054 { 2055 if (fLatch->Value()) { 2056 // this is kind of a hack - the following code up to 2057 // RestoreWindowState assumes the latch is closed 2058 // Would be nicer if all the size of the window were set once 2059 // and correctly - this is not easy thought because the latch 2060 // controls the window size in relative increments 2061 fLatch->SetValue(0); 2062 fMoreOptionsPane->SetMode(0); 2063 } 2064 2065 SwitchMode(InitialMode(node)); 2066 // update the menu to correspond to the mode 2067 MarkNamedMenuItem(fSearchModeMenu, InitialMode(node), true); 2068 2069 BRect initialRect(InitialViewSize(node)); 2070 Window()->ResizeTo(initialRect.Width(), initialRect.Height()); 2071 if (Mode() == (int32)kByAttributeItem) { 2072 RemoveByAttributeItems(); 2073 ResizeAttributeBox(node); 2074 AddByAttributeItems(node); 2075 } 2076 2077 RestoreWindowState(node); 2078 } 2079 2080 2081 void 2082 FindPanel::RestoreMimeTypeMenuSelection(const BNode *node) 2083 { 2084 if (Mode() == (int32)kByForumlaItem || node == NULL || node->InitCheck() != B_OK) 2085 return; 2086 2087 BString buffer; 2088 if (node->ReadAttrString(kAttrQueryInitialMime, &buffer) == B_OK) 2089 SetCurrentMimeType(buffer.String()); 2090 } 2091 2092 2093 void 2094 FindPanel::RestoreWindowState(const BNode *node) 2095 { 2096 fMode = InitialMode(node); 2097 if (!node || node->InitCheck() != B_OK) 2098 return; 2099 2100 ShowOrHideMimeTypeMenu(); 2101 RestoreMimeTypeMenuSelection(node); 2102 MoreOptionsStruct saveMoreOptions; 2103 2104 bool storesMoreOptions = ReadAttr(node, kAttrQueryMoreOptions, 2105 kAttrQueryMoreOptionsForeign, B_RAW_TYPE, 0, &saveMoreOptions, 2106 sizeof(saveMoreOptions), &MoreOptionsStruct::EndianSwap) 2107 != kReadAttrFailed; 2108 2109 if (storesMoreOptions) { 2110 // need to sanitize to true or false here, could have picked 2111 // up garbage from attributes 2112 2113 saveMoreOptions.showMoreOptions = 2114 (saveMoreOptions.showMoreOptions != 0); 2115 2116 fLatch->SetValue(saveMoreOptions.showMoreOptions); 2117 fMoreOptionsPane->SetMode(saveMoreOptions.showMoreOptions); 2118 2119 fSearchTrashCheck->SetValue(saveMoreOptions.searchTrash); 2120 fTemporaryCheck->SetValue(saveMoreOptions.temporary); 2121 2122 fQueryName->SetModificationMessage(NULL); 2123 FillCurrentQueryName(fQueryName, dynamic_cast<FindWindow *>(Window())); 2124 2125 // set modification message after checking the temporary check box, 2126 // and filling out the text control so that we do not 2127 // always trigger clearing of the temporary check box. 2128 fQueryName->SetModificationMessage(new BMessage(kNameModifiedMessage)); 2129 } 2130 2131 // get volumes to perform query on 2132 bool searchAllVolumes = true; 2133 2134 attr_info info; 2135 if (node->GetAttrInfo(kAttrQueryVolume, &info) == B_OK) { 2136 char *buffer = new char[info.size]; 2137 if (node->ReadAttr(kAttrQueryVolume, B_MESSAGE_TYPE, 0, buffer, (size_t)info.size) 2138 == info.size) { 2139 BMessage message; 2140 if (message.Unflatten(buffer) == B_OK) { 2141 for (int32 index = 0; ;index++) { 2142 ASSERT(index < 100); 2143 BVolume volume; 2144 // match a volume with the info embedded in the message 2145 status_t result = MatchArchivedVolume(&volume, &message, index); 2146 if (result == B_OK) { 2147 char name[256]; 2148 volume.GetName(name); 2149 SelectItemWithLabel(fVolMenu, name); 2150 searchAllVolumes = false; 2151 } else if (result != B_DEV_BAD_DRIVE_NUM) 2152 // if B_DEV_BAD_DRIVE_NUM, the volume just isn't mounted this 2153 // time around, keep looking for more 2154 // if other error, bail 2155 break; 2156 } 2157 } 2158 } 2159 delete [] buffer; 2160 } 2161 // mark or unmark "All disks" 2162 fVolMenu->ItemAt(0)->SetMarked(searchAllVolumes); 2163 ShowVolumeMenuLabel(); 2164 2165 switch (Mode()) { 2166 case kByAttributeItem: 2167 { 2168 int32 count = InitialAttrCount(node); 2169 2170 attr_info info; 2171 if (node->GetAttrInfo(kAttrQueryInitialAttrs, &info) != B_OK) 2172 break; 2173 char *buffer = new char[info.size]; 2174 if (node->ReadAttr(kAttrQueryInitialAttrs, B_MESSAGE_TYPE, 0, buffer, (size_t)info.size) 2175 == info.size) { 2176 BMessage message; 2177 if (message.Unflatten(buffer) == B_OK) 2178 for (int32 index = 0; index < count; index++) 2179 fAttrViewList.ItemAt(index)->RestoreState(message, index); 2180 } 2181 delete [] buffer; 2182 break; 2183 } 2184 2185 case kByNameItem: 2186 case kByForumlaItem: 2187 { 2188 BString buffer; 2189 if (node->ReadAttrString(kAttrQueryInitialString, &buffer) == B_OK) { 2190 BTextControl *textControl = dynamic_cast<BTextControl *> 2191 (FindView("TextControl")); 2192 ASSERT(textControl); 2193 2194 textControl->TextView()->SetText(buffer.String()); 2195 } 2196 } 2197 break; 2198 } 2199 2200 // try to restore focus and possibly text selection 2201 BString focusedView; 2202 if (node->ReadAttrString("_trk/focusedView", &focusedView) == B_OK) { 2203 BView *view = FindView(focusedView.String()); 2204 if (view) { 2205 view->MakeFocus(); 2206 BTextControl *textControl = dynamic_cast<BTextControl *>(view); 2207 if (textControl) { 2208 int32 selStart = 0, selEnd = LONG_MAX; 2209 node->ReadAttr("_trk/focusedSelStart", B_INT32_TYPE, 0, 2210 &selStart, sizeof(selStart)); 2211 node->ReadAttr("_trk/focusedSelEnd", B_INT32_TYPE, 0, 2212 &selEnd, sizeof(selEnd)); 2213 textControl->TextView()->Select(selStart, selEnd); 2214 } 2215 } 2216 } 2217 } 2218 2219 2220 void 2221 FindPanel::ResizeAttributeBox(const BNode *node) 2222 { 2223 BBox *box = dynamic_cast<BBox *>(FindView("Box")); 2224 BRect bounds(box->Bounds()); 2225 int32 count = InitialAttrCount(node); 2226 2227 bounds.bottom = count * 30 + 40; 2228 box->ResizeTo(bounds.Width(), bounds.Height()); 2229 } 2230 2231 2232 void 2233 FindPanel::AddByAttributeItems(const BNode *node) 2234 { 2235 BBox *box = dynamic_cast<BBox *>(FindView("Box")); 2236 ASSERT(box); 2237 BRect bounds(box->Bounds()); 2238 2239 int32 numAttributes = InitialAttrCount(node); 2240 if (numAttributes < 1) 2241 numAttributes = 1; 2242 2243 BRect rect(bounds); 2244 rect.InsetBy(5, 5); 2245 rect.bottom = rect.top + 25; 2246 2247 for (int32 index = 0; index < numAttributes; index ++) { 2248 AddOneAttributeItem(box, rect); 2249 rect.OffsetBy(0, 30); 2250 } 2251 SetUpAddRemoveButtons(box); 2252 } 2253 2254 2255 void 2256 FindPanel::AddByNameOrFormulaItems() 2257 { 2258 BBox *box = dynamic_cast<BBox *>(FindView("Box")); 2259 2260 BRect bounds(box->Bounds()); 2261 bounds.InsetBy(10, 10); 2262 BTextControl *textControl = new BTextControl(bounds, "TextControl", "", "", NULL); 2263 textControl->SetDivider(0.0f); 2264 box->AddChild(textControl); 2265 textControl->MakeFocus(); 2266 } 2267 2268 2269 void 2270 FindPanel::RemoveAttrViewItems() 2271 { 2272 for (;;) { 2273 BView *view = FindView("AttrView"); 2274 if (view == NULL) 2275 break; 2276 view->RemoveSelf(); 2277 delete view; 2278 } 2279 2280 fAttrViewList.MakeEmpty(); 2281 } 2282 2283 2284 void 2285 FindPanel::RemoveByAttributeItems() 2286 { 2287 RemoveAttrViewItems(); 2288 BView *view = FindView("add"); 2289 if (view) { 2290 view->RemoveSelf(); 2291 delete view; 2292 } 2293 2294 view = FindView("remove"); 2295 if (view) { 2296 view->RemoveSelf(); 2297 delete view; 2298 } 2299 2300 view = dynamic_cast<BTextControl *>(FindView("TextControl")); 2301 if (view) { 2302 view->RemoveSelf(); 2303 delete view; 2304 } 2305 } 2306 2307 2308 void 2309 FindPanel::ShowOrHideMimeTypeMenu() 2310 { 2311 BMenuField *menuField = dynamic_cast<BMenuField *>(FindView("MimeTypeMenu")); 2312 if (Mode() == (int32)kByForumlaItem && !menuField->IsHidden()) 2313 menuField->Hide(); 2314 else if (menuField->IsHidden()) 2315 menuField->Show(); 2316 } 2317 2318 2319 // #pragma mark - 2320 2321 2322 TAttrView::TAttrView(BRect frame, int32 index) 2323 : BView(frame, "AttrView", B_FOLLOW_NONE, B_WILL_DRAW) 2324 { 2325 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 2326 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 2327 2328 BPopUpMenu *menu = new BPopUpMenu("PopUp"); 2329 2330 // add NAME attribute to popup 2331 BMenu *submenu = new BMenu("Name"); 2332 submenu->SetRadioMode(true); 2333 submenu->SetFont(be_plain_font); 2334 BMessage *message = new BMessage(kAttributeItemMain); 2335 message->AddString("name", "name"); 2336 message->AddInt32("type", B_STRING_TYPE); 2337 BMenuItem *item = new BMenuItem(submenu, message); 2338 menu->AddItem(item); 2339 2340 const int32 operators[] = {B_CONTAINS, B_EQ, B_NE, B_BEGINS_WITH, B_ENDS_WITH}; 2341 const char *operatorLabels[] = {"contains", "is", "is not", "starts with", "ends with"}; 2342 2343 for (int32 i = 0;i < 5;i++) { 2344 message = new BMessage(kAttributeItem); 2345 message->AddInt32("operator", operators[i]); 2346 submenu->AddItem(new BMenuItem(operatorLabels[i], message)); 2347 } 2348 2349 // mark first item 2350 menu->ItemAt(0)->SetMarked(true); 2351 submenu->ItemAt(0)->SetMarked(true); 2352 2353 // add SIZE attribute 2354 submenu = new BMenu("Size"); 2355 submenu->SetRadioMode(true); 2356 submenu->SetFont(be_plain_font); 2357 message = new BMessage(kAttributeItemMain); 2358 message->AddString("name", "size"); 2359 message->AddInt32("type", B_OFF_T_TYPE); 2360 item = new BMenuItem(submenu, message); 2361 menu->AddItem(item); 2362 2363 message = new BMessage(kAttributeItem); 2364 message->AddInt32("operator", B_GE); 2365 submenu->AddItem(new BMenuItem("greater than", message)); 2366 2367 message = new BMessage(kAttributeItem); 2368 message->AddInt32("operator", B_LE); 2369 submenu->AddItem(new BMenuItem("less than", message)); 2370 2371 message = new BMessage(kAttributeItem); 2372 message->AddInt32("operator", B_EQ); 2373 submenu->AddItem(new BMenuItem("is", message)); 2374 2375 // add "modified" field 2376 submenu = new BMenu("Modified"); 2377 submenu->SetRadioMode(true); 2378 submenu->SetFont(be_plain_font); 2379 message = new BMessage(kAttributeItemMain); 2380 message->AddString("name", "last_modified"); 2381 message->AddInt32("type", B_TIME_TYPE); 2382 item = new BMenuItem(submenu, message); 2383 menu->AddItem(item); 2384 2385 message = new BMessage(kAttributeItem); 2386 message->AddInt32("operator", B_LE); 2387 submenu->AddItem(new BMenuItem("before", message)); 2388 2389 message = new BMessage(kAttributeItem); 2390 message->AddInt32("operator", B_GE); 2391 submenu->AddItem(new BMenuItem("after", message)); 2392 2393 BRect bounds(Bounds()); 2394 bounds.right = bounds.left + 100; 2395 bounds.bottom = bounds.top + 15; 2396 fMenuField = new BMenuField(bounds, "MenuField", "", menu); 2397 fMenuField->SetDivider(0.0f); 2398 2399 // add text entry box 2400 bounds = Bounds(); 2401 bounds.left += bounds.right - 180; 2402 bounds.top += 2; 2403 bounds.right -= 42; 2404 BString title("TextEntry"); 2405 title << index; 2406 fTextControl = new BTextControl(bounds, title.String(), "", "", NULL); 2407 fTextControl->SetDivider(0.0f); 2408 AddChild(fTextControl); 2409 2410 AddChild(fMenuField); 2411 // add attributes from currently selected mimetype 2412 } 2413 2414 2415 TAttrView::~TAttrView() 2416 { 2417 } 2418 2419 2420 void 2421 TAttrView::AttachedToWindow() 2422 { 2423 BMenu *menu = fMenuField->Menu(); 2424 // target everything 2425 menu->SetTargetForItems(this); 2426 2427 for (int32 index = menu->CountItems() - 1; index >= 0; index--) 2428 menu->SubmenuAt(index)->SetTargetForItems(this); 2429 } 2430 2431 2432 void 2433 TAttrView::MakeTextViewFocus() 2434 { 2435 fTextControl->MakeFocus(); 2436 } 2437 2438 2439 void 2440 TAttrView::RestoreState(const BMessage &message, int32 index) 2441 { 2442 BMenu *menu = fMenuField->Menu(); 2443 // decode menu selections 2444 2445 AddMimeTypeAttrs(menu); 2446 2447 const char *label; 2448 if (message.FindString("menuSelection", index, &label) == B_OK) { 2449 int32 itemIndex = SelectItemWithLabel(menu, label); 2450 if (itemIndex >=0) { 2451 menu = menu->SubmenuAt(itemIndex); 2452 if (menu && message.FindString("subMenuSelection", index, &label) 2453 == B_OK) 2454 SelectItemWithLabel(menu, label); 2455 } 2456 } 2457 2458 // decode attribute text 2459 ASSERT(fTextControl); 2460 const char *string; 2461 if (message.FindString("attrViewText", index, &string) == B_OK) 2462 fTextControl->TextView()->SetText(string); 2463 2464 int32 logicMenuSelectedIndex; 2465 BMenuField *field = dynamic_cast<BMenuField *>(FindView("Logic")); 2466 if (message.FindInt32("logicalRelation", index, &logicMenuSelectedIndex) == B_OK) 2467 if (field) 2468 field->Menu()->ItemAt(logicMenuSelectedIndex)->SetMarked(true); 2469 else 2470 AddLogicMenu(logicMenuSelectedIndex == 0); 2471 } 2472 2473 2474 void 2475 TAttrView::SaveState(BMessage *message, int32) 2476 { 2477 BMenu *menu = fMenuField->Menu(); 2478 2479 // encode main attribute menu selection 2480 BMenuItem *item = menu->FindMarked(); 2481 message->AddString("menuSelection", item ? item->Label() : ""); 2482 2483 // encode submenu selection 2484 const char *label = ""; 2485 if (item) { 2486 BMenu *submenu = menu->SubmenuAt(menu->IndexOf(item)); 2487 if (submenu) { 2488 item = submenu->FindMarked(); 2489 if (item) 2490 label = item->Label(); 2491 } 2492 } 2493 message->AddString("subMenuSelection", label); 2494 2495 // encode attribute text 2496 ASSERT(fTextControl); 2497 message->AddString("attrViewText", fTextControl->TextView()->Text()); 2498 2499 BMenuField *field = dynamic_cast<BMenuField *>(FindView("Logic")); 2500 if (field) { 2501 BMenuItem *item = field->Menu()->FindMarked(); 2502 ASSERT(item); 2503 message->AddInt32("logicalRelation", item ? field->Menu()->IndexOf(item) : 0); 2504 } 2505 } 2506 2507 void 2508 TAttrView::AddLogicMenu(bool selectAnd) 2509 { 2510 // add "AND/OR" menu 2511 BPopUpMenu *menu = new BPopUpMenu(""); 2512 BMessage *message = new BMessage(); 2513 message->AddInt32("combine", B_AND); 2514 BMenuItem *item = new BMenuItem("And", message); 2515 menu->AddItem(item); 2516 if (selectAnd) 2517 item->SetMarked(true); 2518 2519 message = new BMessage(); 2520 message->AddInt32("combine", B_OR); 2521 item = new BMenuItem("Or", message); 2522 menu->AddItem(item); 2523 if (!selectAnd) 2524 item->SetMarked(true); 2525 2526 menu->SetTargetForItems(this); 2527 2528 BRect bounds(Bounds()); 2529 bounds.left = bounds.right - 40; 2530 bounds.bottom = bounds.top + 15; 2531 BMenuField *menufield = new BMenuField(bounds, "Logic", "", menu); 2532 menufield->SetDivider(0.0f); 2533 menufield->HidePopUpMarker(); 2534 AddChild(menufield); 2535 } 2536 2537 2538 void 2539 TAttrView::RemoveLogicMenu() 2540 { 2541 BMenuField *menufield = dynamic_cast<BMenuField *>(FindView("Logic")); 2542 if (menufield) { 2543 menufield->RemoveSelf(); 2544 delete menufield; 2545 } 2546 } 2547 2548 2549 void 2550 TAttrView::Draw(BRect) 2551 { 2552 BMenuItem *item = fMenuField->Menu()->FindMarked(); 2553 if (!item) 2554 return; 2555 2556 if (item->Submenu()->FindMarked()) { 2557 float width = StringWidth(item->Submenu()->FindMarked()->Label()); 2558 BRect bounds(fTextControl->Frame()); 2559 2560 // draws the is/contains, etc. string 2561 bounds.left -= (width + 10); 2562 bounds.bottom -= 6; 2563 MovePenTo(bounds.LeftBottom()); 2564 DrawString(item->Submenu()->FindMarked()->Label()); 2565 } 2566 } 2567 2568 2569 void 2570 TAttrView::MessageReceived(BMessage *message) 2571 { 2572 BMenuItem *item; 2573 2574 switch (message->what) { 2575 case kAttributeItem: 2576 if (message->FindPointer("source", (void **)&item) != B_OK) 2577 return; 2578 2579 item->Menu()->Superitem()->SetMarked(true); 2580 Invalidate(); 2581 break; 2582 2583 case kAttributeItemMain: 2584 // in case someone selected just and attribute without the 2585 // comparator 2586 if (message->FindPointer("source", (void **)&item) != B_OK) 2587 return; 2588 2589 if (item->Submenu()->ItemAt(0)) 2590 item->Submenu()->ItemAt(0)->SetMarked(true); 2591 Invalidate(); 2592 break; 2593 2594 default: 2595 _inherited::MessageReceived(message); 2596 break; 2597 } 2598 } 2599 2600 2601 void 2602 TAttrView::AddMimeTypeAttrs() 2603 { 2604 BMenu *menu = fMenuField->Menu(); 2605 AddMimeTypeAttrs(menu); 2606 } 2607 2608 2609 void 2610 TAttrView::AddMimeTypeAttrs(BMenu *menu) 2611 { 2612 FindPanel *mainView = dynamic_cast<FindPanel *>(Parent()-> 2613 Parent()->FindView("MainView")); 2614 if (!mainView) 2615 return; 2616 2617 const char *typeName; 2618 if (mainView->CurrentMimeType(&typeName) == NULL) 2619 return; 2620 2621 BMimeType mimeType(typeName); 2622 if (!mimeType.IsInstalled()) 2623 return; 2624 2625 // only add things to menu which have "user-visible" data 2626 BMessage attributeMessage; 2627 if (mimeType.GetAttrInfo(&attributeMessage) != B_OK) 2628 return; 2629 2630 char desc[B_MIME_TYPE_LENGTH]; 2631 mimeType.GetShortDescription(desc); 2632 2633 // go through each field in meta mime and add it to a menu 2634 for (int32 index = 0; ; index++) { 2635 const char *publicName; 2636 if (attributeMessage.FindString("attr:public_name", index, &publicName) != B_OK) 2637 break; 2638 2639 if (!attributeMessage.FindBool("attr:viewable")) 2640 continue; 2641 2642 const char *attributeName; 2643 if (attributeMessage.FindString("attr:name", index, &attributeName) != B_OK) 2644 continue; 2645 2646 int32 type; 2647 if (attributeMessage.FindInt32("attr:type", index, &type) != B_OK) 2648 continue; 2649 2650 BMenu *submenu = new BMenu(publicName); 2651 submenu->SetRadioMode(true); 2652 submenu->SetFont(be_plain_font); 2653 BMessage *message = new BMessage(kAttributeItemMain); 2654 message->AddString("name", attributeName); 2655 message->AddInt32("type", type); 2656 BMenuItem *item = new BMenuItem(submenu, message); 2657 menu->AddItem(item); 2658 menu->SetTargetForItems(this); 2659 2660 switch (type) { 2661 case B_STRING_TYPE: 2662 message = new BMessage(kAttributeItem); 2663 message->AddInt32("operator", B_CONTAINS); 2664 submenu->AddItem(new BMenuItem("contains", message)); 2665 2666 message = new BMessage(kAttributeItem); 2667 message->AddInt32("operator", B_EQ); 2668 submenu->AddItem(new BMenuItem("is", message)); 2669 2670 message = new BMessage(kAttributeItem); 2671 message->AddInt32("operator", B_NE); 2672 submenu->AddItem(new BMenuItem("is not", message)); 2673 submenu->SetTargetForItems(this); 2674 2675 message = new BMessage(kAttributeItem); 2676 message->AddInt32("operator", B_BEGINS_WITH); 2677 submenu->AddItem(new BMenuItem("starts with", message)); 2678 submenu->SetTargetForItems(this); 2679 2680 message = new BMessage(kAttributeItem); 2681 message->AddInt32("operator", B_ENDS_WITH); 2682 submenu->AddItem(new BMenuItem("ends with", message)); 2683 break; 2684 2685 case B_BOOL_TYPE: 2686 case B_INT16_TYPE: 2687 case B_UINT8_TYPE: 2688 case B_INT8_TYPE: 2689 case B_UINT16_TYPE: 2690 case B_INT32_TYPE: 2691 case B_UINT32_TYPE: 2692 case B_INT64_TYPE: 2693 case B_UINT64_TYPE: 2694 case B_OFF_T_TYPE: 2695 case B_FLOAT_TYPE: 2696 case B_DOUBLE_TYPE: 2697 message = new BMessage(kAttributeItem); 2698 message->AddInt32("operator", B_EQ); 2699 submenu->AddItem(new BMenuItem("is", message)); 2700 2701 message = new BMessage(kAttributeItem); 2702 message->AddInt32("operator", B_GE); 2703 submenu->AddItem(new BMenuItem("greater than", message)); 2704 2705 message = new BMessage(kAttributeItem); 2706 message->AddInt32("operator", B_LE); 2707 submenu->AddItem(new BMenuItem("less than", message)); 2708 break; 2709 2710 case B_TIME_TYPE: 2711 message = new BMessage(kAttributeItem); 2712 message->AddInt32("operator", B_LE); 2713 submenu->AddItem(new BMenuItem("before", message)); 2714 2715 message = new BMessage(kAttributeItem); 2716 message->AddInt32("operator", B_GE); 2717 submenu->AddItem(new BMenuItem("after", message)); 2718 break; 2719 } 2720 submenu->SetTargetForItems(this); 2721 } 2722 } 2723 2724 2725 void 2726 TAttrView::GetDefaultName(BString &result) const 2727 { 2728 BMenuItem *item = NULL; 2729 if (fMenuField->Menu() != NULL) 2730 item = fMenuField->Menu()->FindMarked(); 2731 if (item != NULL) 2732 result << item->Label(); 2733 else 2734 result << "Name"; 2735 2736 if (item->Submenu() != NULL) 2737 item = item->Submenu()->FindMarked(); 2738 else 2739 item = NULL; 2740 2741 if (item != NULL) 2742 result << " " << item->Label() << " "; 2743 else 2744 result << " = "; 2745 2746 result << fTextControl->Text(); 2747 } 2748 2749 2750 // #pragma mark - 2751 2752 2753 DeleteTransientQueriesTask::DeleteTransientQueriesTask() 2754 : state(kInitial), 2755 fWalker(NULL) 2756 { 2757 } 2758 2759 2760 DeleteTransientQueriesTask::~DeleteTransientQueriesTask() 2761 { 2762 delete fWalker; 2763 } 2764 2765 2766 bool 2767 DeleteTransientQueriesTask::DoSomeWork() 2768 { 2769 switch (state) { 2770 case kInitial: 2771 Initialize(); 2772 break; 2773 2774 case kAllocatedWalker: 2775 case kTraversing: 2776 if (GetSome()) { 2777 PRINT(("transient query killer done\n")); 2778 return true; 2779 } 2780 break; 2781 2782 case kError: 2783 return true; 2784 2785 } 2786 return false; 2787 } 2788 2789 2790 void 2791 DeleteTransientQueriesTask::Initialize() 2792 { 2793 PRINT(("starting up transient query killer\n")); 2794 BPath path; 2795 status_t result = find_directory(B_USER_DIRECTORY, &path, false); 2796 if (result != B_OK) { 2797 state = kError; 2798 return; 2799 } 2800 fWalker = new WALKER_NS::TNodeWalker(path.Path()); 2801 state = kAllocatedWalker; 2802 } 2803 2804 2805 const int32 kBatchCount = 100; 2806 2807 bool 2808 DeleteTransientQueriesTask::GetSome() 2809 { 2810 state = kTraversing; 2811 for (int32 count = kBatchCount; count > 0; count--) { 2812 entry_ref ref; 2813 if (fWalker->GetNextRef(&ref) != B_OK) { 2814 state = kError; 2815 return true; 2816 } 2817 Model model(&ref); 2818 if (model.IsQuery()) 2819 ProcessOneRef(&model); 2820 #if xDEBUG 2821 else 2822 PRINT(("transient query killer: %s not a query\n", model.Name())); 2823 #endif 2824 } 2825 return false; 2826 } 2827 2828 2829 const int32 kDaysToExpire = 7; 2830 2831 static bool 2832 QueryOldEnough(Model *model) 2833 { 2834 // check if it is old and ready to be deleted 2835 time_t now = time(0); 2836 2837 tm nowTimeData; 2838 tm fileModData; 2839 2840 localtime_r(&now, &nowTimeData); 2841 localtime_r(&model->StatBuf()->st_ctime, &fileModData); 2842 2843 if ((nowTimeData.tm_mday - fileModData.tm_mday) < kDaysToExpire 2844 && (nowTimeData.tm_mday - fileModData.tm_mday) > -kDaysToExpire) { 2845 PRINT(("query %s, not old enough\n", model->Name())); 2846 return false; 2847 } 2848 return true; 2849 } 2850 2851 2852 bool 2853 DeleteTransientQueriesTask::ProcessOneRef(Model *model) 2854 { 2855 BModelOpener opener(model); 2856 2857 // is this a temporary query 2858 if (!MoreOptionsStruct::QueryTemporary(model->Node())) { 2859 PRINT(("query %s, not temporary\n", model->Name())); 2860 return false; 2861 } 2862 2863 if (!QueryOldEnough(model)) 2864 return false; 2865 2866 ASSERT(dynamic_cast<TTracker *>(be_app)); 2867 2868 // check that it is not showing 2869 if (dynamic_cast<TTracker *>(be_app)->EntryHasWindowOpen(model->EntryRef())) { 2870 PRINT(("query %s, showing, can't delete\n", model->Name())); 2871 return false; 2872 } 2873 2874 PRINT(("query %s, old, temporary, not shownig - deleting\n", model->Name())); 2875 BEntry entry(model->EntryRef()); 2876 entry.Remove(); 2877 2878 return true; 2879 } 2880 2881 2882 class DeleteTransientQueriesFunctor : public FunctionObjectWithResult<bool> { 2883 public: 2884 DeleteTransientQueriesFunctor(DeleteTransientQueriesTask *task) 2885 : task(task) 2886 {} 2887 2888 virtual ~DeleteTransientQueriesFunctor() 2889 { 2890 delete task; 2891 } 2892 2893 virtual void operator()() 2894 { result = task->DoSomeWork(); } 2895 2896 private: 2897 DeleteTransientQueriesTask *task; 2898 }; 2899 2900 2901 void 2902 DeleteTransientQueriesTask::StartUpTransientQueryCleaner() 2903 { 2904 // set up a task that wakes up when the machine is idle and starts 2905 // killing off old transient queries 2906 DeleteTransientQueriesFunctor *worker 2907 = new DeleteTransientQueriesFunctor(new DeleteTransientQueriesTask()); 2908 TTracker *tracker = dynamic_cast<TTracker *>(be_app); 2909 ASSERT(tracker); 2910 tracker->MainTaskLoop()->RunWhenIdle(worker, 2911 30 * 60 * 1000000, // half an hour initial delay 2912 5 * 60 * 1000000, // idle for five minutes 2913 10 * 1000000); 2914 } 2915 2916 2917 // #pragma mark - 2918 2919 2920 RecentFindItemsMenu::RecentFindItemsMenu(const char *title, const BMessenger *target, 2921 uint32 what) 2922 : BMenu(title, B_ITEMS_IN_COLUMN), 2923 fTarget(*target), 2924 fWhat(what) 2925 { 2926 } 2927 2928 2929 void 2930 RecentFindItemsMenu::AttachedToWindow() 2931 { 2932 // re-populate the menu with fresh items 2933 for (int32 index = CountItems() - 1; index >= 0; index--) 2934 delete RemoveItem(index); 2935 2936 FindPanel::AddRecentQueries(this, false, &fTarget, fWhat); 2937 BMenu::AttachedToWindow(); 2938 } 2939 2940 2941 #if !B_BEOS_VERSION_DANO 2942 _IMPEXP_TRACKER 2943 #endif 2944 BMenu * 2945 TrackerBuildRecentFindItemsMenu(const char *title) 2946 { 2947 BMessenger tracker(kTrackerSignature); 2948 return new RecentFindItemsMenu(title, &tracker, B_REFS_RECEIVED); 2949 } 2950 2951 2952 // #pragma mark - 2953 2954 2955 DraggableQueryIcon::DraggableQueryIcon(BRect frame, const char *name, 2956 const BMessage *message, BMessenger messenger, uint32 resizeFlags, uint32 flags) 2957 : DraggableIcon(frame, name, B_QUERY_MIMETYPE, B_LARGE_ICON, 2958 message, messenger, resizeFlags, flags) 2959 { 2960 } 2961 2962 2963 bool 2964 DraggableQueryIcon::DragStarted(BMessage *dragMessage) 2965 { 2966 // override to substitute the user-specified query name 2967 dragMessage->RemoveData("be:clip_name"); 2968 2969 FindWindow *window = dynamic_cast<FindWindow *>(Window()); 2970 ASSERT(window); 2971 dragMessage->AddString("be:clip_name", 2972 window->BackgroundView()->UserSpecifiedName() ? 2973 window->BackgroundView()->UserSpecifiedName() : "New Query"); 2974 2975 return true; 2976 } 2977 2978 2979 // #pragma mark - 2980 2981 2982 MostUsedNames::MostUsedNames(const char *fileName, const char *directory, int32 maxCount) 2983 : 2984 fFileName(fileName), 2985 fDirectory(directory), 2986 fLoaded(false), 2987 fCount(maxCount) 2988 { 2989 } 2990 2991 2992 MostUsedNames::~MostUsedNames() 2993 { 2994 // only write back settings when we've been used 2995 if (!fLoaded) 2996 return; 2997 2998 // write most used list to file 2999 3000 BPath path; 3001 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK) 3002 return; 3003 3004 path.Append(fDirectory); 3005 path.Append(fFileName); 3006 BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); 3007 if (file.InitCheck() == B_OK) { 3008 for (int32 i = 0; i < fList.CountItems(); i++) { 3009 list_entry *entry = static_cast<list_entry *>(fList.ItemAt(i)); 3010 3011 char line[B_FILE_NAME_LENGTH + 5]; 3012 3013 // limit upper bound to react more dynamically to changes 3014 if (--entry->count > 20) 3015 entry->count = 20; 3016 3017 // if the item hasn't been chosen in a while, remove it 3018 // (but leave at least one item in the list) 3019 if (entry->count < -10 && i > 0) 3020 continue; 3021 3022 sprintf(line, "%ld %s\n", entry->count, entry->name); 3023 if (file.Write(line, strlen(line)) < B_OK) 3024 break; 3025 } 3026 } 3027 file.Unset(); 3028 3029 // free data 3030 3031 for (int32 i = fList.CountItems(); i-- > 0;) { 3032 list_entry *entry = static_cast<list_entry *>(fList.ItemAt(i)); 3033 free(entry->name); 3034 delete entry; 3035 } 3036 } 3037 3038 3039 bool 3040 MostUsedNames::ObtainList(BList *list) 3041 { 3042 if (!list) 3043 return false; 3044 3045 if (!fLoaded) 3046 UpdateList(); 3047 3048 fLock.Lock(); 3049 3050 list->MakeEmpty(); 3051 for (int32 i = 0; i < fCount; i++) { 3052 list_entry *entry = static_cast<list_entry *>(fList.ItemAt(i)); 3053 if (entry == NULL) 3054 return true; 3055 3056 list->AddItem(entry->name); 3057 } 3058 return true; 3059 } 3060 3061 3062 void 3063 MostUsedNames::ReleaseList() 3064 { 3065 fLock.Unlock(); 3066 } 3067 3068 3069 void 3070 MostUsedNames::AddName(const char *name) 3071 { 3072 fLock.Lock(); 3073 3074 if (!fLoaded) 3075 LoadList(); 3076 3077 // remove last entry if there are more than 3078 // 2*fCount entries in the list 3079 3080 list_entry *entry = NULL; 3081 3082 if (fList.CountItems() > fCount * 2) { 3083 entry = static_cast<list_entry *>(fList.RemoveItem(fList.CountItems() - 1)); 3084 3085 // is this the name we want to add here? 3086 if (strcmp(name, entry->name)) { 3087 free(entry->name); 3088 delete entry; 3089 entry = NULL; 3090 } else 3091 fList.AddItem(entry); 3092 } 3093 3094 if (entry == NULL) { 3095 for (int32 i = 0; (entry = static_cast<list_entry *>(fList.ItemAt(i))) != NULL; i++) 3096 if (!strcmp(entry->name, name)) 3097 break; 3098 } 3099 3100 if (entry == NULL) { 3101 entry = new list_entry; 3102 entry->name = strdup(name); 3103 entry->count = 1; 3104 3105 fList.AddItem(entry); 3106 } else if (entry->count < 0) 3107 entry->count = 1; 3108 else 3109 entry->count++; 3110 3111 fLock.Unlock(); 3112 UpdateList(); 3113 } 3114 3115 3116 int 3117 MostUsedNames::CompareNames(const void *a,const void *b) 3118 { 3119 list_entry *entryA = *(list_entry **)a; 3120 list_entry *entryB = *(list_entry **)b; 3121 3122 if (entryA->count == entryB->count) 3123 return strcasecmp(entryA->name,entryB->name); 3124 3125 return entryB->count - entryA->count; 3126 } 3127 3128 3129 void 3130 MostUsedNames::LoadList() 3131 { 3132 if (fLoaded) 3133 return; 3134 fLoaded = true; 3135 3136 // load the most used names list 3137 3138 BPath path; 3139 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK) 3140 return; 3141 3142 path.Append(fDirectory); 3143 path.Append(fFileName); 3144 3145 FILE *file = fopen(path.Path(), "r"); 3146 if (file == NULL) 3147 return; 3148 3149 char line[B_FILE_NAME_LENGTH + 5]; 3150 while (fgets(line, sizeof(line), file) != NULL) { 3151 int32 length = (int32)strlen(line) - 1; 3152 if (length >= 0 && line[length] == '\n') 3153 line[length] = '\0'; 3154 3155 int32 count = atoi(line); 3156 3157 char *name = strchr(line, ' '); 3158 if (name == NULL || *(++name) == '\0') 3159 continue; 3160 3161 list_entry *entry = new list_entry; 3162 entry->name = strdup(name); 3163 entry->count = count; 3164 3165 fList.AddItem(entry); 3166 } 3167 fclose(file); 3168 } 3169 3170 3171 void 3172 MostUsedNames::UpdateList() 3173 { 3174 AutoLock<Benaphore> locker(fLock); 3175 3176 if (!fLoaded) 3177 LoadList(); 3178 3179 // sort list items 3180 3181 fList.SortItems(MostUsedNames::CompareNames); 3182 } 3183 3184 } // namespace BPrivate 3185 3186