1- // Copyright (c) 2015 SIL International
1+ // Copyright (c) 2015 SIL International
22// This software is licensed under the LGPL, version 2.1 or later
33// (http://www.gnu.org/licenses/lgpl-2.1.html)
44
@@ -39,6 +39,9 @@ public abstract class RuleFormulaVcBase : PatternVcBase
3939 // variant frags
4040 public const int kfragXVariable = 113 ;
4141
42+ public const int kfragNCAbbreviation = 114 ;
43+ public const int kfragStars = 115 ;
44+
4245 // fake flids
4346 public const int ktagFeature = - 200 ;
4447 public const int ktagVariable = - 201 ;
@@ -49,6 +52,7 @@ public abstract class RuleFormulaVcBase : PatternVcBase
4952
5053 protected ITsString m_infinity ;
5154 protected ITsString m_x ;
55+ protected ITsString m_stars ;
5256
5357 protected RuleFormulaVcBase ( LcmCache cache , PropertyTable propertyTable )
5458 : base ( cache , propertyTable )
@@ -57,6 +61,7 @@ protected RuleFormulaVcBase(LcmCache cache, PropertyTable propertyTable)
5761 m_propertyTable = propertyTable ;
5862 m_infinity = TsStringUtils . MakeString ( "\u221e " , userWs ) ;
5963 m_x = TsStringUtils . MakeString ( "X" , userWs ) ;
64+ m_stars = TsStringUtils . MakeString ( "***" , userWs ) ;
6065 }
6166
6267 /// <summary>
@@ -77,6 +82,20 @@ public override void Display(IVwEnv vwenv, int hvo, int frag)
7782 {
7883 switch ( frag )
7984 {
85+ // If there is no writing system for the abbreviation,
86+ // or the abbreviation is empty, display kfragStars as fallback.
87+ case kfragNCAbbreviation :
88+ int abbrWs = WritingSystemServices . ActualWs ( m_cache , WritingSystemServices . kwsFirstAnal , hvo ,
89+ PhNaturalClassTags . kflidAbbreviation ) ;
90+ ITsString abbr = abbrWs != 0
91+ ? m_cache . MainCacheAccessor . get_MultiStringAlt ( hvo , PhNaturalClassTags . kflidAbbreviation , abbrWs )
92+ : null ;
93+ if ( abbr != null && abbr . Length > 0 )
94+ vwenv . AddStringAltMember ( PhNaturalClassTags . kflidAbbreviation , abbrWs , this ) ;
95+ else
96+ vwenv . AddProp ( PhNaturalClassTags . kflidAbbreviation , this , kfragStars ) ;
97+ break ;
98+
8099 case kfragContext :
81100 var ctxtOrVar = m_cache . ServiceLocator . GetInstance < IPhContextOrVarRepository > ( ) . GetObject ( hvo ) ;
82101 bool isOuterIterCtxt = false ;
@@ -114,83 +133,95 @@ public override void Display(IVwEnv vwenv, int hvo, int frag)
114133 // Natural class simple context with a feature-based natural class
115134 var natClass = ( IPhNCFeatures ) ncCtxt . FeatureStructureRA ;
116135
117- int numLines = GetNumLines ( ncCtxt ) ;
118- if ( numLines == 0 )
136+ // User-defined feature-based natural classes should display
137+ // only the abbreviation.
138+ if ( RuleFormulaControl . IsFeatureBasedNCNameUserDefined ( natClass ) )
119139 {
120140 if ( ! isOuterIterCtxt )
121141 OpenSingleLinePile ( vwenv , GetMaxNumLines ( ) ) ;
122142
143+ // use normal brackets around abbreviation
123144 vwenv . AddProp ( ktagInnerNonBoundary , this , kfragLeftBracket ) ;
124- vwenv . AddProp ( PhSimpleContextNCTags . kflidFeatureStructure , this , kfragQuestions ) ;
145+
146+ vwenv . AddObjProp ( PhSimpleContextNCTags . kflidFeatureStructure , this , kfragNCAbbreviation ) ;
147+
125148 vwenv . AddProp ( ktagInnerNonBoundary , this , kfragRightBracket ) ;
126149
127150 if ( ! isOuterIterCtxt )
128151 CloseSingleLinePile ( vwenv ) ;
129152 }
130- else if ( numLines == 1 )
153+ else
131154 {
132- if ( ! isOuterIterCtxt )
133- OpenSingleLinePile ( vwenv , GetMaxNumLines ( ) ) ;
155+ int numLines = GetNumLines ( ncCtxt ) ;
156+ if ( numLines == 0 )
157+ {
158+ if ( ! isOuterIterCtxt )
159+ OpenSingleLinePile ( vwenv , GetMaxNumLines ( ) ) ;
134160
135- // use normal brackets for a single line context
136- vwenv . AddProp ( ktagInnerNonBoundary , this , kfragLeftBracket ) ;
161+ vwenv . AddProp ( ktagInnerNonBoundary , this , kfragLeftBracket ) ;
162+ vwenv . AddProp ( PhSimpleContextNCTags . kflidFeatureStructure , this , kfragQuestions ) ;
163+ vwenv . AddProp ( ktagInnerNonBoundary , this , kfragRightBracket ) ;
137164
138- // special consonant and vowel natural classes only display the abbreviation
139- if ( natClass . Abbreviation . AnalysisDefaultWritingSystem . Text == "C"
140- || natClass . Abbreviation . AnalysisDefaultWritingSystem . Text == "V" )
141- {
142- vwenv . AddObjProp ( PhSimpleContextNCTags . kflidFeatureStructure , this , kfragNC ) ;
165+ if ( ! isOuterIterCtxt )
166+ CloseSingleLinePile ( vwenv ) ;
143167 }
144- else
168+ else if ( numLines == 1 )
145169 {
170+ if ( ! isOuterIterCtxt )
171+ OpenSingleLinePile ( vwenv , GetMaxNumLines ( ) ) ;
172+
173+ // use normal brackets for a single line context
174+ vwenv . AddProp ( ktagInnerNonBoundary , this , kfragLeftBracket ) ;
175+
146176 if ( natClass . FeaturesOA != null && natClass . FeaturesOA . FeatureSpecsOC . Count > 0 )
147177 vwenv . AddObjProp ( PhSimpleContextNCTags . kflidFeatureStructure , this , kfragFeatNC ) ;
148178 else if ( ncCtxt . PlusConstrRS . Count > 0 )
149179 vwenv . AddObjVecItems ( PhSimpleContextNCTags . kflidPlusConstr , this , kfragPlusVariable ) ;
150180 else
151181 vwenv . AddObjVecItems ( PhSimpleContextNCTags . kflidMinusConstr , this , kfragMinusVariable ) ;
152- }
153- vwenv . AddProp ( ktagInnerNonBoundary , this , kfragRightBracket ) ;
154182
155- if ( ! isOuterIterCtxt )
156- CloseSingleLinePile ( vwenv ) ;
157- }
158- else
159- {
160- // multiline context
161-
162- // left bracket pile
163- int maxNumLines = GetMaxNumLines ( ) ;
164- vwenv . Props = m_bracketProps ;
165- vwenv . set_IntProperty ( ( int ) FwTextPropType . ktptMarginLeading , ( int ) FwTextPropVar . ktpvMilliPoint , PileMargin ) ;
166- vwenv . OpenInnerPile ( ) ;
167- AddExtraLines ( maxNumLines - numLines , ktagLeftNonBoundary , vwenv ) ;
168- vwenv . AddProp ( ktagLeftNonBoundary , this , kfragLeftBracketUpHook ) ;
169- for ( int i = 1 ; i < numLines - 1 ; i ++ )
170- vwenv . AddProp ( ktagLeftNonBoundary , this , kfragLeftBracketExt ) ;
171- vwenv . AddProp ( ktagLeftBoundary , this , kfragLeftBracketLowHook ) ;
172- vwenv . CloseInnerPile ( ) ;
173-
174- // feature and variable pile
175- vwenv . set_IntProperty ( ( int ) FwTextPropType . ktptAlign , ( int ) FwTextPropVar . ktpvEnum , ( int ) FwTextAlign . ktalLeft ) ;
176- vwenv . OpenInnerPile ( ) ;
177- AddExtraLines ( maxNumLines - numLines , vwenv ) ;
178- vwenv . AddObjProp ( PhSimpleContextNCTags . kflidFeatureStructure , this , kfragFeatNC ) ;
179- vwenv . AddObjVecItems ( PhSimpleContextNCTags . kflidPlusConstr , this , kfragPlusVariable ) ;
180- vwenv . AddObjVecItems ( PhSimpleContextNCTags . kflidMinusConstr , this , kfragMinusVariable ) ;
181- vwenv . CloseInnerPile ( ) ;
182-
183- // right bracket pile
184- vwenv . Props = m_bracketProps ;
185- if ( ! isOuterIterCtxt )
186- vwenv . set_IntProperty ( ( int ) FwTextPropType . ktptMarginTrailing , ( int ) FwTextPropVar . ktpvMilliPoint , PileMargin ) ;
187- vwenv . OpenInnerPile ( ) ;
188- AddExtraLines ( maxNumLines - numLines , ktagRightNonBoundary , vwenv ) ;
189- vwenv . AddProp ( ktagRightNonBoundary , this , kfragRightBracketUpHook ) ;
190- for ( int i = 1 ; i < numLines - 1 ; i ++ )
191- vwenv . AddProp ( ktagRightNonBoundary , this , kfragRightBracketExt ) ;
192- vwenv . AddProp ( ktagRightBoundary , this , kfragRightBracketLowHook ) ;
193- vwenv . CloseInnerPile ( ) ;
183+ vwenv . AddProp ( ktagInnerNonBoundary , this , kfragRightBracket ) ;
184+
185+ if ( ! isOuterIterCtxt )
186+ CloseSingleLinePile ( vwenv ) ;
187+ }
188+ else
189+ {
190+ // multiline context
191+
192+ // left bracket pile
193+ int maxNumLines = GetMaxNumLines ( ) ;
194+ vwenv . Props = m_bracketProps ;
195+ vwenv . set_IntProperty ( ( int ) FwTextPropType . ktptMarginLeading , ( int ) FwTextPropVar . ktpvMilliPoint , PileMargin ) ;
196+ vwenv . OpenInnerPile ( ) ;
197+ AddExtraLines ( maxNumLines - numLines , ktagLeftNonBoundary , vwenv ) ;
198+ vwenv . AddProp ( ktagLeftNonBoundary , this , kfragLeftBracketUpHook ) ;
199+ for ( int i = 1 ; i < numLines - 1 ; i ++ )
200+ vwenv . AddProp ( ktagLeftNonBoundary , this , kfragLeftBracketExt ) ;
201+ vwenv . AddProp ( ktagLeftBoundary , this , kfragLeftBracketLowHook ) ;
202+ vwenv . CloseInnerPile ( ) ;
203+
204+ // feature and variable pile
205+ vwenv . set_IntProperty ( ( int ) FwTextPropType . ktptAlign , ( int ) FwTextPropVar . ktpvEnum , ( int ) FwTextAlign . ktalLeft ) ;
206+ vwenv . OpenInnerPile ( ) ;
207+ AddExtraLines ( maxNumLines - numLines , vwenv ) ;
208+ vwenv . AddObjProp ( PhSimpleContextNCTags . kflidFeatureStructure , this , kfragFeatNC ) ;
209+ vwenv . AddObjVecItems ( PhSimpleContextNCTags . kflidPlusConstr , this , kfragPlusVariable ) ;
210+ vwenv . AddObjVecItems ( PhSimpleContextNCTags . kflidMinusConstr , this , kfragMinusVariable ) ;
211+ vwenv . CloseInnerPile ( ) ;
212+
213+ // right bracket pile
214+ vwenv . Props = m_bracketProps ;
215+ if ( ! isOuterIterCtxt )
216+ vwenv . set_IntProperty ( ( int ) FwTextPropType . ktptMarginTrailing , ( int ) FwTextPropVar . ktpvMilliPoint , PileMargin ) ;
217+ vwenv . OpenInnerPile ( ) ;
218+ AddExtraLines ( maxNumLines - numLines , ktagRightNonBoundary , vwenv ) ;
219+ vwenv . AddProp ( ktagRightNonBoundary , this , kfragRightBracketUpHook ) ;
220+ for ( int i = 1 ; i < numLines - 1 ; i ++ )
221+ vwenv . AddProp ( ktagRightNonBoundary , this , kfragRightBracketExt ) ;
222+ vwenv . AddProp ( ktagRightBoundary , this , kfragRightBracketLowHook ) ;
223+ vwenv . CloseInnerPile ( ) ;
224+ }
194225 }
195226 }
196227 else
@@ -360,6 +391,10 @@ public override ITsString DisplayVariant(IVwEnv vwenv, int tag, int frag)
360391 tss = m_x ;
361392 break ;
362393
394+ case kfragStars :
395+ tss = m_stars ;
396+ break ;
397+
363398 default :
364399 tss = base . DisplayVariant ( vwenv , tag , frag ) ;
365400 break ;
@@ -428,6 +463,51 @@ void DisplayIterCtxt(int numLines, IVwEnv vwenv)
428463 vwenv . CloseInnerPile ( ) ;
429464 }
430465
466+ /// <summary>
467+ /// Makes the fragment being displayed depend on the name and abbreviation of every
468+ /// feature-based natural class in the given contexts, including those nested in sequence
469+ /// and iteration contexts. Changing either property then rebuilds the whole fragment, so
470+ /// the choice between abbreviation and feature list, line counts and cell widths are all
471+ /// recomputed. Null contexts are ignored.
472+ /// </summary>
473+ protected static void NoteNaturalClassDependencies ( IVwEnv vwenv ,
474+ params IEnumerable < IPhContextOrVar > [ ] contextGroups )
475+ {
476+ var hvos = new List < int > ( ) ;
477+ var tags = new List < int > ( ) ;
478+ foreach ( IEnumerable < IPhContextOrVar > group in contextGroups )
479+ {
480+ foreach ( IPhContextOrVar ctxtOrVar in group )
481+ CollectFeatureNaturalClasses ( ctxtOrVar , hvos , tags ) ;
482+ }
483+ if ( hvos . Count > 0 )
484+ vwenv . NoteDependency ( hvos . ToArray ( ) , tags . ToArray ( ) , hvos . Count ) ;
485+ }
486+
487+ private static void CollectFeatureNaturalClasses ( IPhContextOrVar ctxtOrVar ,
488+ List < int > hvos , List < int > tags )
489+ {
490+ switch ( ctxtOrVar )
491+ {
492+ case IPhSequenceContext seqCtxt :
493+ foreach ( IPhPhonContext member in seqCtxt . MembersRS )
494+ CollectFeatureNaturalClasses ( member , hvos , tags ) ;
495+ break ;
496+
497+ case IPhIterationContext iterCtxt :
498+ CollectFeatureNaturalClasses ( iterCtxt . MemberRA , hvos , tags ) ;
499+ break ;
500+
501+ case IPhSimpleContextNC ncCtxt
502+ when ncCtxt . FeatureStructureRA is IPhNCFeatures natClass :
503+ hvos . Add ( natClass . Hvo ) ;
504+ tags . Add ( PhNaturalClassTags . kflidName ) ;
505+ hvos . Add ( natClass . Hvo ) ;
506+ tags . Add ( PhNaturalClassTags . kflidAbbreviation ) ;
507+ break ;
508+ }
509+ }
510+
431511 /// <summary>
432512 /// Gets the maximum number of lines to display the specified sequence of simple contexts.
433513 /// </summary>
@@ -478,6 +558,13 @@ protected int GetNumLines(IPhContextOrVar ctxtOrVar)
478558 if ( ncCtxt . FeatureStructureRA != null && ncCtxt . FeatureStructureRA . ClassID == PhNCFeaturesTags . kClassId )
479559 {
480560 var natClass = ( IPhNCFeatures ) ncCtxt . FeatureStructureRA ;
561+
562+ // User-defined, feature-based natural classes should display
563+ // only the abbreviation, which is a single line.
564+ if ( RuleFormulaControl . IsFeatureBasedNCNameUserDefined ( natClass ) )
565+ return 1 ;
566+
567+ // Otherwise, the list of features is displayed.
481568 if ( natClass . FeaturesOA != null )
482569 numFeats = natClass . FeaturesOA . FeatureSpecsOC . Count ;
483570 }
@@ -589,19 +676,22 @@ int GetNCCtxtWidth(IPhSimpleContextNC ctxt, IVwEnv vwenv)
589676 {
590677 if ( ctxt . FeatureStructureRA != null && ctxt . FeatureStructureRA . ClassID == PhNCFeaturesTags . kClassId )
591678 {
679+ // User-defined feature-based natural classes display only the abbreviation,
680+ // so get width of the abbreviation.
681+ var natClass = ( IPhNCFeatures ) ctxt . FeatureStructureRA ;
682+ if ( RuleFormulaControl . IsFeatureBasedNCNameUserDefined ( natClass ) )
683+ {
684+ int len = GetStrWidth ( ctxt . FeatureStructureRA . Abbreviation . BestAnalysisAlternative , null , vwenv ) ;
685+ len += GetStrWidth ( m_leftBracket , null , vwenv ) ;
686+ len += GetStrWidth ( m_rightBracket , null , vwenv ) ;
687+ return len ;
688+ }
689+
592690 int numLines = GetNumLines ( ctxt ) ;
593691 if ( numLines == 1 )
594692 {
595693 int len ;
596- if ( ctxt . FeatureStructureRA . Abbreviation . UserDefaultWritingSystem . Text == "C"
597- || ctxt . FeatureStructureRA . Abbreviation . UserDefaultWritingSystem . Text == "V" )
598- {
599- len = GetStrWidth ( ctxt . FeatureStructureRA . Abbreviation . BestAnalysisAlternative , null , vwenv ) ;
600- }
601- else
602- {
603- len = GetNCFeatsWidth ( ctxt , vwenv ) ;
604- }
694+ len = GetNCFeatsWidth ( ctxt , vwenv ) ;
605695 len += GetStrWidth ( m_leftBracket , null , vwenv ) ;
606696 len += GetStrWidth ( m_rightBracket , null , vwenv ) ;
607697 return len ;
0 commit comments