Example #1
0
        /// <summary>
        /// Initializes a new instance of the <see cref="SearchSettingsViewModel"/> class.
        /// Initializes from <see cref="Models.MsPfParameters" />, MSPathFinder parameters.
        /// </summary>
        /// <param name="dialogService">Dialog service for opening dialogs from view model</param>
        /// <param name="mspfParameters">The MSPathFinder parameters.</param>
        public SearchSettingsViewModel(IMainDialogService dialogService, MsPfParameters mspfParameters) : this(dialogService)
        {
            if (mspfParameters == null)
            {
                return;
            }

            SearchModifications.AddRange(
                mspfParameters.Modifications.Select(
                    searchMod => new SearchModificationViewModel(this.dialogService)
            {
                SearchModification = searchMod
            }));
            MinSequenceLength                  = mspfParameters.MinSequenceLength;
            MaxSequenceLength                  = mspfParameters.MaxSequenceLength;
            MinSequenceMass                    = mspfParameters.MinSequenceMass;
            MaxSequenceMass                    = mspfParameters.MaxSequenceMass;
            MinPrecursorIonCharge              = mspfParameters.MinPrecursorIonCharge;
            MaxPrecursorIonCharge              = mspfParameters.MaxPrecursorIonCharge;
            MinProductIonCharge                = mspfParameters.MinProductIonCharge;
            MaxProductIonCharge                = mspfParameters.MaxPrecursorIonCharge;
            PrecursorIonToleranceValue         = mspfParameters.PrecursorTolerancePpm.GetValue();
            ProductIonToleranceValue           = mspfParameters.ProductIonTolerancePpm.GetValue();
            MaxDynamicModificationsPerSequence =
                mspfParameters.MaxDynamicModificationsPerSequence;
        }
Example #2
0
        /// <summary>
        /// Initializes a new instance of the <see cref="ManageModificationsViewModel"/> class.
        /// </summary>
        /// <param name="dialogService">Dialog service for opening dialogs from the view model.</param>
        public ManageModificationsViewModel(IMainDialogService dialogService)
        {
            this.dialogService = dialogService;
            Modifications      = new ReactiveList <Modification>();

            AddCommand    = ReactiveCommand.Create(AddImplementation);
            EditCommand   = ReactiveCommand.Create(EditImplementation, this.WhenAnyValue(x => x.SelectedModification).Select(m => m != null));
            RemoveCommand = ReactiveCommand.Create(RemoveImplementation, this.WhenAnyValue(x => x.SelectedModification).Select(m => m != null));
        }
        /// <summary>
        /// Initializes new instance of <see cref="FragmentViewModel" />.
        /// </summary>
        /// <param name="aminoAcid">The selected amino acid.</param>
        /// <param name="index">The index of this fragment within the greater sequence that it is part of.</param>
        /// <param name="dialogService">Dialog service for opening LCMSSpectator dialogs.</param>
        public FragmentViewModel(AminoAcid aminoAcid, int index = 0, IMainDialogService dialogService = null)
        {
            AminoAcid = aminoAcid;
            Index     = index;
            SetModSymbol(aminoAcid as ModifiedAminoAcid);

            this.dialogService        = dialogService ?? new MainDialogService();
            SelectModificationCommand = ReactiveCommand.Create(SelectModificationImpl);

            // Update the modification symbol when the amino acid changes.
            this.WhenAnyValue(x => x.AminoAcid).Subscribe(aa => SetModSymbol(aa as ModifiedAminoAcid));
        }
Example #4
0
        /// <summary>
        /// Initializes a new instance of the <see cref="ManageModificationsViewModel"/> class.
        /// </summary>
        /// <param name="dialogService">Dialog service for opening dialogs from the view model.</param>
        public ManageModificationsViewModel(IMainDialogService dialogService)
        {
            this.dialogService = dialogService;
            this.Modifications = new ReactiveList <Modification>();

            var addCommand = ReactiveCommand.Create();

            addCommand.Subscribe(_ => this.AddImplementation());
            this.AddCommand = addCommand;

            var editCommand = ReactiveCommand.Create(this.WhenAnyValue(x => x.SelectedModification).Select(m => m != null));

            editCommand.Subscribe(_ => this.EditImplementation());
            this.EditCommand = editCommand;

            var removeCommand = ReactiveCommand.Create(this.WhenAnyValue(x => x.SelectedModification).Select(m => m != null));

            removeCommand.Subscribe(_ => this.RemoveImplementation());
            this.RemoveCommand = removeCommand;
        }
        /// <summary>
        /// Initializes new instance of the <see cref="SequenceViewerViewModel" /> class.
        /// </summary>
        /// <param name="dialogService">Service for opening LCMSSpectator dialogs.</param>
        public SequenceViewerViewModel(IMainDialogService dialogService)
        {
            this.dialogService = dialogService ?? new MainDialogService();
            SequenceFragments  = new ReactiveList <FragmentViewModel> {
                ChangeTrackingEnabled = true
            };

            IonColorDictionary = new IonColorDictionary(2);

            // Update the sequence displayed when the fragmentation sequence or spectrum changes.
            this.WhenAnyValue(
                x => x.SelectedSpectrum,
                x => x.FragmentationSequence,
                x => x.FragmentationSequence.LabeledIonViewModels)
            .Throttle(TimeSpan.FromMilliseconds(500), RxApp.TaskpoolScheduler)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(_ => ParseFragmentationSequence());

            SequenceFragments.ItemChanged.Where(x => x.PropertyName == "AminoAcid")
            .Subscribe(x => UpdateSequence(x.Sender.AminoAcid, x.Sender.Index));
        }
Example #6
0
        /// <summary>
        /// Initializes a new instance of the <see cref="DataSetViewModel"/> class.
        /// </summary>
        /// <param name="dialogService">A dialog service for opening dialogs from the view model</param>
        public DataSetViewModel(IMainDialogService dialogService)
        {
            this.dialogService           = dialogService;
            this.ReadyToClose            = false;
            this.IdFileOpen              = false;
            this.SelectedPrSm            = new PrSm();
            this.ScanViewModel           = new ScanViewModel(dialogService, new List <PrSm>());
            this.CreateSequenceViewModel = new CreateSequenceViewModel(dialogService)
            {
                SelectedDataSetViewModel = this
            };

            this.CreateSequenceViewModel.CreateAndAddPrSmCommand.Subscribe(
                _ => this.ScanViewModel.Data.Add(this.CreateSequenceViewModel.SelectedPrSm));

            // Remove filter by raw file name from ScanViewModel filters
            this.ScanViewModel.Filters.Remove(this.ScanViewModel.Filters.FirstOrDefault(f => f.Name == "Raw File Name"));

            // When a PrSm is selected from the ScanViewModel, update the SelectedPrSm for this data set
            this.ScanViewModel.WhenAnyValue(x => x.SelectedPrSm).Where(prsm => prsm != null).Subscribe(x => this.SelectedPrSm = x);

            // When the scan number in the selected prsm changes, the selected scan in the xic plots should update
            this.WhenAnyValue(x => x.SelectedPrSm)
            .Where(_ => this.SelectedPrSm != null && this.SpectrumViewModel != null && this.XicViewModel != null)
            .Subscribe(prsm =>
            {
                this.SpectrumViewModel.UpdateSpectra(prsm.Scan, this.SelectedPrSm.PrecursorMz);
                this.XicViewModel.SetSelectedScan(prsm.Scan);
                this.XicViewModel.ZoomToScan(prsm.Scan);
            });

            var prsmObservable = this.WhenAnyValue(x => x.SelectedPrSm).Where(prsm => prsm != null);

            prsmObservable
            .Select(prsm => prsm.GetFragmentationSequence()).Where(fragSeq => fragSeq != null)
            .Subscribe(fragSeq =>
            {
                this.SpectrumViewModel.FragmentationSequence = fragSeq;
                this.XicViewModel.FragmentationSequence      = fragSeq;
            });

            // When the prsm changes, update the Scan View Model.
            prsmObservable.Subscribe(prsm => this.ScanViewModel.SelectedPrSm = prsm);

            // When the prsm updates, update the prsm in the sequence creator
            prsmObservable.Subscribe(prsm => this.CreateSequenceViewModel.SelectedPrSm = prsm);

            // When the prsm updates, update the feature map
            prsmObservable.Where(_ => this.FeatureMapViewModel != null).Subscribe(prsm => this.FeatureMapViewModel.FeatureMapViewModel.SelectedPrSm = prsm);

            // When prsm updates, subscribe to scan updates
            prsmObservable.Subscribe(prsm =>
            {
                prsm.WhenAnyValue(x => x.Scan, x => x.PrecursorMz)
                .Where(x => x.Item1 > 0 && x.Item2 > 0 && this.SpectrumViewModel != null)
                .Subscribe(x => this.SpectrumViewModel.UpdateSpectra(x.Item1, x.Item2));
                prsm.WhenAnyValue(x => x.Scan).Where(scan => scan > 0 && this.XicViewModel != null)
                .Subscribe(scan => this.XicViewModel.SetSelectedScan(scan));
            });

            // When a new prsm is created by CreateSequenceViewModel, update SelectedPrSm
            this.CreateSequenceViewModel.WhenAnyValue(x => x.SelectedPrSm).Subscribe(prsm => this.SelectedPrSm = prsm);

            // When IDs are filtered in the ScanViewModel, update feature map with new IDs
            this.ScanViewModel.WhenAnyValue(x => x.FilteredData).Where(_ => this.FeatureMapViewModel != null).Subscribe(data => this.FeatureMapViewModel.UpdateIds(data));

            // Toggle instrument data when ShowInstrumentData setting is changed.
            IcParameters.Instance.WhenAnyValue(x => x.ShowInstrumentData).Select(async x => await this.ScanViewModel.ToggleShowInstrumentDataAsync(x, (PbfLcMsRun)this.LcMs)).Subscribe();

            // When product ion tolerance or ion correlation threshold change, update scorer factory
            IcParameters.Instance.WhenAnyValue(x => x.ProductIonTolerancePpm, x => x.IonCorrelationThreshold)
            .Subscribe(
                x =>
            {
                var scorer = new ScorerFactory(x.Item1, Constants.MinCharge, Constants.MaxCharge, x.Item2);
                this.CreateSequenceViewModel.ScorerFactory = scorer;
                this.ScanViewModel.ScorerFactory           = scorer;
            });

            // When an ID file has been opened, turn on the unidentified scan filter
            this.WhenAnyValue(x => x.IdFileOpen)
            .Where(idFileOpen => idFileOpen)
            .Select(_ => this.ScanViewModel.Filters.FirstOrDefault(f => f.Name == "Hide Unidentified Scans"))
            .Where(f => f != null)
            .Subscribe(f => f.Selected = true);

            // Start MsPf Search Command
            var startMsPfSearchCommand = ReactiveCommand.Create();

            startMsPfSearchCommand.Subscribe(_ => this.StartMsPfSearchImplementation());
            this.StartMsPfSearchCommand = startMsPfSearchCommand;

            // Close command verifies that the user wants to close the dataset, then sets ReadyToClose to true if they are
            var closeCommand = ReactiveCommand.Create();

            closeCommand.Subscribe(_ =>
            {
                this.ReadyToClose =
                    dialogService.ConfirmationBox(
                        string.Format("Are you sure you would like to close {0}?", this.Title), string.Empty);
            });
            this.CloseCommand = closeCommand;
        }
Example #7
0
        /// <summary>
        /// Initializes new instance of the <see cref="StableIsotopeViewModel"/> class.
        /// </summary>
        public StableIsotopeViewModel(IMainDialogService dialogService = null)
        {
            this.dialogService = dialogService ?? new MainDialogService();

            // Initialize default values

            this.IsotopeProportions = new Dictionary <string, IsotopeProportionSelectorViewModel>
            {
                { "C", new IsotopeProportionSelectorViewModel(Atom.Get("C"), IsoProfilePredictor.Predictor.ProbC) },
                { "H", new IsotopeProportionSelectorViewModel(Atom.Get("H"), IsoProfilePredictor.Predictor.ProbH) },
                { "N", new IsotopeProportionSelectorViewModel(Atom.Get("N"), IsoProfilePredictor.Predictor.ProbN) },
                { "O", new IsotopeProportionSelectorViewModel(Atom.Get("O"), IsoProfilePredictor.Predictor.ProbO) },
                { "S", new IsotopeProportionSelectorViewModel(Atom.Get("S"), IsoProfilePredictor.Predictor.ProbS) }
            };

            this.SelectedElement = this.IsotopeProportions["C"];

            this.Mass   = 0;
            this.Charge = 1;

            this.ToleranceUnits = new ReactiveList <ToleranceUnit> {
                ToleranceUnit.Ppm, ToleranceUnit.Th
            };
            this.ToleranceValue = 10.0;
            this.ToleranceUnit  = ToleranceUnit.Ppm;

            this.RelativeIntensityThreshold = 0.1;

            this.ObservedPeaks = new ReactiveList <ListItemViewModel <PeakDataPoint> > {
                ChangeTrackingEnabled = true
            };

            // Add some empty peaks initialy to peak list
            for (int i = 0; i < DefaultNumberOfPeakFields; i++)
            {
                this.ObservedPeaks.Add(new ListItemViewModel <PeakDataPoint>(new PeakDataPoint(0, 0, 0, 0, string.Empty)));
            }

            this.IsotopicEnvelopePlotViewModel = new IsotopicEnvelopePlotViewModel();

            // When selected element is changed, reset all values
            this.WhenAnyValue(x => x.SelectedElement).Subscribe(
                el =>
            {
                foreach (var element in this.IsotopeProportions.Values)
                {
                    element.Reset();
                }
            });

            // When a peak's ShouldBeRemoved flag is set, remove it
            this.ObservedPeaks.ItemChanged.Where(i => i.PropertyName == "ShouldBeRemoved")
            .Where(i => i.Sender.ShouldBeRemoved)
            .Subscribe(i => this.ObservedPeaks.Remove(i.Sender));

            // Commands

            this.BuildPlotCommand = ReactiveCommand.Create(this.WhenAnyValue(x => x.Mass).Select(mass => mass > 0.0));
            this.BuildPlotCommand.Subscribe(_ => this.BuildIsotopicProfilePlot());

            this.ResetToDefaultProportionsCommand = ReactiveCommand.Create();
            this.ResetToDefaultProportionsCommand
            .Where(_ => this.SelectedElement != null)
            .Subscribe(_ => this.SelectedElement.Reset());

            this.TuneConcentrationCommand = ReactiveCommand.Create();
            this.TuneConcentrationCommand.Subscribe(_ => this.TuneConcentration());

            this.PastePeaksFromClipboardCommand = ReactiveCommand.Create();
            this.PastePeaksFromClipboardCommand.Subscribe(_ => this.PastePeaksFromClipboard());

            this.ClearObservedPeaksCommand = ReactiveCommand.Create();
            this.ClearObservedPeaksCommand.Subscribe(_ => this.ObservedPeaks.Clear());

            this.AddObservedPeakCommand = ReactiveCommand.Create();
            this.AddObservedPeakCommand.Subscribe(_ => this.ObservedPeaks
                                                  .Add(new ListItemViewModel <PeakDataPoint>(
                                                           new PeakDataPoint(0.0, 0.0, 0.0, 0.0, string.Empty))));
        }
Example #8
0
        /// <summary>
        /// Initializes a new instance of the MainWindowViewModel class.
        /// </summary>
        /// <param name="dialogService">Service for view model friendly dialogs</param>
        /// <param name="dataReader">Service for reading raw files, id files, and feature files</param>
        public MainWindowViewModel(IMainDialogService dialogService, IDataReader dataReader)
        {
            this.dialogService = dialogService;
            this.dataReader    = dataReader;

            // Initialize child view models
            DataSets = new ReactiveList <DataSetViewModel> {
                ChangeTrackingEnabled = true
            };
            CreateSequenceViewModel = new CreateSequenceViewModel(this.dialogService);
            ScanViewModel           = new ScanViewModel(this.dialogService, new List <PrSm>());

            // Remove filter by unidentified scans from ScanViewModel filters
            ScanViewModel.Filters.Remove(ScanViewModel.Filters.FirstOrDefault(f => f.Name == "Hide Unidentified Scans"));

            // Create commands for file operations
            OpenDataSetCommand     = ReactiveCommand.CreateFromTask(async _ => await OpenDataSetImplementation());
            OpenRawFileCommand     = ReactiveCommand.CreateFromTask(async _ => await OpenRawFileImplementation());
            OpenTsvFileCommand     = ReactiveCommand.CreateFromTask(async _ => await OpenIdFileImplementation());
            OpenFeatureFileCommand = ReactiveCommand.CreateFromTask(async _ => await OpenFeatureFileImplementation());
            OpenFromDmsCommand     = ReactiveCommand.CreateFromTask(async _ => await OpenFromDmsImplementation());

            // Create command to open settings window
            OpenSettingsCommand = ReactiveCommand.Create(() => this.dialogService.OpenSettings(new SettingsViewModel(this.dialogService)));

            // Create command to open isotopic profile viewer
            OpenIsotopicProfileViewerCommand = ReactiveCommand.Create(OpenIsotopicProfileViewer);

            //this.OpenIsotopicProfileViewer(new object());

            // Create command to open about box
            OpenAboutBoxCommand = ReactiveCommand.Create(() => this.dialogService.OpenAboutBox());

            // Create command to open new modification management window
            OpenManageModificationsCommand = ReactiveCommand.Create(ManageModificationsImplementation);

            // Create MSPathFinder search command
            RunMsPathFinderSearchCommand = ReactiveCommand.Create(RunMsPathFinderSearchImplementation);

            // Create export command
            ExportResultsCommand = ReactiveCommand.Create(ExportResultsImplementation, DataSets.WhenAnyValue(x => x.Count).Select(count => count > 0));

            // Create export command
            QuitProgramCommand = ReactiveCommand.Create(() => this.dialogService.QuitProgram());

            ShowSplash = true;

            // When a data set sets its ReadyToClose property to true, remove it from dataset list
            DataSets.ItemChanged.Where(x => x.PropertyName == "ReadyToClose")
            .Select(x => x.Sender).Where(sender => sender.ReadyToClose)
            .Subscribe(dataSet =>
            {
                ScanViewModel.RemovePrSmsFromRawFile(dataSet.Title);
                DataSets.Remove(dataSet);
            });

            // If all datasets are closed, show splash screen
            DataSets.BeforeItemsRemoved.Subscribe(x => ShowSplash = DataSets.Count == 1);

            // If a dataset is opened, show splash screen
            DataSets.BeforeItemsAdded.Subscribe(x => ShowSplash = false);

            // When the data reader is reading an ID file, show the loading screen
            this.dataReader.WhenAnyValue(x => x.ReadingIdFiles)
            .Subscribe(readingIdFiles => IdFileLoading = readingIdFiles);

            // When a PrSm is selected in the Protein Tree, make all data sets show the PrSm
            ScanViewModel.WhenAnyValue(x => x.SelectedPrSm)
            .Where(selectedPrSm => selectedPrSm != null)
            .Subscribe(selectedPrSm =>
            {
                foreach (var dataSet in DataSets)
                {
                    dataSet.SelectedPrSm = selectedPrSm;
                }
            });

            // Warm up InformedProteomics Averagine using arbitrary mass
            Task.Run(() => Averagine.GetIsotopomerEnvelopeFromNominalMass(50000));
        }
Example #9
0
        /// <summary>
        /// Initializes a new instance of the <see cref="XicViewModel"/> class.
        /// </summary>
        /// <param name="dialogService">A dialog service for opening dialogs from the view model</param>
        /// <param name="lcms">the LCMSRun representing the raw file for this dataset.</param>
        public XicViewModel(IMainDialogService dialogService, ILcMsRun lcms)
        {
            this.dialogService = dialogService;
            this.lcms          = lcms;
            this.fragmentXAxis = new LinearAxis
            {
                StringFormat    = "0.###",
                Position        = AxisPosition.Bottom, Title = "Retention Time",
                AbsoluteMinimum = lcms.MinLcScan,
                AbsoluteMaximum = lcms.MaxLcScan + 1
            };
            this.fragmentXAxis.AxisChanged += this.XAxisChanged;
            this.FragmentPlotViewModel      = new XicPlotViewModel(
                this.dialogService,
                new FragmentationSequenceViewModel {
                AddPrecursorIons = false
            },
                lcms,
                "Fragment XIC",
                this.fragmentXAxis,
                false);
            this.heavyFragmentXAxis = new LinearAxis
            {
                StringFormat    = "0.###",
                Position        = AxisPosition.Bottom,
                Title           = "Retention Time",
                AbsoluteMinimum = lcms.MinLcScan,
                AbsoluteMaximum = lcms.MaxLcScan + 1
            };
            this.heavyFragmentXAxis.AxisChanged += this.XAxisChanged;
            this.HeavyFragmentPlotViewModel      = new XicPlotViewModel(
                this.dialogService,
                new FragmentationSequenceViewModel {
                AddPrecursorIons = false
            },
                lcms,
                "Heavy Fragment XIC",
                this.heavyFragmentXAxis,
                false,
                AxisPosition.Right);
            this.precursorXAxis = new LinearAxis
            {
                StringFormat    = "0.###",
                Position        = AxisPosition.Bottom,
                Title           = "Retention Time",
                AbsoluteMinimum = lcms.MinLcScan,
                AbsoluteMaximum = lcms.MaxLcScan + 1
            };
            this.precursorXAxis.AxisChanged += this.XAxisChanged;
            this.PrecursorPlotViewModel      = new XicPlotViewModel(this.dialogService, new PrecursorSequenceIonViewModel(), lcms, "Precursor XIC", this.precursorXAxis)
            {
                IsPlotUpdating = true,
            };

            this.heavyPrecursorXAxis = new LinearAxis
            {
                StringFormat    = "0.###",
                Position        = AxisPosition.Bottom,
                Title           = "Retention Time",
                AbsoluteMinimum = lcms.MinLcScan,
                AbsoluteMaximum = lcms.MaxLcScan + 1
            };
            this.heavyPrecursorXAxis.AxisChanged += this.XAxisChanged;
            this.HeavyPrecursorPlotViewModel      = new XicPlotViewModel(
                this.dialogService,
                new PrecursorSequenceIonViewModel(),
                lcms,
                "Heavy Precursor XIC",
                this.heavyPrecursorXAxis,
                vertAxes: AxisPosition.Right);

            this.showHeavy       = false;
            this.showFragmentXic = false;
            var openHeavyModificationsCommand = ReactiveCommand.Create();

            openHeavyModificationsCommand.Subscribe(_ => this.OpenHeavyModificationsImplentation());
            this.OpenHeavyModificationsCommand = openHeavyModificationsCommand;

            this.PrecursorPlotViewModel.FragmentationSequenceViewModel.HeavyModifications      = IcParameters.Instance.LightModifications.ToArray();
            this.FragmentPlotViewModel.FragmentationSequenceViewModel.HeavyModifications       = IcParameters.Instance.LightModifications.ToArray();
            this.HeavyPrecursorPlotViewModel.FragmentationSequenceViewModel.HeavyModifications = IcParameters.Instance.HeavyModifications.ToArray();
            this.HeavyFragmentPlotViewModel.FragmentationSequenceViewModel.HeavyModifications  = IcParameters.Instance.HeavyModifications.ToArray();

            this.linkedAxes = new Dictionary <LinearAxis, List <LinearAxis> >
            {
                { this.precursorXAxis, new List <LinearAxis>() },
                { this.fragmentXAxis, new List <LinearAxis>() },
                { this.heavyPrecursorXAxis, new List <LinearAxis>() },
                { this.heavyFragmentXAxis, new List <LinearAxis>() }
            };

            this.plotLinkageTracker = new HashSet <Tuple <LinearAxis, LinearAxis> >();
            this.TogglePlotLinks(this.fragmentXAxis, this.heavyFragmentXAxis);
            this.TogglePlotLinks(this.precursorXAxis, this.heavyPrecursorXAxis);
            this.TogglePlotLinks(this.precursorXAxis, this.fragmentXAxis);
            this.TogglePlotLinks(this.heavyPrecursorXAxis, this.heavyFragmentXAxis);

            // Update area ratios when the area of any of the plots changes
            this.WhenAny(x => x.FragmentPlotViewModel.Area, x => x.HeavyFragmentPlotViewModel.Area, (x, y) => x.Value / y.Value)
            .Select(this.FormatRatio)
            .ToProperty(this, x => x.FragmentAreaRatioLabel, out this.fragmentAreaRatioLabel);
            this.WhenAny(x => x.PrecursorPlotViewModel.Area, x => x.HeavyPrecursorPlotViewModel.Area, (x, y) => x.Value / y.Value)
            .Select(this.FormatRatio)
            .ToProperty(this, x => x.PrecursorAreaRatioLabel, out this.precursorAreaRatioLabel);
            this.WhenAnyValue(x => x.ShowFragmentXic, x => x.ShowHeavy)
            .Subscribe(x =>
            {
                this.FragmentPlotViewModel.IsPlotUpdating       = x.Item1;
                this.HeavyFragmentPlotViewModel.IsPlotUpdating  = x.Item1 && x.Item2;
                this.HeavyPrecursorPlotViewModel.IsPlotUpdating = x.Item2;
            });
            this.WhenAnyValue(x => x.FragmentationSequence)
            .Where(fragSeq => fragSeq != null)
            .Subscribe(fragSeq =>
            {
                this.FragmentPlotViewModel.FragmentationSequenceViewModel.FragmentationSequence       = fragSeq;
                this.HeavyFragmentPlotViewModel.FragmentationSequenceViewModel.FragmentationSequence  = fragSeq;
                this.PrecursorPlotViewModel.FragmentationSequenceViewModel.FragmentationSequence      = fragSeq;
                this.HeavyPrecursorPlotViewModel.FragmentationSequenceViewModel.FragmentationSequence = fragSeq;
            });

            this.PrecursorToFragmentLinkLabel = "L";
            this.LightToHeavyLinkLabel        = "L";

            this.LinkLightToHeavyCommand = ReactiveCommand.Create();
            this.LinkLightToHeavyCommand.Subscribe(
                _ =>
            {
                this.TogglePlotLinks(this.fragmentXAxis, this.heavyFragmentXAxis);
                this.TogglePlotLinks(this.precursorXAxis, this.heavyPrecursorXAxis);
                this.LightToHeavyLinkLabel = this.LightToHeavyLinkLabel == "L" ? "U" : "L";
            });

            this.LinkPrecursorToFragmentCommand = ReactiveCommand.Create();
            this.LinkPrecursorToFragmentCommand.Subscribe(
                _ =>
            {
                this.TogglePlotLinks(this.precursorXAxis, this.fragmentXAxis);
                this.TogglePlotLinks(this.heavyPrecursorXAxis, this.heavyFragmentXAxis);
                this.PrecursorToFragmentLinkLabel = this.PrecursorToFragmentLinkLabel == "L" ? "U" : "L";
            });
        }
        /// <summary>
        /// Initializes a new instance of the SpectrumPlotViewModel class.
        /// </summary>
        /// <param name="dialogService">Dialog service for opening dialogs from ViewModel.</param>
        /// <param name="fragSeqVm">Gets or sets the view model for the fragmentation sequence (fragment ion generator)</param>
        /// <param name="multiplier">How much padding should be before the lowest peak and after the highest peak?</param>
        /// <param name="autoZoomXAxis">Should this view model automatically zoom the plot?</param>
        public SpectrumPlotViewModel(IMainDialogService dialogService, IFragmentationSequenceViewModel fragSeqVm, double multiplier, bool autoZoomXAxis = true)
        {
            this.dialogService             = dialogService;
            FragmentationSequenceViewModel = fragSeqVm;
            this.autoZoomXAxis             = autoZoomXAxis;
            errorMapViewModel        = new ErrorMapViewModel(dialogService);
            ShowUnexplainedPeaks     = true;
            ShowFilteredSpectrum     = false;
            ShowDeconvolutedSpectrum = false;
            AutoAdjustYAxis          = true;
            Title = string.Empty;
            XAxis = new LinearAxis
            {
                Title        = "m/z",
                StringFormat = "0.###",
                Position     = AxisPosition.Bottom,
            };
            PlotModel = new AutoAdjustedYPlotModel(XAxis, multiplier)
            {
                IsLegendVisible = false,
                YAxis           =
                {
                    Title        = "Intensity",
                    StringFormat = "0e0"
                }
            };

            SequenceViewerViewModel = new SequenceViewerViewModel();

            ions = new LabeledIonViewModel[0];

            // When Spectrum updates, clear the filtered spectrum, deconvoluted spectrum, and filtered+deconvoluted spectrum
            this.WhenAnyValue(x => x.Spectrum)
            .Subscribe(spectrum =>
            {
                spectrumDirty                = true;
                filteredSpectrum             = null;
                deconvolutedSpectrum         = null;
                filteredDeconvolutedSpectrum = null;
            });

            // If deconvolution option has changed, the X Axis should change.
            this.WhenAnyValue(x => x.ShowDeconvolutedSpectrum)
            .Subscribe(_ => spectrumDirty = true);

            // When Spectrum or ions change, or deconvoluted or filtered spectrum are selected, update spectrum plot
            this.WhenAnyValue(
                x => x.Spectrum,
                x => x.FragmentationSequenceViewModel.LabeledIonViewModels,
                x => x.ShowDeconvolutedSpectrum,
                x => x.ShowDeconvolutedIons,
                x => x.ShowFilteredSpectrum,
                x => x.ShowUnexplainedPeaks,
                x => x.ShowOnlyTop20Peaks)
            .Where(x => x.Item1 != null && x.Item2 != null)
            .Throttle(TimeSpan.FromMilliseconds(400), RxApp.TaskpoolScheduler)
            .SelectMany(async x =>
            {
                var vms = await FragmentationSequenceViewModel.GetLabeledIonViewModels();
                return(await
                       Task.WhenAll(
                           vms.Select(
                               ion =>
                               ion.GetPeaksAsync(GetSpectrum(),
                                                 ShowDeconvolutedSpectrum || ShowDeconvolutedIons))));
            })
            .Subscribe(dataPoints =>
            {
                ions = FragmentationSequenceViewModel.LabeledIonViewModels;
                SetTerminalResidues(dataPoints);
                UpdatePlotModel(dataPoints);

                if (FragmentationSequenceViewModel is FragmentationSequenceViewModel model)
                {
                    SequenceViewerViewModel.FragmentationSequence = model;
                    SequenceViewerViewModel.SelectedSpectrum      = Spectrum as ProductSpectrum;
                }
            });           // Update plot when data changes

            this.WhenAnyValue(x => x.Spectrum).Where(spectrum => spectrum == null).Subscribe(
                _ =>
            {
                PlotModel.Annotations.Clear();
                PlotModel.ClearSeries();
                PlotModel.InvalidatePlot(true);
            });

            // Update ions when relative intensity threshold changes.
            IcParameters.Instance.WhenAnyValue(x => x.PrecursorRelativeIntensityThreshold).Subscribe(precursorRelInt =>
            {
                if (FragmentationSequenceViewModel is PrecursorSequenceIonViewModel precursorFragVm)
                {
                    precursorFragVm.RelativeIntensityThreshold = precursorRelInt;
                }
            });

            // Update plot when settings change
            IcParameters.Instance.WhenAnyValue(x => x.ProductIonTolerancePpm, x => x.IonCorrelationThreshold)
            .Throttle(TimeSpan.FromMilliseconds(400), RxApp.TaskpoolScheduler)
            .SelectMany(async x => await Task.WhenAll(ions.Select(ion => ion.GetPeaksAsync(GetSpectrum(), ShowDeconvolutedSpectrum, false))))
            .Subscribe(UpdatePlotModel);

            // When AutoAdjustYAxis changes, update value in plot model.
            this.WhenAnyValue(x => x.AutoAdjustYAxis)
            .Subscribe(autoAdjust =>
            {
                PlotModel.AutoAdjustYAxis     = autoAdjust;
                PlotModel.YAxis.IsZoomEnabled = !autoAdjust;
                PlotModel.YAxis.IsPanEnabled  = !autoAdjust;

                if (autoAdjust)
                {
                    PlotModel.XAxis.Reset();
                    PlotModel.YAxis.Reset();
                }
            });

            // Update plot axes when FeaturePlotXMin, YMin, XMax, and YMax change
            this.WhenAnyValue(x => x.XMinimum, x => x.XMaximum)
            .Throttle(TimeSpan.FromSeconds(1), RxApp.TaskpoolScheduler)
            .Where(x => !xAxis.ActualMinimum.Equals(x.Item1) || !xAxis.ActualMaximum.Equals(x.Item2))
            .Subscribe(x =>
            {
                xAxis.Zoom(x.Item1, x.Item2);
                PlotModel.InvalidatePlot(false);
            });
            this.WhenAnyValue(y => y.YMinimum, y => y.YMaximum)
            .Throttle(TimeSpan.FromSeconds(1), RxApp.TaskpoolScheduler)
            .Where(y => !PlotModel.YAxis.ActualMinimum.Equals(y.Item1) || !PlotModel.YAxis.ActualMaximum.Equals(y.Item2))
            .Subscribe(
                y =>
            {
                PlotModel.YAxis.Zoom(y.Item1, y.Item2);
                PlotModel.InvalidatePlot(false);
            });

            // Update X min and max properties when x axis is panned or zoomed
            xAxis.AxisChanged += (o, e) =>
            {
                XMinimum = Math.Round(xAxis.ActualMinimum, 3);
                XMaximum = Math.Round(xAxis.ActualMaximum, 3);
            };

            // Update Y min and max properties when Y axis is panned or zoomed
            PlotModel.YAxis.AxisChanged += (o, e) =>
            {
                YMinimum = Math.Round(PlotModel.YAxis.ActualMinimum, 3);
                YMaximum = Math.Round(PlotModel.YAxis.ActualMaximum, 3);
            };

            // Save As Image Command requests a file path from the user and then saves the spectrum plot as an image
            SaveAsImageCommand = ReactiveCommand.Create(SaveAsImageImplementation);

            // Error map command opens a new error map window and passes it the most abundant isotope peak data points
            // and the current sequence.
            OpenErrorMapCommand      = ReactiveCommand.Create(() => dialogService.OpenErrorMapWindow(errorMapViewModel));
            OpenScanSelectionCommand = ReactiveCommand.Create(OpenScanSelectionImplementation);
            SaveAsTsvCommand         = ReactiveCommand.Create(SaveAsTsvImplementation);
            SaveToClipboardCommand   = ReactiveCommand.Create(SaveToClipboardImplementation);
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="SpectrumViewModel"/> class.
        /// </summary>
        /// <param name="dialogService">Dialog service for opening dialogs from view model.</param>
        /// <param name="lcms">LCMSRun for the data set that the spectrum plots are part of.</param>
        public SpectrumViewModel(IMainDialogService dialogService, ILcMsRun lcms)
        {
            this.lcms                = lcms;
            Ms2SpectrumViewModel     = new SpectrumPlotViewModel(dialogService, new FragmentationSequenceViewModel(), 1.05);
            PrevMs1SpectrumViewModel = new SpectrumPlotViewModel(
                dialogService,
                new PrecursorSequenceIonViewModel {
                PrecursorViewMode = PrecursorViewMode.Charges
            },
                1.25,
                false);
            NextMs1SpectrumViewModel = new SpectrumPlotViewModel(
                dialogService,
                new PrecursorSequenceIonViewModel {
                PrecursorViewMode = PrecursorViewMode.Charges
            },
                1.25,
                false);

            // When prev ms1 spectrum plot is zoomed/panned, next ms1 spectrum plot should zoom/pan
            isAxisInternalChange = false;
            PrevMs1SpectrumViewModel.XAxis.AxisChanged += (o, e) =>
            {
                if (isAxisInternalChange)
                {
                    return;
                }

                isAxisInternalChange = true;
                NextMs1SpectrumViewModel.XAxis.Zoom(PrevMs1SpectrumViewModel.XAxis.ActualMinimum, PrevMs1SpectrumViewModel.XAxis.ActualMaximum);
                isAxisInternalChange = false;
            };

            // When next ms1 spectrum plot is zoomed/panned, prev ms1 spectrum plot should zoom/pan
            NextMs1SpectrumViewModel.XAxis.AxisChanged += (o, e) =>
            {
                if (isAxisInternalChange)
                {
                    return;
                }

                isAxisInternalChange = true;
                PrevMs1SpectrumViewModel.XAxis.Zoom(NextMs1SpectrumViewModel.XAxis.ActualMinimum, NextMs1SpectrumViewModel.XAxis.ActualMaximum);
                isAxisInternalChange = false;
            };

            this.WhenAnyValue(x => x.FragmentationSequence)
            .Where(fragSeq => fragSeq != null)
            .Subscribe(fragSeq =>
            {
                Ms2SpectrumViewModel.FragmentationSequenceViewModel.FragmentationSequence     = fragSeq;
                PrevMs1SpectrumViewModel.FragmentationSequenceViewModel.FragmentationSequence = fragSeq;
                NextMs1SpectrumViewModel.FragmentationSequenceViewModel.FragmentationSequence = fragSeq;
            });

            // By default, MS2 Spectrum is shown in the primary view
            PrimarySpectrumViewModel = Ms2SpectrumViewModel;
            Secondary1ViewModel      = PrevMs1SpectrumViewModel;
            Secondary2ViewModel      = NextMs1SpectrumViewModel;

            // Wire commands to swap the spectrum that is shown in the primary view
            SwapSecondary1Command = ReactiveCommand.Create(SwapSecondary1);
            SwapSecondary2Command = ReactiveCommand.Create(SwapSecondary2);
        }
Example #12
0
        /// <summary>
        /// Initializes a new instance of the <see cref="SettingsViewModel"/> class.
        /// </summary>
        /// <param name="dialogService">Dialog service for opening dialogs from a view model.</param>
        public SettingsViewModel(IMainDialogService dialogService)
        {
            this.dialogService  = dialogService;
            this.ToleranceUnits = new ReactiveList <ToleranceUnit> {
                ToleranceUnit.Ppm, ToleranceUnit.Th
            };
            this.PrecursorIonTolerance               = IcParameters.Instance.PrecursorTolerancePpm.GetValue();
            this.PrecursorIonToleranceUnit           = IcParameters.Instance.PrecursorTolerancePpm.GetUnit();
            this.ProductIonTolerance                 = IcParameters.Instance.ProductIonTolerancePpm.GetValue();
            this.ProductIonToleranceUnit             = IcParameters.Instance.ProductIonTolerancePpm.GetUnit();
            this.IonCorrelationThreshold             = IcParameters.Instance.IonCorrelationThreshold;
            this.PointsToSmooth                      = IcParameters.Instance.PointsToSmooth;
            this.PrecursorRelativeIntensityThreshold = IcParameters.Instance.PrecursorRelativeIntensityThreshold;
            this.ShowInstrumentData                  = IcParameters.Instance.ShowInstrumentData;
            this.AutomaticallySelectIonTypes         = IcParameters.Instance.AutomaticallySelectIonTypes;
            this.CidHcdIonTypes                      = IcParameters.Instance.GetCidHcdIonTypes();
            this.EtdIonTypes    = IcParameters.Instance.GetEtdIonTypes();
            this.ExportImageDpi = IcParameters.Instance.ExportImageDpi;

            this.FeatureColors = new ColorListViewModel();
            this.IdColors      = new ColorListViewModel();

            var oxyScanCol = IcParameters.Instance.Ms2ScanColor;

            this.Ms2ScanColor = new Color {
                A = oxyScanCol.A, R = oxyScanCol.R, G = oxyScanCol.G, B = oxyScanCol.B
            };

            foreach (var color in IcParameters.Instance.FeatureColors)
            {
                this.FeatureColors.ColorViewModels.Add(new ColorViewModel {
                    SelectedColor = new Color {
                        A = color.A, R = color.R, B = color.B, G = color.G
                    }
                });
            }

            foreach (var color in IcParameters.Instance.IdColors)
            {
                this.IdColors.ColorViewModels.Add(new ColorViewModel {
                    SelectedColor = new Color {
                        A = color.A, R = color.R, B = color.B, G = color.G
                    }
                });
            }

            this.Modifications = new ReactiveList <SearchModificationViewModel>();
            foreach (var searchModification in IcParameters.Instance.SearchModifications)
            {
                var modificationVm = new SearchModificationViewModel(searchModification, this.dialogService);
                ////modificationVm.RemoveModificationCommand.Subscribe(_ => this.RemoveModification(modificationVm));
                this.Modifications.Add(modificationVm);
            }

            var addModificationCommand = ReactiveCommand.Create();

            addModificationCommand.Subscribe(_ => this.AddModificationImplementation());
            this.AddModificationCommand = addModificationCommand;

            var createNewModificationCommand = ReactiveCommand.Create();

            createNewModificationCommand.Subscribe(_ => this.CreateNewModificationImplementation());
            this.CreateNewModificationCommand = createNewModificationCommand;

            var saveCommand = ReactiveCommand.Create();

            saveCommand.Subscribe(_ => this.SaveImplementation());
            this.SaveCommand = saveCommand;

            var cancelCommand = ReactiveCommand.Create();

            cancelCommand.Subscribe(_ => this.CancelImplementation());
            this.CancelCommand = cancelCommand;

            HeavyModificationsViewModel = new HeavyModificationsViewModel(dialogService);

            this.Status = false;
        }
Example #13
0
        /// <summary>
        /// Initializes a new instance of the <see cref="ScanViewModel"/> class.
        /// </summary>
        /// <param name="dialogService">Dialog service for opening dialogs from the view model.</param>
        /// <param name="ids">The Protein-Spectrum-Match identifications to display.</param>
        public ScanViewModel(IMainDialogService dialogService, IEnumerable <PrSm> ids)
        {
            ClearFiltersCommand = ReactiveCommand.Create(ClearFilters);
            this.dialogService  = dialogService;

            FilteredData     = new PrSm[0];
            FilteredProteins = new ReactiveList <ProteinId>();

            Filters = new ReactiveList <IFilter> {
                ChangeTrackingEnabled = true
            };

            Data = new ReactiveList <PrSm> {
                ChangeTrackingEnabled = true
            };
            InitializeDefaultFilters();
            Data.AddRange(ids);

            IdTree = new IdentificationTree();

            // When a filter is selected/unselected, request a filter value if selected, then filter data
            Filters.ItemChanged.Where(x => x.PropertyName == "Selected")
            .Select(x => x.Sender)
            .Where(sender => !sender.Selected || sender.Name == "Hide Unidentified Scans" || this.dialogService.FilterBox(sender))
            .SelectMany(async _ => await FilterDataAsync(Data))
            .Subscribe(fd => FilteredData = fd);

            // Data changes when items are added or removed
            Data.CountChanged.Throttle(TimeSpan.FromMilliseconds(500), RxApp.TaskpoolScheduler)
            .SelectMany(async _ => await FilterDataAsync(Data))
            .Subscribe(fd => FilteredData = fd);

            // When data is filtered, group it by protein name
            this.WhenAnyValue(x => x.FilteredData).ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(async filteredData =>
            {
                IdTree.ClearIds();
                await IdTree.BuildIdTree(filteredData);
                FilteredProteins.Clear();
                foreach (var protein in IdTree.ProteinIds)
                {
                    FilteredProteins.Add(protein);
                }
            });

            // When data is filtered and a PRSM has not been selected yet, select the first PRSM
            this.WhenAnyValue(x => x.FilteredData)
            .Where(fd => fd.Length > 0)
            .Where(_ => SelectedPrSm == null)
            .Subscribe(fd => SelectedPrSm = fd[0]);

            // When a tree object is selected and it is a PRSM, set the selected PRSM
            this.WhenAnyValue(x => x.TreeViewSelectedItem)
            .Select(x => x as PrSm)
            .Where(p => p != null)
            .Subscribe(p => SelectedPrSm = p);

            // When a tree object is selected and it is an IIdData, set the selected PRSM
            this.WhenAnyValue(x => x.TreeViewSelectedItem)
            .Select(x => x as IIdData)
            .Where(p => p != null)
            .Subscribe(p => SelectedPrSm = p.GetHighestScoringPrSm());

            // When a PrSm's sequence changes, update its score.
            Data.ItemChanged.Where(x => x.PropertyName == "Sequence")
            .Select(x => x.Sender)
            .Where(sender => sender.Sequence.Count > 0)
            .Subscribe(UpdatePrSmScore);

            ExportSpectraCommand     = ReactiveCommand.CreateFromTask(ExportSpectraImplementation);
            ExportPeaksCommand       = ReactiveCommand.CreateFromTask(ExportPeaksImplementation);
            ExportProteinTreeCommand = ReactiveCommand.Create(ExportProteinTreeImplementation);

            ExportProteinTreeAsTsvCommand = ReactiveCommand.Create(ExportProteinTreeAsTsvImplementation);
        }
Example #14
0
        /// <summary>
        /// Initializes a new instance of the <see cref="SearchSettingsViewModel"/> class.
        /// </summary>
        /// <param name="dialogService">Dialog service for opening dialogs from view model.</param>
        public SearchSettingsViewModel(IMainDialogService dialogService)
        {
            this.dialogService = dialogService;
            SearchModes        = new[] { InternalCleavageType.MultipleInternalCleavages, InternalCleavageType.SingleInternalCleavage, InternalCleavageType.NoInternalCleavage };
            ToleranceUnits     = new[] { ToleranceUnit.Ppm, ToleranceUnit.Mz };
            SelectedSequence   = string.Empty;
            FastaEntries       = new ReactiveList <FastaEntry>();
            SequenceProteins   = new FastaEntry[0];

            SearchRunning = false;

            FromFastaEntry = true;
            FromSequence   = false;

            // Browse Spectrum Files Command
            BrowseSpectrumFilesCommand = ReactiveCommand.Create(BrowseSpectrumFilesImplementation);

            // Browse Feature Files Command
            BrowseFeatureFilesCommand = ReactiveCommand.Create(BrowseFeatureFilesImplementation);

            // Browse Fasta DB Files Command
            BrowseFastaDbFilesCommand = ReactiveCommand.Create(BrowseFastaDbFilesImplementation);

            // Browse Output Directories Command
            BrowseOutputDirectoriesCommand = ReactiveCommand.Create(BrowseOutputDirectoriesImplementation);

            // Select All Proteins Command
            SelectAllProteinsCommand = ReactiveCommand.Create(() => SelectProteinsImplementation(true), FastaEntries.WhenAnyValue(x => x.Count).Select(count => count > 0));

            // Select No Proteins Command
            SelectNoProteinsCommand = ReactiveCommand.Create(() => SelectProteinsImplementation(false), FastaEntries.WhenAnyValue(x => x.Count).Select(count => count > 0));

            // Manage Modifications Command
            ManageModificationsCommand = ReactiveCommand.Create(ManageModificationsImplementation);

            // Add Modifications Command
            AddModificationCommand = ReactiveCommand.Create(AddModificationImplementation);

            // Run Command - Disabled when there is no SpectrumFilePath, FastaDbFilePath, or OutputFilePath selected
            RunCommand = ReactiveCommand.CreateFromTask(async _ => await RunImplementation(),
                                                        this.WhenAnyValue(
                                                            x => x.SpectrumFilePath,
                                                            x => x.FastaDbFilePath,
                                                            x => x.OutputFilePath)
                                                        .Select(x => !string.IsNullOrWhiteSpace(x.Item1) &&
                                                                !string.IsNullOrWhiteSpace(x.Item2) &&
                                                                !string.IsNullOrWhiteSpace(x.Item3))
                                                        );

            // Prev tab command
            PrevTabCommand = ReactiveCommand.Create(() => TabIndex--,
                                                    Observable.Merge(
                                                        new[]
            {
                this.WhenAnyValue(x => x.TabIndex).Select(tabIndex => tabIndex > 0),
                RunCommand.IsExecuting.Select(exec => !exec)
            }));

            // Next tab command
            NextTabCommand = ReactiveCommand.Create(() => TabIndex++,
                                                    Observable.Merge(
                                                        new[]
            {
                this.WhenAnyValue(x => x.TabIndex).Select(tabIndex => tabIndex < MaxTabIndex),
                RunCommand.IsExecuting.Select(exec => !exec)
            }));

            // Cancel Command
            CancelCommand = ReactiveCommand.Create(CancelImplementation);

            // Default values
            SelectedSearchMode         = InternalCleavageType.SingleInternalCleavage;
            MinSequenceLength          = 21;
            MaxSequenceLength          = 300;
            MinPrecursorIonCharge      = 2;
            MaxPrecursorIonCharge      = 30;
            MinProductIonCharge        = 1;
            MaxProductIonCharge        = 15;
            MinSequenceMass            = 3000.0;
            MaxSequenceMass            = 50000.0;
            PrecursorIonToleranceValue = 10;
            PrecursorIonToleranceUnit  = ToleranceUnit.Ppm;
            ProductIonToleranceValue   = 10;
            PrecursorIonToleranceUnit  = ToleranceUnit.Ppm;
            minScanNumber = 0;
            maxScanNumber = 0;
            maxDynamicModificationsPerSequence = 0;
            FixedNTerm            = true;
            FixedCTerm            = true;
            NumMatchesPerSpectrum = 1;

            // When search mode is selected, display correct search mode description
            this.WhenAnyValue(x => x.SelectedSearchMode)
            .Subscribe(searchMode => SearchModeDescription = searchModeDescriptions[(int)searchMode]);

            // When Spectrum file path is selected, use its directory for the output path by default if a output path
            // has not already been selected.
            this.WhenAnyValue(x => x.SpectrumFilePath)
            .Where(_ => string.IsNullOrWhiteSpace(OutputFilePath))
            .Select(Path.GetDirectoryName)
            .Subscribe(specDir => OutputFilePath = specDir);

            SearchModifications = new ReactiveList <SearchModificationViewModel> {
                ChangeTrackingEnabled = true
            };

            this.WhenAnyValue(x => x.FastaDbFilePath).Subscribe(_ => LoadFastaFile());

            this.WhenAnyValue(x => x.FastaEntries.Count, x => x.SelectedSequence)
            .Select(x => FastaEntries.Where(entry => entry.ProteinSequenceText.Contains(x.Item2)).ToArray())
            .Subscribe(entries => SequenceProteins = entries);

            // Remove search modification when its Remove property is set to true.
            SearchModifications.ItemChanged.Where(x => x.PropertyName == "Remove")
            .Select(x => x.Sender).Where(sender => sender.Remove)
            .Subscribe(searchMod => SearchModifications.Remove(searchMod));

            ModificationsUpdated = false;
        }