Ejemplo n.º 1
0
        public MetricsViewModel(ISourcesCacheProvider cacheProvider)
        {
            _model = new MetricsModel
            {
                Caption = "Metrics",
                Cache   = cacheProvider.CurrentCache
            };
            Disposables.Add(_model);

            _model.WhenAnyValue(x => x.Caption, x => x.Online)
            .Subscribe(v => Title = v.Item1 + (v.Item2 ? " >" : " ||"));

            var dynamicFilter = _model.SelectedSourcesChanged
                                .Select(_ => Filters.CreateFilterMetricBySources(_model));

            var observable = _model.Cache.Metrics
                             .Connect()
                             .Filter(dynamicFilter)
                             .Publish();

            _metricsCache = observable
                            .AsObservableCache()
                            .DisposeWith(Disposables);

            observable
            .Transform(x => new MetricValueItem {
                Metric = x
            })
            .Sort(SortExpressionComparer <MetricValueItem>
                  .Ascending(x => x.Metric.Instance.Source.Name)
                  .ThenByAscending(x => x.Metric.Instance.Name)
                  .ThenByAscending(x => x.Metric.Name))
            .ObserveOnDispatcher()
            .Bind(out var metricValuesStatic)
            .Subscribe()
            .DisposeWith(Disposables);

            observable
            .Connect()
            .DisposeWith(Disposables);

            var metricValuesDynamicSource = new SourceList <MetricValueItem>()
                                            .DisposeWith(Disposables);

            metricValuesDynamicSource
            .Connect()
            .ObserveOnDispatcher()
            .Bind(out var metricValuesDynamic)
            .Subscribe()
            .DisposeWith(Disposables);

            _model.WhenAnyValue(x => x.MetricDiagramVisible)
            .Subscribe(x => MetricValuesList = x ? metricValuesStatic : metricValuesDynamic);

            _model.SelectedSourcesChanged.Subscribe(_ => UpdateSelectedMetrics());
            UpdateSelectedMetrics();

            var canStart = _model.WhenAny(x => x.Online, x => !x.Value);

            StartCommand = ReactiveCommand.Create(OnStart, canStart);

            var canStop = _model.WhenAny(x => x.Online, x => x.Value);

            StopCommand = ReactiveCommand.Create(OnStop, canStop);

            UpdateCommand = ReactiveCommand.Create(OnUpdate, canStop);

            var mapper = Mappers.Xy <MetricValueItem>()
                         .X(item => (double)item.Interval.Ticks / TimeSpan.FromMinutes(5).Ticks)
                         .Y(item => item.Value);

            //lets save the mapper globally.
            Charting.For <MetricValueItem>(mapper);

            SeriesCollection = new ChartValues <MetricValueItem>();

            //lets set how to display the X Labels
            DateTimeFormatter = value => new DateTime((long)value * TimeSpan.FromMinutes(5).Ticks).ToString("t");

            UpdateCommand.Subscribe(results =>
            {
                if (_model.MetricDiagramVisible)
                {
                    SeriesCollection.Clear();
                    if (results != null)
                    {
                        SeriesCollection.AddRange(results);
                    }
                }
                else
                {
                    metricValuesDynamicSource.Edit(innerList =>
                    {
                        innerList.Clear();
                        innerList.AddRange(results);
                    });
                }
            });
        }
        public NuGetPackageListViewModel(INavigationService navigationService, INuGetPackageService nugetPackageService)
        {
            _navigationService   = navigationService;
            _nugetPackageService = nugetPackageService;

            PackageDetails = ReactiveCommand.CreateFromTask <NuGetPackageViewModel>(ExecutePackageDetails);
            Refresh        = ReactiveCommand.CreateFromTask(ExecuteRefresh);

            Refresh.ThrownExceptions.Subscribe(exception => this.Log().Warn(exception));
            PackageDetails.ThrownExceptions.Subscribe(exception => this.Log().Warn(exception));

            var nugetChangeSet =
                _nugetCache
                .Connect()
                .RefCount();

            var sortChanged =
                this.WhenAnyValue(x => x.SelectedTag)
                .Where(x => !string.IsNullOrEmpty(x))
                .Select(selectedTag =>
                        SortExpressionComparer <NuGetPackageViewModel>
                        .Ascending(x => x.PackageMetadata.DownloadCount)
                        .ThenByAscending(x => x.PackageMetadata.Tags.Contains(selectedTag)));

            nugetChangeSet
            .Transform(x => new NuGetPackageViewModel(x))
            .Sort(sortChanged)
            .Bind(out _searchResults)
            .DisposeMany()
            .Subscribe()
            .DisposeWith(Disposal);

            _nugetCache
            .CountChanged
            .Select(count => count > 0)
            .ToProperty(this, x => x.HasItems, out _hasItems)
            .DisposeWith(Disposal);

            nugetChangeSet
            .Transform(x => x.Tags.Split(','))
            .RemoveKey()
            .TransformMany(x => x)
            .DistinctValues(x => x)
            .Bind(out _tags)
            .Subscribe()
            .DisposeWith(Disposal);

            Search =
                ReactiveCommand.CreateFromObservable <string, IEnumerable <IPackageSearchMetadata> >(SearchNuGetPackages);

            Search
            .Select(packageSearchMetadata => packageSearchMetadata)
            .Subscribe(packages =>
                       _nugetCache.EditDiff(packages, (first, second) => first.Identity.Id == second.Identity.Id))
            .DisposeWith(Disposal);

            this.WhenAnyValue(x => x.SearchText)
            .Where(x => !string.IsNullOrEmpty(x))
            .Throttle(TimeSpan.FromMilliseconds(750), RxApp.TaskpoolScheduler)
            .Select(term => term.Trim())
            .DistinctUntilChanged()
            .InvokeCommand(this, x => x.Search)
            .DisposeWith(Disposal);

            this.WhenAnyValue(x => x.SearchText)
            .Where(string.IsNullOrEmpty)
            .Subscribe(_ => _nugetCache.Clear())
            .DisposeWith(Disposal);

            this.WhenAnyObservable(x => x.Refresh.IsExecuting)
            .StartWith(false)
            .DistinctUntilChanged()
            .ToProperty(this, x => x.IsRefreshing, out _isRefreshing)
            .DisposeWith(Disposal);
        }
Ejemplo n.º 3
0
        public TailViewModel([NotNull] ILogger logger,
                             [NotNull] ISchedulerProvider schedulerProvider,
                             [NotNull] IEnumerable <IFileWatcher> fileWatcher,
                             [NotNull] ISelectionMonitor selectionMonitor,
                             [NotNull] IClipboardHandler clipboardHandler,
                             [NotNull] ISearchInfoCollection searchInfoCollection,
                             [NotNull] IInlineViewerFactory inlineViewerFactory,
                             [NotNull] ISetting <GeneralOptions> generalOptions,
                             [NotNull] ISearchMetadataCollection searchMetadataCollection,
                             [NotNull] SearchOptionsViewModel searchOptionsViewModel,
                             [NotNull] SearchHints searchHints)
        {
            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }
            if (schedulerProvider == null)
            {
                throw new ArgumentNullException(nameof(schedulerProvider));
            }
            if (fileWatcher == null)
            {
                throw new ArgumentNullException(nameof(fileWatcher));
            }
            if (selectionMonitor == null)
            {
                throw new ArgumentNullException(nameof(selectionMonitor));
            }
            if (clipboardHandler == null)
            {
                throw new ArgumentNullException(nameof(clipboardHandler));
            }
            if (searchInfoCollection == null)
            {
                throw new ArgumentNullException(nameof(searchInfoCollection));
            }
            if (inlineViewerFactory == null)
            {
                throw new ArgumentNullException(nameof(inlineViewerFactory));
            }
            if (generalOptions == null)
            {
                throw new ArgumentNullException(nameof(generalOptions));
            }
            if (searchMetadataCollection == null)
            {
                throw new ArgumentNullException(nameof(searchMetadataCollection));
            }
            if (searchOptionsViewModel == null)
            {
                throw new ArgumentNullException(nameof(searchOptionsViewModel));
            }
            if (searchHints == null)
            {
                throw new ArgumentNullException(nameof(searchHints));
            }

            _stateProvider = new TailViewPersister(this);

            var enumerable = fileWatcher as IFileWatcher[] ?? fileWatcher.ToArray();

            Names            = enumerable.Select(t => t.FullName);
            SelectionMonitor = selectionMonitor;
            SearchOptions    = searchOptionsViewModel;
            SearchHints      = searchHints;
            SearchCollection = new SearchCollection(searchInfoCollection, schedulerProvider);

            CopyToClipboardCommand =
                new Command(() => clipboardHandler.WriteToClipboard(selectionMonitor.GetSelectedText()));
            OpenFileCommand   = new Command(() => Process.Start(enumerable[0].FullName));
            OpenFolderCommand = new Command(() => Process.Start(enumerable[0].Folder));

            UsingDarkTheme = generalOptions.Value
                             .ObserveOn(schedulerProvider.MainThread)
                             .Select(options => options.Theme == Theme.Dark)
                             .ForBinding();

            HighlightTail = generalOptions.Value
                            .ObserveOn(schedulerProvider.MainThread)
                            .Select(options => options.HighlightTail)
                            .ForBinding();

            HighlightDuration = generalOptions.Value
                                .ObserveOn(schedulerProvider.MainThread)
                                .Select(options => new Duration(TimeSpan.FromSeconds(options.HighlightDuration)))
                                .ForBinding();

            //An observable which acts as a scroll command
            var autoChanged = this.WhenValueChanged(vm => vm.AutoTail);
            var scroller    = _userScrollRequested.CombineLatest(autoChanged, (user, auto) =>
            {
                var mode = AutoTail ? ScrollReason.Tail : ScrollReason.User;
                return(new ScrollRequest(mode, user.PageSize, user.FirstIndex));
            })
                              .Do(x => logger.Info("Scrolling to {0}/{1}", x.FirstIndex, x.PageSize))
                              .DistinctUntilChanged();

            FileStatus = enumerable
                         .Select(t => t.Status)
                         .Merge()
                         .Scan(default(FileStatus), (status, fileStatus) => status | fileStatus)
                         .ForBinding();

            //command to add the current search to the tail collection
            var searchInvoker =
                SearchHints.SearchRequested.Subscribe(
                    request => { searchInfoCollection.Add(request.Text, request.UseRegEx); });

            //User feedback to show file size
            FileSizeText = enumerable
                           .Select(t => t.Latest)
                           .Merge()
                           .Select(t => t.Size)
                           .Scan(0f, (previousSize, currentSize) => previousSize + currentSize / 2f)
                           .Select(t => ((long)t).FormatWithAbbreviation())
                           .DistinctUntilChanged()
                           .ForBinding();


            //tailer is the main object used to tail, scroll and filter in a file
            var lineScroller = new LineScroller(SearchCollection.Latest.ObserveOn(schedulerProvider.Background),
                                                scroller);

            //load lines into observable collection
            var lineProxyFactory = new LineProxyFactory(new TextFormatter(searchMetadataCollection),
                                                        new LineMatches(searchMetadataCollection));

            var loader = lineScroller.Lines.Connect()
                         .RecordChanges(logger, "Received")
                         .Transform(lineProxyFactory.Create, new ParallelisationOptions(ParallelType.Ordered, 3))
                         .Sort(SortExpressionComparer <LineProxy> .Ascending(proxy => proxy))
                         .ObserveOn(schedulerProvider.MainThread)
                         .Bind(out _data, 100)
                         .DisposeMany()
                         .LogErrors(logger)
                         .Subscribe();


            //monitor matching lines and start index,
            Count = searchInfoCollection.All
                    .GroupBy(t => t)
                    .Select(groupedProvider => groupedProvider.Key.Count)
                    .Scan(0, (i, providerCount) => i + providerCount)
                    .ForBinding();
            CountText = searchInfoCollection.All
                        .GroupBy(t => t)
                        .Select(groupedProvider => groupedProvider.Key.Count)
                        .Scan(0, (i, providerCount) => i + providerCount)
                        .Select(latestCount => $"{latestCount.ToString("##,###")} lines")
                        .ForBinding();
            //iterate over every items to evaluate the lines' count
            LatestCount = SearchCollection.Latest
                          .GroupBy(t => t)
                          .Do(Console.WriteLine)
                          .Scan(0, (acc, provider) =>
            {
                if (provider.Key is IndexCollection && provider.Key.NumberOfPreviousProvider == 0)
                {
                    acc = 0;
                }
                else if (provider.Key is FileSearchResult && provider.Key.NumberOfPreviousProvider == 0)
                {
                    acc = 0;
                }
                return(provider.Key.Count + acc);
            })
                          .ForBinding();

            ////track first visible index
            var firstIndexMonitor = lineScroller.Lines.Connect()
                                    .Buffer(TimeSpan.FromMilliseconds(25)).FlattenBufferResult()
                                    .ToCollection()
                                    .Select(lines => lines.Count == 0 ? 0 : lines.Select(l => l.Index).Max() - lines.Count + 1)
                                    .ObserveOn(schedulerProvider.MainThread)
                                    .Subscribe(first => { FirstIndex = first; });

            //Create objects required for inline viewing
            var isUserDefinedChanged = SearchCollection.WhenValueChanged(sc => sc.Selected)
                                       .Where(selected => selected != null)
                                       .Select(selected => selected.IsUserDefined)
                                       .DistinctUntilChanged()
                                       .Replay(1)
                                       .RefCount();

            var inlineViewerVisible = isUserDefinedChanged.CombineLatest(this.WhenValueChanged(vm => vm.ShowInline),
                                                                         (userDefined, showInline) => userDefined && showInline);

            CanViewInline       = isUserDefinedChanged.ForBinding();
            InlineViewerVisible = inlineViewerVisible.ForBinding();

            //return an empty line provider unless user is viewing inline - this saves needless trips to the file

            var inline = searchInfoCollection.All.CombineLatest(inlineViewerVisible,
                                                                (index, ud) => ud ? index : new EmptyLineProvider());

            var firstVisibleRow = _data.ToObservableChangeSet().ToCollection()
                                  .Select(collection => collection.FirstOrDefault());

            //var itemToSelect = this.WhenValueChanged(vm => vm.SelectedItem)
            //    .CombineLatest(firstVisibleRow, (selected, first) => selected ?? first);
            ////
            InlineViewer = inlineViewerFactory.Create(inline, this.WhenValueChanged(vm => vm.SelectedItem),
                                                      lineProxyFactory);

            _cleanUp = new CompositeDisposable(lineScroller,
                                               loader,
                                               firstIndexMonitor,
                                               FileStatus,
                                               Count,
                                               LatestCount,
                                               FileSizeText,
                                               CanViewInline,
                                               InlineViewer,
                                               InlineViewerVisible,
                                               SearchCollection,
                                               searchInfoCollection,
                                               HighlightTail,
                                               UsingDarkTheme,
                                               searchHints,
                                               searchMetadataCollection,
                                               searchMetadataCollection,
                                               SelectionMonitor,
                                               SearchOptions,
                                               searchInvoker,
                                               _userScrollRequested.SetAsComplete());
        }
Ejemplo n.º 4
0
 public IObservable <IComparer <T> > GetObservable() => this.WhenAnyValue(x => x.IsDescending)
 .StartWith(IsDescending)
 .Select(isDescending => isDescending
         ? SortExpressionComparer <T> .Descending(Comparer)
         : SortExpressionComparer <T> .Ascending(Comparer));
Ejemplo n.º 5
0
        public TailViewModel([NotNull] ILogger logger,
                             [NotNull] ISchedulerProvider schedulerProvider,
                             [NotNull] IFileWatcher fileWatcher,
                             [NotNull] ISelectionMonitor selectionMonitor,
                             [NotNull] IClipboardHandler clipboardHandler,
                             [NotNull] ISearchInfoCollection searchInfoCollection,
                             [NotNull] IInlineViewerFactory inlineViewerFactory,
                             [NotNull] GeneralOptionBindings generalOptionBindings,
                             [NotNull] ICombinedSearchMetadataCollection combinedSearchMetadataCollection,
                             [NotNull] IStateBucketService stateBucketService,
                             [NotNull] ITailViewStateRestorer restorer,
                             [NotNull] SearchHints searchHints,
                             [NotNull] ITailViewStateControllerFactory tailViewStateControllerFactory,
                             [NotNull] IThemeProvider themeProvider,
                             [NotNull] SearchCollection searchCollection,
                             [NotNull] ITextFormatter textFormatter,
                             [NotNull] ILineMatches lineMatches,
                             [NotNull] IObjectProvider objectProvider,
                             [NotNull] IDialogCoordinator dialogCoordinator)
        {
            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }
            if (schedulerProvider == null)
            {
                throw new ArgumentNullException(nameof(schedulerProvider));
            }
            if (fileWatcher == null)
            {
                throw new ArgumentNullException(nameof(fileWatcher));
            }
            if (clipboardHandler == null)
            {
                throw new ArgumentNullException(nameof(clipboardHandler));
            }
            if (searchInfoCollection == null)
            {
                throw new ArgumentNullException(nameof(searchInfoCollection));
            }
            if (inlineViewerFactory == null)
            {
                throw new ArgumentNullException(nameof(inlineViewerFactory));
            }
            if (stateBucketService == null)
            {
                throw new ArgumentNullException(nameof(stateBucketService));
            }
            if (themeProvider == null)
            {
                throw new ArgumentNullException(nameof(themeProvider));
            }
            if (textFormatter == null)
            {
                throw new ArgumentNullException(nameof(textFormatter));
            }
            if (lineMatches == null)
            {
                throw new ArgumentNullException(nameof(lineMatches));
            }
            if (objectProvider == null)
            {
                throw new ArgumentNullException(nameof(objectProvider));
            }
            if (dialogCoordinator == null)
            {
                throw new ArgumentNullException(nameof(dialogCoordinator));
            }
            if (combinedSearchMetadataCollection == null)
            {
                throw new ArgumentNullException(nameof(combinedSearchMetadataCollection));
            }

            Name                  = fileWatcher.FullName;
            SelectionMonitor      = selectionMonitor ?? throw new ArgumentNullException(nameof(selectionMonitor));
            GeneralOptionBindings = generalOptionBindings;
            SearchHints           = searchHints ?? throw new ArgumentNullException(nameof(searchHints));

            CopyToClipboardCommand     = new Command(() => clipboardHandler.WriteToClipboard(selectionMonitor.GetSelectedText()));
            OpenFileCommand            = new Command(() => Process.Start(fileWatcher.FullName));
            OpenFolderCommand          = new Command(() => Process.Start(fileWatcher.Folder));
            CopyPathToClipboardCommand = new Command(() => clipboardHandler.WriteToClipboard(fileWatcher.FullName));
            UnClearCommand             = new Command(fileWatcher.Reset);
            ClearCommand             = new Command(fileWatcher.Clear);
            KeyAutoTail              = new Command(() => { AutoTail = true; });
            OpenSearchOptionsCommand = new Command(async() =>
            {
                await Task.Run(() =>
                {
                    var content = objectProvider.Get <SearchOptionsViewModel>(new Argument <ICombinedSearchMetadataCollection>(combinedSearchMetadataCollection));
                    dialogCoordinator.Show(this, content, x => content.Dispose());
                });
            });

            var closeOnDeselect = this.WhenValueChanged(vm => vm.IsSelected, false)
                                  .Where(selected => !selected)
                                  .Subscribe(_ => dialogCoordinator.Close());

            SearchCollection         = searchCollection ?? throw new ArgumentNullException(nameof(searchCollection));
            SearchMetadataCollection = combinedSearchMetadataCollection.Local;

            var horizonalScrollArgs = new ReplaySubject <TextScrollInfo>(1);

            HorizonalScrollChanged = args => horizonalScrollArgs.OnNext(args);

            _tailViewStateControllerFactory = tailViewStateControllerFactory;

            //this deals with state when loading the system at start up and at shut-down
            _persister = new TailViewPersister(this, restorer);

            FileStatus = fileWatcher.Status.ForBinding();

            //command to add the current search to the tail collection
            var searchInvoker = SearchHints.SearchRequested.Subscribe(request => searchInfoCollection.Add(request.Text, request.UseRegEx));

            //An observable which acts as a scroll command
            var autoChanged = this.WhenValueChanged(vm => vm.AutoTail);
            var scroller    = _userScrollRequested.CombineLatest(autoChanged, (user, auto) =>
            {
                var mode = AutoTail ? ScrollReason.Tail : ScrollReason.User;
                return(new ScrollRequest(mode, user.PageSize, user.FirstIndex));
            })
                              .Do(x => logger.Info("Scrolling to {0}/{1}", x.FirstIndex, x.PageSize))
                              .DistinctUntilChanged();

            //User feedback to show file size
            FileSizeText = fileWatcher.Latest.Select(fn => fn.Size)
                           .Select(size => size.FormatWithAbbreviation())
                           .DistinctUntilChanged()
                           .ForBinding();


            //tailer is the main object used to tail, scroll and filter in a file
            var selectedProvider = SearchCollection.Latest.ObserveOn(schedulerProvider.Background);

            var lineScroller = new LineScroller(selectedProvider, scroller);

            MaximumChars = lineScroller.MaximumLines()
                           .ObserveOn(schedulerProvider.MainThread)
                           .ForBinding();

            var lineProxyFactory = new LineProxyFactory(textFormatter, lineMatches, horizonalScrollArgs.DistinctUntilChanged(), themeProvider);

            var loader = lineScroller.Lines.Connect()
                         .LogChanges(logger, "Received")
                         .Transform(lineProxyFactory.Create)
                         .LogChanges(logger, "Sorting")
                         .Sort(SortExpressionComparer <LineProxy> .Ascending(proxy => proxy))
                         .ObserveOn(schedulerProvider.MainThread)
                         .Bind(out _data, 100)
                         .LogChanges(logger, "Bound")
                         .DisposeMany()
                         .LogErrors(logger)
                         .Subscribe();

            //monitor matching lines and start index,
            Count       = searchInfoCollection.All.Select(latest => latest.Count).ForBinding();
            CountText   = searchInfoCollection.All.Select(latest => $"{latest.Count:##,###} lines").ForBinding();
            LatestCount = SearchCollection.Latest.Select(latest => latest.Count).ForBinding();

            ////track first visible index
            var firstIndexMonitor = lineScroller.Lines.Connect()
                                    .Buffer(TimeSpan.FromMilliseconds(25)).FlattenBufferResult()
                                    .ToCollection()
                                    .Select(lines => lines.Count == 0 ? 0 : lines.Select(l => l.Index).Max() - lines.Count + 1)
                                    .ObserveOn(schedulerProvider.MainThread)
                                    .Subscribe(first =>
            {
                FirstIndex = first;
            });

            //Create objects required for inline viewing
            var isUserDefinedChanged = SearchCollection.WhenValueChanged(sc => sc.Selected)
                                       .Where(selected => selected != null)
                                       .Select(selected => selected.IsUserDefined)
                                       .DistinctUntilChanged()
                                       .Replay(1)
                                       .RefCount();

            var showInline          = this.WhenValueChanged(vm => vm.ShowInline);
            var inlineViewerVisible = isUserDefinedChanged.CombineLatest(showInline, (userDefined, showInlne) => userDefined && showInlne);

            CanViewInline       = isUserDefinedChanged.ForBinding();
            InlineViewerVisible = inlineViewerVisible.ForBinding();

            //return an empty line provider unless user is viewing inline - this saves needless trips to the file
            var inline = searchInfoCollection.All.CombineLatest(inlineViewerVisible, (index, ud) => ud ? index : EmptyLineProvider.Instance);

            InlineViewer = inlineViewerFactory.Create(combinedSearchMetadataCollection, inline, this.WhenValueChanged(vm => vm.SelectedItem));

            _cleanUp = new CompositeDisposable(lineScroller,
                                               loader,
                                               firstIndexMonitor,
                                               FileStatus,
                                               Count,
                                               CountText,
                                               LatestCount,
                                               FileSizeText,
                                               CanViewInline,
                                               InlineViewer,
                                               InlineViewerVisible,
                                               SearchCollection,
                                               searchInfoCollection,
                                               searchHints,
                                               SelectionMonitor,
                                               closeOnDeselect,
                                               Disposable.Create(dialogCoordinator.Close),
                                               searchInvoker,
                                               MaximumChars,
                                               _stateMonitor,
                                               combinedSearchMetadataCollection,
                                               horizonalScrollArgs.SetAsComplete(),
                                               _userScrollRequested.SetAsComplete());
        }
Ejemplo n.º 6
0
        public SearchProxyCollection(ISearchMetadataCollection metadataCollection,
            Guid id,
            Action<SearchMetadata> changeScopeAction,
            ISchedulerProvider schedulerProvider,
            IColourProvider colourProvider,
            IIconProvider iconsProvider,
            ITextAssociationCollection textAssociationCollection,
            IThemeProvider themeProvider)
        {
            var proxyItems = metadataCollection.Metadata.Connect()
                .WhereReasonsAre(ChangeReason.Add, ChangeReason.Remove) //ignore updates because we update from here
                .Transform(meta =>
                {
                    return new SearchOptionsProxy(meta,
                        changeScopeAction,
                        colourProvider,
                        themeProvider,
                        new IconSelector(iconsProvider, schedulerProvider),
                        m => metadataCollection.Remove(m.SearchText),
                        iconsProvider.DefaultIconSelector,
                        id);
                })
                .SubscribeMany(so =>
                {
                    //when a value changes, write the original value back to the metadata collection
                    var anyPropertyHasChanged = so.WhenAnyPropertyChanged()
                        .Select(_ => (SearchMetadata) so)
                        .Subscribe(metadataCollection.AddorUpdate);

                    //when an icon or colour has changed we need to record user choice so 
                    //the same choice can be used again
                    var iconChanged = so.WhenValueChanged(proxy => proxy.IconKind, false).ToUnit();
                    var colourChanged = so.WhenValueChanged(proxy => proxy.HighlightHue, false).ToUnit();
                    var ignoreCaseChanged = so.WhenValueChanged(proxy => proxy.CaseSensitive, false).ToUnit();

                    var textAssociationChanged = iconChanged.Merge(colourChanged).Merge(ignoreCaseChanged)
                        .Throttle(TimeSpan.FromMilliseconds(250))
                        .Select(_ => new TextAssociation(so.Text, so.CaseSensitive, so.UseRegex, so.HighlightHue.Swatch,
                            so.IconKind.ToString(), so.HighlightHue.Name, DateTime.UtcNow))
                        .Subscribe(textAssociationCollection.MarkAsChanged);

                    return new CompositeDisposable(anyPropertyHasChanged, textAssociationChanged);
                })
                .AsObservableCache();

            Count = proxyItems.CountChanged.StartWith(0).ForBinding();

            var monitor = MonitorPositionalChanges().Subscribe(metadataCollection.Add);
            
            //load data onto grid
            var collection = new ObservableCollectionExtended<SearchOptionsProxy>();

            var includedLoader = proxyItems
                .Connect(proxy => !proxy.IsExclusion)
                .Sort(SortExpressionComparer<SearchOptionsProxy>.Ascending(proxy => proxy.Position))
                .ObserveOn(schedulerProvider.MainThread)
                //force reset for each new or removed item dues to a bug in the underlying dragablz control which inserts in an incorrect position
                .Bind(collection, new ObservableCollectionAdaptor<SearchOptionsProxy, string>(0))
                .DisposeMany()
                .Subscribe();

            var excludedLoader = proxyItems
                .Connect(proxy => proxy.IsExclusion)
                .Sort(SortExpressionComparer<SearchOptionsProxy>.Ascending(proxy => proxy.Text))
                .ObserveOn(schedulerProvider.MainThread)
                //force reset for each new or removed item dues to a bug in the underlying dragablz control which inserts in an incorrect position
                .Bind(out var excluded)
                .DisposeMany()
                .Subscribe();


            Excluded = excluded;
            Included = new ReadOnlyObservableCollection<SearchOptionsProxy>(collection);

            _cleanUp = new CompositeDisposable(proxyItems, includedLoader, excludedLoader, monitor);
        }
Ejemplo n.º 7
0
        public InlineViewer([NotNull] IObservable <ILineProvider> lineProvider,
                            [NotNull] IObservable <LineProxy> selectedChanged,
                            [NotNull] IClipboardHandler clipboardHandler,
                            [NotNull] ISchedulerProvider schedulerProvider,
                            [NotNull] ISelectionMonitor selectionMonitor,
                            [NotNull] ILogger logger,
                            [NotNull] IThemeProvider themeProvider,
                            [NotNull] ITextFormatter textFormatter,
                            [NotNull] ILineMatches lineMatches)
        {
            if (lineProvider == null)
            {
                throw new ArgumentNullException(nameof(lineProvider));
            }
            if (selectedChanged == null)
            {
                throw new ArgumentNullException(nameof(selectedChanged));
            }
            if (clipboardHandler == null)
            {
                throw new ArgumentNullException(nameof(clipboardHandler));
            }
            if (schedulerProvider == null)
            {
                throw new ArgumentNullException(nameof(schedulerProvider));
            }
            if (selectionMonitor == null)
            {
                throw new ArgumentNullException(nameof(selectionMonitor));
            }
            if (themeProvider == null)
            {
                throw new ArgumentNullException(nameof(themeProvider));
            }
            SelectionMonitor       = selectionMonitor;
            CopyToClipboardCommand = new Command(() => clipboardHandler.WriteToClipboard(selectionMonitor.GetSelectedText()));

            _isSettingScrollPosition = false;
            var pageSize = this.WhenValueChanged(vm => vm.PageSize);

            //if use selection is null, tail the file
            var scrollSelected = selectedChanged
                                 .CombineLatest(lineProvider, pageSize, (proxy, lp, pge) => proxy == null ? new ScrollRequest(pge, 0) : new ScrollRequest(pge, proxy.Start))
                                 .DistinctUntilChanged();

            var horizonalScrollArgs = new ReplaySubject <TextScrollInfo>(1);

            HorizonalScrollChanged = hargs =>
            {
                horizonalScrollArgs.OnNext(hargs);
            };

            var scrollUser = _userScrollRequested
                             .Where(x => !_isSettingScrollPosition)
                             .Select(request => new ScrollRequest(ScrollReason.User, request.PageSize, request.FirstIndex));

            var scroller = scrollSelected.Merge(scrollUser)
                           .ObserveOn(schedulerProvider.Background)
                           .DistinctUntilChanged();


            var lineScroller = new LineScroller(lineProvider, scroller);

            Count = lineProvider.Select(lp => lp.Count).ForBinding();

            MaximumChars = lineScroller.MaximumLines()
                           .ObserveOn(schedulerProvider.MainThread)
                           .ForBinding();


            var proxyFactory = new LineProxyFactory(textFormatter, lineMatches, horizonalScrollArgs.DistinctUntilChanged(), themeProvider);

            //load lines into observable collection
            var loader = lineScroller.Lines.Connect()
                         .Transform(proxyFactory.Create, new ParallelisationOptions(ParallelType.Ordered, 3))
                         .Sort(SortExpressionComparer <LineProxy> .Ascending(proxy => proxy))
                         .ObserveOn(schedulerProvider.MainThread)
                         .Bind(out _data)
                         .DisposeMany()
                         .LogErrors(logger)
                         .Subscribe();

            // track first visible index [required to set scroll extent]
            var firstIndexMonitor = lineScroller.Lines.Connect()
                                    .Buffer(TimeSpan.FromMilliseconds(250)).FlattenBufferResult()
                                    .ToCollection()
                                    .Select(lines => lines.Count == 0 ? 0 : lines.Select(l => l.Index).Max() - lines.Count + 1)
                                    .ObserveOn(schedulerProvider.MainThread)
                                    .Subscribe(first =>
            {
                try
                {
                    _isSettingScrollPosition = true;
                    FirstIndex = first;
                }
                finally
                {
                    _isSettingScrollPosition = false;
                }
            });

            _cleanUp = new CompositeDisposable(lineScroller,
                                               loader,
                                               Count,
                                               firstIndexMonitor,
                                               SelectionMonitor,
                                               MaximumChars,
                                               horizonalScrollArgs.SetAsComplete(),
                                               _userScrollRequested.SetAsComplete());
        }
Ejemplo n.º 8
0
        public InlineViewer([NotNull] InlineViewerArgs args,
                            [NotNull] IClipboardHandler clipboardHandler,
                            [NotNull] ISchedulerProvider schedulerProvider,
                            [NotNull] ISelectionMonitor selectionMonitor)
        {
            if (args == null)
            {
                throw new ArgumentNullException(nameof(args));
            }
            if (clipboardHandler == null)
            {
                throw new ArgumentNullException(nameof(clipboardHandler));
            }
            if (schedulerProvider == null)
            {
                throw new ArgumentNullException(nameof(schedulerProvider));
            }
            if (selectionMonitor == null)
            {
                throw new ArgumentNullException(nameof(selectionMonitor));
            }
            SelectionMonitor       = selectionMonitor;
            CopyToClipboardCommand = new Command(() => clipboardHandler.WriteToClipboard(selectionMonitor.GetSelectedText()));

            _isSettingScrollPosition = false;

            var lineProvider    = args.LineProvider;
            var selectedChanged = args.SelectedChanged;
            var pageSize        = this.WhenValueChanged(vm => vm.PageSize);
            var scrollSelected  = selectedChanged.Where(proxy => proxy != null)
                                  .CombineLatest(lineProvider, pageSize, (proxy, lp, pge) => new ScrollRequest(pge, (int)proxy.Start, true));

            var scrollUser = _userScrollRequested
                             .Where(x => !_isSettingScrollPosition)
                             .Select(request => new ScrollRequest(ScrollReason.User, request.PageSize, request.FirstIndex));

            var scroller = scrollSelected.Merge(scrollUser)
                           .ObserveOn(schedulerProvider.Background)
                           .DistinctUntilChanged();

            var lineScroller = new LineScroller(lineProvider, scroller);

            Count = lineProvider.Select(lp => lp.Count).ForBinding();

            //load lines into observable collection
            var loader = lineScroller.Lines.Connect()
                         .Transform(line => new LineProxy(line))
                         .Sort(SortExpressionComparer <LineProxy> .Ascending(proxy => proxy))
                         .ObserveOn(schedulerProvider.MainThread)
                         .Bind(out _data)
                         .Subscribe();

            // track first visible index[required to set scroll extent]
            var firstIndexMonitor = lineScroller.Lines.Connect()
                                    .Buffer(TimeSpan.FromMilliseconds(250)).FlattenBufferResult()
                                    .ToCollection()
                                    .Select(lines => lines.Count == 0 ? 0 : lines.Select(l => l.Index).Max() - lines.Count + 1)
                                    .ObserveOn(schedulerProvider.MainThread)
                                    .Subscribe(first =>
            {
                try
                {
                    _isSettingScrollPosition = true;
                    FirstIndex = first;
                }
                finally
                {
                    _isSettingScrollPosition = false;
                }
            });

            _cleanUp = new CompositeDisposable(lineScroller,
                                               loader,
                                               Count,
                                               firstIndexMonitor,
                                               SelectionMonitor,
                                               Disposable.Create(() =>
            {
                _userScrollRequested.OnCompleted();
            }));
        }
Ejemplo n.º 9
0
        public CreateViewModel(INorthwindRepository northwindRepository)
        {
            this.northwindRepository = northwindRepository;

            products        = new SourceCache <ProductOnStore, int>(p => p.ProductID);
            productsInOrder = new SourceCache <ProductInOrder, int>(p => p.ProductID);
            employees       = new SourceList <Employee>();
            customers       = new SourceList <Customer>();

            var canRemoveAllExecute = productsInOrder.CountChanged.
                                      Select(currentCountOfItems =>
            {
                if (currentCountOfItems == 0)
                {
                    return(false);
                }
                else
                {
                    return(true);
                }
            });

            productsInOrder.CountChanged.Subscribe(currentCount => { CountOfProductsInOrder = currentCount; });

            var canCreateOrderExecute = this.WhenAnyValue(vm => vm.SelectedCustomer, vm => vm.SelectedEmployee, vm => vm.CountOfProductsInOrder).
                                        Select(x =>
            {
                if (x.Item1 == null || x.Item2 == null || x.Item3 == 0)
                {
                    return(false);
                }
                return(true);
            });

            this.WhenAnyValue(vm => vm.SelectedCustomer, vm => vm.SelectedEmployee, vm => vm.CountOfProductsInOrder).
            Subscribe(x =>
            {
                if (x.Item1 != null || x.Item2 != null || x.Item3 != 0)
                {
                    OrderDate = DateTime.Now.ToLongDateString();
                }
                else
                {
                    OrderDate = "";
                }
            });

            products.Connect().
            OnItemAdded(a => MakeSubscribtion(a)).
            Filter(x => x.UnitsInStock != 0).
            Sort(SortExpressionComparer <ProductOnStore> .Ascending(item => item.ProductID)).
            ObserveOnDispatcher().
            Bind(out _productsInStore).
            Subscribe();

            productsInOrder.Connect().
            Sort(SortExpressionComparer <ProductInOrder> .Ascending(x => x.ProductID)).
            ObserveOnDispatcher().
            Bind(out _productsInOrder).
            OnItemAdded(x => SubscribeToChanges(x)).
            OnItemRemoved(y => RemoveProduct(y)).
            Subscribe();

            employees.Connect().
            ObserveOnDispatcher().
            Bind(out _employees).
            Subscribe();

            customers.Connect().
            ObserveOnDispatcher().
            Bind(out _customers).
            Subscribe();

            RemoveAllCommand   = ReactiveCommand.Create(RemoveAllCommandExecute, canRemoveAllExecute);
            CreateOrderCommand = ReactiveCommand.Create(CreateOrderExecute, canCreateOrderExecute);
        }
Ejemplo n.º 10
0
        public MainViewModel(
            ProviderViewModelFactory providerFactory,
            AuthViewModelFactory authFactory,
            IProviderStorage storage,
            IScheduler current,
            IScheduler main)
        {
            _storage = storage;
            _refresh = ReactiveCommand.CreateFromTask(
                storage.Refresh,
                outputScheduler: main);

            var providers = storage.Read();

            providers.Transform(x => providerFactory(x, authFactory(x)))
            .Sort(SortExpressionComparer <IProviderViewModel> .Descending(x => x.Created))
            .ObserveOn(RxApp.MainThreadScheduler)
            .StartWithEmpty()
            .Bind(out _providers)
            .Subscribe();

            _isLoading = _refresh
                         .IsExecuting
                         .ToProperty(this, x => x.IsLoading, scheduler: current);

            _isReady = _refresh
                       .IsExecuting
                       .Skip(1)
                       .Select(executing => !executing)
                       .ToProperty(this, x => x.IsReady, scheduler: current);

            providers.Where(changes => changes.Any())
            .ObserveOn(RxApp.MainThreadScheduler)
            .OnItemAdded(x => SelectedProvider   = Providers.FirstOrDefault())
            .OnItemRemoved(x => SelectedProvider = null)
            .Subscribe();

            var canRemove = this
                            .WhenAnyValue(x => x.SelectedProvider)
                            .Select(provider => provider != null);

            _remove = ReactiveCommand.CreateFromTask(
                () => storage.Remove(SelectedProvider.Id),
                canRemove);

            var canAddProvider = this
                                 .WhenAnyValue(x => x.SelectedSupportedType)
                                 .Select(type => !string.IsNullOrWhiteSpace(type));

            _add = ReactiveCommand.CreateFromTask(
                () => storage.Add(SelectedSupportedType),
                canAddProvider);

            _welcomeScreenVisible = this
                                    .WhenAnyValue(x => x.SelectedProvider)
                                    .Select(provider => provider == null)
                                    .ToProperty(this, x => x.WelcomeScreenVisible);

            _welcomeScreenCollapsed = this
                                      .WhenAnyValue(x => x.WelcomeScreenVisible)
                                      .Select(visible => !visible)
                                      .ToProperty(this, x => x.WelcomeScreenCollapsed);

            var canUnSelect = this
                              .WhenAnyValue(x => x.SelectedProvider)
                              .Select(provider => provider != null);

            _unselect = ReactiveCommand.Create(
                () => { SelectedProvider = null; },
                canUnSelect);

            Activator = new ViewModelActivator();
            this.WhenActivated(disposables =>
            {
                SelectedSupportedType = SupportedTypes.FirstOrDefault();
                _refresh.Execute()
                .Subscribe()
                .DisposeWith(disposables);
            });
        }
Ejemplo n.º 11
0
        public MainWindowViewModel()
        {
            List <IDto> dtos = new List <IDto>();

            AddressDto   address1   = new AddressDto(1);
            QuotationDto quotation1 = new QuotationDto(1, 1);
            QuotationDto quotation2 = new QuotationDto(1, 2);
            ProjectDto   project1   = new ProjectDto(1, 1, "1st");

            AddressDto   address2   = new AddressDto(2);
            QuotationDto quotation3 = new QuotationDto(2, 3);
            QuotationDto quotation4 = new QuotationDto(2, 4);
            ProjectDto   project2   = new ProjectDto(2, 2, "2nd");

            FolderDto quotFolder1 = new FolderDto(1, DtoType.Quotation);
            FolderDto projFolder1 = new FolderDto(1, DtoType.Project);
            FolderDto quotFolder2 = new FolderDto(2, DtoType.Quotation);
            FolderDto projFolder2 = new FolderDto(2, DtoType.Project);

            #region add to list
            dtos.Add(address1);
            dtos.Add(quotation1);
            dtos.Add(quotation2);
            dtos.Add(project1);
            dtos.Add(address2);
            dtos.Add(quotation3);
            dtos.Add(quotation4);
            dtos.Add(project2);
            dtos.Add(quotFolder1);
            dtos.Add(quotFolder2);
            dtos.Add(projFolder1);
            dtos.Add(projFolder2);
            #endregion

            Source.AddOrUpdate(dtos);

            bool DefaultPredicate(Node <IDto, string> node) => node.IsRoot;

            //bool DefaultPredicate(Node<IDto, string> node) => node.Item.DtoType == DtoType.Folder;

            var tree = Source.Connect()
                       .TransformToTree(dto => dto.GetMyParentId(), Observable.Return((Func <Node <IDto, string>, bool>)DefaultPredicate))
                       .Transform(n => new MyNode(n))
                       .Sort(SortExpressionComparer <MyNode> .Ascending(node =>
            {
                switch (node.Item.DtoType)
                {
                case DtoType.Address:
                    return(Convert.ToInt32((((int)node.Item.DtoType).ToString() + node.Item.AddressId.ToString())));

                case DtoType.Project:
                    return(Convert.ToInt32((((int)node.Item.DtoType).ToString() + node.Item.AddressId.ToString() + (node.Item as ProjectDto).ProjectId.ToString())));

                case DtoType.Quotation:
                    return(Convert.ToInt32((((int)node.Item.DtoType).ToString() + node.Item.AddressId.ToString() + (node.Item as QuotationDto).QuotationId.ToString())));

                default:
                    return(node.Item.AddressId);
                }
            }))
                       .Bind(out _mainItems)
                       .DisposeMany()
                       .Subscribe();

            AddSomethingCommand = new Command(AddSomethingNew);
            MoveCommand         = new Command(MoveProjectQuotation);
        }
        public StatisticService(INorthwindRepository northwindRepository)
        {
            this.northwindRepository = northwindRepository;

            productsList     = new SourceList <Product>();
            customersList    = new SourceList <Customer>();
            orderDetailsList = new SourceList <Order_Detail>();
            ordersList       = new SourceList <Order>();

            #region Customers statistics
            customersByCountries = customersList.Connect().
                                   GroupOn(customer => customer.Country).
                                   Transform(customersGroup => new CustomersByCountry()
            {
                CountryName = customersGroup.GroupKey, CustomersCount = customersGroup.List.Count
            }).
                                   ObserveOnDispatcher().
                                   Bind(out _customersByCountries);

            purchasesByCustomers = orderDetailsList.Connect().
                                   Transform(orderDetail => new { CompanyName = orderDetail.Order.Customer.CompanyName, PurchaseByOrderDetail = orderDetail.UnitPrice * orderDetail.Quantity }).
                                   GroupOn(orderDetail => orderDetail.CompanyName).
                                   Transform(groupOfOrderDetails => new PurchasesByCustomers()
            {
                CompanyName = groupOfOrderDetails.GroupKey, Purchases = groupOfOrderDetails.List.Items.Sum(a => a.PurchaseByOrderDetail)
            }).
                                   Sort(SortExpressionComparer <PurchasesByCustomers> .Descending(a => a.Purchases)).
                                   Top(10).
                                   ObserveOnDispatcher().
                                   Bind(out _purchasesByCustomers);
            #endregion

            #region Employees statistics
            salesByEmployees = orderDetailsList.Connect().
                               Transform(orderDetail => new { LastName = orderDetail.Order.Employee.LastName, SaleByOrderDetail = orderDetail.UnitPrice * orderDetail.Quantity }).
                               GroupOn(orderDetail => orderDetail.LastName).
                               Transform(groupOfOrderDetail => new EmployeeSales()
            {
                LastName = groupOfOrderDetail.GroupKey, Sales = groupOfOrderDetail.List.Items.Sum(a => a.SaleByOrderDetail)
            }).
                               ObserveOnDispatcher().
                               Sort(SortExpressionComparer <EmployeeSales> .Ascending(a => a.Sales)).
                               Bind(out _salesByEmployees);
            #endregion

            #region Products statistics
            productsByCategories = productsList.Connect().
                                   GroupOn(product => product.Category.CategoryName).
                                   Transform(groupOfProducts => new ProductsByCateogries()
            {
                CategoryName = groupOfProducts.GroupKey, NumberOfProducts = groupOfProducts.List.Count
            }).
                                   ObserveOnDispatcher().
                                   Bind(out _productsByCategories);
            #endregion

            #region Orders statistics
            ordersByCountries = ordersList.Connect().
                                GroupOn(order => order.Customer.Country).
                                Transform(groupOfOrders => new OrdersByCountry()
            {
                Country = groupOfOrders.GroupKey, NumberOfOrders = groupOfOrders.List.Count
            }).
                                ObserveOnDispatcher().
                                Top(10).
                                Bind(out _ordersByCountries);

            salesByCountries = orderDetailsList.Connect().Transform(orderDetail => new { Country = orderDetail.Order.Customer.Country, SaleByOrderDetail = orderDetail.UnitPrice * orderDetail.Quantity }).
                               GroupOn(orderDetail => orderDetail.Country).
                               Transform(groupOfOrderDetails => new SalesByCountry()
            {
                Country = groupOfOrderDetails.GroupKey, Sales = groupOfOrderDetails.List.Items.Sum(a => a.SaleByOrderDetail)
            }).
                               Sort(SortExpressionComparer <SalesByCountry> .Ascending(a => a.Sales)).
                               ObserveOnDispatcher().
                               Bind(out _salesByCountries);

            salesByCategories = orderDetailsList.Connect().
                                Transform(orderDetail => new { Category = orderDetail.Product.Category.CategoryName, SaleByOrderDetail = orderDetail.UnitPrice * orderDetail.Quantity }).
                                GroupOn(orderDetail => orderDetail.Category).
                                Transform(groupOfOrderDeatils => new SalesByCategory()
            {
                Category = groupOfOrderDeatils.GroupKey, Sales = groupOfOrderDeatils.List.Items.Sum(a => a.SaleByOrderDetail)
            }).
                                ObserveOnDispatcher().
                                Bind(out _salesByCategories);
            #endregion

            FillCollections();
        }
Ejemplo n.º 13
0
        public void DoesNotThrow2()
        {
            var cache      = new SourceCache <Data, int>(d => d.Id);
            var disposable = cache.Connect().Sort(new BehaviorSubject <IComparer <Data> >(SortExpressionComparer <Data> .Ascending(d => d.Id))).Subscribe();

            disposable.Dispose();
        }
Ejemplo n.º 14
0
        public LogEntryViewer(ILogEntryService logEntryService)
        {
            //build an observable filter
            var filter = this.WhenAnyValue(x => x.SearchText)
                         .Throttle(TimeSpan.FromMilliseconds(250))
                         .Select(BuildFilter);

            //filter, sort and populate reactive list.
            var shared = logEntryService.Items.Connect()
                         .Transform(le => new LogEntryProxy(le))
                         .DelayRemove(TimeSpan.FromSeconds(0.75), proxy => proxy.FlagForRemove())
                         .Publish();

            var loader = shared.Filter(filter)
                         .Sort(SortExpressionComparer <LogEntryProxy> .Descending(le => le.TimeStamp).ThenByDescending(l => l.Key), SortOptions.UseBinarySearch)
                         .ObserveOn(RxApp.MainThreadScheduler)
                         .Bind(Data)
                         .DisposeMany()
                         .Subscribe();

            //aggregate total items
            var summariser = shared
                             .QueryWhenChanged(items =>
            {
                var debug = items.Count(le => le.Level == LogLevel.Debug);
                var info  = items.Count(le => le.Level == LogLevel.Info);
                var warn  = items.Count(le => le.Level == LogLevel.Warning);
                var error = items.Count(le => le.Level == LogLevel.Error);
                return(new LogEntrySummary(debug, info, warn, error));
            })
                             .Subscribe(s => Summary = s);

            //manage user selection, delete items command
            var selectedItems = _selectionController.SelectedItems.Connect().Publish();

            //Build a message from selected items
            _deleteItemsText = selectedItems.QueryWhenChanged(query =>
            {
                if (query.Count == 0)
                {
                    return("Select log entries to delete");
                }
                if (query.Count == 1)
                {
                    return("Delete selected log entry?");
                }
                return($"Delete {query.Count} log entries?");
            })
                               .ToProperty(this, viewmodel => viewmodel.DeleteItemsText, "Select log entries to delete");


            //make a command out of selected items - enabling the command when there is a selection
            DeleteCommand = ReactiveCommand.Create(() =>
            {
                var toRemove = _selectionController.SelectedItems.Items.Select(proxy => proxy.Original).ToArray();
                _selectionController.Clear();
                logEntryService.Remove(toRemove);
            }, selectedItems.QueryWhenChanged(query => query.Count > 0));

            var connected      = selectedItems.Connect();
            var connectedItems = shared.Connect();

            _cleanUp = Disposable.Create(() =>
            {
                loader.Dispose();
                connected.Dispose();
                _deleteItemsText.Dispose();
                DeleteCommand.Dispose();
                _selectionController.Dispose();
                summariser.Dispose();
                connectedItems.Dispose();
            });
        }
Ejemplo n.º 15
0
        public FileTailerViewModel(ILogger logger, ISchedulerProvider schedulerProvider, FileInfo fileInfo)
        {
            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }
            if (schedulerProvider == null)
            {
                throw new ArgumentNullException(nameof(schedulerProvider));
            }
            if (fileInfo == null)
            {
                throw new ArgumentNullException(nameof(fileInfo));
            }

            var filterRequest = this.WhenValueChanged(vm => vm.SearchText).Throttle(TimeSpan.FromMilliseconds(125));
            var autoChanged   = this.WhenValueChanged(vm => vm.AutoTail);
            var scroller      = _userScrollRequested
                                .CombineLatest(autoChanged, (user, auto) =>
            {
                var mode = AutoTail ? ScrollingMode.Tail : ScrollingMode.User;
                return(new ScrollRequest(mode, user.PageSize, user.FirstIndex));
            })
                                .Sample(TimeSpan.FromMilliseconds(150))
                                .DistinctUntilChanged();

            var tailer = new FileTailer(fileInfo, filterRequest, scroller);


            //create user display for count line count
            var lineCounter = tailer.TotalLines.CombineLatest(tailer.MatchedLines, (total, matched) =>
            {
                return(total == matched
                    ? $"File has {total.ToString("#,###")} lines"
                    : $"Showing {matched.ToString("#,###")} of {total.ToString("#,###")} lines");
            })
                              .Subscribe(text => LineCountText = text);


            //load lines into observable collection
            var loader = tailer.Lines.Connect()
                         .Transform(line => new LineProxy(line))
                         .Sort(SortExpressionComparer <LineProxy> .Ascending(proxy => proxy.Number))
                         .ObserveOn(schedulerProvider.MainThread)
                         .Bind(out _data)
                         .Subscribe(changes => logger.Info($"Rows changed {changes.Adds} adds, {changes.Removes} removed"),
                                    ex => logger.Error(ex, "There is a problem with bind data"));


            //monitor matching lines and start index,
            var matchedLinesMonitor = tailer.MatchedLines
                                      .Subscribe(matched => MatchedLineCount = matched);

            //track first visible index
            var firstIndexMonitor = tailer.Lines.Connect()
                                    .QueryWhenChanged(lines => lines.Count == 0 ? 0 : lines.Select(l => l.Index).Min())
                                    .Subscribe(first => FirstIndex = first);


            _cleanUp = new CompositeDisposable(tailer,
                                               lineCounter,
                                               loader,
                                               firstIndexMonitor,
                                               matchedLinesMonitor,
                                               Disposable.Create(() =>
            {
                _userScrollRequested.OnCompleted();
            }));
        }
Ejemplo n.º 16
0
 private static IComparer <EntryModel> GetSorting(Func <EntryModel, IComparable> f)
 {
     return(SortExpressionComparer <EntryModel> .Ascending(f));
 }
Ejemplo n.º 17
0
        public FileTailerViewModel([NotNull] ILogger logger,
                                   [NotNull] ISchedulerProvider schedulerProvider,
                                   [NotNull] IFileWatcher fileWatcher,
                                   [NotNull] ISelectionMonitor selectionMonitor,
                                   [NotNull] IClipboardHandler clipboardHandler,
                                   [NotNull] ISearchInfoCollection searchInfoCollection,
                                   [NotNull] IInlineViewerFactory inlineViewerFactory,
                                   [NotNull] ISetting <GeneralOptions> generalOptions,
                                   [NotNull] IRecentSearchCollection recentSearchCollection,
                                   [NotNull] SearchHints searchHints)
        {
            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }
            if (schedulerProvider == null)
            {
                throw new ArgumentNullException(nameof(schedulerProvider));
            }
            if (fileWatcher == null)
            {
                throw new ArgumentNullException(nameof(fileWatcher));
            }
            if (selectionMonitor == null)
            {
                throw new ArgumentNullException(nameof(selectionMonitor));
            }
            if (clipboardHandler == null)
            {
                throw new ArgumentNullException(nameof(clipboardHandler));
            }
            if (searchInfoCollection == null)
            {
                throw new ArgumentNullException(nameof(searchInfoCollection));
            }
            if (inlineViewerFactory == null)
            {
                throw new ArgumentNullException(nameof(inlineViewerFactory));
            }
            if (generalOptions == null)
            {
                throw new ArgumentNullException(nameof(generalOptions));
            }
            if (searchHints == null)
            {
                throw new ArgumentNullException(nameof(searchHints));
            }

            SelectionMonitor       = selectionMonitor;
            SearchHints            = searchHints;
            CopyToClipboardCommand = new Command(() => clipboardHandler.WriteToClipboard(selectionMonitor.GetSelectedText()));
            SelectedItemsCount     = selectionMonitor.Selected.Connect().QueryWhenChanged(collection => collection.Count).ForBinding();
            SearchCollection       = new SearchCollection(searchInfoCollection, schedulerProvider);

            UsingDarkTheme = generalOptions.Value
                             .ObserveOn(schedulerProvider.MainThread)
                             .Select(options => options.Theme == Theme.Dark)
                             .ForBinding();

            HighlightTail = generalOptions.Value
                            .ObserveOn(schedulerProvider.MainThread)
                            .Select(options => options.HighlightTail)
                            .ForBinding();

            HighlightDuration = generalOptions.Value
                                .ObserveOn(schedulerProvider.MainThread)
                                .Select(options => new Duration(TimeSpan.FromSeconds(options.HighlightDuration)))
                                .ForBinding();

            //An observable which acts as a scroll command
            var autoChanged = this.WhenValueChanged(vm => vm.AutoTail);
            var scroller    = _userScrollRequested.CombineLatest(autoChanged, (user, auto) =>
            {
                var mode = AutoTail ? ScrollReason.Tail : ScrollReason.User;
                return(new ScrollRequest(mode, user.PageSize, user.FirstIndex));
            })
                              .ObserveOn(schedulerProvider.Background)
                              .DistinctUntilChanged();

            //tailer is the main object used to tail, scroll and filter in a file
            var lineScroller = new LineScroller(SearchCollection.Latest.ObserveOn(schedulerProvider.Background), scroller);

            //Add a complete file display [No search info here]
            var indexed = fileWatcher.Latest.Index()
                          .Replay(1).RefCount();

            IsLoading = indexed.Take(1).Select(_ => false).StartWith(true).ForBinding();
            searchInfoCollection.Add("<All>", indexed, SearchType.All);

            //command to add the current search to the tail collection
            KeepSearchCommand = new Command(() =>
            {
                var text   = SearchHints.SearchText;
                var latest = fileWatcher.Latest
                             .Search(s => s.Contains(text, StringComparison.OrdinalIgnoreCase))
                             .Replay(1).RefCount();

                searchInfoCollection.Add(text, latest);
                recentSearchCollection.Add(new RecentSearch(text));
                SearchHints.SearchText = string.Empty;
            }, () => SearchHints.SearchText.IsLongerThanOrEqualTo(3));

            //User feedback to show file size
            FileSizeText = fileWatcher.Latest.Select(fn => fn.Size)
                           .Select(size => size.FormatWithAbbreviation())
                           .DistinctUntilChanged()
                           .ForBinding();

            //User feedback to guide them whilst typing
            SearchHint = this.WhenValueChanged(vm => vm.SearchHints.SearchText)
                         .Select(text =>
            {
                if (string.IsNullOrEmpty(text))
                {
                    return("Type to search");
                }
                return(text.Length < 3 ? "Enter at least 3 characters" : "Hit enter to search");
            }).ForBinding();

            //load lines into observable collection
            var lineProxyFactory = new LineProxyFactory(new TextFormatter(searchInfoCollection));
            var loader           = lineScroller.Lines.Connect()
                                   .Transform(lineProxyFactory.Create, new ParallelisationOptions(ParallelType.Ordered, 5))
                                   .Sort(SortExpressionComparer <LineProxy> .Ascending(proxy => proxy))
                                   .ObserveOn(schedulerProvider.MainThread)
                                   .Bind(out _data)
                                   .DisposeMany()
                                   .Subscribe(changes => logger.Info($"Rows changed. {changes.Adds} adds, {changes.Removes} removed"),
                                              ex => logger.Error(ex, "There is a problem with bind data"));

            //monitor matching lines and start index,
            Count       = indexed.Select(latest => latest.Count).ForBinding();
            CountText   = indexed.Select(latest => $"{latest.Count.ToString("##,###")} lines").ForBinding();
            LatestCount = SearchCollection.Latest.Select(latest => latest.Count).ForBinding();

            //track first visible index
            var firstIndexMonitor = lineScroller.Lines.Connect()
                                    .Buffer(TimeSpan.FromMilliseconds(250)).FlattenBufferResult()
                                    .ToCollection()
                                    .Select(lines => lines.Count == 0 ? 0 : lines.Select(l => l.Index).Max() - lines.Count + 1)
                                    .ObserveOn(schedulerProvider.MainThread)
                                    .Subscribe(first => FirstIndex = first);

            //Create objects required for inline viewing
            var isUserDefinedChanged = SearchCollection.WhenValueChanged(sc => sc.Selected)
                                       .Select(selected => selected.IsUserDefined)
                                       .DistinctUntilChanged();

            var inlineViewerVisible = isUserDefinedChanged.CombineLatest(this.WhenValueChanged(vm => vm.ShowInline),
                                                                         (userDefined, showInline) => userDefined && showInline);

            CanViewInline       = isUserDefinedChanged.ForBinding();
            InlineViewerVisible = inlineViewerVisible.ForBinding();

            //return an empty line provider unless user is viewing inline - this saves needless trips to the file
            var inline = indexed.CombineLatest(inlineViewerVisible, (index, ud) => ud ? index : new EmptyLineProvider());

            InlineViewer = inlineViewerFactory.Create(inline, this.WhenValueChanged(vm => vm.SelectedItem), lineProxyFactory);

            _cleanUp = new CompositeDisposable(lineScroller,
                                               loader,
                                               firstIndexMonitor,
                                               IsLoading,
                                               Count,
                                               LatestCount,
                                               FileSizeText,
                                               SearchHint,
                                               SelectedItemsCount,
                                               CanViewInline,
                                               InlineViewer,
                                               InlineViewerVisible,
                                               SearchCollection,
                                               searchInfoCollection,
                                               HighlightTail,
                                               UsingDarkTheme,
                                               searchHints,
                                               Disposable.Create(() =>
            {
                _userScrollRequested.OnCompleted();
                SelectionMonitor?.Dispose();
            }));
        }
Ejemplo n.º 18
0
        public SearchOptionsViewModel(ISearchMetadataCollection metadataCollection,
                                      ISchedulerProvider schedulerProvider,
                                      SearchHints searchHints)
        {
            SearchHints = searchHints;
            //TODO: options for colour

            var swatches = new SwatchesProvider().Swatches;

            bool binding = false;

            var orderChanged = Observable.FromEventPattern <OrderChangedEventArgs>(
                h => PositionMonitor.OrderChanged += h,
                h => PositionMonitor.OrderChanged -= h)
                               .Throttle(TimeSpan.FromMilliseconds(125))
                               .Select(evt => evt.EventArgs)
                               .Where(args => args.PreviousOrder != null && args.NewOrder.Length == args.PreviousOrder.Length)
                               .Select(positionChangedArgs =>
            {
                //reprioritise filters and highlights
                return(positionChangedArgs.NewOrder
                       .OfType <SearchOptionsProxy>()
                       .Select((item, index) => new { Meta = (SearchMetadata)item, index })
                       //.Where(x => x.index != x.Meta.Position)
                       .Select(x => new SearchMetadata(x.Meta, x.index))
                       .ToArray());
            })
                               .Subscribe(positionChangedArgs =>
            {
                positionChangedArgs.ForEach(metadataCollection.AddorUpdate);
            });

            ReadOnlyObservableCollection <SearchOptionsProxy> data;

            var userOptions = metadataCollection.Metadata.Connect()
                              .WhereReasonsAre(ChangeReason.Add, ChangeReason.Remove) //ignore updates because we update from here
                              .Transform(meta => new SearchOptionsProxy(meta, swatches, m => metadataCollection.Remove(m.SearchText)))
                              .SubscribeMany(so =>
            {
                //when a value changes, write the original value back to the cache
                return(so.WhenAnyPropertyChanged()
                       .Select(_ => new SearchMetadata(so.Position, so.Text, so.Filter, so.Highlight, so.UseRegex, so.IgnoreCase))
                       .Subscribe(metadataCollection.AddorUpdate));
            })
                              .Sort(SortExpressionComparer <SearchOptionsProxy> .Ascending(proxy => proxy.Position))

                              .ObserveOn(schedulerProvider.MainThread)
                              .Bind(out data)
                              .Subscribe();

            Data = data;

            //command to add the current search to the tail collection
            var searchInvoker = SearchHints.SearchRequested.Subscribe(request =>
            {
                schedulerProvider.Background.Schedule(() =>
                {
                    metadataCollection.AddorUpdate(new SearchMetadata(metadataCollection.NextIndex(), request.Text, false, true, request.UseRegEx, true));
                });
            });


            _cleanUp = new CompositeDisposable(searchInvoker,
                                               userOptions,
                                               searchInvoker,
                                               orderChanged);
        }
Ejemplo n.º 19
0
        public Indexer([NotNull] IObservable <FileSegmentCollection> fileSegments,
                       int compression = 10,
                       int tailSize    = 1000000,
                       int sizeOfFileAtWhichThereIsAbsolutelyNoPointInIndexing = 250000000,
                       Encoding encoding    = null,
                       IScheduler scheduler = null)
        {
            if (fileSegments == null)
            {
                throw new ArgumentNullException(nameof(fileSegments));
            }

            //TODO: When File segment has got smaller => roll-over [do something about it]

            scheduler = scheduler ?? Scheduler.Default;

            var shared = fileSegments.Replay(1).RefCount();

            //1. Get information from segment info
            var infoSubscriber = shared.Select(segments => segments.Info)
                                 .Take(1)
                                 .Subscribe(info =>
            {
                Info     = info;
                Encoding = encoding ?? info.GetEncoding();
            });

            //2. create  a resulting index object from the collection of index fragments
            Result = _indicies
                     .Connect()
                     .Sort(SortExpressionComparer <Index> .Ascending(si => si.Start))
                     .ToCollection()
                     .Scan((IndexCollection)null, (previous, notification) => new IndexCollection(notification, previous, Info, Encoding))
                     .Replay(1).RefCount();


            //3. Scan the tail so results can be returned quickly
            var tailScanner = shared.Select(segments => segments.Tail).DistinctUntilChanged()
                              .Scan((Index)null, (previous, current) =>
            {
                if (previous == null)
                {
                    var initial = Scan(current.Start, -1, 1);
                    return(initial ?? new Index(0, 0, 0, 0, IndexType.Tail));
                }
                var latest = Scan(previous.End, -1, 1);
                return(latest == null ? null : new Index(latest, previous));
            })
                              .Where(tail => tail != null)
                              .Replay(1).RefCount();

            //4. estimate =
            var tailSubscriber = tailScanner.Subscribe(tail =>
            {
                _indicies.Edit(innerList =>
                {
                    var existing = innerList.FirstOrDefault(si => si.Type == IndexType.Tail);
                    if (existing != null)
                    {
                        innerList.Remove(existing);
                    }
                    innerList.Add(tail);
                });
            });


            //Scan the remainer of the file
            var headSubscriber = tailScanner.FirstAsync()
                                 .Subscribe(tail =>
            {
                if (tail.Start == 0)
                {
                    return;
                }

                //Need iterate one at a time through the
                var estimateLines = EstimateNumberOfLines(tail, Info);
                var estimate      = new Index(0, tail.Start, compression, estimateLines, IndexType.Page);
                _indicies.Add(estimate);

                //keep it as an estimate for files over 250 meg [for now]
                if (tail.Start > sizeOfFileAtWhichThereIsAbsolutelyNoPointInIndexing)
                {
                    return;
                }

                //todo: index first and last segment for large sized file

                scheduler.Schedule(() =>
                {
                    var actual = Scan(0, tail.Start, compression);
                    _indicies.Edit(innerList =>
                    {
                        innerList.Remove(estimate);
                        innerList.Add(actual);
                    });
                });
            });


            _cleanUp = new CompositeDisposable(infoSubscriber, _indicies, tailSubscriber, tailSubscriber, headSubscriber);
        }
Ejemplo n.º 20
0
        public SearchOptionsViewModel(ISearchMetadataCollection metadataCollection,
                                      ISearchMetadataFactory searchMetadataFactory,
                                      ISchedulerProvider schedulerProvider,
                                      IColourProvider colourProvider,
                                      IIconProvider iconsProvider,
                                      SearchHints searchHints)
        {
            SearchHints = searchHints;

            var proxyItems = metadataCollection.Metadata.Connect()
                             .WhereReasonsAre(ChangeReason.Add, ChangeReason.Remove) //ignore updates because we update from here
                             .Transform(meta =>
            {
                return(new SearchOptionsProxy(meta,
                                              colourProvider,
                                              new IconSelector(iconsProvider, schedulerProvider),
                                              m => metadataCollection.Remove(m.SearchText),
                                              iconsProvider.DefaultIconSelector,
                                              Id));
            })
                             .SubscribeMany(so =>
            {
                //when a value changes, write the original value back to the cache
                return(so.WhenAnyPropertyChanged()
                       .Select(_ => (SearchMetadata)so)
                       .Subscribe(metadataCollection.AddorUpdate));
            })
                             .AsObservableCache();

            var monitor = MonitorPositionalChanges()
                          .Subscribe(metadataCollection.Add);


            //load data onto grid
            var collection = new ObservableCollectionExtended <SearchOptionsProxy>();

            var userOptions = proxyItems.Connect()
                              .Sort(SortExpressionComparer <SearchOptionsProxy> .Ascending(proxy => proxy.Position))
                              .ObserveOn(schedulerProvider.MainThread)
                              //force reset for each new or removed item dues to a bug in the underlying dragablz control which inserts in an incorrect position
                              .Bind(collection, new ObservableCollectionAdaptor <SearchOptionsProxy, string>(0))
                              .DisposeMany()
                              .Subscribe();

            Data = new ReadOnlyObservableCollection <SearchOptionsProxy>(collection);

            //command to add the current search to the tail collection
            var searchInvoker = SearchHints.SearchRequested.Subscribe(request =>
            {
                schedulerProvider.Background.Schedule(() =>
                {
                    var meta = searchMetadataFactory.Create(request.Text,
                                                            request.UseRegEx,
                                                            metadataCollection.NextIndex(),
                                                            false);
                    metadataCollection.AddorUpdate(meta);
                });
            });

            _cleanUp = new CompositeDisposable(searchInvoker,
                                               userOptions,
                                               searchInvoker,
                                               monitor,
                                               SearchHints);
        }
Ejemplo n.º 21
0
        public void RebuildGrid()
        {
            Monik?.ApplicationVerbose("Matrix.RebuildGrid started");

            MainGrid.Children.Clear();

            if (Rows == null || Columns == null ||
                Rows.Count == 0 || Columns.Count == 0)
            {
                Monik?.ApplicationVerbose("Matrix.RebuildGrid skip func");
                return;
            }

            var columns     = Columns.ToList();
            int columnCount = Columns.Count;
            var rows        = Rows.ToList();
            int rowCount    = Rows.Count;

            //////////////////
            // 1. Fill columns
            //////////////////
            MainGrid.ColumnDefinitions.Clear();
            // rows header
            MainGrid.ColumnDefinitions.Add(
                new ColumnDefinition {
                Width = new GridLength(30, GridUnitType.Pixel)
            });

            // columns
            for (int i = 0; i < columnCount; i++)
            {
                var it = columns[i];
                Monik?.ApplicationVerbose($"Matrix.RebuildGrid add column {it.Id}::{it.Name}::{it.Order}");

                var cd = new ColumnDefinition();
                cd.DataContext = it;
                cd.Width       = new GridLength(it.Size / 10.0, GridUnitType.Star);
                MainGrid.ColumnDefinitions.Add(cd);

                PropertyDescriptor pd = DependencyPropertyDescriptor.FromProperty(ColumnDefinition.WidthProperty, typeof(ColumnDefinition));
                pd.AddValueChanged(cd, new EventHandler(ColumnWidthPropertyChanged));

                ContentControl cc = new ContentControl();
                cc.Content         = it;
                cc.MouseMove      += Head_MouseMove;
                cc.ContextMenu     = HeadContextMenu;
                cc.ContentTemplate = (DataTemplate)this.Resources["DefaultHorizontalHeaderTemplate"];
                MainGrid.Children.Add(cc);

                // Update number of Cards in Column
                CardsObservable
                .Filter(x => x.ColumnDeterminant == it.Id)
                .ToCollection()
                .Subscribe(x => it.CurNumberOfCards = x.Count());

                // dont draw excess splitter
                if (i < columnCount - 1)
                {
                    MainGrid.Children.Add(BuildVerticalSpliter(i, rowCount));
                }

                Grid.SetColumn(cc, i + 1);
                Grid.SetRow(cc, 0);
            }

            ///////////////
            // 2. Fill rows
            ///////////////
            MainGrid.RowDefinitions.Clear();
            // columns header
            MainGrid.RowDefinitions.Add(
                new RowDefinition {
                Height = new GridLength(30, GridUnitType.Pixel)
            });

            // rows
            for (int i = 0; i < rowCount; i++)
            {
                var it = rows[i];
                Monik?.ApplicationVerbose($"Matrix.RebuildGrid add row {it.Id}::{it.Name}::{it.Order}");

                var rd = new RowDefinition();
                rd.DataContext = it;
                rd.Height      = new GridLength(it.Size / 10.0, GridUnitType.Star);
                MainGrid.RowDefinitions.Add(rd);

                PropertyDescriptor pd = DependencyPropertyDescriptor.FromProperty(RowDefinition.HeightProperty, typeof(RowDefinition));
                pd.AddValueChanged(rd, new EventHandler(RowWidthPropertyChanged));

                ContentControl cc = new ContentControl();
                cc.Content         = it;
                cc.MouseMove      += Head_MouseMove;
                cc.ContextMenu     = HeadContextMenu;
                cc.ContentTemplate = (DataTemplate)this.Resources["DefaulVerticalHeaderTemplate"];
                MainGrid.Children.Add(cc);

                // Update number of Cards in Row
                CardsObservable
                .Filter(x => x.RowDeterminant == it.Id)
                .ToCollection()
                .Subscribe(x => it.CurNumberOfCards = x.Count());

                // dont draw excess splitter
                if (i < rowCount - 1)
                {
                    MainGrid.Children.Add(BuildHorizontalSpliter(i, columnCount));
                }

                Grid.SetColumn(cc, 0);
                Grid.SetRow(cc, i + 1);
            }

            ////////////////////////
            // 3. Fill Intersections
            ////////////////////////
            for (int i = 0; i < Columns.Count; i++)
            {
                for (int j = 0; j < Rows.Count; j++)
                {
                    int colDet = columns[i].Id;
                    int rowDet = rows[j].Id;

                    CardsObservable
                    .Filter(x => x.ColumnDeterminant == colDet && x.RowDeterminant == rowDet)
                    .Sort(SortExpressionComparer <ICard> .Ascending(c => c.Order))
                    .ObserveOnDispatcher()
                    .Bind(out ReadOnlyObservableCollection <ICard> intersectionCards)
                    .Subscribe();

                    Intersection cell = new Intersection(this)
                    {
                        DataContext       = this,
                        ColumnDeterminant = colDet,
                        RowDeterminant    = rowDet,
                        SelfCards         = intersectionCards
                    };

                    MainGrid.Children.Add(cell);
                    Grid.SetColumn(cell, i + 1);
                    Grid.SetColumnSpan(cell, 1);
                    Grid.SetRow(cell, j + 1);
                    Grid.SetRowSpan(cell, 1);
                }
            }

            Monik?.ApplicationVerbose("Matrix.RebuildGrid finished");
        }
Ejemplo n.º 22
0
        public SearchListViewModel(IDrinkService drinkService)
        {
            _drinkDataService = drinkService;

            Func <ItemViewModel, bool> search(string searchTerm) =>
            viewModel =>
            {
                if (string.IsNullOrEmpty(searchTerm))
                {
                    return(true);
                }

                var lower = searchTerm.ToLower();
                return(viewModel.Title.ToLower().Contains(lower) ||
                       (viewModel.Description?.ToLower().Contains(lower) ?? false));
            };

            var searchChanged =
                this.WhenAnyValue(x => x.SearchText)
                .Throttle(TimeSpan.FromMilliseconds(800), RxApp.TaskpoolScheduler)
                .DistinctUntilChanged()
                .Select(search);

            var items =
                _drinkDataService
                .ChangeSet
                .Transform(x => new ItemViewModel
            {
                Id          = x.Id,
                Title       = x.Title,
                Type        = x.Type,
                Description = x.Description
            });

            items
            .MergeMany((item, id) => item.Remove)
            .InvokeCommand(this, x => x.Remove)
            .DisposeWith(Garbage);

            var filter = items
                         .AutoRefresh(x => x.Id)
                         .DeferUntilLoaded()
                         .Filter(searchChanged);

            filter
            .Sort(SortExpressionComparer <ItemViewModel>
                  .Descending(x => x.Type)
                  .ThenByAscending(x => x.Id))
            .Bind(out _items)
            .DisposeMany()
            .Subscribe()
            .DisposeWith(Garbage);

            Add     = ReactiveCommand.CreateFromObservable <EventArgs, Unit>(ExecuteAdd);
            Refresh = ReactiveCommand.CreateFromObservable <EventArgs, Unit>(ExecuteRefresh);
            Remove  = ReactiveCommand.CreateFromObservable(ExecuteRemove, Observable.Return(true));

            this.WhenAnyObservable(x => x.Refresh.IsExecuting)
            .StartWith(false)
            .DistinctUntilChanged()
            .ToProperty(this, nameof(IsRefreshing), out _isRefreshing)
            .DisposeWith(Garbage);
        }
Ejemplo n.º 23
0
        private void InitializeBindings()
        {
            #region Log views
            var logViewsSharedSource = LogViewsSourceCache.Connect();
            logViewsSharedSource
            .AutoRefresh(l => l.DateLastOpened)
            .Sort(SortExpressionComparer <LogView> .Descending(l => l.DateLastOpened))
            .ObserveOnDispatcher()
            .Bind(out var logViews)
            .Subscribe();
            logViewsSharedSource
            .AutoRefresh(l => l.DateLastOpened)
            .Sort(SortExpressionComparer <LogView> .Descending(l => l.DateLastOpened))
            .Top(_numberInRecents)
            .ObserveOnDispatcher()
            .Bind(out var recentlyUsedLogViews)
            .Subscribe();
            logViewsSharedSource
            .AutoRefresh(l => l.DateLastOpened)
            .AutoRefresh(l => l.IsPinned)
            .Filter(l => l.IsPinned)
            .Sort(SortExpressionComparer <LogView> .Descending(l => l.DateLastOpened))
            .ObserveOnDispatcher()
            .Bind(out var pinnedLogViews)
            .Subscribe();

            LogViews             = logViews;
            RecentlyUsedLogViews = recentlyUsedLogViews;
            PinnedLogViews       = pinnedLogViews;
            #endregion

            #region Log files
            var logFilesSharedSource = LogFilesSourceCache.Connect();

            logFilesSharedSource
            .AutoRefresh(l => l.DateLastOpened)
            .Sort(SortExpressionComparer <LogFile> .Descending(l => l.DateLastOpened))
            .ObserveOnDispatcher()
            .Bind(out var allLogFiles)
            .Subscribe();
            logFilesSharedSource
            .AutoRefresh(l => l.DateLastOpened)
            .Sort(SortExpressionComparer <LogFile> .Descending(l => l.DateLastOpened))
            .Top(_numberInRecents)
            .ObserveOnDispatcher()
            .Bind(out var recentlyUsedLogFiles)
            .Subscribe();
            logFilesSharedSource
            .AutoRefresh(l => l.IsPinned)
            .AutoRefresh(l => l.DateLastOpened)
            .Filter(l => l.IsPinned)
            .Sort(SortExpressionComparer <LogFile> .Descending(l => l.DateLastOpened))
            .ObserveOnDispatcher()
            .Bind(out var pinnedLogFiles)
            .Subscribe();

            LogFiles             = allLogFiles;
            RecentlyUsedLogFiles = recentlyUsedLogFiles;
            PinnedLogFiles       = pinnedLogFiles;
            #endregion

            #region Databases
            var databasesSharedSource = DatabasesSourceCache.Connect();

            databasesSharedSource
            .ObserveOnDispatcher()
            .Bind(out var databases)
            .Subscribe();
            databasesSharedSource
            .AutoRefresh(d => d.DateLastOpened)
            .Sort(SortExpressionComparer <Database> .Descending(d => d.DateLastOpened))
            .Top(_numberInRecents)
            .ObserveOnDispatcher()
            .Bind(out var recentlyUsedDatabases)
            .Subscribe();
            databasesSharedSource
            .AutoRefresh(l => l.IsPinned)
            .AutoRefresh(d => d.DateLastOpened)
            .Filter(l => l.IsPinned)
            .Sort(SortExpressionComparer <Database> .Descending(d => d.DateLastOpened))
            .ObserveOnDispatcher()
            .Bind(out var pinnedDatabases)
            .Subscribe();

            Databases             = databases;
            RecentlyUsedDatabases = recentlyUsedDatabases;
            PinnedDatabases       = pinnedDatabases;
            #endregion

            #region Searchable openable objects
            //TODO: Right now if item added to source caches, this source cache won't see it. Maybe do a join?
            //TODO: Need to open search popup when user clicks on search box if there's text in the search box.
            AllOpenableObjectsSourceCache.AddOrUpdate(LogViewsSourceCache.Items);
            AllOpenableObjectsSourceCache.AddOrUpdate(LogFilesSourceCache.Items);
            AllOpenableObjectsSourceCache.AddOrUpdate(DatabasesSourceCache.Items);


            AllOpenableObjectsSourceCache.Connect()
            .AutoRefresh(d => SearchTerm)
            .Filter(o => string.IsNullOrEmpty(SearchTerm) || o.Identifier.ToLower().Contains(SearchTerm.ToLower()))
            .ObserveOnDispatcher()
            .Bind(out var searchableOpenableObjects)
            .Subscribe();
            SearchableOpenableObjects = searchableOpenableObjects;
            #endregion
        }
Ejemplo n.º 24
0
        public SearchOptionsViewModel(ISearchMetadataCollection metadataCollection,
                                      ISearchMetadataFactory searchMetadataFactory,
                                      ISchedulerProvider schedulerProvider,
                                      IColourProvider colourProvider,
                                      IIconProvider iconsProvider,
                                      ITextAssociationCollection textAssociationCollection,
                                      SearchHints searchHints)
        {
            SearchHints = searchHints;

            var proxyItems = metadataCollection.Metadata.Connect()
                             .WhereReasonsAre(ChangeReason.Add, ChangeReason.Remove) //ignore updates because we update from here
                             .Transform(meta =>
            {
                return(new SearchOptionsProxy(meta,
                                              colourProvider,
                                              new IconSelector(iconsProvider, schedulerProvider),
                                              m => metadataCollection.Remove(m.SearchText),
                                              iconsProvider.DefaultIconSelector,
                                              Id));
            })
                             .SubscribeMany(so =>
            {
                //when a value changes, write the original value back to the metadata collection
                var anyPropertyHasChanged = so.WhenAnyPropertyChanged()
                                            .Select(_ => (SearchMetadata)so)
                                            .Subscribe(metadataCollection.AddorUpdate);

                //when an icon or colour has changed we need to record user choice so
                //the same choice can be used again
                var iconChanged       = so.WhenValueChanged(proxy => proxy.IconKind, false).ToUnit();
                var colourChanged     = so.WhenValueChanged(proxy => proxy.HighlightHue, false).ToUnit();
                var ignoreCaseChanged = so.WhenValueChanged(proxy => proxy.IgnoreCase, false).ToUnit();

                var textAssociationChanged = iconChanged.Merge(colourChanged).Merge(ignoreCaseChanged)
                                             .Throttle(TimeSpan.FromMilliseconds(250))
                                             .Select(_ => new TextAssociation(so.Text, so.IgnoreCase, so.UseRegex, so.HighlightHue.Swatch, so.IconKind.ToString(), so.HighlightHue.Name, DateTime.Now))
                                             .Subscribe(textAssociationCollection.MarkAsChanged);

                return(new CompositeDisposable(anyPropertyHasChanged, textAssociationChanged));
            })
                             .AsObservableCache();

            var monitor = MonitorPositionalChanges()
                          .Subscribe(metadataCollection.Add);


            //load data onto grid
            var collection = new ObservableCollectionExtended <SearchOptionsProxy>();

            var userOptions = proxyItems.Connect()
                              .Sort(SortExpressionComparer <SearchOptionsProxy> .Ascending(proxy => proxy.Position))
                              .ObserveOn(schedulerProvider.MainThread)
                              //force reset for each new or removed item dues to a bug in the underlying dragablz control which inserts in an incorrect position
                              .Bind(collection, new ObservableCollectionAdaptor <SearchOptionsProxy, string>(0))
                              .DisposeMany()
                              .Subscribe();

            Data = new ReadOnlyObservableCollection <SearchOptionsProxy>(collection);

            //command to add the current search to the tail collection
            var searchInvoker = SearchHints.SearchRequested
                                .ObserveOn(schedulerProvider.Background)
                                .Subscribe(request =>
            {
                var meta = searchMetadataFactory.Create(request.Text,
                                                        request.UseRegEx,
                                                        metadataCollection.NextIndex(),
                                                        false);
                metadataCollection.AddorUpdate(meta);
            });

            _cleanUp = new CompositeDisposable(searchInvoker,
                                               userOptions,
                                               searchInvoker,
                                               monitor,
                                               SearchHints);
        }
Ejemplo n.º 25
0
        public LogEntryViewer(ILogEntryService logEntryService)
        {
            _logEntryService = logEntryService;

            //apply filter when search text has changed
            var filterApplier = this.WhenAnyValue(x => x.SearchText)
                                .Throttle(TimeSpan.FromMilliseconds(250))
                                .Select(BuildFilter)
                                .Subscribe(_filter.Change);


            //filter, sort and populate reactive list.
            var loader = logEntryService.Items.Connect()
                         .Transform(le => new LogEntryProxy(le))
                         .DelayRemove(TimeSpan.FromSeconds(0.75), proxy =>
            {
                proxy.FlagForRemove();
                _selectionController.DeSelect(proxy);
            })
                         .Filter(_filter)
                         .Sort(SortExpressionComparer <LogEntryProxy> .Descending(le => le.TimeStamp).ThenByDescending(l => l.Key), SortOptimisations.ComparesImmutableValuesOnly)
                         .ObserveOn(RxApp.MainThreadScheduler)
                         .Bind(_data)
                         .DisposeMany()
                         .Subscribe();

            //aggregate total items
            var summariser = logEntryService.Items.Connect()
                             .QueryWhenChanged(query =>
            {
                var items = query.Items.ToList();
                var debug = items.Count(le => le.Level == LogLevel.Debug);
                var info  = items.Count(le => le.Level == LogLevel.Info);
                var warn  = items.Count(le => le.Level == LogLevel.Warning);
                var error = items.Count(le => le.Level == LogLevel.Error);
                return(new LogEntrySummary(debug, info, warn, error));
            })
                             .Subscribe(s => Summary = s);

            //manage user selection, delete items command
            var selectedItems = _selectionController.SelectedItems.Connect().Publish();

            //Build a message from selected items
            _deleteItemsText = selectedItems.QueryWhenChanged(query =>
            {
                if (query.Count == 0)
                {
                    return("Select log entries to delete");
                }
                if (query.Count == 1)
                {
                    return("Delete selected log entry?");
                }
                return(string.Format("Delete {0} log entries?", query.Count));
            })
                               .ToProperty(this, viewmodel => viewmodel.DeleteItemsText, "Select log entries to delete");


            //make a command out of selected items - enabling the command when there is a selection
            _deleteCommand = selectedItems
                             .QueryWhenChanged(query => query.Count > 0)
                             .ToCommand();

            //Assign action when the command is invoked
            var commandInvoker = this.WhenAnyObservable(x => x.DeleteCommand)
                                 .Subscribe(_ =>
            {
                var toRemove = _selectionController.SelectedItems.Items.Select(proxy => proxy.Key).ToArray();
                _logEntryService.Remove(toRemove);
            });

            var connected = selectedItems.Connect();

            _cleanUp = Disposable.Create(() =>
            {
                loader.Dispose();
                filterApplier.Dispose();
                _filter.Dispose();
                connected.Dispose();
                _deleteItemsText.Dispose();
                _deleteCommand.Dispose();
                commandInvoker.Dispose();
                _selectionController.Dispose();
                summariser.Dispose();
            });
        }
 // TODO: use some LINQ to make more fluent OR use IObservable<IComparer> to use .Sort overload
 protected IObservable <ISortedChangeSet <TrackViewModel, uint> > Sort(IObservable <IChangeSet <TrackViewModel, uint> > trackViewModelsChangesFlow)
 {
     return(trackViewModelsChangesFlow.Sort(SortExpressionComparer <TrackViewModel> .Descending(vm => vm.AddedToLibraryDateTime)));
 }
Ejemplo n.º 27
0
        public TailViewModel([NotNull] ILogger logger,
                             [NotNull] ISchedulerProvider schedulerProvider,
                             [NotNull] IFileWatcher fileWatcher,
                             [NotNull] ISelectionMonitor selectionMonitor,
                             [NotNull] IClipboardHandler clipboardHandler,
                             [NotNull] ISearchInfoCollection searchInfoCollection,
                             [NotNull] IInlineViewerFactory inlineViewerFactory,
                             [NotNull] ISetting <GeneralOptions> generalOptions,
                             [NotNull] ISearchMetadataCollection searchMetadataCollection,
                             [NotNull] IStateBucketService stateBucketService,
                             [NotNull] SearchOptionsViewModel searchOptionsViewModel,
                             //ISearchStateToMetadataMapper metadataMapper,
                             [NotNull] SearchHints searchHints,
                             [NotNull]  ITailViewStateControllerFactory tailViewStateControllerFactory)
        {
            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }
            if (schedulerProvider == null)
            {
                throw new ArgumentNullException(nameof(schedulerProvider));
            }
            if (fileWatcher == null)
            {
                throw new ArgumentNullException(nameof(fileWatcher));
            }
            if (selectionMonitor == null)
            {
                throw new ArgumentNullException(nameof(selectionMonitor));
            }
            if (clipboardHandler == null)
            {
                throw new ArgumentNullException(nameof(clipboardHandler));
            }
            if (searchInfoCollection == null)
            {
                throw new ArgumentNullException(nameof(searchInfoCollection));
            }
            if (inlineViewerFactory == null)
            {
                throw new ArgumentNullException(nameof(inlineViewerFactory));
            }
            if (generalOptions == null)
            {
                throw new ArgumentNullException(nameof(generalOptions));
            }
            if (searchMetadataCollection == null)
            {
                throw new ArgumentNullException(nameof(searchMetadataCollection));
            }
            if (stateBucketService == null)
            {
                throw new ArgumentNullException(nameof(stateBucketService));
            }
            if (searchOptionsViewModel == null)
            {
                throw new ArgumentNullException(nameof(searchOptionsViewModel));
            }
            if (searchHints == null)
            {
                throw new ArgumentNullException(nameof(searchHints));
            }

            Name                     = fileWatcher.FullName;
            SelectionMonitor         = selectionMonitor;
            SearchOptions            = searchOptionsViewModel;
            SearchHints              = searchHints;
            SearchCollection         = new SearchCollection(searchInfoCollection, schedulerProvider);
            CopyToClipboardCommand   = new Command(() => clipboardHandler.WriteToClipboard(selectionMonitor.GetSelectedText()));
            OpenFileCommand          = new Command(() => Process.Start(fileWatcher.FullName));
            OpenFolderCommand        = new Command(() => Process.Start(fileWatcher.Folder));
            SearchMetadataCollection = searchMetadataCollection;
            UsingDarkTheme           = generalOptions.Value
                                       .ObserveOn(schedulerProvider.MainThread)
                                       .Select(options => options.Theme == Theme.Dark)
                                       .ForBinding();

            HighlightTail = generalOptions.Value
                            .ObserveOn(schedulerProvider.MainThread)
                            .Select(options => options.HighlightTail)
                            .ForBinding();

            HighlightDuration = generalOptions.Value
                                .ObserveOn(schedulerProvider.MainThread)
                                .Select(options => new Duration(TimeSpan.FromSeconds(options.HighlightDuration)))
                                .ForBinding();

            _stateProvider = new TailViewPersister(this);

            //this controller responsible for loading and persisting user search stuff
            var stateController = tailViewStateControllerFactory.Create(this);

            //An observable which acts as a scroll command
            var autoChanged = this.WhenValueChanged(vm => vm.AutoTail);
            var scroller    = _userScrollRequested.CombineLatest(autoChanged, (user, auto) =>
            {
                var mode = AutoTail ? ScrollReason.Tail : ScrollReason.User;
                return(new ScrollRequest(mode, user.PageSize, user.FirstIndex));
            })
                              .Do(x => logger.Info("Scrolling to {0}/{1}", x.FirstIndex, x.PageSize))
                              .DistinctUntilChanged();

            FileStatus = fileWatcher.Status.ForBinding();

            //command to add the current search to the tail collection
            var searchInvoker = SearchHints.SearchRequested.Subscribe(request =>
            {
                searchInfoCollection.Add(request.Text, request.UseRegEx);
            });

            //User feedback to show file size
            FileSizeText = fileWatcher.Latest.Select(fn => fn.Size)
                           .Select(size => size.FormatWithAbbreviation())
                           .DistinctUntilChanged()
                           .ForBinding();

            //tailer is the main object used to tail, scroll and filter in a file
            var lineScroller = new LineScroller(SearchCollection.Latest.ObserveOn(schedulerProvider.Background), scroller);

            //load lines into observable collection
            var lineProxyFactory = new LineProxyFactory(new TextFormatter(searchMetadataCollection), new LineMatches(searchMetadataCollection));
            var loader           = lineScroller.Lines.Connect()

                                   .LogChanges(logger, "Received")
                                   .Transform(lineProxyFactory.Create, new ParallelisationOptions(ParallelType.Ordered, 3))
                                   .LogChanges(logger, "Sorting")
                                   .Sort(SortExpressionComparer <LineProxy> .Ascending(proxy => proxy))
                                   .ObserveOn(schedulerProvider.MainThread)
                                   .Bind(out _data, 100)
                                   .LogChanges(logger, "Bound")
                                   .DisposeMany()
                                   .LogErrors(logger)
                                   .Subscribe();


            //monitor matching lines and start index,
            Count       = searchInfoCollection.All.Select(latest => latest.Count).ForBinding();
            CountText   = searchInfoCollection.All.Select(latest => $"{latest.Count.ToString("##,###")} lines").ForBinding();
            LatestCount = SearchCollection.Latest.Select(latest => latest.Count).ForBinding();

            ////track first visible index
            var firstIndexMonitor = lineScroller.Lines.Connect()
                                    .Buffer(TimeSpan.FromMilliseconds(25)).FlattenBufferResult()
                                    .ToCollection()
                                    .Select(lines => lines.Count == 0 ? 0 : lines.Select(l => l.Index).Max() - lines.Count + 1)
                                    .ObserveOn(schedulerProvider.MainThread)
                                    .Subscribe(first =>
            {
                FirstIndex = first;
            });

            //Create objects required for inline viewing
            var isUserDefinedChanged = SearchCollection.WhenValueChanged(sc => sc.Selected)
                                       .Where(selected => selected != null)
                                       .Select(selected => selected.IsUserDefined)
                                       .DistinctUntilChanged()
                                       .Replay(1)
                                       .RefCount();

            var inlineViewerVisible = isUserDefinedChanged.CombineLatest(this.WhenValueChanged(vm => vm.ShowInline),
                                                                         (userDefined, showInline) => userDefined && showInline);

            CanViewInline       = isUserDefinedChanged.ForBinding();
            InlineViewerVisible = inlineViewerVisible.ForBinding();

            //return an empty line provider unless user is viewing inline - this saves needless trips to the file
            var inline = searchInfoCollection.All.CombineLatest(inlineViewerVisible, (index, ud) => ud ? index : new EmptyLineProvider());

            InlineViewer = inlineViewerFactory.Create(inline, this.WhenValueChanged(vm => vm.SelectedItem), lineProxyFactory);

            _cleanUp = new CompositeDisposable(lineScroller,
                                               loader,
                                               firstIndexMonitor,
                                               FileStatus,
                                               Count,
                                               LatestCount,
                                               FileSizeText,
                                               CanViewInline,
                                               InlineViewer,
                                               InlineViewerVisible,
                                               SearchCollection,
                                               searchInfoCollection,
                                               HighlightTail,
                                               UsingDarkTheme,
                                               searchHints,
                                               searchMetadataCollection,
                                               SelectionMonitor,
                                               SearchOptions,
                                               searchInvoker,
                                               stateController,
                                               _userScrollRequested.SetAsComplete());
        }
Ejemplo n.º 28
0
        public MainViewModel(MainState state, IProviderFactory factory, ProviderViewModelFactory createViewModel)
        {
            _factory = factory;
            Refresh  = ReactiveCommand.Create(state.Providers.Refresh);

            _isLoading = Refresh
                         .IsExecuting
                         .ToProperty(this, x => x.IsLoading);

            _isReady = Refresh
                       .IsExecuting
                       .Select(executing => !executing)
                       .ToProperty(this, x => x.IsReady);

            state.Providers.Connect()
            .Transform(ps => createViewModel(ps, factory.CreateProvider(ps.Parameters)))
            .Sort(SortExpressionComparer <IProviderViewModel> .Descending(x => x.Created))
            .ObserveOn(RxApp.MainThreadScheduler)
            .Bind(out _providers)
            .Subscribe();

            var canRemove = this
                            .WhenAnyValue(x => x.SelectedProvider)
                            .Select(provider => provider != null);

            Remove = ReactiveCommand.Create(
                () => state.Providers.RemoveKey(SelectedProvider.Id),
                canRemove);

            var canAddProvider = this
                                 .WhenAnyValue(x => x.SelectedSupportedType)
                                 .Select(type => Enum.IsDefined(typeof(ProviderType), type));

            Add = ReactiveCommand.Create(
                () => state.Providers.AddOrUpdate(new ProviderState {
                Type = SelectedSupportedType
            }),
                canAddProvider);

            _welcomeScreenVisible = this
                                    .WhenAnyValue(x => x.SelectedProvider)
                                    .Select(provider => provider == null)
                                    .ToProperty(this, x => x.WelcomeScreenVisible);

            _welcomeScreenCollapsed = this
                                      .WhenAnyValue(x => x.WelcomeScreenVisible)
                                      .Select(visible => !visible)
                                      .ToProperty(this, x => x.WelcomeScreenCollapsed);

            var canUnselect = this
                              .WhenAnyValue(x => x.SelectedProvider)
                              .Select(provider => provider != null);

            Unselect = ReactiveCommand.Create(() => Unit.Default, canUnselect);
            Unselect.Subscribe(unit => SelectedProvider = null);

            var outputCollectionChanges = Providers
                                          .ToObservableChangeSet(x => x.Id)
                                          .Publish()
                                          .RefCount();

            outputCollectionChanges
            .Filter(provider => provider.Id == state.SelectedProviderId)
            .ObserveOn(RxApp.MainThreadScheduler)
            .OnItemAdded(provider => SelectedProvider = provider)
            .Subscribe();

            outputCollectionChanges
            .OnItemRemoved(provider => SelectedProvider = null)
            .Subscribe();

            this.WhenAnyValue(x => x.SelectedProvider)
            .Skip(1)
            .Select(provider => provider?.Id ?? Guid.Empty)
            .Subscribe(id => state.SelectedProviderId = id);

            SelectedSupportedType = state.SelectedSupportedType ?? SupportedTypes.First();
            this.WhenAnyValue(x => x.SelectedSupportedType)
            .Subscribe(type => state.SelectedSupportedType = type);
        }
Ejemplo n.º 29
0
 .Sort(SortExpressionComparer <WalletViewModelBase> .Descending(i => i.WalletState).ThenByDescending(i => i.IsLoggedIn).ThenByAscending(i => i.Title))
Ejemplo n.º 30
0
        public MainWindowVM(RunMode mode, string source, MainWindow mainWindow)
        {
            this.Mode       = mode;
            this.MainWindow = mainWindow;
            this._Installer = new Lazy <InstallerVM>(() => new InstallerVM(this));
            this._Compiler  = new Lazy <CompilerVM>(() => new CompilerVM(this, source));

            // Set up logging
            _logSubj
            .ToObservableChangeSet()
            .Buffer(TimeSpan.FromMilliseconds(250))
            .Where(l => l.Count > 0)
            .FlattenBufferResult()
            .Top(5000)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Bind(this.Log)
            .Subscribe()
            .DisposeWith(this.CompositeDisposable);
            Utils.SetLoggerFn(s => _logSubj.OnNext(s));
            Utils.SetStatusFn((msg, progress) => WorkQueue.Report(msg, progress));

            // Wire mode to drive the active pane.
            // Note:  This is currently made into a derivative property driven by mode,
            // but it can be easily changed into a normal property that can be set from anywhere if needed
            this._ActivePane = this.WhenAny(x => x.Mode)
                               .Select <RunMode, ViewModel>(m =>
            {
                switch (m)
                {
                case RunMode.Compile:
                    return(this._Compiler.Value);

                case RunMode.Install:
                    return(this._Installer.Value);

                default:
                    return(default);
                }
            })
                               .ToProperty(this, nameof(this.ActivePane));
            this.WhenAny(x => x.ActivePane)
            .ObserveOn(RxApp.TaskpoolScheduler)
            .WhereCastable <ViewModel, InstallerVM>()
            .Subscribe(vm => vm.ModListPath = source)
            .DisposeWith(this.CompositeDisposable);

            // Initialize work queue
            WorkQueue.Init(
                report_function: (id, msg, progress) => this._statusSubject.OnNext(new CPUStatus()
            {
                ID = id, Msg = msg, Progress = progress
            }),
                report_queue_size: (max, current) => this.SetQueueSize(max, current));

            // Compile progress updates and populate ObservableCollection
            this._statusSubject
            .ObserveOn(RxApp.TaskpoolScheduler)
            .ToObservableChangeSet(x => x.ID)
            .Batch(TimeSpan.FromMilliseconds(250))
            .EnsureUniqueChanges()
            .ObserveOn(RxApp.MainThreadScheduler)
            .Sort(SortExpressionComparer <CPUStatus> .Ascending(s => s.ID), SortOptimisations.ComparesImmutableValuesOnly)
            .Bind(this.StatusList)
            .Subscribe()
            .DisposeWith(this.CompositeDisposable);
        }