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