/// ------------------------------------------------------------------------------------
        /// <summary>
        /// Find the first and second interlinear fields in the RTF fields collection.
        /// </summary>
        /// ------------------------------------------------------------------------------------
        private void GetFirstAndSecondInterlinearFields(out RTFFieldInfo firstField,
                                                        out RTFFieldInfo secondField)
        {
            firstField  = null;
            secondField = null;

            foreach (var rtfField in m_rtfFields)
            {
                //if (rtfField.isFirstLine)
                if (rtfField.isInterlinearField)
                {
                    rtfField.isFirstLine = true;
                    firstField           = rtfField;
                    int firstFieldIndex = m_rtfFields.IndexOf(firstField);
                    // Find the second interlinear field
                    for (int i = firstFieldIndex + 1; i < m_rtfFields.Count; i++)
                    {
                        if (m_rtfFields[i].isInterlinearField)
                        {
                            secondField = m_rtfFields[i];
                            break;
                        }
                    }
                    return;
                }
            }
        }
        /// ------------------------------------------------------------------------------------
        /// <summary>
        /// Stores record's field names and their values in a collection. Only fields whose
        /// values are non null are saved.
        /// </summary>
        /// ------------------------------------------------------------------------------------
        private void GetFieldsAndValuesFromRecord()
        {
            m_rtfFields.Clear();
            m_numInterlinearFields  = 0;
            m_firstILLineTabs       = null;
            m_subordinateILLineTabs = null;

            using (Graphics g = CreateGraphics())
            {
                // If there's more than one interlinear field,
                // then get the information for those fields.
                if (m_recEntry.HasInterlinearData ||
                    (m_recEntry.InterlinearFields != null && m_recEntry.InterlinearFields.Count > 1))
                {
                    GetInterlinearFieldsAndValues(g);
                }

                // Go through the fields in the record and use reflection to get their values
                // from the record cache entry. Don't bother with fields whose value is null,
                // those that aren't supposed to be visible in the record view, and those
                // that are interlinear (interlinear fields are handled above).
                foreach (var field in App.Project.Fields)
                {
                    string fieldValue = m_recEntry[field.Name];
                    if (!field.VisibleInRecView || field.DisplayIndexInRecView < 0 ||
                        fieldValue == null || m_recEntry.GetIsInterlinearField(field.Name))
                    {
                        continue;
                    }

                    // Save the field name, it's displayable name (e.g. Freeform = Free Form)
                    // and the field's value. Replace any backslashes with double ones for
                    // the sake of RTF.
                    var info = new RTFFieldInfo();
                    info.field        = field.Name;
                    info.label        = field.DisplayName;
                    info.displayIndex = field.DisplayIndexInRecView;
                    info.fieldValue   = fieldValue.Replace("\\", "\\\\");

                    // All headers are bold
                    using (var headerFont = FontHelper.MakeFont(FontHelper.UIFont, FontStyle.Bold))
                    {
                        info.labelWidth = TextRenderer.MeasureText(g, field.DisplayName,
                                                                   headerFont, Size.Empty, kTxtFmtFlags).Width;
                    }

                    info.valueWidth = TextRenderer.MeasureText(g, fieldValue, field.Font,
                                                               Size.Empty, kTxtFmtFlags).Width;

                    m_rtfFields.Add(info);
                }
            }

            // Now sort the list on the order in which the fields should be displayed.
            m_rtfFields.Sort((x, y) => x.displayIndex.CompareTo(y.displayIndex));
        }
        /// ------------------------------------------------------------------------------------
        private void FormatInterlinearForRTF(RTFFieldInfo rtfField, StringBuilder bldr,
                                             string firstLineTabStopsString, string subordinateTabStopsString)
        {
            bldr.AppendLine((rtfField.isFirstLine ?
                             firstLineTabStopsString : subordinateTabStopsString));

            int  dataFontNumber = m_fontNumbers[rtfField.field];
            int  dataFontSize   = m_fontSizes[rtfField.field];
            Font dataFont       = m_fonts[rtfField.field];

            bldr.AppendFormat(kFmtOneLineOneCol, new object[] { m_fieldLabelColorRefNumber,
                                                                m_uiFontSize, m_uiFontNumber, rtfField.label });

            bldr.AppendFormat(kline, dataFontNumber, dataFontSize, string.Empty);

            for (int col = 0; col < rtfField.columnValues.Length; col++)
            {
                // The first field dosen't have sub columns.
                if (rtfField.isFirstLine)
                {
                    bldr.Append(ApplyFontStyle(dataFont, true));
                    bldr.Append(rtfField.columnValues[col]);
                    bldr.Append(ApplyFontStyle(dataFont, false));
                    bldr.Append("\\tab ");
                }
                else
                {
                    // Go through the column's sub columns
                    foreach (string subcolValue in rtfField.parsedColValues[col])
                    {
                        bldr.Append(ApplyFontStyle(dataFont, true));
                        bldr.Append(subcolValue);
                        bldr.Append(ApplyFontStyle(dataFont, false));
                        bldr.Append("\\tab ");
                    }
                }
            }

            // Strip off the last tab and append the correct line ending marker.
            bldr.Remove(bldr.Length - 5, 5);

            if (m_useExactLineSpacing)
            {
                // Add a zero width space at the end of the line using the largest font so all
                // the lines will have uniform spacing between. I tried using a regular space
                // but the RTF control ignored it. I also tried forcing the line spacing using
                // the \slN RTF code, but that didn't seem to work either. It looked great in
                // Word, but not the RichTextBox. Grrr!
                bldr.AppendFormat(@"\fs{0}\f{1} {2}", m_maxFontSize, m_maxFontNumber,
                                  kZeroWidthSpace);
            }

            bldr.AppendLine(@"\par");
        }
        /// ------------------------------------------------------------------------------------
        /// <summary>
        /// Gets the interlinear fields information.
        /// </summary>
        /// ------------------------------------------------------------------------------------
        private void GetInterlinearFieldsAndValues(IDeviceContext g)
        {
            if (string.IsNullOrEmpty(m_recEntry.FirstInterlinearField))
            {
                return;
            }

            SortedList sortedFieldInfo = new SortedList();
            var        tmpFields       = new List <RTFFieldInfo>();

            foreach (var fieldName in m_recEntry.InterlinearFields)
            {
                var      field     = App.Project.GetFieldForName(fieldName);
                string[] colValues = m_recEntry.GetParsedFieldValues(field, true);
                if (field == null || !field.VisibleInRecView ||
                    field.DisplayIndexInRecView < 0 || colValues == null)
                {
                    continue;
                }

                var info = new RTFFieldInfo();
                info.isInterlinearField = true;
                info.field        = field.Name;
                info.label        = field.DisplayName;
                info.displayIndex = field.DisplayIndexInRecView;
                info.columnValues = colValues;
                using (var headerFont = FontHelper.MakeFont(FontHelper.UIFont, FontStyle.Bold))
                {
                    info.labelWidth = TextRenderer.MeasureText(g, info.label,
                                                               headerFont, Size.Empty, kTxtFmtFlags).Width;
                }

                // Sort the info by their display order
                sortedFieldInfo.Add(info.displayIndex, info);
            }

            bool firstField = true;

            foreach (RTFFieldInfo info in sortedFieldInfo.Values)
            {
                if (firstField)
                {
                    // The first field is the one with the lowest DisplayIndexInRecView
                    firstField       = false;
                    info.isFirstLine = true;
                    tmpFields.Insert(0, info);
                }
                else
                {
                    info.parsedColValues = new Dictionary <int, string[]>();
                    tmpFields.Add(info);
                }
            }

            // Now parse the columns into sub columns for interlinear
            // rows that are not the first interlinear row.
            if (tmpFields.Count > 1)             // Make sure there are interlinear rows to parse
            {
                for (int col = 0; col < tmpFields[0].columnValues.Length; col++)
                {
                    // Copy the values for each row for a single column.
                    List <string> colValues = new List <string>();
                    for (int row = 1; row < tmpFields.Count; row++)
                    {
                        // If the current row doesn't have an value for
                        // the current column just add an empty space.
                        colValues.Add(col >= tmpFields[row].columnValues.Length ? string.Empty :
                                      tmpFields[row].columnValues[col]);
                    }

                    Dictionary <int, List <string> > parsedColValues =
                        GetInterlinearSubColumnContents(colValues);

                    // After parsing a single column, we need to save the results.
                    for (int row = 1; row < tmpFields.Count; row++)
                    {
                        tmpFields[row].parsedColValues[col] = parsedColValues[row - 1].ToArray();
                    }
                }
            }

            m_numInterlinearFields = tmpFields.Count;
            m_rtfFields.AddRange(tmpFields);
        }