/// <summary>
        /// Gets the line layout for a given line.
        /// </summary>
        /// <param name="lineIndex">The line.</param>
        /// <param name="lineContexts"></param>
        /// <returns></returns>
        public override Layout GetLineLayout(
            int lineIndex,
            LineContexts lineContexts)
        {
            // Make sure we're on the proper thread.
            CheckGuiThread();

            // If we have a context, never cache it.
            if (lineContexts != LineContexts.None)
            {
                return(base.GetLineLayout(lineIndex, lineContexts));
            }

            // We need to get a write lock on the entire cache to avoid
            // anything else making changes.
            Layout layout;

            using (new ReadLock(access))
            {
                CachedLine line = GetCachedLine(lineIndex);
                layout = line.Layout;
            }

            // Process any queued changes.
            ProcessQueuedLineChanges();

            // Return the resulting layout.
            return(layout);
        }
        /// <summary>
        /// Uses the cache to retrieve the heights of the individual lines.
        /// </summary>
        /// <param name="startLineIndex">The start line.</param>
        /// <param name="endLineIndex">The end line.</param>
        /// <returns></returns>
        public override int GetLineLayoutHeight(
            int startLineIndex,
            int endLineIndex)
        {
            // Make sure we're on the proper thread.
            CheckGuiThread();

            // We need to get a write lock on the entire cache to avoid
            // anything else making changes.
            int results = 0;

            using (new ReadLock(access))
            {
                // Normalize to our line count.
                endLineIndex = Math.Min(endLineIndex, LineBuffer.LineCount - 1);

                // Go through the lines and get the count.
                for (int index = startLineIndex;
                     index <= endLineIndex;
                     index++)
                {
                    CachedLine line = GetCachedLine(index);

                    results += line.Height;
                }
            }

            // Process any queued changes.
            ProcessQueuedLineChanges();

            // Return the resulting height of the region.
            return(results);
        }
        /// <summary>
        /// Processes the lines inserted. This will never be called inside the
        /// access' write lock.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="args">The <see cref="LineRangeEventArgs"/> instance containing the event data.</param>
        private void ProcessLinesInserted(
            object sender,
            LineRangeEventArgs args)
        {
            // Make sure we're on the proper thread.
            CheckGuiThread();

            // Create a short list of new cache items for the line. Since we are
            // dealing with indexes, we need to get the count.
            int count    = args.EndLineIndex - args.StartLineIndex + 1;
            var newLines = new CachedLine[count];

            for (int index = 0;
                 index < count;
                 index++)
            {
                newLines[index] = new CachedLine();
            }

            // Insert the lines into the array.
            lines.InsertRange(args.StartLineIndex, newLines);

            // Call the base implementation to cascade the events up.
            base.OnLinesInserted(sender, args);
        }
        /// <summary>
        /// Gets the cached line and ensures it is populated.
        /// </summary>
        /// <param name="lineIndex">The line.</param>
        /// <returns></returns>
        private CachedLine GetCachedLine(int lineIndex)
        {
            if (lineIndex >= lines.Count)
            {
                return(new CachedLine());
            }

            // Pull out the cached line.
            CachedLine cachedLine = lines[lineIndex];

            cachedLine.Cache(EditorViewRenderer, lineIndex);
            return(cachedLine);
        }
        /// <summary>
        /// Gets the lines that are visible in the given view area.
        /// </summary>
        /// <param name="viewArea">The view area.</param>
        /// <param name="startLine">The start line.</param>
        /// <param name="endLine">The end line.</param>
        public override void GetLineLayoutRange(
            Rectangle viewArea,
            out int startLine,
            out int endLine)
        {
            // Make sure we're on the proper thread.
            CheckGuiThread();

            // We need to get a write lock on the entire cache to avoid
            // anything else making changes.
            using (new ReadLock(access))
            {
                // Start the start and end line to the first one.
                startLine = endLine = 0;

                // Go through and find the lines that are within range of the
                // view port.
                int    height = 0;
                double bottom = viewArea.Y + viewArea.Height;

                for (int lineIndex = 0;
                     lineIndex < lines.Count;
                     lineIndex++)
                {
                    // Get the line for the current index.
                    CachedLine line = GetCachedLine(lineIndex);

                    // If the line is current below the view port, then update
                    // the two lines.
                    endLine = lineIndex;

                    if (height < viewArea.Y)
                    {
                        // We are below the line, so update it.
                        startLine = lineIndex;
                    }

                    if (height > bottom)
                    {
                        break;
                    }

                    // Update the line height.
                    height += line.Height;
                }
            }

            // Process any queued changes.
            ProcessQueuedLineChanges();
        }
        private bool OnIdleUpdate()
        {
            // Make sure we're identified as running.
            isRunning = true;

            // Keep track of when we started. UtcNow requires less CPU overhead
            // so we use that instead.
            DateTime started = DateTime.UtcNow;

            // Loop through the lines in the cache renderer and update each
            // one in turn. We restart at the last point we updated to make sure
            // we can get through all the lines in the short time we have in
            // this loop.
            for (; currentIndex < lines.Count;
                 currentIndex++)
            {
                // Check to see if we exceeded our time yet.
                if ((DateTime.UtcNow - started) > maximumTime)
                {
                    // We have to stop processing now, but we need to keep going.
                    return(true);
                }

                // Get the next line and check to see if it isn't cached. If it
                // isn't, then we need to cache it but also keep track so we
                // go through the lines again.
                CachedLine line = lines[currentIndex];

                if (!line.IsCached)
                {
                    needRestart = true;
                    line.Cache(renderer, currentIndex);
                }
            }

            // If we need to restart, then cycle through it again.
            if (needRestart)
            {
                needRestart  = false;
                currentIndex = 0;
                return(true);
            }

            // If we got this far, we have completely gone through the lines
            // without updating any of them. To avoid the overhead of calling
            // this repeatedly, we return false and will restart later.
            isRunning = false;
            return(false);
        }
        /// <summary>
        /// Processes the line changed. This will never be called inside the
        /// access' write lock.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="args">The args.</param>
        private void ProcessLineChanged(
            object sender,
            LineChangedArgs args)
        {
            // Make sure we're on the proper thread.
            CheckGuiThread();

            // Get the line and reset it.
            CachedLine line = lines[args.LineIndex];

            line.Reset();

            // Call the base implementation to cascade the events up.
            base.OnLineChanged(sender, args);
        }
        private void ClearCacheLines(TextRange selection)
        {
            LinePosition firstLinePosition = selection.FirstLinePosition;
            int          firstLineIndex    = firstLinePosition.GetLineIndex(
                LineBuffer, LinePositionOptions.NoBoundsChecking);
            LinePosition lastLinePosition = selection.LastLinePosition;
            int          lastLineIndex    = lastLinePosition.GetLineIndex(
                LineBuffer, LinePositionOptions.NoBoundsChecking);

            for (int lineIndex = firstLineIndex;
                 lineIndex <= lastLineIndex;
                 lineIndex++)
            {
                CachedLine line = lines[lineIndex];
                line.Reset();
            }
        }