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 #3
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;
            }
        }
        /// <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;
            }
        }
        /// <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;
            }
        }