Author: mmlr Date: 2010-01-30 09:08:58 +0100 (Sat, 30 Jan 2010) New Revision: 35339 Changeset: http://dev.haiku-os.org/changeset/35339/haiku Modified: haiku/trunk/src/kits/tracker/Commands.h haiku/trunk/src/kits/tracker/CountView.cpp haiku/trunk/src/kits/tracker/CountView.h haiku/trunk/src/kits/tracker/Pose.cpp haiku/trunk/src/kits/tracker/Pose.h haiku/trunk/src/kits/tracker/PoseView.cpp haiku/trunk/src/kits/tracker/PoseView.h haiku/trunk/src/kits/tracker/SettingsViews.cpp haiku/trunk/src/kits/tracker/SettingsViews.h haiku/trunk/src/kits/tracker/TextWidget.cpp haiku/trunk/src/kits/tracker/TextWidget.h haiku/trunk/src/kits/tracker/TrackerSettings.cpp haiku/trunk/src/kits/tracker/TrackerSettings.h Log: Implement type ahead filtering (this time for real and without abusing the vertically sorted pose list). When enabled typing will filter based on the currently visible attribute columns. Using shift-space as a delimiter independent filtering strings can be typed, so you can filter based on multiple attributes at once to refine results while you type. Filtering stays active until you cancel it using the escape key. While the filtered result is displayed all normal file operations can be used. Using the return key while filtering auto-selects and opens the first filter result, allowing for fast traversal through directories and directly opening the topmost result. * Introduces fFilteredPoseList which stores the active filter result. The list is only used when filtering is currently active, so no syncing is required otherwise. * Some minor adjustments to leave out invalidations where non-visible poses are updated. * Account for the now possible multiple lists throughout BPoseView. * Add filter string output to the CountView and made that one a bit wider. * Added all the settings-cruft for type ahead filtering (defaults to off). Modified: haiku/trunk/src/kits/tracker/Commands.h =================================================================== --- haiku/trunk/src/kits/tracker/Commands.h 2010-01-30 01:37:04 UTC (rev 35338) +++ haiku/trunk/src/kits/tracker/Commands.h 2010-01-30 08:08:58 UTC (rev 35339) @@ -125,6 +125,7 @@ const uint32 kShowSelectionWhenInactiveChanged = 'Sswi'; const uint32 kTransparentSelectionChanged = 'Trse'; const uint32 kSortFolderNamesFirstChanged = 'Sfnf'; +const uint32 kTypeAheadFilteringChanged = 'Tafc'; const uint32 kDesktopFilePanelRootChanged = 'Dfpr'; const uint32 kFavoriteCountChanged = 'Fvct'; Modified: haiku/trunk/src/kits/tracker/CountView.cpp =================================================================== --- haiku/trunk/src/kits/tracker/CountView.cpp 2010-01-30 01:37:04 UTC (rev 35338) +++ haiku/trunk/src/kits/tracker/CountView.cpp 2010-01-30 08:08:58 UTC (rev 35339) @@ -59,7 +59,8 @@ fBarberPoleMap(NULL), fLastBarberPoleOffset(5), fStartSpinningAfter(0), - fTypeAheadString("") + fTypeAheadString(""), + fFilterString("") { GetTrackerResources()->GetBitmapResource(B_MESSAGE_TYPE, R_BarberPoleBitmap, &fBarberPoleMap); @@ -186,6 +187,7 @@ fLastCount = fPoseView->CountItems(); Invalidate(TextInvalRect()); } + // invalidate barber pole area if necessary TrySpinningBarberPole(); } @@ -209,20 +211,26 @@ } BString itemString; - if (!IsTypingAhead()) { + if (IsTypingAhead()) + itemString << TypeAhead(); + else if (IsFiltering()) { + itemString << fLastCount << " " << Filter(); + } else { if (fLastCount == 0) itemString << "no items"; else if (fLastCount == 1) itemString << "1 item"; else itemString << fLastCount << " items"; - } else - itemString << TypeAhead(); + } + BString string(itemString); BRect textRect(TextInvalRect()); - TruncateString(&string, B_TRUNCATE_END, textRect.Width()); + TruncateString(&string, IsTypingAhead() ? B_TRUNCATE_BEGINNING + : IsFiltering() ? B_TRUNCATE_MIDDLE : B_TRUNCATE_END, + textRect.Width()); if (IsTypingAhead()) { // use a muted gray for the typeahead @@ -347,6 +355,44 @@ void +BCountView::AddFilter(const char *string) +{ + fFilterString += string; + Invalidate(); +} + + +void +BCountView::RemoveFilter() +{ + fFilterString.Truncate(fFilterString.Length() - 1); + Invalidate(); +} + + +void +BCountView::CancelFilter() +{ + fFilterString.Truncate(0); + Invalidate(); +} + + +const char * +BCountView::Filter() const +{ + return fFilterString.String(); +} + + +bool +BCountView::IsFiltering() const +{ + return fFilterString.Length() > 0; +} + + +void BCountView::SetBorderHighlighted(bool highlighted) { if (fBorderHighlighted == highlighted) Modified: haiku/trunk/src/kits/tracker/CountView.h =================================================================== --- haiku/trunk/src/kits/tracker/CountView.h 2010-01-30 01:37:04 UTC (rev 35338) +++ haiku/trunk/src/kits/tracker/CountView.h 2010-01-30 08:08:58 UTC (rev 35339) @@ -63,6 +63,12 @@ const char *TypeAhead() const; bool IsTypingAhead() const; + void AddFilter(const char *string); + void RemoveFilter(); + void CancelFilter(); + const char *Filter() const; + bool IsFiltering() const; + void SetBorderHighlighted(bool highlighted); private: @@ -80,6 +86,7 @@ float fLastBarberPoleOffset; bigtime_t fStartSpinningAfter; BString fTypeAheadString; + BString fFilterString; }; } // namespace BPrivate Modified: haiku/trunk/src/kits/tracker/Pose.cpp =================================================================== --- haiku/trunk/src/kits/tracker/Pose.cpp 2010-01-30 01:37:04 UTC (rev 35338) +++ haiku/trunk/src/kits/tracker/Pose.cpp 2010-01-30 08:08:58 UTC (rev 35339) @@ -216,7 +216,7 @@ OneCheckAndUpdate(BTextWidget *widget, BPose *, BPoseView *poseView, BColumn *column, BPoint poseLoc) { - widget->CheckAndUpdate(poseLoc, column, poseView); + widget->CheckAndUpdate(poseLoc, column, poseView, true); } @@ -233,7 +233,7 @@ void BPose::UpdateWidgetAndModel(Model *resolvedModel, const char *attrName, - uint32 attrType, int32, BPoint poseLoc, BPoseView *poseView) + uint32 attrType, int32, BPoint poseLoc, BPoseView *poseView, bool visible) { if (poseView->ViewMode() != kListMode) poseLoc = Location(poseView); @@ -242,7 +242,7 @@ if (attrName) { // pick up new attributes and find out if icon needs updating - if (resolvedModel->AttrChanged(attrName)) + if (resolvedModel->AttrChanged(attrName) && visible) UpdateIcon(poseLoc, poseView); // ToDo: the following code is wrong, because this sort of hashing @@ -252,7 +252,7 @@ if (widget) { BColumn *column = poseView->ColumnFor(attrHash); if (column) - widget->CheckAndUpdate(poseLoc, column, poseView); + widget->CheckAndUpdate(poseLoc, column, poseView, visible); } else if (attrType == 0) { // attribute got likely removed, so let's search the // column for the matching attribute name @@ -261,7 +261,7 @@ BTextWidget *widget = fWidgetList.ItemAt(i); BColumn *column = poseView->ColumnFor(widget->AttrHash()); if (column != NULL && !strcmp(column->AttrName(), attrName)) { - widget->CheckAndUpdate(poseLoc, column, poseView); + widget->CheckAndUpdate(poseLoc, column, poseView, visible); break; } } @@ -274,7 +274,8 @@ if (resolvedModel->InitCheck() != B_OK) return; - UpdateIcon(poseLoc, poseView); + if (visible) + UpdateIcon(poseLoc, poseView); } // distribute stat changes @@ -286,7 +287,7 @@ if (column->StatField()) { BTextWidget *widget = WidgetFor(column->AttrHash()); if (widget) - widget->CheckAndUpdate(poseLoc, column, poseView); + widget->CheckAndUpdate(poseLoc, column, poseView, visible); } } } Modified: haiku/trunk/src/kits/tracker/Pose.h =================================================================== --- haiku/trunk/src/kits/tracker/Pose.h 2010-01-30 01:37:04 UTC (rev 35338) +++ haiku/trunk/src/kits/tracker/Pose.h 2010-01-30 08:08:58 UTC (rev 35339) @@ -97,7 +97,8 @@ BRect CalcRect(const BPoseView *) const; void UpdateAllWidgets(int32 poseIndex, BPoint poseLoc, BPoseView *); void UpdateWidgetAndModel(Model *resolvedModel, const char *attrName, - uint32 attrType, int32 poseIndex, BPoint poseLoc, BPoseView *view); + uint32 attrType, int32 poseIndex, BPoint poseLoc, + BPoseView *view, bool visible); bool UpdateVolumeSpaceBar(BVolume *volume); void UpdateIcon(BPoint poseLoc, BPoseView *); Modified: haiku/trunk/src/kits/tracker/PoseView.cpp =================================================================== --- haiku/trunk/src/kits/tracker/PoseView.cpp 2010-01-30 01:37:04 UTC (rev 35338) +++ haiku/trunk/src/kits/tracker/PoseView.cpp 2010-01-30 08:08:58 UTC (rev 35339) @@ -98,7 +98,7 @@ const float kDoubleClickTresh = 6; -const float kCountViewWidth = 62; +const float kCountViewWidth = 76; const uint32 kAddNewPoses = 'Tanp'; const uint32 kAddPosesCompleted = 'Tapc'; @@ -187,6 +187,7 @@ fActivePose(NULL), fExtent(LONG_MAX, LONG_MAX, LONG_MIN, LONG_MIN), fPoseList(new PoseList(40, true)), + fFilteredPoseList(new PoseList()), fVSPoseList(new PoseList()), fSelectionList(new PoseList()), fMimeTypesInSelectionCache(20, true), @@ -229,6 +230,10 @@ fIsWatchingDateFormatChange(false), fHasPosesInClipboard(false), fCursorCheck(false), + fFiltering(false), + fFilterStrings(4, true), + fLastFilterStringCount(1), + fLastFilterStringLength(0), fLastKeyTime(0), fLastDeskbarFrameCheckTime(LONGLONG_MIN), fDeskbarFrame(0, 0, -1, -1) @@ -236,12 +241,14 @@ fViewState->SetViewMode(viewMode); fShowSelectionWhenInactive = TrackerSettings().ShowSelectionWhenInactive(); fTransparentSelection = TrackerSettings().TransparentSelection(); + fFilterStrings.AddItem(new BString("")); } BPoseView::~BPoseView() { delete fPoseList; + delete fFilteredPoseList; delete fVSPoseList; delete fColumnList; delete fSelectionList; @@ -829,6 +836,7 @@ app->StopWatching(this, kShowSelectionWhenInactiveChanged); app->StopWatching(this, kTransparentSelectionChanged); app->StopWatching(this, kSortFolderNamesFirstChanged); + app->StopWatching(this, kTypeAheadFilteringChanged); app->Unlock(); } @@ -905,7 +913,7 @@ // add Option-Return as a shortcut filter because AddShortcut doesn't allow // us to have shortcuts without Command yet AddFilter(new ShortcutFilter(B_ESCAPE, 0, B_CANCEL, this)); - // Escape key, currently used only to abort an on-going clipboard cut + // Escape key, used to abort an on-going clipboard cut or filtering AddFilter(new ShortcutFilter(B_ESCAPE, B_SHIFT_KEY, kCancelSelectionToClipboard, this)); // Escape + SHIFT will remove current selection from clipboard, or all poses from current folder if 0 selected @@ -926,6 +934,7 @@ app->StartWatching(this, kShowSelectionWhenInactiveChanged); app->StartWatching(this, kTransparentSelectionChanged); app->StartWatching(this, kSortFolderNamesFirstChanged); + app->StartWatching(this, kTypeAheadFilteringChanged); app->Unlock(); } @@ -1538,7 +1547,9 @@ // above the top of the view (leaving you with an empty window) if (ViewMode() == kListMode) { BRect bounds(Bounds()); - float lastItemTop = (fPoseList->CountItems() - 1) * fListElemHeight; + float lastItemTop + = ((fFiltering ? fFilteredPoseList : fPoseList)->CountItems() - 1) + * fListElemHeight; if (bounds.top > lastItemTop) BView::ScrollTo(bounds.left, max_c(lastItemTop, 0)); } @@ -1621,6 +1632,91 @@ void +BPoseView::AddPoseToList(PoseList *list, bool visibleList, bool insertionSort, + BPose *pose, BRect &viewBounds, float &listViewScrollBy, bool forceDraw) +{ + int32 poseIndex = list->CountItems(); + + BRect poseBounds; + bool havePoseBounds = false; + bool addedItem = false; + bool needToDraw = true; + + if (insertionSort && list->CountItems()) { + int32 orientation = BSearchList(list, pose, &poseIndex); + + if (orientation == kInsertAfter) + poseIndex++; + + if (visibleList) { + // we only care about the positions if this is a visible list + poseBounds = CalcPoseRectList(pose, poseIndex); + havePoseBounds = true; + + BRect srcRect(Extent()); + srcRect.top = poseBounds.top; + srcRect = srcRect & viewBounds; + BRect destRect(srcRect); + destRect.OffsetBy(0, fListElemHeight); + + // special case the addition of a pose that scrolls + // the extent into the view for the first time: + if (destRect.bottom > viewBounds.top + && destRect.top > destRect.bottom) { + // make destRect valid + destRect.top = viewBounds.top; + } + + if (srcRect.Intersects(viewBounds) + || destRect.Intersects(viewBounds)) { + // The visual area is affected by the insertion. + // If items have been added above the visual area, + // delay the scrolling. srcRect.bottom holds the + // current Extent(). So if the bottom is still above + // the viewBounds top, it means the view is scrolled + // to show the area below the items that have already + // been added. + if (srcRect.top == viewBounds.top + && srcRect.bottom >= viewBounds.top) { + // if new pose above current view bounds, cache up + // the draw and do it later + listViewScrollBy += fListElemHeight; + needToDraw = false; + } else { + FinishPendingScroll(listViewScrollBy, viewBounds); + list->AddItem(pose, poseIndex); + + fMimeTypeListIsDirty = true; + addedItem = true; + if (srcRect.IsValid()) { + CopyBits(srcRect, destRect); + srcRect.bottom = destRect.top; + SynchronousUpdate(srcRect); + } else { + SynchronousUpdate(destRect); + } + needToDraw = false; + } + } + } + } + + if (!addedItem) { + list->AddItem(pose, poseIndex); + fMimeTypeListIsDirty = true; + } + + if (visibleList && needToDraw && forceDraw) { + if (!havePoseBounds) + poseBounds = CalcPoseRectList(pose, poseIndex); + if (viewBounds.Intersects(poseBounds)) + SynchronousUpdate(poseBounds); + } +} + + + +void BPoseView::CreatePoses(Model **models, PoseInfo *poseInfoArray, int32 count, BPose **resultingPoses, bool insertionSort, int32 *lastPoseIndexPtr, BRect *boundsPtr, bool forceDraw) @@ -1676,75 +1772,14 @@ switch (ViewMode()) { case kListMode: { - poseIndex = fPoseList->CountItems(); + AddPoseToList(fPoseList, !fFiltering, insertionSort, pose, + viewBounds, listViewScrollBy, forceDraw); - bool havePoseBounds = false; - bool addedItem = false; - bool needToDraw = true; - - if (insertionSort && fPoseList->CountItems()) { - int32 orientation = BSearchList(pose, &poseIndex); - - if (orientation == kInsertAfter) - poseIndex++; - - poseBounds = CalcPoseRectList(pose, poseIndex); - havePoseBounds = true; - BRect srcRect(Extent()); - srcRect.top = poseBounds.top; - srcRect = srcRect & viewBounds; - BRect destRect(srcRect); - destRect.OffsetBy(0, fListElemHeight); - // special case the addition of a pose that scrolls - // the extent into the view for the first time: - if (destRect.bottom > viewBounds.top - && destRect.top > destRect.bottom) { - // make destRect valid - destRect.top = viewBounds.top; - } - - if (srcRect.Intersects(viewBounds) - || destRect.Intersects(viewBounds)) { - // The visual area is affected by the insertion. - // If items have been added above the visual area, - // delay the scrolling. srcRect.bottom holds the - // current Extent(). So if the bottom is still above - // the viewBounds top, it means the view is scrolled - // to show the area below the items that have already - // been added. - if (srcRect.top == viewBounds.top - && srcRect.bottom >= viewBounds.top) { - // if new pose above current view bounds, cache up - // the draw and do it later - listViewScrollBy += fListElemHeight; - needToDraw = false; - } else { - FinishPendingScroll(listViewScrollBy, viewBounds); - fPoseList->AddItem(pose, poseIndex); - fMimeTypeListIsDirty = true; - addedItem = true; - if (srcRect.IsValid()) { - CopyBits(srcRect, destRect); - srcRect.bottom = destRect.top; - SynchronousUpdate(srcRect); - } else { - SynchronousUpdate(destRect); - } - needToDraw = false; - } - } + if (fFiltering && FilterPose(pose)) { + AddPoseToList(fFilteredPoseList, true, insertionSort, pose, + viewBounds, listViewScrollBy, forceDraw); } - if (!addedItem) { - fPoseList->AddItem(pose, poseIndex); - fMimeTypeListIsDirty = true; - } - if (needToDraw && forceDraw) { - if (!havePoseBounds) - poseBounds = CalcPoseRectList(pose, poseIndex); - if (viewBounds.Intersects(poseBounds)) - SynchronousUpdate(poseBounds); - } break; } @@ -2149,10 +2184,14 @@ case B_CANCEL: if (FSClipboardHasRefs()) FSClipboardClear(); + else if (fFiltering) + CancelFiltering(); break; case kCancelSelectionToClipboard: - FSClipboardRemovePoses(TargetModel()->NodeRef(), (fSelectionList->CountItems() > 0 ? fSelectionList : fPoseList)); + FSClipboardRemovePoses(TargetModel()->NodeRef(), + (fSelectionList->CountItems() > 0 + ? fSelectionList : fPoseList)); break; case kFSClipboardChanges: @@ -2264,7 +2303,8 @@ BPose *pose = fSelectionList->FirstItem(); if (pose) { pose->EditFirstWidget(BPoint(0, - fPoseList->IndexOf(pose) * fListElemHeight), this); + (fFiltering ? fFilteredPoseList : fPoseList)->IndexOf(pose) + * fListElemHeight), this); } break; } @@ -2436,6 +2476,18 @@ Invalidate(); } break; + + case kTypeAheadFilteringChanged: + { + TrackerSettings settings; + bool typeAheadFiltering; + if (message->FindBool("TypeAheadFiltering", &typeAheadFiltering) == B_OK) + settings.SetTypeAheadFiltering(typeAheadFiltering); + + if (fFiltering && !typeAheadFiltering) + CancelFiltering(); + break; + } } } break; @@ -2537,12 +2589,13 @@ BRect rect(Bounds()); // add widget for all visible poses - int32 count = fPoseList->CountItems(); + PoseList *poseList = fFiltering ? fFilteredPoseList : fPoseList; + int32 count = poseList->CountItems(); int32 startIndex = (int32)(rect.top / fListElemHeight); BPoint loc(0, startIndex * fListElemHeight); for (int32 index = startIndex; index < count; index++) { - BPose *pose = fPoseList->ItemAt(index); + BPose *pose = poseList->ItemAt(index); if (!pose->WidgetFor(newColumn->AttrHash())) pose->AddWidget(this, newColumn); @@ -2806,6 +2859,9 @@ // toggle view layout between listmode and non-listmode, if necessary BContainerWindow *window = ContainerWindow(); if (oldMode == kListMode) { + if (fFiltering) + CancelFiltering(); + fTitleView->RemoveSelf(); if (window) @@ -2953,11 +3009,13 @@ void BPoseView::SetPosesClipboardMode(uint32 clipboardMode) { - int32 count = fPoseList->CountItems(); if (ViewMode() == kListMode) { + PoseList *poseList = fFiltering ? fFilteredPoseList : fPoseList; + int32 count = poseList->CountItems(); + BPoint loc(0,0); for (int32 index = 0; index < count; index++) { - BPose *pose = fPoseList->ItemAt(index); + BPose *pose = poseList->ItemAt(index); if (pose->ClipboardMode() != clipboardMode) { pose->SetClipboardMode(clipboardMode); Invalidate(pose->CalcRect(loc, this, false)); @@ -2965,6 +3023,7 @@ loc.y += fListElemHeight; } } else { + int32 count = fPoseList->CountItems(); for (int32 index = 0; index < count; index++) { BPose *pose = fPoseList->ItemAt(index); if (pose->ClipboardMode() != clipboardMode) { @@ -3024,9 +3083,16 @@ if (!fullInvalidateNeeded) { if (ViewMode() == kListMode) { - loc.y = foundNodeIndex * fListElemHeight; - if (loc.y <= bounds.bottom && loc.y >= bounds.top) - Invalidate(pose->CalcRect(loc, this, false)); + if (fFiltering) { + pose = fFilteredPoseList->FindPose(&clipNode->node, + &foundNodeIndex); + } + + if (pose != NULL) { + loc.y = foundNodeIndex * fListElemHeight; + if (loc.y <= bounds.bottom && loc.y >= bounds.top) + Invalidate(pose->CalcRect(loc, this, false)); + } } else { BRect poseRect(pose->CalcRect(this)); if (bounds.Contains(poseRect.LeftTop()) @@ -3629,9 +3695,10 @@ BPoint loc(0, start * fListElemHeight); BRect bounds(Bounds()); - int32 count = fPoseList->CountItems(); + PoseList *poseList = fFiltering ? fFilteredPoseList : fPoseList; + int32 count = poseList->CountItems(); for (int32 index = start; index < end && index < count; index++) { - BPose *pose = fPoseList->ItemAt(index); + BPose *pose = poseList->ItemAt(index); fSelectionList->AddItem(pose); if (index == start) fSelectionPivotPose = pose; @@ -3724,10 +3791,11 @@ if (ViewMode() == kListMode) { // TODO: need a simple call to CalcRect that works both in listView and // icon view modes without the need for an index/pos - int32 count = fPoseList->CountItems(); + PoseList *poseList = fFiltering ? fFilteredPoseList : fPoseList; + int32 count = poseList->CountItems(); BPoint loc(0, 0); for (int32 index = 0; index < count; index++) { - if (pose == fPoseList->ItemAt(index)) { + if (pose == poseList->ItemAt(index)) { Invalidate(pose->CalcRect(loc, this)); break; } @@ -5187,7 +5255,11 @@ if (pose->TargetModel()->OpenNode() == B_OK) { pose->UpdateAllWidgets(index, loc, this); pose->TargetModel()->CloseNode(); - CheckPoseSortOrder(pose, index); + _CheckPoseSortOrder(fPoseList, pose, index); + if (fFiltering + && fFilteredPoseList->FindPose(&itemNode, &index) != NULL) { + _CheckPoseSortOrder(fFilteredPoseList, pose, index); + } } } else { // also must watch for renames on zombies @@ -5239,6 +5311,11 @@ attr_info info; memset(&info, 0, sizeof(attr_info)); if (pose) { + int32 poseListIndex = index; + bool visible = true; + if (fFiltering) + visible = fFilteredPoseList->DeepFindPose(&itemNode, &index) != NULL; + BPoint loc(0, index * fListElemHeight); Model *model = pose->TargetModel(); @@ -5263,9 +5340,12 @@ if (attrName && model->Node()) { // the call below might fail if the attribute has been removed model->Node()->GetAttrInfo(attrName, &info); - pose->UpdateWidgetAndModel(model, attrName, info.type, index, loc, this); - } else - pose->UpdateWidgetAndModel(model, 0, 0, index, loc, this); + pose->UpdateWidgetAndModel(model, attrName, info.type, index, + loc, this, visible); + } else { + pose->UpdateWidgetAndModel(model, 0, 0, index, loc, this, + visible); + } model->CloseNode(); } else { @@ -5283,8 +5363,13 @@ // may overlap and we get aliasing attrHash = AttrHashString(attrName, info.type); } - if (!attrName || attrHash == PrimarySort() || attrHash == SecondarySort()) - CheckPoseSortOrder(pose, index); + + if (!attrName || attrHash == PrimarySort() + || attrHash == SecondarySort()) { + _CheckPoseSortOrder(fPoseList, pose, poseListIndex); + if (fFiltering) + _CheckPoseSortOrder(fFilteredPoseList, pose, index); + } } else { // we received an attr changed notification for a zombie model, it means // that although we couldn't open the node the first time, it seems @@ -5310,13 +5395,19 @@ BPoint location; if (ViewMode() == kListMode) { // need to find the index of the pose in the pose list - int32 count = fPoseList->CountItems(); + bool found = false; + PoseList *poseList = fFiltering ? fFilteredPoseList : fPoseList; + int32 count = poseList->CountItems(); for (int32 index = 0; index < count; index++) { - if (fPoseList->ItemAt(index) == pose) { + if (poseList->ItemAt(index) == pose) { location.Set(0, index * fListElemHeight); + found = true; break; } } + + if (!found) + return; } pose->UpdateIcon(location, this); @@ -5732,9 +5823,10 @@ bool iconMode = ViewMode() != kListMode; - int32 count = fPoseList->CountItems(); + PoseList *poseList = fFiltering ? fFilteredPoseList : fPoseList; + int32 count = poseList->CountItems(); for (int32 index = startIndex; index < count; index++) { - BPose *pose = fPoseList->ItemAt(index); + BPose *pose = poseList->ItemAt(index); fSelectionList->AddItem(pose); if (index == startIndex) fSelectionPivotPose = pose; @@ -5780,9 +5872,10 @@ bool iconMode = ViewMode() != kListMode; - int32 count = fPoseList->CountItems(); + PoseList *poseList = fFiltering ? fFilteredPoseList : fPoseList; + int32 count = poseList->CountItems(); for (int32 index = startIndex; index < count; index++) { - BPose *pose = fPoseList->ItemAt(index); + BPose *pose = poseList->ItemAt(index); if (pose->IsSelected()) { fSelectionList->RemoveItem(pose); @@ -5832,7 +5925,8 @@ expression = expressionPointer; - int32 count = fPoseList->CountItems(); + PoseList *poseList = fFiltering ? fFilteredPoseList : fPoseList; + int32 count = poseList->CountItems(); TrackerString name; RegExp regExpression; @@ -5857,7 +5951,7 @@ // TrackerString::CompileRegExp and reuse the expression. However, then we // have to take care of the case sensitivity ourselves. for (int32 index = 0; index < count; index++) { - BPose *pose = fPoseList->ItemAt(index); + BPose *pose = poseList->ItemAt(index); name = pose->TargetModel()->Name(); if (name.Matches(expression.String(), !ignoreCase, expressionType) ^ invertSelection) { matchCount++; @@ -5910,6 +6004,9 @@ } case B_RETURN: + if (fFiltering && fSelectionList->CountItems() == 0) + SelectPose(fFilteredPoseList->FirstItem(), 0); + OpenSelection(); break; @@ -5917,14 +6014,15 @@ // select the first entry (if in listview mode), and // scroll to the top of the view if (ViewMode() == kListMode) { + PoseList *poseList = fFiltering ? fFilteredPoseList : fPoseList; BPose *pose = fSelectionList->LastItem(); if (pose != NULL && fMultipleSelection && (modifiers() & B_SHIFT_KEY) != 0) { - int32 index = fPoseList->IndexOf(pose); + int32 index = poseList->IndexOf(pose); // select all items from the current one till the top for (int32 i = index; i-- > 0; ) { - pose = fPoseList->ItemAt(i); + pose = poseList->ItemAt(i); if (pose == NULL) continue; @@ -5932,7 +6030,8 @@ AddPoseToSelection(pose, i, i == 0); } } else - SelectPose(fPoseList->FirstItem(), 0); + SelectPose(poseList->FirstItem(), 0); + } else if (fVScrollBar) fVScrollBar->SetValue(0); break; @@ -5941,15 +6040,16 @@ // select the last entry (if in listview mode), and // scroll to the bottom of the view if (ViewMode() == kListMode) { + PoseList *poseList = fFiltering ? fFilteredPoseList : fPoseList; BPose *pose = fSelectionList->FirstItem(); if (pose != NULL && fMultipleSelection && (modifiers() & B_SHIFT_KEY) != 0) { - int32 index = fPoseList->IndexOf(pose); - int32 count = fPoseList->CountItems() - 1; + int32 index = poseList->IndexOf(pose); + int32 count = poseList->CountItems() - 1; // select all items from the current one to the bottom for (int32 i = index; i <= count; i++) { - pose = fPoseList->ItemAt(i); + pose = poseList->ItemAt(i); if (pose == NULL) continue; @@ -5957,7 +6057,8 @@ AddPoseToSelection(pose, i, i == count); } } else - SelectPose(fPoseList->LastItem(), fPoseList->CountItems() - 1); + SelectPose(poseList->LastItem(), poseList->CountItems() - 1); + } else if (fVScrollBar) { float max, min; fVScrollBar->GetRange(&min, &max); @@ -5985,6 +6086,9 @@ if (IsFilePanel()) _inherited::KeyDown(bytes, count); else { + if (TrackerSettings().TypeAheadFiltering()) + break; + if (fSelectionList->IsEmpty()) sMatchString[0] = '\0'; else { @@ -6029,6 +6133,22 @@ case B_BACKSPACE: { + if (TrackerSettings().TypeAheadFiltering()) { + BString *lastString = fFilterStrings.LastItem(); + if (lastString->Length() == 0) { + int32 stringCount = fFilterStrings.CountItems(); + if (stringCount > 1) + delete fFilterStrings.RemoveItemAt(stringCount - 1); + else + break; + } else + lastString->Truncate(lastString->Length() - 1); + + fCountView->RemoveFilter(); + FilterChanged(); + break; + } + if (strlen(sMatchString) == 0) break; @@ -6056,6 +6176,22 @@ // create a null-terminated version of typed char char searchChar[4] = { key, 0 }; + if (TrackerSettings().TypeAheadFiltering()) { + if (key == ' ' && modifiers() & B_SHIFT_KEY) { + if (fFilterStrings.LastItem()->Length() == 0) + break; + + fFilterStrings.AddItem(new BString()); + fCountView->AddFilter("|"); + break; + } + + fFilterStrings.LastItem()->Append(searchChar); + fCountView->AddFilter(searchChar); + FilterChanged(); + break; + } + bigtime_t doubleClickSpeed; get_click_speed(&doubleClickSpeed); @@ -6199,34 +6335,36 @@ BPose *selectedPose = fSelectionList->LastItem(); if (ViewMode() == kListMode) { + PoseList *poseList = fFiltering ? fFilteredPoseList : fPoseList; + switch (arrowKey) { case B_UP_ARROW: case B_LEFT_ARROW: if (selectedPose) { - resultingIndex = fPoseList->IndexOf(selectedPose) - 1; - poseToSelect = fPoseList->ItemAt(resultingIndex); + resultingIndex = poseList->IndexOf(selectedPose) - 1; + poseToSelect = poseList->ItemAt(resultingIndex); if (!poseToSelect && arrowKey == B_LEFT_ARROW) { - resultingIndex = fPoseList->CountItems() - 1; - poseToSelect = fPoseList->LastItem(); + resultingIndex = poseList->CountItems() - 1; + poseToSelect = poseList->LastItem(); } } else { - resultingIndex = fPoseList->CountItems() - 1; - poseToSelect = fPoseList->LastItem(); + resultingIndex = poseList->CountItems() - 1; + poseToSelect = poseList->LastItem(); } break; case B_DOWN_ARROW: case B_RIGHT_ARROW: if (selectedPose) { - resultingIndex = fPoseList->IndexOf(selectedPose) + 1; - poseToSelect = fPoseList->ItemAt(resultingIndex); + resultingIndex = poseList->IndexOf(selectedPose) + 1; + poseToSelect = poseList->ItemAt(resultingIndex); if (!poseToSelect && arrowKey == B_RIGHT_ARROW) { resultingIndex = 0; - poseToSelect = fPoseList->FirstItem(); + poseToSelect = poseList->FirstItem(); } } else { resultingIndex = 0; - poseToSelect = fPoseList->FirstItem(); + poseToSelect = poseList->FirstItem(); } break; } @@ -6632,7 +6770,8 @@ BPoint tempLoc; GetMouse(&tempLoc, &button); if (button) { - int32 index = fPoseList->IndexOf(pose); + int32 index + = (fFiltering ? fFilteredPoseList : fPoseList)->IndexOf(pose); message.AddInt32("buttons", (int32)button); BRect dragRect(GetDragRect(index)); BBitmap *dragBitmap = NULL; @@ -6721,14 +6860,15 @@ BRect bounds(Bounds()); - BPose *pose = fPoseList->ItemAt(clickedPoseIndex); + PoseList *poseList = fFiltering ? fFilteredPoseList : fPoseList; + BPose *pose = poseList->ItemAt(clickedPoseIndex); if (ViewMode() == kListMode) { - int32 count = fPoseList->CountItems(); + int32 count = poseList->CountItems(); int32 startIndex = (int32)(bounds.top / fListElemHeight); BPoint loc(0, startIndex * fListElemHeight); for (int32 index = startIndex; index < count; index++) { - pose = fPoseList->ItemAt(index); + pose = poseList->ItemAt(index); if (pose->IsSelected()) { BRect poseRect(pose->CalcRect(loc, this, true)); if (poseRect.Intersects(inner)) { @@ -6791,18 +6931,19 @@ BRect result; BRect bounds(Bounds()); - BPose *pose = fPoseList->ItemAt(clickedPoseIndex); + PoseList *poseList = fFiltering ? fFilteredPoseList : fPoseList; + BPose *pose = poseList->ItemAt(clickedPoseIndex); if (ViewMode() == kListMode) { // get starting rect of clicked pose result = CalcPoseRectList(pose, clickedPoseIndex, true); // add rects for visible poses only - int32 count = fPoseList->CountItems(); + int32 count = poseList->CountItems(); int32 startIndex = (int32)(bounds.top / fListElemHeight); BPoint loc(0, startIndex * fListElemHeight); for (int32 index = startIndex; index < count; index++) { - pose = fPoseList->ItemAt(index); + pose = poseList->ItemAt(index); if (pose->IsSelected()) result = result | pose->CalcRect(loc, this, true); @@ -6994,9 +7135,10 @@ BPoint loc(0, startIndex * fListElemHeight); - int32 count = fPoseList->CountItems(); + PoseList *poseList = fFiltering ? fFilteredPoseList : fPoseList; + int32 count = poseList->CountItems(); for (int32 index = startIndex; index < count; index++) { - BPose *pose = fPoseList->ItemAt(index); + BPose *pose = poseList->ItemAt(index); BRect poseRect(pose->CalcRect(loc, this)); if (selectionRect.Intersects(poseRect)) { @@ -7026,7 +7168,7 @@ int32 oldIndex = (int32)(*oldList)->ItemAt(index); if (!newList->HasItem((void *)oldIndex)) { - BPose *pose = fPoseList->ItemAt(oldIndex); + BPose *pose = poseList->ItemAt(oldIndex); pose->Select(!pose->IsSelected()); loc.Set(0, oldIndex * fListElemHeight); BRect poseRect(pose->CalcRect(loc, this)); @@ -7128,8 +7270,9 @@ } if (ViewMode() == kListMode) { - int32 currSelIndex = fPoseList->IndexOf(pose); - int32 lastSelIndex = fPoseList->IndexOf(fSelectionPivotPose); + PoseList *poseList = fFiltering ? fFilteredPoseList : fPoseList; + int32 currSelIndex = poseList->IndexOf(pose); + int32 lastSelIndex = poseList->IndexOf(fSelectionPivotPose); int32 startRange; [... truncated: 849 lines follow ...]