Exemple #1
0
        public void Convert_ToLowerCase_ConvertGivenStringToLowerCase()
        {
            var sorting = new Sorting.Sorting(InputString);

            var lowerCaseString = sorting.InputString.ToLower();

            Assert.AreEqual(ExpectedOutputStringLowerCase, lowerCaseString);
        }
        public virtual void InitMediaNavigation(out string mediaNavigationMode, out NavigationData navigationData)
        {
            Prepare();

            string             nextScreenName;
            AbstractScreenData nextScreen = null;

            // Try to load the prefered next screen from settings.
            if (NavigationData.LoadScreenHierarchy(_viewName, out nextScreenName))
            {
                // Support for browsing mode.
                if (nextScreenName == Consts.USE_BROWSE_MODE)
                {
                    SetBrowseMode();
                }

                if (_availableScreens != null)
                {
                    nextScreen = _availableScreens.FirstOrDefault(s => s.GetType().ToString() == nextScreenName);
                }
            }

            IEnumerable <Guid> optionalMIATypeIDs = MediaNavigationModel.GetMediaSkinOptionalMIATypes(MediaNavigationMode);

            if (_optionalMias != null)
            {
                optionalMIATypeIDs = optionalMIATypeIDs.Union(_optionalMias);
                optionalMIATypeIDs = optionalMIATypeIDs.Except(_necessaryMias);
            }
            // Prefer custom view specification.
            ViewSpecification rootViewSpecification = _customRootViewSpecification ??
                                                      new MediaLibraryQueryViewSpecification(_viewName, _filter, _necessaryMias, optionalMIATypeIDs, true, _necessaryMias)
            {
                MaxNumItems = Consts.MAX_NUM_ITEMS_VISIBLE
            };

            if (nextScreen == null)
            {
                nextScreen = _defaultScreen;
            }

            ScreenConfig nextScreenConfig;

            NavigationData.LoadLayoutSettings(nextScreen.GetType().ToString(), out nextScreenConfig);

            Sorting.Sorting nextSortingMode  = _availableSortings.FirstOrDefault(sorting => sorting.GetType().ToString() == nextScreenConfig.Sorting) ?? _defaultSorting;
            Sorting.Sorting nextGroupingMode = _availableGroupings == null || String.IsNullOrEmpty(nextScreenConfig.Grouping) ? null : _availableGroupings.FirstOrDefault(grouping => grouping.GetType().ToString() == nextScreenConfig.Grouping) ?? _defaultGrouping;

            navigationData = new NavigationData(null, _viewName, MediaNavigationRootState,
                                                MediaNavigationRootState, rootViewSpecification, nextScreen, _availableScreens, nextSortingMode, nextGroupingMode)
            {
                AvailableSortings  = _availableSortings,
                AvailableGroupings = _availableGroupings,
                LayoutType         = nextScreenConfig.LayoutType,
                LayoutSize         = nextScreenConfig.LayoutSize
            };
            mediaNavigationMode = MediaNavigationMode;
        }
Exemple #3
0
        public void FilterOut_NonLetters_RemovesAnyNonLetters()
        {
            var sorting     = new Sorting.Sorting(InputString);
            var regexFilter = @"[^A-Za-z]";

            sorting.FilteredString = sorting.FilterOut(regexFilter);

            Assert.AreEqual(ExpectedOutputStringOnlyLetters, sorting.FilteredString);
        }
Exemple #4
0
        public void FilterOut_Numbers_RemoveNumbersOnly()
        {
            var sorting     = new Sorting.Sorting(InputString);
            var regexFilter = @"[^A-Za-z\p{P}\p{S}]";

            sorting.FilteredString = sorting.FilterOut(regexFilter);

            Assert.AreEqual(ExpectedOutputStringNoNumbers, sorting.FilteredString);
        }
Exemple #5
0
        public void FilterOut_SpecialCharacters_RemoveSpecialCharactersOnly()
        {
            var sorting     = new Sorting.Sorting(InputString);
            var regexFilter = @"[^A-Za-z0-9]";

            sorting.FilteredString = sorting.FilterOut(regexFilter);

            Assert.AreEqual(ExpectedOutputStringNoSpecialCharacters, sorting.FilteredString);
        }
Exemple #6
0
        public void Sort_Alphabetically_SortGivenStringAlphabeticallyAndReturnSortedArray()
        {
            var sorting = new Sorting.Sorting("thisisatest");

            string[] expectedSortedStringArray = { "a", "e", "h", "i", "i", "s", "s", "s", "t", "t", "t" };

            var actualSortedStringArray = sorting.SortLowerCaseFilteredString(sorting.InputString);

            CollectionAssert.AreEqual(expectedSortedStringArray, actualSortedStringArray);
        }
        /// <summary>
        /// Returns all media items of the current screen and all sub-screens recursively.
        /// </summary>
        /// <returns>Enumeration of media items.</returns>
        public IEnumerable <MediaItem> GetAllMediaItems()
        {
            List <MediaItem> result = new List <MediaItem>(GetAllMediaItemsOverride());

            Sorting.Sorting sorting = CurrentSorting;
            if (sorting != null)
            {
                result.Sort(sorting);
            }
            return(result);
        }
Exemple #8
0
        public void Concatenate_StringArrayIntoToString_ConcatenateTheGivenStringArrayIntoSingleString()
        {
            var sorting = new Sorting.Sorting();

            string[] sortedStringArray          = { "a", "e", "h", "i", "i", "s", "s", "s", "t", "t", "t" };
            var      expectedConcatenatedString = "aehiisssttt";

            var actualConcatenatedString = sorting.ConcatenateGivenString(sortedStringArray);

            Assert.AreEqual(expectedConcatenatedString, actualConcatenatedString);
        }
Exemple #9
0
        public void Sort_FilteredLowerCaseString_SortsLowerCaseStringInAlphabeticOrder()
        {
            var sorting     = new Sorting.Sorting(InputString);
            var regexFilter = @"[^A-Za-z]";

            sorting.FilteredString = sorting.FilterOut(regexFilter);

            sorting.FilteredString = sorting.FilteredString.ToLower();

            var sortedStringArray = sorting.SortLowerCaseFilteredString(sorting.FilteredString);

            sorting.OutputString = sorting.ConcatenateGivenString(sortedStringArray);

            Assert.AreEqual(ExpectedSortedLowerCaseOutput, sorting.OutputString);
        }
        protected void SortFilterValuesList()
        {
            Sorting.Sorting sorting = CurrentSorting;
            if (sorting == null)
            {
                return;
            }

            lock (_syncObj)
            {
                if (_buildingList)
                { // Another thread is already building the items list - mark it as dirty and let the other thread
                  // rebuild it.
                    _listDirty = true;
                    return;
                }
                if (!_sortable)
                {
                    return;
                }
                // Mark the list as being built
                _buildingList = true;
                _listDirty    = false;
            }
            try
            {
                ItemsList         items     = _items;
                List <FilterItem> itemsList = items.Select(li => li as FilterItem).ToList();
                itemsList.Sort((i1, i2) => sorting.Compare(i1.MediaItem, i2.MediaItem));

                bool dirty;
                lock (_syncObj)
                    dirty = _listDirty;
                if (dirty)
                {
                    UpdateOrRebuildView(items, false);
                    return;
                }
                items.Clear();
                CollectionUtils.AddAll(items, itemsList);
                UpdateOrRebuildView(items, false);
            }
            finally
            {
                lock (_syncObj)
                    _buildingList = false;
            }
        }
        /// <summary>
        /// Updates the GUI data for a filter values selection screen which reflects the available filter values for
        /// the current base view specification of our <see cref="AbstractScreenData._navigationData"/>.
        /// </summary>
        protected async Task ReloadFilterValuesList(bool createNewList)
        {
            MediaLibraryQueryViewSpecification currentVS = _navigationData.BaseViewSpecification as MediaLibraryQueryViewSpecification;

            if (currentVS == null)
            { // Should never happen
                ServiceRegistration.Get <ILogger>().Error("FilterScreenData: Wrong type of media library view '{0}'", _navigationData.BaseViewSpecification);
                return;
            }
            // Control other threads reentering this method
            lock (_syncObj)
            {
                if (_buildingList)
                { // Another thread is already building the items list - mark it as dirty and let the other thread
                  // rebuild it.
                    _listDirty = true;
                    return;
                }
                // Mark the list as being built
                _buildingList = true;
                _listDirty    = false;
            }
            try
            {
                ItemsList items;
                if (createNewList)
                {
                    items = new ItemsList();
                }
                else
                {
                    items = _items;
                    items.Clear();
                }

                try
                {
                    Display_ListBeingBuilt();
                    IFilter            filter        = currentVS.FilterTree.BuildFilter(_filterPath);
                    ICollection <Guid> necessaryMIAs = _necessaryFilteredMIATypeIds ?? currentVS.NecessaryMIATypeIds;

                    // If the number of values to create exceeds MAX_NUM_ITEMS_VISIBLE we need to try and group the items.
                    // We request all values first, rather than groups, on the assumption that most of the time the limit
                    // shouldn't be reached given that we are filtering the values.
                    bool grouping = false;
                    ICollection <FilterValue> fv = await _filterCriterion.GetAvailableValuesAsync(necessaryMIAs, _clusterFilter, filter).ConfigureAwait(false);

                    if (fv.Count > Consts.MAX_NUM_ITEMS_VISIBLE && _clusterFilter == null)
                    {
                        ICollection <FilterValue> groupValues = await _filterCriterion.GroupValuesAsync(necessaryMIAs, _clusterFilter, filter).ConfigureAwait(false);

                        if (groupValues != null && groupValues.Count > 0)
                        {
                            fv       = groupValues;
                            grouping = true;
                        }
                    }
                    if (fv.Count > Consts.MAX_NUM_ITEMS_VISIBLE)
                    {
                        Display_TooManyItems(fv.Count);
                    }
                    else
                    {
                        bool dirty;
                        lock (_syncObj)
                            dirty = _listDirty;
                        if (dirty)
                        {
                            UpdateOrRebuildView(items, createNewList);
                            return;
                        }

                        _sortable = true;
                        int totalNumItems           = 0;
                        List <FilterItem> itemsList = new List <FilterItem>();
                        // Build collection of available (filter/display) screens which will remain in the next view - that is all currently
                        // available screens without the screen which equals this current screen. But we cannot simply remove "this"
                        // from the collection, because "this" could be a derived screen (in case our base screen showed groups).
                        // So we need an equality criterion when the screen to be removed is equal to this screen in terms of its
                        // filter criterion. But with the given data, we actually cannot derive that equality.
                        // So we simply use the MenuItemLabel, which should be the same in this and the base screen of the same filter.
                        foreach (FilterValue f in fv)
                        {
                            //Used for enclosure
                            FilterValue filterValue = f;
                            _sortable &= filterValue.Item != null;
                            string  filterTitle                      = filterValue.Title;
                            IFilter selectAttributeFilter            = filterValue.SelectAttributeFilter;
                            MediaLibraryQueryViewSpecification subVS = currentVS.CreateSubViewSpecification(filterTitle,
                                                                                                            FilterTreePath.Combine(_filterPath, filterValue.RelativeFilterPath), filterValue.Filter, filterValue.LinkedId);
                            T filterValueItem = new T
                            {
                                // Support non-playable MediaItems (i.e. Series, Seasons)
                                MediaItem   = filterValue.Item,
                                SimpleTitle = filterTitle,
                                NumItems    = filterValue.NumItems,
                                Id          = filterValue.Id,
                                Command     = new MethodDelegateCommand(() =>
                                {
                                    if (grouping)
                                    {
                                        NavigateToGroup(subVS, selectAttributeFilter);
                                    }
                                    else
                                    {
                                        NavigateToSubView(subVS);
                                    }
                                }),
                                View = subVS.BuildView()
                            };
                            itemsList.Add(filterValueItem);
                            if (filterValue.NumItems.HasValue)
                            {
                                totalNumItems += filterValue.NumItems.Value;
                            }
                        }
                        if (_sortable)
                        {
                            Sorting.Sorting sorting = CurrentSorting;
                            if (sorting != null)
                            {
                                itemsList.Sort((i1, i2) => sorting.Compare(i1.MediaItem, i2.MediaItem));
                            }
                        }
                        // Derived classes can implement special initial selection handling here,
                        // e.g. the first unwatched episode could be selected from a list of episodes
                        SetSelectedItem(itemsList);
                        CollectionUtils.AddAll(items, itemsList);
                        Display_Normal(items.Count, totalNumItems == 0 ? new int?() : totalNumItems);
                    }
                }
                catch (Exception e)
                {
                    ServiceRegistration.Get <ILogger>().Warn("AbstractFiltersScreenData: Error creating filter values list", e);
                    Display_ItemsInvalid();
                }
                UpdateOrRebuildView(items, createNewList);
            }
            finally
            {
                lock (_syncObj)
                    _buildingList = false;
            }
        }
Exemple #12
0
 // If the suppressActions parameter is set to <c>true</c>, no actions will be built. Instead, they will be inherited from
 // the parent navigation step. That is used for subview navigation where the navigation step doesn't produce own
 // workflow actions.
 protected NavigationData(NavigationData parent, string navigationContextName, Guid parentWorkflowStateId, Guid currentWorkflowStateId,
     ViewSpecification baseViewSpecification, AbstractScreenData defaultScreen, ICollection<AbstractScreenData> availableScreens,
     Sorting.Sorting currentSorting, bool suppressActions)
 {
   _parent = parent;
   _navigationContextName = navigationContextName;
   _currentWorkflowStateId = currentWorkflowStateId;
   _baseWorkflowStateId = parentWorkflowStateId;
   _baseViewSpecification = baseViewSpecification;
   _currentScreenData = defaultScreen;
   _availableScreens = availableScreens ?? new List<AbstractScreenData>();
   _currentSorting = currentSorting;
   if (suppressActions)
     _dynamicWorkflowActions = null;
   else
     BuildWorkflowActions();
 }
        /// <summary>
        /// Updates the GUI data for a media items view screen which reflects the data of the <see cref="CurrentView"/>.
        /// </summary>
        /// <param name="createNewList">If set to <c>true</c>, this method will re-create the
        /// <see cref="AbstractScreenData.Items"/> list, else it will reuse it.</param>
        protected void UpdateMediaItems(bool createNewList)
        {
            View view;

            // Control other threads reentering this method
            lock (_syncObj)
            {
                if (_buildingList)
                { // Another thread is already building the items list - mark it as dirty and let the other thread
                  // rebuild it.
                    _listDirty = true;
                    return;
                }
                // Mark the list as being built
                view = _view;
                InstallViewChangeNotificator(_view.CreateViewChangeNotificator());
                _buildingList = true;
                _listDirty    = false;
            }
            try
            {
                Display_ListBeingBuilt();
                ItemsList items;
                if (createNewList)
                {
                    items = new ItemsList();
                }
                else
                {
                    items = _items;
                    items.Clear();
                }
                try
                {
                    // TODO: Add the items in a separate job while the UI already shows the new screen
                    if (view.IsValid)
                    {
                        // Add items for sub views
                        IList <View>      subViews   = view.SubViews;
                        IList <MediaItem> mediaItems = view.MediaItems;
                        lock (_syncObj)
                            if (_listDirty)
                            {
                                goto RebuildView;
                            }
                        if (subViews == null || mediaItems == null)
                        {
                            Display_ItemsInvalid();
                        }
                        else
                        {
                            if (subViews.Count + mediaItems.Count > Consts.MAX_NUM_ITEMS_VISIBLE)
                            {
                                Display_TooManyItems(subViews.Count + mediaItems.Count);
                            }
                            else
                            {
                                int totalNumItems = 0;

                                bool subViewsPreSorted          = false;
                                List <NavigationItem> viewsList = new List <NavigationItem>();
                                foreach (View sv in subViews)
                                {
                                    if (sv.Specification.SortedSubViews)
                                    {
                                        subViewsPreSorted = true;
                                    }
                                    ViewItem item    = new ViewItem(sv, null, sv.AbsNumItems);
                                    View     subView = sv;
                                    item.Command = new MethodDelegateCommand(() => NavigateToView(subView.Specification));
                                    viewsList.Add(item);
                                    if (sv.AbsNumItems.HasValue)
                                    {
                                        totalNumItems += sv.AbsNumItems.Value;
                                    }
                                }
                                // Morpheus_xx, 2014-05-03: Only sort the subviews here, if they are not pre-sorted by the ViewSpecification
                                if (!subViewsPreSorted)
                                {
                                    viewsList.Sort((v1, v2) => string.Compare(v1.SortString, v2.SortString));
                                }
                                CollectionUtils.AddAll(items, viewsList);

                                lock (_syncObj)
                                    if (_listDirty)
                                    {
                                        goto RebuildView;
                                    }

                                PlayableItemCreatorDelegate picd      = PlayableItemCreator;
                                List <PlayableMediaItem>    itemsList = mediaItems.Select(childItem => picd(childItem)).Where(item => item != null).ToList();
                                Sorting.Sorting             sorting   = CurrentSorting;
                                if (sorting != null)
                                {
                                    itemsList.Sort((i1, i2) => sorting.Compare(i1.MediaItem, i2.MediaItem));
                                }
                                else
                                {
                                    // Default sorting: Use SortString
                                    itemsList.Sort((i1, i2) => string.Compare(i1.SortString, i2.SortString));
                                }

                                // Derived classes can implement special initial selection handling here,
                                // e.g. the first unwatched episode could be selected from a list of episodes
                                SetSelectedItem(itemsList);
                                CollectionUtils.AddAll(items, itemsList);

                                // Support custom sorting logic by view specification. At this time it can work on both MediaItems and SubViews.
                                if (view.Specification.CustomItemsListSorting != null)
                                {
                                    view.Specification.CustomItemsListSorting(items, sorting);
                                }

                                _currentTotalNumItems = totalNumItems == 0 ? new int?() : totalNumItems;
                                Display_Normal(items.Count, _currentTotalNumItems);
                            }
                        }
                    }
                    else
                    {
                        Display_ItemsInvalid();
                    }
                }
                catch (Exception e)
                {
                    ServiceRegistration.Get <ILogger>().Warn("AbstractItemsScreenData: Error creating items list", e);
                    Display_ItemsInvalid();
                }
RebuildView:
                bool dirty;
                lock (_syncObj)
                    if (_listDirty)
                    {
                        dirty         = true;
                        _buildingList = false;
                    }
                    else
                    {
                        dirty = false;
                    }
                if (dirty)
                {
                    UpdateMediaItems(createNewList);
                }
                else
                {
                    _items = items;
                    _items.FireChange();
                }
            }
            finally
            {
                lock (_syncObj)
                    _buildingList = false;
            }
        }
Exemple #14
0
        /// <summary>
        /// Updates the GUI data for a media items view screen which reflects the data of the <see cref="CurrentView"/>.
        /// </summary>
        /// <param name="createNewList">If set to <c>true</c>, this method will re-create the
        /// <see cref="AbstractScreenData.Items"/> list, else it will reuse it.</param>
        protected void UpdateMediaItems(bool createNewList)
        {
            View view;

            // Control other threads reentering this method
            lock (_syncObj)
            {
                if (_buildingList)
                { // Another thread is already building the items list - mark it as dirty and let the other thread
                  // rebuild it.
                    _listDirty = true;
                    return;
                }
                // Mark the list as being built
                view = _view;
                InstallViewChangeNotificator(_view.CreateViewChangeNotificator());
                _buildingList = true;
                _listDirty    = false;
            }
            try
            {
                Display_ListBeingBuilt();
                ItemsList items;
                if (createNewList)
                {
                    items = new ItemsList();
                }
                else
                {
                    items = _items;
                    items.Clear();
                }
                try
                {
                    // TODO: Add the items in a separate job while the UI already shows the new screen
                    if (view.IsValid)
                    {
                        // Add items for sub views
                        IList <View>      subViews   = view.SubViews;
                        IList <MediaItem> mediaItems = view.MediaItems;
                        lock (_syncObj)
                            if (_listDirty)
                            {
                                goto RebuildView;
                            }
                        if (subViews == null || mediaItems == null)
                        {
                            Display_ItemsInvalid();
                        }
                        else
                        {
                            if (subViews.Count + mediaItems.Count > Consts.MAX_NUM_ITEMS_VISIBLE)
                            {
                                Display_TooManyItems(subViews.Count + mediaItems.Count);
                            }
                            else
                            {
                                int totalNumItems = 0;

                                List <NavigationItem> viewsList = new List <NavigationItem>();
                                foreach (View sv in subViews)
                                {
                                    ViewItem item    = new ViewItem(sv, null, sv.AbsNumItems);
                                    View     subView = sv;
                                    item.Command = new MethodDelegateCommand(() => NavigateToView(subView.Specification));
                                    viewsList.Add(item);
                                    if (sv.AbsNumItems.HasValue)
                                    {
                                        totalNumItems += sv.AbsNumItems.Value;
                                    }
                                }
                                viewsList.Sort((v1, v2) => string.Compare(v1.SortString, v2.SortString));
                                CollectionUtils.AddAll(items, viewsList);

                                lock (_syncObj)
                                    if (_listDirty)
                                    {
                                        goto RebuildView;
                                    }

                                PlayableItemCreatorDelegate picd      = PlayableItemCreator;
                                List <PlayableMediaItem>    itemsList = mediaItems.Select(childItem => picd(childItem)).Where(item => item != null).ToList();
                                Sorting.Sorting             sorting   = CurrentSorting;
                                if (sorting != null)
                                {
                                    itemsList.Sort((i1, i2) => sorting.Compare(i1.MediaItem, i2.MediaItem));
                                }
                                else
                                {
                                    // Default sorting: Use SortString
                                    itemsList.Sort((i1, i2) => string.Compare(i1.SortString, i2.SortString));
                                }
                                CollectionUtils.AddAll(items, itemsList);

                                Display_Normal(items.Count, totalNumItems == 0 ? new int?() : totalNumItems);
                            }
                        }
                    }
                    else
                    {
                        Display_ItemsInvalid();
                    }
                }
                catch (Exception e)
                {
                    ServiceRegistration.Get <ILogger>().Warn("AbstractItemsScreenData: Error creating items list", e);
                    Display_ItemsInvalid();
                }
RebuildView:
                bool dirty;
                lock (_syncObj)
                    if (_listDirty)
                    {
                        dirty         = true;
                        _buildingList = false;
                    }
                    else
                    {
                        dirty = false;
                    }
                if (dirty)
                {
                    UpdateMediaItems(createNewList);
                }
                else
                {
                    _items = items;
                    _items.FireChange();
                }
            }
            finally
            {
                lock (_syncObj)
                    _buildingList = false;
            }
        }
        public virtual void InitMediaNavigation(MediaNavigationConfig config, out string mediaNavigationMode, out NavigationData navigationData)
        {
            PrepareAsync().Wait();

            IFilterTree filterTree = _customFilterTree ?? (_rootRole.HasValue ? new RelationshipFilterTree(_rootRole.Value) : (IFilterTree) new SimpleFilterTree());

            if (_filter != null)
            {
                filterTree.AddFilter(_filter);
            }

            //Default configuration
            string viewName = _viewName;

            AbstractScreenData nextScreen = null;

            //Apply any custom configuration
            if (config != null)
            {
                if (_availableScreens != null)
                {
                    //Use the configured root screen to load the next screen from the hierarchy
                    //and remove it from the list of available screens
                    AbstractScreenData configRoot = config.RootScreenType != null?_availableScreens.FirstOrDefault(s => s.GetType() == config.RootScreenType) : null;

                    if (configRoot != null)
                    {
                        viewName = configRoot.GetType().ToString();
                        _availableScreens.Remove(configRoot);
                    }

                    //Use the configured default screen if there is no saved screen hierarchy
                    AbstractScreenData configDefault = config.DefaultScreenType != null?_availableScreens.FirstOrDefault(s => s.GetType() == config.DefaultScreenType) : null;

                    if (configDefault != null)
                    {
                        _defaultScreen = configDefault;
                        // If we want to force the default screen to be shown, set the next screen
                        // here to avoid loading it from the screen hierarchy below.
                        if (config.AlwaysUseDefaultScreen)
                        {
                            nextScreen = configDefault;
                        }
                    }
                }

                //Apply any additional filters
                if (config.LinkedId.HasValue)
                {
                    filterTree.AddLinkedId(config.LinkedId.Value, config.FilterPath);
                }
                if (config.Filter != null)
                {
                    filterTree.AddFilter(config.Filter, config.FilterPath);
                }
            }

            IEnumerable <Guid> optionalMIATypeIDs = MediaNavigationModel.GetMediaSkinOptionalMIATypes(MediaNavigationMode);

            if (_optionalMias != null)
            {
                optionalMIATypeIDs = optionalMIATypeIDs.Union(_optionalMias).Except(_necessaryMias);
            }

            // Try to load the prefered next screen from settings if not already set.
            if (nextScreen == null && NavigationData.LoadScreenHierarchy(viewName, out string nextScreenName))
            {
                // Support for browsing mode.
                if (nextScreenName == Consts.USE_BROWSE_MODE)
                {
                    SetBrowseMode(optionalMIATypeIDs);
                }

                if (_availableScreens != null)
                {
                    nextScreen = _availableScreens.FirstOrDefault(s => s.GetType().ToString() == nextScreenName);
                }
            }

            if (_applyUserFilter)
            {
                var userFilter = UserHelper.GetUserRestrictionFilter(_necessaryMias.ToList());
                if (userFilter != null)
                {
                    filterTree.AddFilter(userFilter);
                }
            }

            // Prefer custom view specification.
            ViewSpecification rootViewSpecification = _customRootViewSpecification ??
                                                      // Always use the default view name for the root view specification, not any custom name that may
                                                      // have been specified in a navigation config, otherwise toggling browse mode won't work correctly.
                                                      // To switch to browse mode we update the root screen hierarchy to point to the browse screen and
                                                      // then navigate to the root wf state. The name of the root screen hierarchy is determined by the
                                                      // name specified here so it should always point to the actual root view name.
                                                      new MediaLibraryQueryViewSpecification(_viewName, filterTree, _necessaryMias, optionalMIATypeIDs, true)
            {
                MaxNumItems = Consts.MAX_NUM_ITEMS_VISIBLE,
            };

            if (nextScreen == null)
            {
                nextScreen = _defaultScreen;
            }

            ScreenConfig nextScreenConfig;

            NavigationData.LoadLayoutSettings(nextScreen.GetType().ToString(), out nextScreenConfig);

            Sorting.Sorting nextSortingMode  = _availableSortings.FirstOrDefault(sorting => sorting.GetType().ToString() == nextScreenConfig.Sorting) ?? _defaultSorting;
            Sorting.Sorting nextGroupingMode = _availableGroupings == null || String.IsNullOrEmpty(nextScreenConfig.Grouping) ? null : _availableGroupings.FirstOrDefault(grouping => grouping.GetType().ToString() == nextScreenConfig.Grouping) ?? _defaultGrouping;

            navigationData = new NavigationData(null, viewName, MediaNavigationRootState,
                                                MediaNavigationRootState, rootViewSpecification, nextScreen, _availableScreens, nextSortingMode, nextGroupingMode)
            {
                AvailableSortings  = _availableSortings,
                AvailableGroupings = _availableGroupings,
                LayoutType         = nextScreenConfig.LayoutType,
                LayoutSize         = nextScreenConfig.LayoutSize
            };
            mediaNavigationMode = MediaNavigationMode;
        }
        /// <summary>
        /// Updates the GUI data for a filter values selection screen which reflects the available filter values for
        /// the current base view specification of our <see cref="AbstractScreenData._navigationData"/>.
        /// </summary>
        protected void ReloadFilterValuesList(bool createNewList)
        {
            MediaLibraryQueryViewSpecification currentVS = _navigationData.BaseViewSpecification as MediaLibraryQueryViewSpecification;

            if (currentVS == null)
            { // Should never happen
                ServiceRegistration.Get <ILogger>().Error("FilterScreenData: Wrong type of media library view '{0}'", _navigationData.BaseViewSpecification);
                return;
            }
            // Control other threads reentering this method
            lock (_syncObj)
            {
                if (_buildingList)
                { // Another thread is already building the items list - mark it as dirty and let the other thread
                  // rebuild it.
                    _listDirty = true;
                    return;
                }
                // Mark the list as being built
                _buildingList = true;
                _listDirty    = false;
            }
            try
            {
                ItemsList items;
                if (createNewList)
                {
                    items = new ItemsList();
                }
                else
                {
                    items = _items;
                    items.Clear();
                }

                try
                {
                    Display_ListBeingBuilt();
                    bool grouping = true;
                    //If currentVS is the base view it's possible that it has a filter that is incompatible with _filterCriterion.
                    //This is the case if a plugin has added a base filter to exclude certain items, e.g. TV excludes recordings
                    //and the new filter filters by a different media type, e.g. series'. Ignore the base filter in this case.
                    IFilter currentFilter = (_navigationData.Parent != null && CanFilter(_navigationData.Parent.CurrentScreenData)) ||
                                            currentVS.CanCombineFilters(_filteredMias) ? currentVS.Filter : null;
                    ICollection <FilterValue> fv = _clusterFilter == null?
                                                   _filterCriterion.GroupValues(currentVS.NecessaryMIATypeIds, _clusterFilter, currentFilter) : null;

                    if (fv == null || fv.Count <= Consts.MAX_NUM_ITEMS_VISIBLE)
                    {
                        fv       = _filterCriterion.GetAvailableValues(currentVS.NecessaryMIATypeIds, _clusterFilter, currentFilter);
                        grouping = false;
                    }
                    if (fv.Count > Consts.MAX_NUM_ITEMS_VISIBLE)
                    {
                        Display_TooManyItems(fv.Count);
                    }
                    else
                    {
                        bool dirty;
                        lock (_syncObj)
                            dirty = _listDirty;
                        if (dirty)
                        {
                            UpdateOrRebuildView(items, createNewList);
                            return;
                        }

                        _sortable = true;
                        int totalNumItems           = 0;
                        List <FilterItem> itemsList = new List <FilterItem>();
                        // Build collection of available (filter/display) screens which will remain in the next view - that is all currently
                        // available screens without the screen which equals this current screen. But we cannot simply remove "this"
                        // from the collection, because "this" could be a derived screen (in case our base screen showed groups).
                        // So we need an equality criterion when the screen to be removed is equal to this screen in terms of its
                        // filter criterion. But with the given data, we actually cannot derive that equality.
                        // So we simply use the MenuItemLabel, which should be the same in this and the base screen of the same filter.
                        foreach (FilterValue filterValue in fv)
                        {
                            _sortable &= filterValue.Item != null;
                            string  filterTitle                      = filterValue.Title;
                            IFilter selectAttributeFilter            = filterValue.SelectAttributeFilter;
                            MediaLibraryQueryViewSpecification subVS = currentVS.CreateSubViewSpecification(filterTitle, filterValue.Filter, _filteredMias);
                            T filterValueItem = new T
                            {
                                // Support non-playable MediaItems (i.e. Series, Seasons)
                                MediaItem   = filterValue.Item,
                                SimpleTitle = filterTitle,
                                NumItems    = filterValue.NumItems,
                                Id          = filterValue.Id,
                                Command     = grouping ?
                                              new MethodDelegateCommand(() => NavigateToGroup(subVS, selectAttributeFilter)) :
                                              new MethodDelegateCommand(() => NavigateToSubView(subVS))
                            };
                            itemsList.Add(filterValueItem);
                            if (filterValue.NumItems.HasValue)
                            {
                                totalNumItems += filterValue.NumItems.Value;
                            }
                        }
                        if (_sortable)
                        {
                            Sorting.Sorting sorting = CurrentSorting;
                            if (sorting != null)
                            {
                                itemsList.Sort((i1, i2) => sorting.Compare(i1.MediaItem, i2.MediaItem));
                            }
                        }
                        CollectionUtils.AddAll(items, itemsList);
                        Display_Normal(items.Count, totalNumItems == 0 ? new int?() : totalNumItems);
                    }
                }
                catch (Exception e)
                {
                    ServiceRegistration.Get <ILogger>().Warn("AbstractFiltersScreenData: Error creating filter values list", e);
                    Display_ItemsInvalid();
                }
                UpdateOrRebuildView(items, createNewList);
            }
            finally
            {
                lock (_syncObj)
                    _buildingList = false;
            }
        }