// // // // // $Revision$ // using System; using System.Collections; using System.Collections.Generic; using System.Text; using System.Threading; using System.Windows.Forms; using System.Drawing; using System.CodeDom.Compiler; using System.IO; using System.Diagnostics; using ICSharpCode.Core; using ICSharpCode.SharpDevelop.Gui.OptionPanels; using ICSharpCode.SharpDevelop.Project; using ICSharpCode.TextEditor; using ICSharpCode.TextEditor.Document; namespace ICSharpCode.SharpDevelop.Gui { /// /// This class displays the errors and warnings which the compiler outputs and /// allows the user to jump to the source of the warning / error /// public class CompilerMessageView : AbstractPadContent, IClipboardHandler { static CompilerMessageView instance; /// /// Gets the instance of the CompilerMessageView. This property is thread-safe, but /// most instance methods of the CompilerMessageView aren't. /// public static CompilerMessageView Instance { get { if (instance == null) WorkbenchSingleton.SafeThreadCall((MethodInvoker)InitializeInstance); return instance; } } static void InitializeInstance() { WorkbenchSingleton.Workbench.GetPad(typeof(CompilerMessageView)).CreatePad(); } //TextEditorControl textEditorControl = new TextEditorControl(); RichTextBox textEditorControl = new RichTextBox(); Panel myPanel = new Panel(); ToolStrip toolStrip; List messageCategories = new List(); int selectedCategory = 0; public int SelectedCategoryIndex { get { return selectedCategory; } set { if (selectedCategory != value) { selectedCategory = value; textEditorControl.Text = (value < 0) ? "" : StringParser.Parse(messageCategories[value].Text); //textEditorControl.Refresh(); OnSelectedCategoryIndexChanged(EventArgs.Empty); } } } public bool WordWrap { get { return properties.Get("WordWrap", true); } set { properties.Set("WordWrap", value); } } public MessageViewCategory SelectedMessageViewCategory { get { if (selectedCategory >= 0) { return messageCategories[selectedCategory]; } return null; } } // The compiler message view properties. Properties properties = null; public List MessageCategories { get { return messageCategories; } } public override Control Control { get { return myPanel; } } public CompilerMessageView() { instance = this; AddCategory(TaskService.BuildMessageViewCategory); myPanel.SuspendLayout(); textEditorControl.Dock = DockStyle.Fill; textEditorControl.BorderStyle = BorderStyle.FixedSingle; textEditorControl.BackColor = SystemColors.Window; textEditorControl.LinkClicked += delegate(object sender, LinkClickedEventArgs e) { FileService.OpenFile("browser://" + e.LinkText); }; // auto-scrolling on RichTextBox only works when HideSelection=false. // See comments to http://weblogs.asp.net/jdanforth/archive/2004/01/23/62026.aspx textEditorControl.HideSelection = false; textEditorControl.ReadOnly = true; textEditorControl.ContextMenuStrip = MenuService.CreateContextMenu(this, "/SharpDevelop/Pads/CompilerMessageView/ContextMenu"); properties = (Properties)PropertyService.Get(OutputWindowOptionsPanel.OutputWindowsProperty, new Properties()); textEditorControl.Font = FontSelectionPanel.ParseFont(properties.Get("DefaultFont", ResourceService.CourierNew10.ToString()).ToString()); properties.PropertyChanged += new PropertyChangedEventHandler(PropertyChanged); //textEditorControl.ActiveTextAreaControl.TextArea.DoubleClick += TextEditorControlDoubleClick; textEditorControl.DoubleClick += TextEditorControlDoubleClick; toolStrip = ToolbarService.CreateToolStrip(this, "/SharpDevelop/Pads/CompilerMessageView/Toolbar"); toolStrip.Stretch = true; toolStrip.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; myPanel.Controls.AddRange(new Control[] { textEditorControl, toolStrip} ); SetWordWrap(); myPanel.ResumeLayout(false); SetText(messageCategories[selectedCategory], messageCategories[selectedCategory].Text); } void SetWordWrap() { bool wordWrap = this.WordWrap; textEditorControl.WordWrap = wordWrap; if (wordWrap) { textEditorControl.ScrollBars = RichTextBoxScrollBars.ForcedVertical; } else { textEditorControl.ScrollBars = RichTextBoxScrollBars.ForcedBoth; } } public override void RedrawContent() { // messageCategory.Items.Clear(); // foreach (MessageViewCategory category in messageCategories) { // messageCategory.Items.Add(StringParser.Parse(category.DisplayCategory)); // } } #region Category handling /// /// Adds a category to the compiler message view. This method is thread-safe. /// public void AddCategory(MessageViewCategory category) { if (WorkbenchSingleton.InvokeRequired) { WorkbenchSingleton.SafeThreadAsyncCall((Action)AddCategory, category); return; } messageCategories.Add(category); category.Cleared += new EventHandler(CategoryTextCleared); category.TextSet += new TextEventHandler(CategoryTextSet); category.TextAppended += new TextEventHandler(CategoryTextAppended); OnMessageCategoryAdded(EventArgs.Empty); } void CategoryTextCleared(object sender, EventArgs e) { WorkbenchSingleton.SafeThreadAsyncCall(this, "ClearText", sender); } void ClearText(MessageViewCategory category) { if (messageCategories[SelectedCategoryIndex] == category) { textEditorControl.Text = String.Empty; //textEditorControl.Refresh(); } } void CategoryTextSet(object sender, TextEventArgs e) { WorkbenchSingleton.SafeThreadAsyncCall(this, "SetText", (MessageViewCategory)sender, e.Text); } object appendCallLock = new object(); volatile int pendingAppendCalls = 0; void CategoryTextAppended(object sender, TextEventArgs e) { lock (appendCallLock) { pendingAppendCalls += 1; if (pendingAppendCalls < 5) { WorkbenchSingleton.SafeThreadAsyncCall(this, "AppendText", sender, ((MessageViewCategory)sender).Text, e.Text); } else if (pendingAppendCalls == 5) { WorkbenchSingleton.SafeThreadAsyncCall(this, "AppendTextCombined", sender); } } } const int WM_SETREDRAW = 0x00B; [System.Security.SuppressUnmanagedCodeSecurityAttribute] [System.Runtime.InteropServices.DllImport("user32.dll")] static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); void SetUpdate(bool update) { SendMessage(textEditorControl.Handle, WM_SETREDRAW, update ? new IntPtr(1) : IntPtr.Zero, IntPtr.Zero); } void AppendTextCombined(MessageViewCategory category) { Application.DoEvents(); Thread.Sleep(50); Application.DoEvents(); lock (appendCallLock) { SetUpdate(false); SetText(category, category.Text); SetUpdate(true); textEditorControl.SelectionStart = textEditorControl.TextLength; if (LoggingService.IsDebugEnabled) { LoggingService.Debug("Replaced " + pendingAppendCalls + " appends with one set call"); } pendingAppendCalls = 0; } textEditorControl.Refresh(); } void AppendText(MessageViewCategory category, string fullText, string text) { lock (appendCallLock) { if (pendingAppendCalls >= 5) { return; } pendingAppendCalls -= 1; } if (messageCategories[SelectedCategoryIndex] != category) { SelectCategory(category.Category, fullText); return; } if (text != null) { text = StringParser.Parse(text); textEditorControl.AppendText(text); textEditorControl.SelectionStart = textEditorControl.TextLength; /*textEditorControl.Document.ReadOnly = false; textEditorControl.Document.Insert(textEditorControl.Document.TextLength, text); textEditorControl.Document.ReadOnly = true; textEditorControl.ActiveTextAreaControl.Caret.Position = new Point(0, textEditorControl.Document.TotalNumberOfLines); textEditorControl.ActiveTextAreaControl.ScrollTo(textEditorControl.Document.TotalNumberOfLines);*/ } } void SetText(MessageViewCategory category, string text) { if (messageCategories[SelectedCategoryIndex] != category) { SelectCategory(category.Category); return; } if (text == null) { text = String.Empty; } else { text = StringParser.Parse(text); } textEditorControl.Text = text; //textEditorControl.Refresh(); } public void SelectCategory(string categoryName) { for (int i = 0; i < messageCategories.Count; ++i) { MessageViewCategory category = (MessageViewCategory)messageCategories[i]; if (category.Category == categoryName) { SelectedCategoryIndex = i; break; } } if (!this.IsVisible) { ActivateThisPad(); } } void SelectCategory(string categoryName, string text) { for (int i = 0; i < messageCategories.Count; ++i) { MessageViewCategory category = (MessageViewCategory)messageCategories[i]; if (category.Category == categoryName) { selectedCategory = i; textEditorControl.Text = StringParser.Parse(text); //textEditorControl.Refresh(); OnSelectedCategoryIndexChanged(EventArgs.Empty); break; } } } public MessageViewCategory GetCategory(string categoryName) { foreach (MessageViewCategory category in messageCategories) { if (category.Category == categoryName) { return category; } } return null; } #endregion /// /// Makes this pad visible (usually BEFORE build or debug events) /// void ActivateThisPad() { WorkbenchSingleton.Workbench.WorkbenchLayout.ActivatePad(this.GetType().FullName); } /// /// Occurs when the mouse pointer is over the control and a /// mouse button is pressed. /// void TextEditorControlDoubleClick(object sender, EventArgs e) { string fullText = textEditorControl.Text; // Any text? if (fullText.Length > 0) { //int line = textEditorControl.ActiveTextAreaControl.Caret.Line; //string textLine = TextUtilities.GetLineAsString(textEditorControl.Document, line); Point clickPos = textEditorControl.PointToClient(Control.MousePosition); int index = textEditorControl.GetCharIndexFromPosition(clickPos); int start = index; // find start of current line while (--start > 0 && fullText[start - 1] != '\n'); // find end of current line while (++index < fullText.Length && fullText[index] != '\n'); string textLine = fullText.Substring(start, index - start); FileLineReference lineReference = OutputTextLineParser.GetFileLineReference(textLine); if (lineReference != null) { // Open matching file. FileService.JumpToFilePosition(Path.GetFullPath(lineReference.FileName), lineReference.Line, lineReference.Column); } } } /// /// Changes wordwrap settings if that property has changed. /// void PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.Key == "WordWrap") { SetWordWrap(); ToolbarService.UpdateToolbar(toolStrip); } if (e.Key == "DefaultFont") { textEditorControl.Font = FontSelectionPanel.ParseFont(properties.Get("DefaultFont", ResourceService.CourierNew10.ToString()).ToString()); } } protected virtual void OnMessageCategoryAdded(EventArgs e) { if (MessageCategoryAdded != null) { MessageCategoryAdded(this, e); } } protected virtual void OnSelectedCategoryIndexChanged(EventArgs e) { if (SelectedCategoryIndexChanged != null) { SelectedCategoryIndexChanged(this, e); } } public event EventHandler MessageCategoryAdded; public event EventHandler SelectedCategoryIndexChanged; #region ICSharpCode.SharpDevelop.Gui.IClipboardHandler interface implementation public bool EnableCut { get { return false; } } public bool EnableCopy { get { //return textEditorControl.ActiveTextAreaControl.TextArea.ClipboardHandler.EnableCopy; return textEditorControl.SelectionLength > 0; } } public bool EnablePaste { get { return false; } } public bool EnableDelete { get { return false; } } public bool EnableSelectAll { get { //return textEditorControl.ActiveTextAreaControl.TextArea.ClipboardHandler.EnableSelectAll; return textEditorControl.TextLength > 0; } } public void Cut() { } public void Copy() { //textEditorControl.ActiveTextAreaControl.TextArea.ClipboardHandler.Copy(null, null); textEditorControl.Copy(); } public void Paste() { } public void Delete() { } public void SelectAll() { //textEditorControl.ActiveTextAreaControl.TextArea.ClipboardHandler.SelectAll(null, null); textEditorControl.SelectAll(); } #endregion } }