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