Beispiel #1
0
        internal static Tuple <int, string, List <object> > GetExportDataForBlock(Block block, int blockNumber, string bookId, VoiceActor.VoiceActor voiceActor = null, string singleVoiceNarratorOverride = null, bool useCharacterIdInScript = true)
        {
            // NOTE: if the order here changes, there may be changes needed in GenerateExcelFile
            List <object> list = new List <object>();

            list.Add(blockNumber);
            if (voiceActor != null)
            {
                list.Add(voiceActor.Name);
            }
            list.Add(block.StyleTag);
            list.Add(bookId);
            list.Add(block.ChapterNumber);
            list.Add(block.InitialStartVerseNumber);
            string characterId;

            if (singleVoiceNarratorOverride != null)
            {
                characterId = singleVoiceNarratorOverride;
            }
            else
            {
                characterId = useCharacterIdInScript ? block.CharacterIdInScript : block.CharacterId;
            }
            list.Add(CharacterVerseData.IsCharacterStandard(characterId) ? CharacterVerseData.GetStandardCharacterIdAsEnglish(characterId) : characterId);
            list.Add(block.Delivery);
            list.Add(block.GetText(true));
            list.Add(block.GetText(false).Length);
            return(new Tuple <int, string, List <object> >(voiceActor == null ? -1 : voiceActor.Id, bookId, list));
        }
        private List <object> GetExportDataForReferenceBlock(Block refBlock, string bookId)
        {
            var row = new List <object>();

            row.Add(null);
            if (IncludeVoiceActors)
            {
                row.Add(null);
            }
            row.Add(refBlock.StyleTag);
            row.Add(bookId);
            row.Add(refBlock.ChapterNumber);
            row.Add(refBlock.InitialVerseNumberOrBridge);
            row.Add((CharacterVerseData.IsCharacterStandard(refBlock.CharacterId) ?
                     CharacterVerseData.GetStandardCharacterIdAsEnglish(refBlock.CharacterId) : refBlock.CharacterId));
            if (LocalizationManager.UILanguageId != "en")
            {
                row.Add(null);
            }
            row.Add(refBlock.Delivery);
            row.Add(null);
            row.Add(refBlock.GetText(true, true));
            if (Project.ReferenceText.HasSecondaryReferenceText)
            {
                row.Add(refBlock.PrimaryReferenceText);
            }
            row.Add(0);
            return(row);
        }
        private void UpdateDisplay()
        {
            var blockRef      = m_viewModel.GetBlockVerseRef();
            int versesInBlock = m_viewModel.CurrentBlock.LastVerse - blockRef.VerseNum;
            var displayedRefMinusBlockStartRef = m_scriptureReference.VerseControl.VerseRef.BBBCCCVVV - blockRef.BBBCCCVVV;

            if (displayedRefMinusBlockStartRef < 0 || displayedRefMinusBlockStartRef > versesInBlock)
            {
                m_scriptureReference.VerseControl.VerseRef = m_viewModel.GetBlockVerseRef();
            }
            m_labelXofY.Visible = m_viewModel.IsCurrentBlockRelevant;
            Debug.Assert(m_viewModel.RelevantBlockCount >= m_viewModel.CurrentBlockDisplayIndex);
            m_labelXofY.Text      = string.Format(m_xOfYFmt, m_viewModel.CurrentBlockDisplayIndex, m_viewModel.RelevantBlockCount);
            m_chkSingleVoice.Text = string.Format(m_singleVoiceCheckboxFmt, m_viewModel.CurrentBookId);

            m_viewModel.GetBlockVerseRef().SendScrReference();

            HideCharacterFilter();
            m_btnAssign.Enabled = false;

            LoadCharacterListBox(m_viewModel.GetCharactersForCurrentReference());
            UpdateShortcutDisplay();

            m_chkSingleVoice.Checked = m_viewModel.IsCurrentBookSingleVoice;

            m_menuBtnSplitBlock.Enabled = !CharacterVerseData.IsCharacterStandard(m_viewModel.CurrentBlock.CharacterId, false);
        }
        public void DataIntegrity_AllCharacterIdsAndDefaultCharactersHaveCharacterDetail()
        {
            CharacterDetailData.TabDelimitedCharacterDetailData = Resources.CharacterDetail;             //resets cache

            var           charactersHavingDetail   = CharacterDetailData.Singleton.GetAll().Select(d => d.CharacterId).ToList();
            ISet <string> missingCharacters        = new SortedSet <string>();
            ISet <string> missingDefaultCharacters = new SortedSet <string>();

            foreach (CharacterVerse cv in ControlCharacterVerseData.Singleton.GetAllQuoteInfo())
            {
                if (!charactersHavingDetail.Contains(cv.Character))
                {
                    if (CharacterVerseData.IsCharacterStandard(cv.Character) || cv.Character == CharacterVerseData.kNeedsReview)
                    {
                        continue;
                    }
                    // For interruptions, we actually never pay any attention to the character ID. They are always narrator.
                    // But in some verses we have both a normal narrator Quotation and an interruption, so to avoid a problem
                    // in other data integrity checks, we just use the dummy character id "interruption-XXX" (where XXX is the
                    // 3-letter book code.
                    if (cv.QuoteType == QuoteType.Interruption || cv.Character.StartsWith("interruption-"))
                    {
                        continue;
                    }

                    var characters = cv.Character.Split('/');
                    if (characters.Length > 1)
                    {
                        foreach (var character in characters.Where(character => !charactersHavingDetail.Contains(character)))
                        {
                            missingCharacters.Add(character);
                        }
                    }
                    else
                    {
                        missingCharacters.Add(cv.Character);
                    }
                }

                if (!(string.IsNullOrEmpty(cv.DefaultCharacter) || charactersHavingDetail.Contains(cv.DefaultCharacter)))
                {
                    if (CharacterVerseData.IsCharacterStandard(cv.DefaultCharacter) || cv.DefaultCharacter == CharacterVerseData.kNeedsReview)
                    {
                        continue;
                    }

                    missingDefaultCharacters.Add(cv.DefaultCharacter);
                }
            }

            Assert.False(missingCharacters.Any() || missingDefaultCharacters.Any(),
                         (missingCharacters.Any() ? "Characters in Character-Verse data but not in Character-Detail:" +
                          Environment.NewLine +
                          missingCharacters.OnePerLineWithIndent() : "") +
                         (missingDefaultCharacters.Any() ? Environment.NewLine +
                          "Default characters in Character-Verse data but not in Character-Detail:" +
                          Environment.NewLine +
                          missingDefaultCharacters.OnePerLineWithIndent() : ""));
        }
Beispiel #5
0
        public static int MigrateDeprecatedCharacterIds(Project project)
        {
            var cvInfo = new CombinedCharacterVerseData(project);
            var characterDetailDictionary = project.AllCharacterDetailDictionary;
            int numberOfChangesMade       = 0;       // For testing

            foreach (BookScript book in project.Books)
            {
                int bookNum = BCVRef.BookToNumber(book.BookId);

                foreach (Block block in book.GetScriptBlocks().Where(block => block.CharacterId != null &&
                                                                     block.CharacterId != CharacterVerseData.kUnknownCharacter &&
                                                                     !CharacterVerseData.IsCharacterStandard(block.CharacterId)))
                {
                    if (block.CharacterId == CharacterVerseData.kAmbiguousCharacter)
                    {
                        if (block.UserConfirmed)
                        {
                            block.UserConfirmed = false;
                            numberOfChangesMade++;
                        }
                    }
                    else
                    {
                        var unknownCharacter = !characterDetailDictionary.ContainsKey(block.CharacterIdInScript);
                        if (unknownCharacter && project.ProjectCharacterVerseData.GetCharacters(bookNum, block.ChapterNumber, block.InitialStartVerseNumber,
                                                                                                block.InitialEndVerseNumber, block.LastVerseNum).
                            Any(c => c.Character == block.CharacterId && c.Delivery == (block.Delivery ?? "")))
                        {
                            // PG-471: This is a known character who spoke in an unexpected location and was therefore added to the project CV file,
                            // but was subsequently removed or renamed from the master character detail list.
                            project.ProjectCharacterVerseData.Remove(bookNum, block.ChapterNumber, block.InitialStartVerseNumber,
                                                                     block.InitialEndVerseNumber, block.CharacterId, block.Delivery ?? "");
                            block.CharacterId         = CharacterVerseData.kUnknownCharacter;
                            block.CharacterIdInScript = null;
                            numberOfChangesMade++;
                        }
                        else
                        {
                            var characters = cvInfo.GetCharacters(bookNum, block.ChapterNumber, block.InitialStartVerseNumber, block.InitialEndVerseNumber, block.LastVerseNum).ToList();
                            if (unknownCharacter || !characters.Any(c => c.Character == block.CharacterId && c.Delivery == (block.Delivery ?? "")))
                            {
                                if (characters.Count(c => c.Character == block.CharacterId) == 1)
                                {
                                    block.Delivery = characters.First(c => c.Character == block.CharacterId).Delivery;
                                }
                                else
                                {
                                    block.SetCharacterAndDelivery(characters);
                                }
                                numberOfChangesMade++;
                            }
                        }
                    }
                }
            }
            return(numberOfChangesMade);
        }
Beispiel #6
0
        public void DataIntegrity_AllCharacterIdsAndDefaultCharactersHaveCharacterDetail(bool readHypotheticalAsNarrator)
        {
            ControlCharacterVerseData.ReadHypotheticalAsNarrator = readHypotheticalAsNarrator;
            CharacterDetailData.TabDelimitedCharacterDetailData  = Resources.CharacterDetail;            //resets cache

            try
            {
                var           charactersHavingDetail   = CharacterDetailData.Singleton.GetAll().Select(d => d.CharacterId).ToList();
                ISet <string> missingCharacters        = new SortedSet <string>();
                ISet <string> missingDefaultCharacters = new SortedSet <string>();
                foreach (CharacterVerse cv in ControlCharacterVerseData.Singleton.GetAllQuoteInfo())
                {
                    if (!charactersHavingDetail.Contains(cv.Character))
                    {
                        if (CharacterVerseData.IsCharacterStandard(cv.Character))
                        {
                            continue;
                        }

                        var characters = cv.Character.Split('/');
                        if (characters.Length > 1)
                        {
                            foreach (var character in characters.Where(character => !charactersHavingDetail.Contains(character)))
                            {
                                missingCharacters.Add(character);
                            }
                        }
                        else
                        {
                            missingCharacters.Add(cv.Character);
                        }
                    }

                    if (!(string.IsNullOrEmpty(cv.DefaultCharacter) || charactersHavingDetail.Contains(cv.DefaultCharacter)))
                    {
                        if (CharacterVerseData.IsCharacterStandard(cv.DefaultCharacter))
                        {
                            continue;
                        }

                        missingDefaultCharacters.Add(cv.DefaultCharacter);
                    }
                }

                Assert.False(missingCharacters.Any() || missingDefaultCharacters.Any(),
                             "Characters in Character-Verse data but not in Character-Detail:" +
                             Environment.NewLine +
                             missingCharacters.OnePerLineWithIndent() +
                             Environment.NewLine +
                             "Default characters in Character-Verse data but not in Character-Detail:" +
                             Environment.NewLine +
                             missingDefaultCharacters.OnePerLineWithIndent());
            }
            finally
            {
                ControlCharacterVerseData.ReadHypotheticalAsNarrator = false;
            }
        }
        internal static List <object> GetExportDataForBlock(Block block, int blockNumber, string bookId,
                                                            VoiceActor.VoiceActor voiceActor, string singleVoiceNarratorOverride, bool useCharacterIdInScript, bool includeSecondaryDirectorsGuide)
        {
            // NOTE: if the order here changes, there may be changes needed in GenerateExcelFile
            List <object> list = new List <object>();

            list.Add(blockNumber);
            if (voiceActor != null)
            {
                list.Add(voiceActor.Name);
            }
            list.Add(block.StyleTag);
            list.Add(bookId);
            list.Add(block.ChapterNumber);
            list.Add(block.InitialVerseNumberOrBridge);
            string characterId;

            if (singleVoiceNarratorOverride != null)
            {
                characterId = singleVoiceNarratorOverride;
            }
            else
            {
                characterId = useCharacterIdInScript ? block.CharacterIdInScript : block.CharacterId;
            }
            list.Add(CharacterVerseData.IsCharacterStandard(characterId) ? CharacterVerseData.GetStandardCharacterIdAsEnglish(characterId) : characterId);

            // add a column for the localized character id
            if (LocalizationManager.UILanguageId != "en")
            {
                list.Add(CharacterVerseData.GetCharacterNameForUi(characterId));
            }

            list.Add(block.Delivery);
            list.Add(block.GetText(true));
            list.Add(block.PrimaryReferenceText);
            if (includeSecondaryDirectorsGuide)
            {
                var primaryRefBlock = (block.MatchesReferenceText) ? block.ReferenceBlocks.Single() : null;
                if (primaryRefBlock != null && primaryRefBlock.MatchesReferenceText)
                {
                    list.Add(primaryRefBlock.PrimaryReferenceText);
                }
                else
                {
                    list.Add(null);
                }
            }
            list.Add(block.GetText(false).Length);
            return(list);
        }
 internal bool AddToReservedGroupIfAppropriate(string characterId)
 {
     if (CharacterVerseData.IsCharacterOfType(characterId, CharacterVerseData.StandardCharacter.Narrator))
     {
         AddToBestNarratorGroup(characterId);
         return(true);
     }
     if (CharacterVerseData.IsCharacterStandard(characterId, false))
     {
         if (ExtraBiblicalGroup != null)
         {
             ExtraBiblicalGroup.CharacterIds.Add(characterId);
             return(true);
         }
     }
     return(false);
 }
Beispiel #9
0
        public string CharacterSelect(int splitId, IEnumerable <AssignCharacterViewModel.Character> characters = null)
        {
            if ((characters == null) && !string.IsNullOrEmpty(s_characterSelect))
            {
                return(string.Format(s_characterSelect, splitId));
            }

            const string optionTemplate = "<option value=\"{0}\">{1}</option>";
            var          sb             = new StringBuilder("<select class=\"select-character\" data-splitid=\"{0}\"><option value=\"\"></option>");

            if (characters != null)
            {
                foreach (var character in characters)
                {
                    if (CharacterVerseData.IsCharacterStandard(character.CharacterId))
                    {
                    }
                    if (character.IsNarrator)
                    {
                        sb.AppendFormat(optionTemplate,
                                        CharacterVerseData.GetStandardCharacterId(BookCode, CharacterVerseData.StandardCharacter.Narrator),
                                        character.LocalizedDisplay);
                    }
                    else
                    {
                        var stdCharacterType = CharacterVerseData.GetStandardCharacterType(character.CharacterId);
                        if (stdCharacterType == CharacterVerseData.StandardCharacter.NonStandard)
                        {
                            sb.AppendFormat(optionTemplate, character.CharacterId, character.LocalizedDisplay);
                        }
                        else
                        {
                            sb.AppendFormat(optionTemplate,
                                            CharacterVerseData.GetStandardCharacterId(BookCode, stdCharacterType),
                                            character.LocalizedDisplay);
                        }
                    }
                }
            }

            sb.Append("</select>");
            s_characterSelect = sb.ToString();

            return(string.Format(s_characterSelect, splitId));
        }
Beispiel #10
0
        public virtual Color GetForeColorByCharacterId(string characterId)
        {
            if (string.IsNullOrWhiteSpace(characterId))
            {
                return(SpeechNonCharacter);
            }

            if (characterId == "Jesus")
            {
                return(SpeechJesus);
            }

            if (!CharacterVerseData.IsCharacterStandard(characterId))
            {
                return(SpeechCharacter);
            }

            return(SpeechNonCharacter);
        }
Beispiel #11
0
        public void DataIntegrity_AllNonNarratorCharacterDetailsHaveCharacterIdOrDefaultCharacter()
        {
            var characterIds = new List <string>(ControlCharacterVerseData.Singleton.GetAllQuoteInfo().Select(d => d.Character)
                                                 .SelectMany(characters => characters.Split('/')));

            var           defaultCharacters = ControlCharacterVerseData.Singleton.GetAllQuoteInfo().Select(d => d.DefaultCharacter).ToList();
            ISet <string> missingCharacters = new SortedSet <string>();

            foreach (string character in CharacterDetailData.Singleton.GetAll().Select(d => d.CharacterId))
            {
                if (!CharacterVerseData.IsCharacterStandard(character) &&
                    (!(characterIds.Contains(character) || defaultCharacters.Contains(character))))
                {
                    missingCharacters.Add(character);
                }
            }
            Assert.False(missingCharacters.Any(),
                         "Characters in Character-Detail data but not in Character-Verse data:" +
                         Environment.NewLine +
                         missingCharacters.OnePerLineWithIndent());
        }
        public bool IsModified(Character newCharacter, Delivery newDelivery)
        {
            Block currentBlock = CurrentBlock;

            if (CharacterVerseData.IsCharacterStandard(currentBlock.CharacterId, false))
            {
                return(false);                // Can't change these.
            }
            if (newCharacter == null)
            {
                return(!(currentBlock.CharacterIsUnclear() || currentBlock.CharacterId == null));
            }
            if (newCharacter.IsNarrator)
            {
                if (!currentBlock.CharacterIs(CurrentBookId, CharacterVerseData.StandardCharacter.Narrator))
                {
                    return(true);
                }
            }
            else if (newCharacter.CharacterId != currentBlock.CharacterId)
            {
                return(true);
            }

            if (newDelivery == null)
            {
                return(true);
            }

            if (newDelivery.IsNormal)
            {
                return(!string.IsNullOrEmpty(currentBlock.Delivery));
            }

            return(newDelivery.Text != currentBlock.Delivery);
        }
Beispiel #13
0
        // internal for testing
        internal static int MigrateDeprecatedCharacterIds(Project project)
        {
            var cvInfo = new CombinedCharacterVerseData(project);
            var characterDetailDictionary = CharacterDetailData.Singleton.GetDictionary();
            int numberOfChangesMade       = 0;       // For testing

            foreach (BookScript book in project.Books)
            {
                int bookNum = BCVRef.BookToNumber(book.BookId);

                foreach (Block block in book.GetScriptBlocks().Where(block => block.CharacterId != null &&
                                                                     block.CharacterId != CharacterVerseData.kUnexpectedCharacter &&
                                                                     !CharacterVerseData.IsCharacterStandard(block.CharacterId)))
                {
                    if (block.CharacterId == CharacterVerseData.kAmbiguousCharacter)
                    {
                        if (block.UserConfirmed)
                        {
                            block.UserConfirmed = false;
                            numberOfChangesMade++;
                        }
                    }
                    else
                    {
                        var knownFactoryCharacter = characterDetailDictionary.ContainsKey(block.CharacterIdInScript);
                        var unknownCharacter      = !knownFactoryCharacter && !project.IsProjectSpecificCharacter(block.CharacterIdInScript);
                        if (unknownCharacter && project.ProjectCharacterVerseData.GetCharacters(bookNum, block.ChapterNumber, block.AllVerses)
                            .Any(c => c.Character == block.CharacterId && c.Delivery == (block.Delivery ?? "")))
                        {
                            // PG-471: This is a formerly known character who spoke in an unexpected location and was therefore added to the project CV file,
                            // but was subsequently removed or renamed from the master character detail list.
                            project.ProjectCharacterVerseData.RemoveAllEntriesForBlock(bookNum, block);
                            block.UserConfirmed       = false;
                            block.CharacterId         = CharacterVerseData.kUnexpectedCharacter;
                            block.CharacterIdInScript = null;
                            numberOfChangesMade++;
                        }
                        else
                        {
                            var characters = cvInfo.GetCharacters(bookNum, block.ChapterNumber, block.AllVerses,
                                                                  includeAlternatesAndRareQuotes: true, includeNarratorOverrides: true);
                            if (unknownCharacter || !characters.Any(c => c.Character == block.CharacterId && c.Delivery == (block.Delivery ?? "")))
                            {
                                if (characters.Count(c => c.Character == block.CharacterId) == 1)
                                {
                                    // Note: For normal projects, if we get here, we will almost certainly be changing the delivery.
                                    // However, this is not the case when processing the reference text project using Glyssen after
                                    // generating it from the Director's Guide because we get here repeatedly for blocks assigned to
                                    // multiple-character IDs because we intentionally do not set the CharacterIdOverrideForScript,
                                    // preferring rather to leave it up to the program to assign the then-current default to the
                                    // vernacular block at the time the matchup is applied.
                                    var onlyKnownDelivery = characters.First(c => c.Character == block.CharacterId).Delivery;
                                    if ((block.Delivery ?? "") != onlyKnownDelivery)
                                    {
                                        block.Delivery = characters.First(c => c.Character == block.CharacterId).Delivery;
                                        numberOfChangesMade++;
                                    }
                                }
                                else
                                {
                                    var match = characters.SingleOrDefault(c => c.ResolvedDefaultCharacter == block.CharacterId &&
                                                                           c.Delivery == (block.Delivery ?? ""));
                                    if (match != null)
                                    {
                                        block.CharacterId         = match.Character;
                                        block.CharacterIdInScript = match.ResolvedDefaultCharacter;
                                    }
                                    else
                                    {
                                        block.SetCharacterAndDelivery(characters);
                                    }
                                    numberOfChangesMade++;
                                }
                            }
                            else if (knownFactoryCharacter)
                            {
                                var projectCvEntryRemoved = false;
                                foreach (var verseNum in block.AllVerses.SelectMany(v => v.AllVerseNumbers))
                                {
                                    if (ControlCharacterVerseData.Singleton.GetCharacters(bookNum, block.ChapterNumber, verseNum)
                                        .Any(cv => cv.Character == block.CharacterId && cv.Delivery == (block.Delivery ?? "")))
                                    {
                                        projectCvEntryRemoved |= project.ProjectCharacterVerseData.RemoveEntryForBlock(bookNum, block, verseNum);
                                    }
                                }


                                if (project.RemoveProjectCharacterDetail(block.CharacterId) || projectCvEntryRemoved)
                                {
                                    numberOfChangesMade++;
                                }
                            }
                        }
                    }
                }
            }
            return(numberOfChangesMade);
        }
        internal List <CharacterGroup> GenerateCharacterGroups()
        {
            List <CharacterGroup> characterGroups = CreateGroupsForActors(m_project.VoiceActorList.Actors).ToList();

            if (characterGroups.Count == 0)
            {
                return(characterGroups);                // REVIEW: Maybe we should throw an exception instead.
            }
            m_project.SetDefaultCharacterGroupGenerationPreferences();

            List <VoiceActor.VoiceActor> nonCameoActors = m_project.VoiceActorList.Actors.Where(a => !a.IsCameo).ToList();

            if (nonCameoActors.Count == 0)
            {
                return(characterGroups);                // All cameo actors! This should never happen.
            }
            var sortedDict = from entry in m_keyStrokesByCharacterId orderby entry.Value descending select entry;

            IReadOnlyDictionary <string, CharacterDetail> characterDetails = m_project.AllCharacterDetailDictionary;
            var includedCharacterDetails = characterDetails.Values.Where(c => sortedDict.Select(e => e.Key).Contains(c.CharacterId)).ToList();

            // In the first loop, we're looking for actors that could only possibly play one character role.
            // Since we're not doing strict age matching, this is most likely only to find any candidates in
            // the case of children (and then only if the project includes a limited selection of books)
            var characterDetailsUniquelyMatchedToActors = new Dictionary <CharacterDetail, List <VoiceActor.VoiceActor> >();

            foreach (var actor in nonCameoActors)
            {
                // After we find the second match, we can quit looking because we're only interested in unique matches.
                var matches = includedCharacterDetails.Where(c => !CharacterVerseData.IsCharacterStandard(c.CharacterId) && actor.Matches(c)).Take(2).ToList();
                if (matches.Any())
                {
                    if (matches.Count == 1)
                    {
                        var characterDetail = matches.First();
                        if (characterDetailsUniquelyMatchedToActors.ContainsKey(characterDetail))
                        {
                            characterDetailsUniquelyMatchedToActors[characterDetail].Add(actor);
                        }
                        else
                        {
                            characterDetailsUniquelyMatchedToActors[characterDetail] = new List <VoiceActor.VoiceActor> {
                                actor
                            }
                        };
                    }
                }
            }

            // This loop uses the results of the previous one to add the characters to a group, and to close that
            // group to further additions. If there's more than one candidate actor, we pick one arbitrarily,
            // since afterwards we'll be clearing actor names anyway.
            // Since all groups now have an actor assigned up-front, at the
            // end of the generation process, we'll need to clear all actor assignments except for the ones that
            // are pre-determined here.
            List <int> actorsWithRealAssignments = new List <int>();

            foreach (var characterDetailToActors in characterDetailsUniquelyMatchedToActors)
            {
                var matchingActors = characterDetailToActors.Value;

                var matchingGroups = characterGroups.Where(g => matchingActors.Any(a => a.Id == g.VoiceActorId)).ToList();

                matchingGroups.First().CharacterIds.Add(characterDetailToActors.Key.CharacterId);

                if (matchingGroups.Count == 1)
                {
                    actorsWithRealAssignments.Add(matchingGroups[0].VoiceActorId);
                }

                foreach (var characterGroup in matchingGroups)
                {
                    characterGroup.Closed = true;
                }
            }

            // TODO: Make sure we didn't close all the groups (unless we assigned all the character IDs

            foreach (var character in includedCharacterDetails)
            {
                var matchingActors = characterGroups.Where(g => !g.Closed).Select(g => g.VoiceActor).Where(a => a.Matches(character)).ToList();
                if (matchingActors.Count == 1)
                {
                    var            matchingActor = matchingActors.First();
                    CharacterGroup groupForActor = characterGroups.Single(g => g.VoiceActorId == matchingActor.Id);
                    groupForActor.CharacterIds.Add(character.CharacterId);
                    actorsWithRealAssignments.Add(matchingActor.Id);
                }
            }

            int maxMaleNarrators   = m_project.CharacterGroupGenerationPreferences.NumberOfMaleNarrators;
            int maxFemaleNarrators = m_project.CharacterGroupGenerationPreferences.NumberOfFemaleNarrators;

            TrialGroupConfiguration bestConfiguration = null;

            do
            {
                var trialConfigurationsForNarratorsAndExtras = TrialGroupConfiguration.GeneratePossibilities(characterGroups,
                                                                                                             ref maxMaleNarrators, ref maxFemaleNarrators, includedCharacterDetails, m_project);

                if (trialConfigurationsForNarratorsAndExtras.Any())
                {
                    foreach (var configuration in trialConfigurationsForNarratorsAndExtras)
                    {
                        foreach (var entry in sortedDict)
                        {
                            string characterId = entry.Key;

                            if (configuration.Groups.Any(g => g.CharacterIds.Contains(characterId)))
                            {
                                continue;
                            }

                            CharacterDetail characterDetail;
                            if (!characterDetails.TryGetValue(characterId, out characterDetail))
                            {
                                if (characterId == CharacterVerseData.AmbiguousCharacter || characterId == CharacterVerseData.UnknownCharacter)
                                {
                                    continue;                                     // This should never happen in production code!
                                }
                                //Debug.WriteLine("No character details for unexpected character ID (see PG-): " + characterId);
                                //continue;
                                throw new KeyNotFoundException("No character details for unexpected character ID (see PG-471): " + characterId);
                            }

                            if (!configuration.AddToReservedGroupIfAppropriate(characterId))
                            {
                                AddCharacterToBestGroup(characterDetail, configuration);
                            }
                        }
                    }
                    bestConfiguration = TrialGroupConfiguration.Best(trialConfigurationsForNarratorsAndExtras, bestConfiguration);
                    if (bestConfiguration.MinimumProximity >= Proximity.kDefaultMinimumProximity)
                    {
                        return(GetFinalizedGroups(bestConfiguration.Groups, actorsWithRealAssignments));
                    }
                }
                if (maxMaleNarrators == 0)
                {
                    maxFemaleNarrators--;
                }
                else if (maxFemaleNarrators == 0)
                {
                    maxMaleNarrators--;
                }
                else if (bestConfiguration != null && (bestConfiguration.GroupWithWorstProximity.ContainsCharacterWithGender(CharacterGender.Female) ||
                                                       bestConfiguration.GroupWithWorstProximity.ContainsCharacterWithGender(CharacterGender.PreferFemale)))
                {
                    maxFemaleNarrators--;
                }
                else if (bestConfiguration != null && (bestConfiguration.GroupWithWorstProximity.ContainsCharacterWithGender(CharacterGender.Male) ||
                                                       bestConfiguration.GroupWithWorstProximity.ContainsCharacterWithGender(CharacterGender.PreferMale)))
                {
                    maxMaleNarrators--;
                }
                else if (maxMaleNarrators > maxFemaleNarrators)
                {
                    maxMaleNarrators--;
                }
                else
                {
                    maxFemaleNarrators--;
                }
            } while (maxMaleNarrators + maxFemaleNarrators > 0);

            Debug.Assert(bestConfiguration != null);
            return(GetFinalizedGroups(bestConfiguration.Groups, actorsWithRealAssignments));
        }
            internal static List <TrialGroupConfiguration> GeneratePossibilities(List <CharacterGroup> characterGroups,
                                                                                 ref int numberOfMaleNarratorGroups, ref int numberOfFemaleNarratorGroups, List <CharacterDetail> includedCharacterDetails, Project project)
            {
                var list = new List <TrialGroupConfiguration>(2);

                var availableAdultGroups  = GetGroupsAvailableForNarratorOrExtraBiblical(characterGroups);
                var availableMaleGroups   = availableAdultGroups.Where(g => g.VoiceActor.Gender == ActorGender.Male).ToList();
                var availableFemaleGroups = availableAdultGroups.Where(g => g.VoiceActor.Gender == ActorGender.Female).ToList();

                numberOfMaleNarratorGroups   = Math.Min(numberOfMaleNarratorGroups, availableMaleGroups.Count);
                numberOfFemaleNarratorGroups = Math.Min(numberOfFemaleNarratorGroups, availableFemaleGroups.Count);
                if (numberOfMaleNarratorGroups + numberOfFemaleNarratorGroups == 0)
                {
                    numberOfMaleNarratorGroups = 1;
                }
                int numberOfExtraBiblicalGroups = Math.Min(1, includedCharacterDetails.Count(c => CharacterVerseData.IsCharacterStandard(c.CharacterId, false)));

                if (availableMaleGroups.Count >= Math.Max(Math.Min(1, numberOfExtraBiblicalGroups), numberOfMaleNarratorGroups))
                {
                    list.Add(new TrialGroupConfiguration(characterGroups, numberOfMaleNarratorGroups, numberOfFemaleNarratorGroups,
                                                         Math.Min(1, numberOfExtraBiblicalGroups), 0, project, includedCharacterDetails));
                }

                if (numberOfExtraBiblicalGroups > 0 && availableFemaleGroups.Count >= Math.Max(1, numberOfFemaleNarratorGroups))
                {
                    list.Add(new TrialGroupConfiguration(characterGroups, numberOfMaleNarratorGroups, numberOfFemaleNarratorGroups,
                                                         0, numberOfExtraBiblicalGroups, project, includedCharacterDetails));
                }

                if (!list.Any())
                {
                    list.Add(new TrialGroupConfiguration(characterGroups, numberOfMaleNarratorGroups, numberOfFemaleNarratorGroups,
                                                         numberOfExtraBiblicalGroups, 0, project, includedCharacterDetails));
                    numberOfMaleNarratorGroups   = list[0].NarratorGroups.Count(g => g.VoiceActor.Gender == ActorGender.Male);
                    numberOfFemaleNarratorGroups = list[0].NarratorGroups.Count(g => g.VoiceActor.Gender == ActorGender.Female);
                    Debug.Assert(numberOfMaleNarratorGroups + numberOfFemaleNarratorGroups == 1);
                }
                return(list);
            }
 /// <summary>
 /// Gets whether the specified block represents Scripture text. (Only Scripture blocks can have their
 /// character/delivery changed. Book titles, chapters, and section heads have characters assigned
 /// programmatically and cannot be changed.)
 /// </summary>
 public bool GetIsBlockScripture(Block block)
 {
     return(!CharacterVerseData.IsCharacterStandard(block.CharacterId, false));
 }
        private bool IsRelevant(Block block)
        {
            if (block.MultiBlockQuote == MultiBlockQuote.Continuation || block.MultiBlockQuote == MultiBlockQuote.ChangeOfDelivery)
            {
                return(false);
            }
            if ((Mode & BlocksToDisplay.ExcludeUserConfirmed) > 0 && block.UserConfirmed)
            {
                return(false);
            }
            if ((Mode & BlocksToDisplay.NeedAssignments) > 0)
            {
                return(block.UserConfirmed || block.CharacterIsUnclear());
            }
            if ((Mode & BlocksToDisplay.AllExpectedQuotes) > 0)
            {
                if (!GetIsBlockScripture(block))
                {
                    return(false);
                }
                return(ControlCharacterVerseData.Singleton.GetCharacters(CurrentBookId, block.ChapterNumber, block.InitialStartVerseNumber,
                                                                         block.LastVerse, versification: Versification).Any(c => c.IsExpected));
            }
            if ((Mode & BlocksToDisplay.MissingExpectedQuote) > 0)
            {
                if (block.IsQuote || CharacterVerseData.IsCharacterStandard(block.CharacterId, false))
                {
                    return(false);
                }
                IEnumerable <BCVRef> versesWithPotentialMissingQuote =
                    ControlCharacterVerseData.Singleton.GetCharacters(CurrentBookId, block.ChapterNumber, block.InitialStartVerseNumber,
                                                                      block.LastVerse, versification: Versification).Where(c => c.IsExpected).Select(c => c.BcvRef);
                if (!versesWithPotentialMissingQuote.Any())
                {
                    return(false);
                }
                foreach (BCVRef verse in versesWithPotentialMissingQuote)
                {
                    if (m_navigator.PeekBackwardWithinBookWhile(b => b.ChapterNumber == verse.Chapter &&
                                                                b.LastVerse == verse.Verse).All(b => !b.IsQuote) &&
                        m_navigator.PeekForwardWithinBookWhile(b => b.ChapterNumber == verse.Chapter &&
                                                               b.InitialStartVerseNumber == verse.Verse).All(b => !b.IsQuote))
                    {
                        return(true);
                    }
                }
                return(false);
            }
            if ((Mode & BlocksToDisplay.MoreQuotesThanExpectedSpeakers) > 0)
            {
                if (!block.IsQuote)
                {
                    return(false);
                }

                var expectedSpeakers = ControlCharacterVerseData.Singleton.GetCharacters(CurrentBookId, block.ChapterNumber, block.InitialStartVerseNumber,
                                                                                         block.InitialEndVerseNumber, versification: Versification).Distinct(new CharacterEqualityComparer()).Count();

                var actualquotes = 1;                 // this is the quote represented by the given block.

                if (actualquotes > expectedSpeakers)
                {
                    return(true);
                }

                // Check surrounding blocks to count quote blocks for same verse.
                actualquotes += m_navigator.PeekBackwardWithinBookWhile(b => b.ChapterNumber == block.ChapterNumber &&
                                                                        b.InitialStartVerseNumber == block.InitialStartVerseNumber)
                                .Count(b => b.IsQuote && (b.MultiBlockQuote == MultiBlockQuote.Start || b.MultiBlockQuote == MultiBlockQuote.None));

                if (actualquotes > expectedSpeakers)
                {
                    return(true);
                }

                actualquotes += m_navigator.PeekForwardWithinBookWhile(b => b.ChapterNumber == block.ChapterNumber &&
                                                                       b.InitialStartVerseNumber == block.InitialStartVerseNumber)
                                .Count(b => b.IsQuote && (b.MultiBlockQuote == MultiBlockQuote.Start || b.MultiBlockQuote == MultiBlockQuote.None));

                return(actualquotes > expectedSpeakers);
            }
            if ((Mode & BlocksToDisplay.AllScripture) > 0)
            {
                return(GetIsBlockScripture(block));
            }
            if ((Mode & BlocksToDisplay.AllQuotes) > 0)
            {
                return(block.IsQuote);
            }
            return(false);
        }
Beispiel #18
0
        public CastSizePlanningViewModel(Project project)
        {
            m_project = project;
            var prefs = m_project.CharacterGroupGenerationPreferences;

            m_narratorsOption = prefs.NarratorsOption;
            if (m_narratorsOption == NarratorsOption.Custom)
            {
                m_customMaleNarratorCount   = prefs.NumberOfMaleNarrators;
                m_customFemaleNarratorCount = prefs.NumberOfFemaleNarrators;
                if (m_customMaleNarratorCount + m_customFemaleNarratorCount > MaximumNarratorsValue)
                {
                    var remainingFemaleNarrators = MaximumNarratorsValue - MaleNarrators;
                    m_customFemaleNarratorCount = Math.Max(remainingFemaleNarrators, 0);
                    if (remainingFemaleNarrators < 0)
                    {
                        m_customMaleNarratorCount += remainingFemaleNarrators;
                    }
                }
            }
            else
            {
                m_customMaleNarratorCount   = 0;
                m_customFemaleNarratorCount = 0;
            }

            // if not yet specified, use "Narration by Author"
            if (m_narratorsOption == NarratorsOption.NotSet)
            {
                m_narratorsOption = NarratorsOption.NarrationByAuthor;
            }

            var smallCast           = new CastSizeRowValues(2, 2, 0);
            int extraCharacterCount = 0;

            foreach (var bookId in m_project.IncludedBooks.Where(b => !b.SingleVoice).Select(b => b.BookId))
            {
                extraCharacterCount = 1;
                // OT numbers are based on actual generation from Kuna San Blas project
                // NT numbers are based on actual generation from Acholi project
                switch (bookId)
                {
                case "GEN":
                    smallCast.Male   = Math.Max(smallCast.Male, 9);
                    smallCast.Female = 3;
                    break;

                case "EXO":
                    smallCast.Male   = Math.Max(smallCast.Male, 6);
                    smallCast.Female = 3;
                    break;

                case "LEV":
                    smallCast.Male = Math.Max(smallCast.Male, 3);
                    break;

                case "NUM":
                    smallCast.Male = Math.Max(smallCast.Male, 8);
                    break;

                case "DEU":
                    smallCast.Male = Math.Max(smallCast.Male, 2);
                    break;

                case "JOS":
                    smallCast.Male = Math.Max(smallCast.Male, 7);
                    break;

                case "JDG":
                    smallCast.Male = Math.Max(smallCast.Male, 8);
                    break;

                case "RUT":
                    smallCast.Male   = Math.Max(smallCast.Male, 3);
                    smallCast.Female = 4;
                    break;

                case "1SA":
                    smallCast.Male = Math.Max(smallCast.Male, 11);
                    break;

                case "2SA":
                    smallCast.Male = Math.Max(smallCast.Male, 12);
                    break;

                case "1KI":
                    smallCast.Male = Math.Max(smallCast.Male, 10);
                    break;

                case "2KI":
                    smallCast.Male = Math.Max(smallCast.Male, 9);
                    break;

                case "1CH":
                    smallCast.Male = Math.Max(smallCast.Male, 5);
                    break;

                case "2CH":
                    smallCast.Male = Math.Max(smallCast.Male, 9);
                    break;

                case "EZR":
                    smallCast.Male = Math.Max(smallCast.Male, 5);
                    break;

                case "NEH":
                    smallCast.Male = Math.Max(smallCast.Male, 7);
                    break;

                case "EST":
                    smallCast.Male = Math.Max(smallCast.Male, 6);
                    break;

                case "JOB":
                    smallCast.Male = Math.Max(smallCast.Male, 7);
                    break;

                case "PSA":
                    smallCast.Male = Math.Max(smallCast.Male, 3);
                    break;

                case "PRO":
                    smallCast.Male = Math.Max(smallCast.Male, 0);
                    break;

                case "ECC":
                    smallCast.Male = Math.Max(smallCast.Male, 1);
                    break;

                case "SNG":
                    smallCast.Male = Math.Max(smallCast.Male, 2);
                    break;

                case "ISA":
                    smallCast.Male = Math.Max(smallCast.Male, 6);
                    break;

                case "JER":
                    smallCast.Male = Math.Max(smallCast.Male, 9);
                    break;

                case "LAM":
                    smallCast.Male = Math.Max(smallCast.Male, 1);
                    break;

                case "EZK":
                    smallCast.Male = Math.Max(smallCast.Male, 4);
                    break;

                case "DAN":
                    smallCast.Male = Math.Max(smallCast.Male, 5);
                    break;

                case "HOS":
                    smallCast.Male = Math.Max(smallCast.Male, 2);
                    break;

                case "JOL":
                    smallCast.Male = Math.Max(smallCast.Male, 2);
                    break;

                case "AMO":
                    smallCast.Male = Math.Max(smallCast.Male, 4);
                    break;

                case "OBA":
                    smallCast.Male = Math.Max(smallCast.Male, 1);
                    break;

                case "JON":
                    smallCast.Male = Math.Max(smallCast.Male, 4);
                    break;

                case "MIC":
                    smallCast.Male = Math.Max(smallCast.Male, 3);
                    break;

                case "NAM":
                    smallCast.Male = Math.Max(smallCast.Male, 1);
                    break;

                case "HAB":
                    smallCast.Male = Math.Max(smallCast.Male, 1);
                    break;

                case "ZEP":
                    smallCast.Male = Math.Max(smallCast.Male, 1);
                    break;

                case "HAG":
                    smallCast.Male = Math.Max(smallCast.Male, 2);
                    break;

                case "ZEC":
                    smallCast.Male = Math.Max(smallCast.Male, 5);
                    break;

                case "MAL":
                    smallCast.Male = Math.Max(smallCast.Male, 1);
                    break;

                //// New Testament

                case "MAT":
                    smallCast.Male = Math.Max(smallCast.Male, 10);
                    break;

                case "MRK":
                    smallCast.Male = Math.Max(smallCast.Male, 12);
                    break;

                case "LUK":
                    smallCast.Male = Math.Max(smallCast.Male, 13);
                    break;

                case "JHN":
                    smallCast.Male = Math.Max(smallCast.Male, 10);
                    break;

                case "ACT":
                    smallCast.Male = Math.Max(smallCast.Male, 13);
                    break;

                case "ROM":
                    smallCast.Male = Math.Max(smallCast.Male, 2);
                    break;

                case "1CO":
                    smallCast.Male = Math.Max(smallCast.Male, 5);
                    break;

                case "2CO":
                    smallCast.Male = Math.Max(smallCast.Male, 3);
                    break;

                case "GAL":
                    smallCast.Male = Math.Max(smallCast.Male, 3);
                    break;

                case "EPH":
                    smallCast.Male = Math.Max(smallCast.Male, 0);
                    break;

                case "PHP":
                    smallCast.Male = Math.Max(smallCast.Male, 0);
                    break;

                case "COL":
                    smallCast.Male = Math.Max(smallCast.Male, 1);
                    break;

                case "1TH":
                    smallCast.Male = Math.Max(smallCast.Male, 0);
                    break;

                case "2TH":
                    smallCast.Male = Math.Max(smallCast.Male, 0);
                    break;

                case "1TI":
                    smallCast.Male = Math.Max(smallCast.Male, 0);
                    break;

                case "2TI":
                    smallCast.Male = Math.Max(smallCast.Male, 0);
                    break;

                case "TIT":
                    smallCast.Male = Math.Max(smallCast.Male, 1);
                    break;

                case "PHM":
                    smallCast.Male = Math.Max(smallCast.Male, 0);
                    break;

                case "HEB":
                    switch (m_project.DramatizationPreferences.ScriptureQuotationsShouldBeSpokenBy)
                    {
                    case DramatizationOption.DedicatedCharacter:
                        smallCast.Male = Math.Max(smallCast.Male, 2);
                        break;

                    case DramatizationOption.DefaultCharacter:
                        smallCast.Male = Math.Max(smallCast.Male, 4);                                         // Not sure if 4 is correct
                        break;

                    case DramatizationOption.Narrator:
                        smallCast.Male = Math.Max(smallCast.Male, 1);
                        break;
                    }
                    break;

                case "JAS":
                    smallCast.Male = Math.Max(smallCast.Male, 2);
                    break;

                case "1PE":
                    smallCast.Male = Math.Max(smallCast.Male, 0);
                    break;

                case "2PE":
                    smallCast.Male = Math.Max(smallCast.Male, 2);
                    break;

                case "1JN":
                    smallCast.Male = Math.Max(smallCast.Male, 0);
                    break;

                case "2JN":
                    smallCast.Male = Math.Max(smallCast.Male, 0);
                    break;

                case "3JN":
                    smallCast.Male = Math.Max(smallCast.Male, 0);
                    break;

                case "JUD":
                    smallCast.Male = Math.Max(smallCast.Male, 3);
                    break;

                case "REV":
                    smallCast.Male = Math.Max(smallCast.Male, 15);
                    break;
                }
            }

            IReadOnlyDictionary <string, CharacterDetail> characterDetails = Project.AllCharacterDetailDictionary;
            int maleCharacterCount   = 0;
            int femaleCharacterCount = 0;
            int childCharacterCount  = 0;

            foreach (var character in Project.AllCharacterIds.Where(c => !CharacterVerseData.IsCharacterStandard(c)))
            {
                CharacterDetail characterInfo;
                try
                {
                    characterInfo = characterDetails[character];
                }
                catch (KeyNotFoundException e)
                {
                    throw new KeyNotFoundException(String.Format("Unable to find details for character {0}", character), e);
                }
                if (characterInfo.Age == CharacterAge.Child)
                {
                    childCharacterCount++;
                }
                else
                {
                    if (characterInfo.Gender == CharacterGender.Female || characterInfo.Gender == CharacterGender.PreferFemale)
                    {
                        femaleCharacterCount++;
                    }
                    else
                    {
                        maleCharacterCount++;
                    }
                }
            }

            smallCast.Male   = Math.Min(maleCharacterCount, smallCast.Male);
            smallCast.Male  += extraCharacterCount;
            smallCast.Female = Math.Min(femaleCharacterCount, smallCast.Female);
            smallCast.Child  = Math.Min(1, childCharacterCount);

            if (smallCast.Female == 0)
            {
                if ((m_project.DramatizationPreferences.BookIntroductionsDramatization == ExtraBiblicalMaterialSpeakerOption.FemaleActor) ||
                    (m_project.DramatizationPreferences.SectionHeadDramatization == ExtraBiblicalMaterialSpeakerOption.FemaleActor) ||
                    (m_project.DramatizationPreferences.BookTitleAndChapterDramatization == ExtraBiblicalMaterialSpeakerOption.FemaleActor))
                {
                    smallCast.Female = 1;
                }
            }

            var largeCast = new CastSizeRowValues(smallCast.Male * 2, Math.Min(8, femaleCharacterCount), Math.Min(4, childCharacterCount));

            if (largeCast.Male > 10)
            {
                largeCast.Male = Math.Min(39, largeCast.Male);
            }
            largeCast.Male = Math.Min(maleCharacterCount + extraCharacterCount, largeCast.Male);

            m_baseRowValues[CastSizeOption.Small]       = smallCast;
            m_baseRowValues[CastSizeOption.Recommended] = new CastSizeRowValues(
                (int)Math.Ceiling((double)(smallCast.Male + largeCast.Male) / 2),
                (int)Math.Ceiling((double)(smallCast.Female + largeCast.Female) / 2),
                (int)Math.Ceiling((double)(smallCast.Child + largeCast.Child) / 2));
            m_baseRowValues[CastSizeOption.Large] = largeCast;

            if (prefs.NumberOfMaleActors == 0 && prefs.NumberOfFemaleActors == 0 && prefs.NumberOfChildActors == 0)
            {
                m_updatedCustomActorCounts = GetCastSizeRowValues(CastSizeOption.Recommended);
            }
            m_updatedCustomActorCounts = new CastSizeRowValues(prefs.NumberOfMaleActors, prefs.NumberOfFemaleActors, prefs.NumberOfChildActors);
        }