void BuildLogRecordTableContextMenu(int rowIndex)
        {
            contextMenuStrip.Items.Clear();

            if (-1 < rowIndex)
            {
                var callId            = (mLogRecordDataGridView.Rows[rowIndex].DataBoundItem as CallDataRecord).CallId;
                var copyClickedCallId = BuildCopyCallIdMenuItem(callId);

                var numberOfSelectedRows = mLogRecordDataGridView.SelectedRows.Count;

                var label =
                    String.Format("Copy &selected Log Record{0} to Clipboard", (1 < numberOfSelectedRows ? "s" : ""));

                var copySelectedLogRecords = new ToolStripMenuItem(label, null, new EventHandler(delegate(object sender, EventArgs e)
                {
                    // if the call IDs are taken from the selected rows collection, they are in the
                    // order they were selected.
                    // This way they are in the order they appear on screen
                    var selectedLogRecords = from logRow in mLogRecordDataGridView.Rows.Cast <DataGridViewRow>()
                                             where logRow.Selected
                                             select MainReportParser.LogStringFromCallDataRecord(logRow.DataBoundItem as CallDataRecord);

                    Clipboard.SetText(String.Join(Environment.NewLine, selectedLogRecords));
                }));
                copySelectedLogRecords.Enabled = (0 < numberOfSelectedRows);


                var copyAllLogRecords = new ToolStripMenuItem("Copy &all Log Records to Clipboard", null, new EventHandler(delegate(object sender, EventArgs e)
                {
                    var allLogRecords = from logRow in mLogRecordDataGridView.Rows.Cast <DataGridViewRow>()
                                        select MainReportParser.LogStringFromCallDataRecord(logRow.DataBoundItem as CallDataRecord);

                    Clipboard.SetText(String.Join(Environment.NewLine, allLogRecords));
                }));

                contextMenuStrip.Items.Add(copyClickedCallId);
                contextMenuStrip.Items.Add(copySelectedLogRecords);
                contextMenuStrip.Items.Add(copyAllLogRecords);
                contextMenuStrip.Items.Add((new ToolStripSeparator()));
            }

            var reload = BuildReloadMenuItem();

            var dataRecordFilter = new ToolStripMenuItem("&Filter Log Records", null, new EventHandler(delegate(object sender, EventArgs e)
            {
                this.dataRecordFilterDialog.Show();
                this.dataRecordFilterDialog.BringToFront();
            }));

            dataRecordFilter.Enabled = this.loadedFiles.Any() && !this.Busy;

            contextMenuStrip.Items.Add(dataRecordFilter);
            contextMenuStrip.Items.Add((new ToolStripSeparator()));
            contextMenuStrip.Items.Add(reload);
        }
        private void LoadFiles(string[] files)
        {
            if (this.Busy)
            {
                return;
            }

            this.Busy = true;

            // ensure the logfiles are read in alphabetical order
            // we are assuming this correlates with the logfiles
            // being in the correct time order
            loadedFiles = files.OrderBy(s => s).ToArray();

            BackgroundWorker worker = new BackgroundWorker();

            worker.WorkerReportsProgress = true;

            IEnumerable <CallSummary> callSummaryList = null;

            DateTime minimumTime = DateTime.MinValue;
            DateTime maximumTime = DateTime.MinValue;

            worker.DoWork += delegate(object sender, DoWorkEventArgs e)
            {
                MainReportParser.Reset();
                records.Clear();

                foreach (string logfile in loadedFiles)
                {
                    if (File.Exists(logfile))
                    {
                        worker.ReportProgress(0, logfile);
                        records.AddRange(MainReportParser.ParseFile(logfile));
                    }
                    else if (Directory.Exists(logfile))
                    {
                        foreach (string containedLogFile in Directory.EnumerateFiles(logfile, "MainReportLog_*.txt", SearchOption.AllDirectories))
                        {
                            worker.ReportProgress(0, containedLogFile);
                            records.AddRange(MainReportParser.ParseFile(containedLogFile));
                        }
                    }
                }

                if (0 < records.Count())
                {
                    minimumTime = records.Min(record => record.Timestamp);
                    maximumTime = records.Max(record => record.Timestamp);

                    callSummaryList = BuildCallSummaryTable();
                }
            };

            worker.ProgressChanged += delegate(object sender, ProgressChangedEventArgs e)
            {
                statusLabel.Text = String.Format("Loading {0}...", e.UserState);
            };

            worker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e)
            {
                if (minimumTime != DateTime.MinValue)
                {
                    summaryFilterDialog.SetTimeSpan(minimumTime, maximumTime);
                    dataRecordFilterDialog.SetTimeSpan(minimumTime, maximumTime);
                }

                this.Busy = false;
                DisplayCallSummaryTable();
            };

            worker.RunWorkerAsync();
        }