first commit

This commit is contained in:
2026-01-07 11:33:05 +08:00
commit fc54ffd43b
215 changed files with 31856 additions and 0 deletions

View File

@@ -0,0 +1,214 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Drawing;
using System.Windows.Forms;
namespace ICSharpCode.TextEditor.Gui.CompletionWindow
{
/// <summary>
/// Description of AbstractCompletionWindow.
/// </summary>
public abstract class AbstractCompletionWindow : System.Windows.Forms.Form
{
protected TextEditorControl control;
protected Size drawingSize;
Rectangle workingScreen;
Form parentForm;
protected AbstractCompletionWindow(Form parentForm, TextEditorControl control)
{
workingScreen = Screen.GetWorkingArea(parentForm);
// SetStyle(ControlStyles.Selectable, false);
this.parentForm = parentForm;
this.control = control;
SetLocation();
StartPosition = FormStartPosition.Manual;
FormBorderStyle = FormBorderStyle.None;
ShowInTaskbar = false;
MinimumSize = new Size(1, 1);
Size = new Size(1, 1);
}
protected virtual void SetLocation()
{
TextArea textArea = control.ActiveTextAreaControl.TextArea;
TextLocation caretPos = textArea.Caret.Position;
int xpos = textArea.TextView.GetDrawingXPos(caretPos.Y, caretPos.X);
int rulerHeight = textArea.TextEditorProperties.ShowHorizontalRuler ? textArea.TextView.FontHeight : 0;
Point pos = new Point(textArea.TextView.DrawingPosition.X + xpos,
textArea.TextView.DrawingPosition.Y + (textArea.Document.GetVisibleLine(caretPos.Y)) * textArea.TextView.FontHeight
- textArea.TextView.TextArea.VirtualTop.Y + textArea.TextView.FontHeight + rulerHeight);
Point location = control.ActiveTextAreaControl.PointToScreen(pos);
// set bounds
Rectangle bounds = new Rectangle(location, drawingSize);
if (!workingScreen.Contains(bounds)) {
if (bounds.Right > workingScreen.Right) {
bounds.X = workingScreen.Right - bounds.Width;
}
if (bounds.Left < workingScreen.Left) {
bounds.X = workingScreen.Left;
}
if (bounds.Top < workingScreen.Top) {
bounds.Y = workingScreen.Top;
}
if (bounds.Bottom > workingScreen.Bottom) {
bounds.Y = bounds.Y - bounds.Height - control.ActiveTextAreaControl.TextArea.TextView.FontHeight;
if (bounds.Bottom > workingScreen.Bottom) {
bounds.Y = workingScreen.Bottom - bounds.Height;
}
}
}
Bounds = bounds;
}
protected override CreateParams CreateParams {
get {
CreateParams p = base.CreateParams;
AddShadowToWindow(p);
return p;
}
}
static int shadowStatus;
/// <summary>
/// Adds a shadow to the create params if it is supported by the operating system.
/// </summary>
public static void AddShadowToWindow(CreateParams createParams)
{
if (shadowStatus == 0) {
// Test OS version
shadowStatus = -1; // shadow not supported
if (Environment.OSVersion.Platform == PlatformID.Win32NT) {
Version ver = Environment.OSVersion.Version;
if (ver.Major > 5 || ver.Major == 5 && ver.Minor >= 1) {
shadowStatus = 1;
}
}
}
if (shadowStatus == 1) {
createParams.ClassStyle |= 0x00020000; // set CS_DROPSHADOW
}
}
protected override bool ShowWithoutActivation {
get {
return true;
}
}
protected void ShowCompletionWindow()
{
Owner = parentForm;
Enabled = true;
this.Show();
control.Focus();
if (parentForm != null) {
parentForm.LocationChanged += new EventHandler(this.ParentFormLocationChanged);
}
control.ActiveTextAreaControl.VScrollBar.ValueChanged += new EventHandler(ParentFormLocationChanged);
control.ActiveTextAreaControl.HScrollBar.ValueChanged += new EventHandler(ParentFormLocationChanged);
control.ActiveTextAreaControl.TextArea.DoProcessDialogKey += new DialogKeyProcessor(ProcessTextAreaKey);
control.ActiveTextAreaControl.Caret.PositionChanged += new EventHandler(CaretOffsetChanged);
control.ActiveTextAreaControl.TextArea.LostFocus += new EventHandler(this.TextEditorLostFocus);
control.Resize += new EventHandler(ParentFormLocationChanged);
foreach (Control c in Controls) {
c.MouseMove += ControlMouseMove;
}
}
void ParentFormLocationChanged(object sender, EventArgs e)
{
SetLocation();
}
public virtual bool ProcessKeyEvent(char ch)
{
return false;
}
protected virtual bool ProcessTextAreaKey(Keys keyData)
{
if (!Visible) {
return false;
}
switch (keyData) {
case Keys.Escape:
Close();
return true;
}
return false;
}
protected virtual void CaretOffsetChanged(object sender, EventArgs e)
{
}
protected void TextEditorLostFocus(object sender, EventArgs e)
{
if (!control.ActiveTextAreaControl.TextArea.Focused && !this.ContainsFocus) {
Close();
}
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
// take out the inserted methods
parentForm.LocationChanged -= new EventHandler(ParentFormLocationChanged);
foreach (Control c in Controls) {
c.MouseMove -= ControlMouseMove;
}
if (control.ActiveTextAreaControl.VScrollBar != null) {
control.ActiveTextAreaControl.VScrollBar.ValueChanged -= new EventHandler(ParentFormLocationChanged);
}
if (control.ActiveTextAreaControl.HScrollBar != null) {
control.ActiveTextAreaControl.HScrollBar.ValueChanged -= new EventHandler(ParentFormLocationChanged);
}
control.ActiveTextAreaControl.TextArea.LostFocus -= new EventHandler(this.TextEditorLostFocus);
control.ActiveTextAreaControl.Caret.PositionChanged -= new EventHandler(CaretOffsetChanged);
control.ActiveTextAreaControl.TextArea.DoProcessDialogKey -= new DialogKeyProcessor(ProcessTextAreaKey);
control.Resize -= new EventHandler(ParentFormLocationChanged);
Dispose();
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
ControlMouseMove(this, e);
}
/// <summary>
/// Invoked when the mouse moves over this form or any child control.
/// Shows the mouse cursor on the text area if it has been hidden.
/// </summary>
/// <remarks>
/// Derived classes should attach this handler to the MouseMove event
/// of all created controls which are not added to the Controls
/// collection.
/// </remarks>
protected void ControlMouseMove(object sender, MouseEventArgs e)
{
control.ActiveTextAreaControl.TextArea.ShowHiddenCursor(false);
}
}
}

View File

@@ -0,0 +1,293 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Drawing;
using System.Windows.Forms;
namespace ICSharpCode.TextEditor.Gui.CompletionWindow
{
/// <summary>
/// Description of CodeCompletionListView.
/// </summary>
public class CodeCompletionListView : System.Windows.Forms.UserControl
{
ICompletionData[] completionData;
int firstItem = 0;
int selectedItem = -1;
ImageList imageList;
public ImageList ImageList {
get {
return imageList;
}
set {
imageList = value;
}
}
public int FirstItem {
get {
return firstItem;
}
set {
if (firstItem != value) {
firstItem = value;
OnFirstItemChanged(EventArgs.Empty);
}
}
}
public ICompletionData SelectedCompletionData {
get {
if (selectedItem < 0) {
return null;
}
return completionData[selectedItem];
}
}
public int ItemHeight {
get {
return Math.Max(imageList.ImageSize.Height, (int)(Font.Height * 1.25));
}
}
public int MaxVisibleItem {
get {
return Height / ItemHeight;
}
}
public CodeCompletionListView(ICompletionData[] completionData)
{
Array.Sort(completionData, DefaultCompletionData.Compare);
this.completionData = completionData;
// this.KeyDown += new System.Windows.Forms.KeyEventHandler(OnKey);
// SetStyle(ControlStyles.Selectable, false);
// SetStyle(ControlStyles.UserPaint, true);
// SetStyle(ControlStyles.DoubleBuffer, false);
}
public void Close()
{
if (completionData != null) {
Array.Clear(completionData, 0, completionData.Length);
}
base.Dispose();
}
public void SelectIndex(int index)
{
int oldSelectedItem = selectedItem;
int oldFirstItem = firstItem;
index = Math.Max(0, index);
selectedItem = Math.Max(0, Math.Min(completionData.Length - 1, index));
if (selectedItem < firstItem) {
FirstItem = selectedItem;
}
if (firstItem + MaxVisibleItem <= selectedItem) {
FirstItem = selectedItem - MaxVisibleItem + 1;
}
if (oldSelectedItem != selectedItem) {
if (firstItem != oldFirstItem) {
Invalidate();
} else {
int min = Math.Min(selectedItem, oldSelectedItem) - firstItem;
int max = Math.Max(selectedItem, oldSelectedItem) - firstItem;
Invalidate(new Rectangle(0, 1 + min * ItemHeight, Width, (max - min + 1) * ItemHeight));
}
OnSelectedItemChanged(EventArgs.Empty);
}
}
public void CenterViewOn(int index)
{
int oldFirstItem = this.FirstItem;
int firstItem = index - MaxVisibleItem / 2;
if (firstItem < 0)
this.FirstItem = 0;
else if (firstItem >= completionData.Length - MaxVisibleItem)
this.FirstItem = completionData.Length - MaxVisibleItem;
else
this.FirstItem = firstItem;
if (this.FirstItem != oldFirstItem) {
Invalidate();
}
}
public void ClearSelection()
{
if (selectedItem < 0)
return;
int itemNum = selectedItem - firstItem;
selectedItem = -1;
Invalidate(new Rectangle(0, itemNum * ItemHeight, Width, (itemNum + 1) * ItemHeight + 1));
Update();
OnSelectedItemChanged(EventArgs.Empty);
}
public void PageDown()
{
SelectIndex(selectedItem + MaxVisibleItem);
}
public void PageUp()
{
SelectIndex(selectedItem - MaxVisibleItem);
}
public void SelectNextItem()
{
SelectIndex(selectedItem + 1);
}
public void SelectPrevItem()
{
SelectIndex(selectedItem - 1);
}
public void SelectItemWithStart(string startText)
{
if (startText == null || startText.Length == 0) return;
string originalStartText = startText;
startText = startText.ToLower();
int bestIndex = -1;
int bestQuality = -1;
// Qualities: 0 = match start
// 1 = match start case sensitive
// 2 = full match
// 3 = full match case sensitive
double bestPriority = 0;
for (int i = 0; i < completionData.Length; ++i) {
string itemText = completionData[i].Text;
string lowerText = itemText.ToLower();
if (lowerText.StartsWith(startText)) {
double priority = completionData[i].Priority;
int quality;
if (lowerText == startText) {
if (itemText == originalStartText)
quality = 3;
else
quality = 2;
} else if (itemText.StartsWith(originalStartText)) {
quality = 1;
} else {
quality = 0;
}
bool useThisItem;
if (bestQuality < quality) {
useThisItem = true;
} else {
if (bestIndex == selectedItem) {
useThisItem = false;
} else if (i == selectedItem) {
useThisItem = bestQuality == quality;
} else {
useThisItem = bestQuality == quality && bestPriority < priority;
}
}
if (useThisItem) {
bestIndex = i;
bestPriority = priority;
bestQuality = quality;
}
}
}
if (bestIndex < 0) {
ClearSelection();
} else {
if (bestIndex < firstItem || firstItem + MaxVisibleItem <= bestIndex) {
SelectIndex(bestIndex);
CenterViewOn(bestIndex);
} else {
SelectIndex(bestIndex);
}
}
}
protected override void OnPaint(PaintEventArgs pe)
{
float yPos = 1;
float itemHeight = ItemHeight;
// Maintain aspect ratio
int imageWidth = (int)(itemHeight * imageList.ImageSize.Width / imageList.ImageSize.Height);
int curItem = firstItem;
Graphics g = pe.Graphics;
while (curItem < completionData.Length && yPos < Height) {
RectangleF drawingBackground = new RectangleF(1, yPos, Width - 2, itemHeight);
if (drawingBackground.IntersectsWith(pe.ClipRectangle)) {
// draw Background
if (curItem == selectedItem) {
g.FillRectangle(SystemBrushes.Highlight, drawingBackground);
} else {
g.FillRectangle(SystemBrushes.Window, drawingBackground);
}
// draw Icon
int xPos = 0;
if (imageList != null && completionData[curItem].ImageIndex < imageList.Images.Count) {
g.DrawImage(imageList.Images[completionData[curItem].ImageIndex], new RectangleF(1, yPos, imageWidth, itemHeight));
xPos = imageWidth;
}
// draw text
if (curItem == selectedItem) {
g.DrawString(completionData[curItem].Text, Font, SystemBrushes.HighlightText, xPos, yPos);
} else {
g.DrawString(completionData[curItem].Text, Font, SystemBrushes.WindowText, xPos, yPos);
}
}
yPos += itemHeight;
++curItem;
}
g.DrawRectangle(SystemPens.Control, new Rectangle(0, 0, Width - 1, Height - 1));
}
protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e)
{
float yPos = 1;
int curItem = firstItem;
float itemHeight = ItemHeight;
while (curItem < completionData.Length && yPos < Height) {
RectangleF drawingBackground = new RectangleF(1, yPos, Width - 2, itemHeight);
if (drawingBackground.Contains(e.X, e.Y)) {
SelectIndex(curItem);
break;
}
yPos += itemHeight;
++curItem;
}
}
protected override void OnPaintBackground(PaintEventArgs pe)
{
}
protected virtual void OnSelectedItemChanged(EventArgs e)
{
if (SelectedItemChanged != null) {
SelectedItemChanged(this, e);
}
}
protected virtual void OnFirstItemChanged(EventArgs e)
{
if (FirstItemChanged != null) {
FirstItemChanged(this, e);
}
}
public event EventHandler SelectedItemChanged;
public event EventHandler FirstItemChanged;
}
}

View File

@@ -0,0 +1,364 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Drawing;
using System.Diagnostics;
using System.Windows.Forms;
using ICSharpCode.TextEditor.Document;
namespace ICSharpCode.TextEditor.Gui.CompletionWindow
{
public class CodeCompletionWindow : AbstractCompletionWindow
{
ICompletionData[] completionData;
CodeCompletionListView codeCompletionListView;
VScrollBar vScrollBar = new VScrollBar();
ICompletionDataProvider dataProvider;
IDocument document;
bool showDeclarationWindow = true;
bool fixedListViewWidth = true;
const int ScrollbarWidth = 16;
const int MaxListLength = 10;
int startOffset;
int endOffset;
DeclarationViewWindow declarationViewWindow = null;
Rectangle workingScreen;
public static CodeCompletionWindow ShowCompletionWindow(Form parent, TextEditorControl control, string fileName, ICompletionDataProvider completionDataProvider, char firstChar)
{
return ShowCompletionWindow(parent, control, fileName, completionDataProvider, firstChar, true, true);
}
public static CodeCompletionWindow ShowCompletionWindow(Form parent, TextEditorControl control, string fileName, ICompletionDataProvider completionDataProvider, char firstChar, bool showDeclarationWindow, bool fixedListViewWidth)
{
ICompletionData[] completionData = completionDataProvider.GenerateCompletionData(fileName, control.ActiveTextAreaControl.TextArea, firstChar);
if (completionData == null || completionData.Length == 0) {
return null;
}
CodeCompletionWindow codeCompletionWindow = new CodeCompletionWindow(completionDataProvider, completionData, parent, control, showDeclarationWindow, fixedListViewWidth);
codeCompletionWindow.CloseWhenCaretAtBeginning = firstChar == '\0';
codeCompletionWindow.ShowCompletionWindow();
return codeCompletionWindow;
}
CodeCompletionWindow(ICompletionDataProvider completionDataProvider, ICompletionData[] completionData, Form parentForm, TextEditorControl control, bool showDeclarationWindow, bool fixedListViewWidth) : base(parentForm, control)
{
this.dataProvider = completionDataProvider;
this.completionData = completionData;
this.document = control.Document;
this.showDeclarationWindow = showDeclarationWindow;
this.fixedListViewWidth = fixedListViewWidth;
workingScreen = Screen.GetWorkingArea(Location);
startOffset = control.ActiveTextAreaControl.Caret.Offset + 1;
endOffset = startOffset;
if (completionDataProvider.PreSelection != null) {
startOffset -= completionDataProvider.PreSelection.Length + 1;
endOffset--;
}
codeCompletionListView = new CodeCompletionListView(completionData);
codeCompletionListView.ImageList = completionDataProvider.ImageList;
codeCompletionListView.Dock = DockStyle.Fill;
codeCompletionListView.SelectedItemChanged += new EventHandler(CodeCompletionListViewSelectedItemChanged);
codeCompletionListView.DoubleClick += new EventHandler(CodeCompletionListViewDoubleClick);
codeCompletionListView.Click += new EventHandler(CodeCompletionListViewClick);
Controls.Add(codeCompletionListView);
if (completionData.Length > MaxListLength) {
vScrollBar.Dock = DockStyle.Right;
vScrollBar.Minimum = 0;
vScrollBar.Maximum = completionData.Length - 1;
vScrollBar.SmallChange = 1;
vScrollBar.LargeChange = MaxListLength;
codeCompletionListView.FirstItemChanged += new EventHandler(CodeCompletionListViewFirstItemChanged);
Controls.Add(vScrollBar);
}
this.drawingSize = GetListViewSize();
SetLocation();
if (declarationViewWindow == null) {
declarationViewWindow = new DeclarationViewWindow(parentForm);
}
SetDeclarationViewLocation();
declarationViewWindow.ShowDeclarationViewWindow();
declarationViewWindow.MouseMove += ControlMouseMove;
control.Focus();
CodeCompletionListViewSelectedItemChanged(this, EventArgs.Empty);
if (completionDataProvider.DefaultIndex >= 0) {
codeCompletionListView.SelectIndex(completionDataProvider.DefaultIndex);
}
if (completionDataProvider.PreSelection != null) {
CaretOffsetChanged(this, EventArgs.Empty);
}
vScrollBar.ValueChanged += VScrollBarValueChanged;
document.DocumentAboutToBeChanged += DocumentAboutToBeChanged;
}
bool inScrollUpdate;
void CodeCompletionListViewFirstItemChanged(object sender, EventArgs e)
{
if (inScrollUpdate) return;
inScrollUpdate = true;
vScrollBar.Value = Math.Min(vScrollBar.Maximum, codeCompletionListView.FirstItem);
inScrollUpdate = false;
}
void VScrollBarValueChanged(object sender, EventArgs e)
{
if (inScrollUpdate) return;
inScrollUpdate = true;
codeCompletionListView.FirstItem = vScrollBar.Value;
codeCompletionListView.Refresh();
control.ActiveTextAreaControl.TextArea.Focus();
inScrollUpdate = false;
}
void SetDeclarationViewLocation()
{
// This method uses the side with more free space
int leftSpace = Bounds.Left - workingScreen.Left;
int rightSpace = workingScreen.Right - Bounds.Right;
Point pos;
// The declaration view window has better line break when used on
// the right side, so prefer the right side to the left.
if (rightSpace * 2 > leftSpace) {
declarationViewWindow.FixedWidth = false;
pos = new Point(Bounds.Right, Bounds.Top);
if (declarationViewWindow.Location != pos) {
declarationViewWindow.Location = pos;
}
} else {
declarationViewWindow.Width = declarationViewWindow.GetRequiredLeftHandSideWidth(new Point(Bounds.Left, Bounds.Top));
declarationViewWindow.FixedWidth = true;
if (Bounds.Left < declarationViewWindow.Width) {
pos = new Point(0, Bounds.Top);
} else {
pos = new Point(Bounds.Left - declarationViewWindow.Width, Bounds.Top);
}
if (declarationViewWindow.Location != pos) {
declarationViewWindow.Location = pos;
}
declarationViewWindow.Refresh();
}
}
protected override void SetLocation()
{
base.SetLocation();
if (declarationViewWindow != null) {
SetDeclarationViewLocation();
}
}
Util.MouseWheelHandler mouseWheelHandler = new Util.MouseWheelHandler();
public void HandleMouseWheel(MouseEventArgs e)
{
int scrollDistance = mouseWheelHandler.GetScrollAmount(e);
if (scrollDistance == 0)
return;
if (control.TextEditorProperties.MouseWheelScrollDown)
scrollDistance = -scrollDistance;
int newValue = vScrollBar.Value + vScrollBar.SmallChange * scrollDistance;
vScrollBar.Value = Math.Max(vScrollBar.Minimum, Math.Min(vScrollBar.Maximum - vScrollBar.LargeChange + 1, newValue));
}
void CodeCompletionListViewSelectedItemChanged(object sender, EventArgs e)
{
ICompletionData data = codeCompletionListView.SelectedCompletionData;
if (showDeclarationWindow && data != null && data.Description != null && data.Description.Length > 0) {
declarationViewWindow.Description = data.Description;
SetDeclarationViewLocation();
} else {
declarationViewWindow.Description = null;
}
}
public override bool ProcessKeyEvent(char ch)
{
switch (dataProvider.ProcessKey(ch)) {
case CompletionDataProviderKeyResult.BeforeStartKey:
// increment start+end, then process as normal char
++startOffset;
++endOffset;
return base.ProcessKeyEvent(ch);
case CompletionDataProviderKeyResult.NormalKey:
// just process normally
return base.ProcessKeyEvent(ch);
case CompletionDataProviderKeyResult.InsertionKey:
return InsertSelectedItem(ch);
default:
throw new InvalidOperationException("Invalid return value of dataProvider.ProcessKey");
}
}
void DocumentAboutToBeChanged(object sender, DocumentEventArgs e)
{
// => startOffset test required so that this startOffset/endOffset are not incremented again
// for BeforeStartKey characters
if (e.Offset >= startOffset && e.Offset <= endOffset) {
if (e.Length > 0) { // length of removed region
endOffset -= e.Length;
}
if (!string.IsNullOrEmpty(e.Text)) {
endOffset += e.Text.Length;
}
}
}
/// <summary>
/// When this flag is set, code completion closes if the caret moves to the
/// beginning of the allowed range. This is useful in Ctrl+Space and "complete when typing",
/// but not in dot-completion.
/// </summary>
public bool CloseWhenCaretAtBeginning { get; set; }
protected override void CaretOffsetChanged(object sender, EventArgs e)
{
int offset = control.ActiveTextAreaControl.Caret.Offset;
if (offset == startOffset) {
if (CloseWhenCaretAtBeginning)
Close();
return;
}
if (offset < startOffset || offset > endOffset) {
Close();
} else {
codeCompletionListView.SelectItemWithStart(control.Document.GetText(startOffset, offset - startOffset));
}
}
protected override bool ProcessTextAreaKey(Keys keyData)
{
if (!Visible) {
return false;
}
switch (keyData) {
case Keys.Home:
codeCompletionListView.SelectIndex(0);
return true;
case Keys.End:
codeCompletionListView.SelectIndex(completionData.Length-1);
return true;
case Keys.PageDown:
codeCompletionListView.PageDown();
return true;
case Keys.PageUp:
codeCompletionListView.PageUp();
return true;
case Keys.Down:
codeCompletionListView.SelectNextItem();
return true;
case Keys.Up:
codeCompletionListView.SelectPrevItem();
return true;
case Keys.Tab:
InsertSelectedItem('\t');
return true;
case Keys.Return:
InsertSelectedItem('\n');
return true;
}
return base.ProcessTextAreaKey(keyData);
}
void CodeCompletionListViewDoubleClick(object sender, EventArgs e)
{
InsertSelectedItem('\0');
}
void CodeCompletionListViewClick(object sender, EventArgs e)
{
control.ActiveTextAreaControl.TextArea.Focus();
}
protected override void Dispose(bool disposing)
{
if (disposing) {
document.DocumentAboutToBeChanged -= DocumentAboutToBeChanged;
if (codeCompletionListView != null) {
codeCompletionListView.Dispose();
codeCompletionListView = null;
}
if (declarationViewWindow != null) {
declarationViewWindow.Dispose();
declarationViewWindow = null;
}
}
base.Dispose(disposing);
}
bool InsertSelectedItem(char ch)
{
document.DocumentAboutToBeChanged -= DocumentAboutToBeChanged;
ICompletionData data = codeCompletionListView.SelectedCompletionData;
bool result = false;
if (data != null) {
control.BeginUpdate();
try {
if (endOffset - startOffset > 0) {
control.Document.Remove(startOffset, endOffset - startOffset);
}
Debug.Assert(startOffset <= document.TextLength);
result = dataProvider.InsertAction(data, control.ActiveTextAreaControl.TextArea, startOffset, ch);
} finally {
control.EndUpdate();
}
}
Close();
return result;
}
Size GetListViewSize()
{
int height = codeCompletionListView.ItemHeight * Math.Min(MaxListLength, completionData.Length);
int width = codeCompletionListView.ItemHeight * 10;
if (!fixedListViewWidth) {
width = GetListViewWidth(width, height);
}
return new Size(width, height);
}
/// <summary>
/// Gets the list view width large enough to handle the longest completion data
/// text string.
/// </summary>
/// <param name="defaultWidth">The default width of the list view.</param>
/// <param name="height">The height of the list view. This is
/// used to determine if the scrollbar is visible.</param>
/// <returns>The list view width to accommodate the longest completion
/// data text string; otherwise the default width.</returns>
int GetListViewWidth(int defaultWidth, int height)
{
float width = defaultWidth;
using (Graphics graphics = codeCompletionListView.CreateGraphics()) {
for (int i = 0; i < completionData.Length; ++i) {
float itemWidth = graphics.MeasureString(completionData[i].Text.ToString(), codeCompletionListView.Font).Width;
if(itemWidth > width) {
width = itemWidth;
}
}
}
float totalItemsHeight = codeCompletionListView.ItemHeight * completionData.Length;
if (totalItemsHeight > height) {
width += ScrollbarWidth; // Compensate for scroll bar.
}
return (int)width;
}
}
}

View File

@@ -0,0 +1,125 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Drawing;
using System.Windows.Forms;
using ICSharpCode.TextEditor.Util;
namespace ICSharpCode.TextEditor.Gui.CompletionWindow
{
public interface IDeclarationViewWindow
{
string Description {
get;
set;
}
void ShowDeclarationViewWindow();
void CloseDeclarationViewWindow();
}
public class DeclarationViewWindow : Form, IDeclarationViewWindow
{
string description = string.Empty;
bool fixedWidth;
public string Description {
get {
return description;
}
set {
description = value;
if (value == null && Visible) {
Visible = false;
} else if (value != null) {
if (!Visible) ShowDeclarationViewWindow();
Refresh();
}
}
}
public bool FixedWidth {
get {
return fixedWidth;
}
set {
fixedWidth = value;
}
}
public int GetRequiredLeftHandSideWidth(Point p) {
if (description != null && description.Length > 0) {
using (Graphics g = CreateGraphics()) {
Size s = TipPainterTools.GetLeftHandSideDrawingSizeHelpTipFromCombinedDescription(this, g, Font, null, description, p);
return s.Width;
}
}
return 0;
}
public bool HideOnClick;
public DeclarationViewWindow(Form parent)
{
SetStyle(ControlStyles.Selectable, false);
StartPosition = FormStartPosition.Manual;
FormBorderStyle = FormBorderStyle.None;
Owner = parent;
ShowInTaskbar = false;
Size = new Size(0, 0);
base.CreateHandle();
}
protected override CreateParams CreateParams {
get {
CreateParams p = base.CreateParams;
AbstractCompletionWindow.AddShadowToWindow(p);
return p;
}
}
protected override bool ShowWithoutActivation {
get {
return true;
}
}
protected override void OnClick(EventArgs e)
{
base.OnClick(e);
if (HideOnClick) Hide();
}
public void ShowDeclarationViewWindow()
{
Show();
}
public void CloseDeclarationViewWindow()
{
Close();
Dispose();
}
protected override void OnPaint(PaintEventArgs pe)
{
if (description != null && description.Length > 0) {
if (fixedWidth) {
TipPainterTools.DrawFixedWidthHelpTipFromCombinedDescription(this, pe.Graphics, Font, null, description);
} else {
TipPainterTools.DrawHelpTipFromCombinedDescription(this, pe.Graphics, Font, null, description);
}
}
}
protected override void OnPaintBackground(PaintEventArgs pe)
{
pe.Graphics.FillRectangle(SystemBrushes.Info, pe.ClipRectangle);
}
}
}

View File

@@ -0,0 +1,114 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
// <version>$Revision$</version>
// </file>
using System;
namespace ICSharpCode.TextEditor.Gui.CompletionWindow
{
public interface ICompletionData
{
int ImageIndex {
get;
}
string Text {
get;
set;
}
string Description {
get;
}
/// <summary>
/// Gets a priority value for the completion data item.
/// When selecting items by their start characters, the item with the highest
/// priority is selected first.
/// </summary>
double Priority {
get;
}
/// <summary>
/// Insert the element represented by the completion data into the text
/// editor.
/// </summary>
/// <param name="textArea">TextArea to insert the completion data in.</param>
/// <param name="ch">Character that should be inserted after the completion data.
/// \0 when no character should be inserted.</param>
/// <returns>Returns true when the insert action has processed the character
/// <paramref name="ch"/>; false when the character was not processed.</returns>
bool InsertAction(TextArea textArea, char ch);
}
public class DefaultCompletionData : ICompletionData
{
string text;
string description;
int imageIndex;
public int ImageIndex {
get {
return imageIndex;
}
}
public string Text {
get {
return text;
}
set {
text = value;
}
}
public virtual string Description {
get {
return description;
}
}
double priority;
public double Priority {
get {
return priority;
}
set {
priority = value;
}
}
public virtual bool InsertAction(TextArea textArea, char ch)
{
textArea.InsertString(text);
return false;
}
public DefaultCompletionData(string text, int imageIndex)
{
this.text = text;
this.imageIndex = imageIndex;
}
public DefaultCompletionData(string text, string description, int imageIndex)
{
this.text = text;
this.description = description;
this.imageIndex = imageIndex;
}
public static int Compare(ICompletionData a, ICompletionData b)
{
if (a == null)
throw new ArgumentNullException("a");
if (b == null)
throw new ArgumentNullException("b");
return string.Compare(a.Text, b.Text, StringComparison.InvariantCultureIgnoreCase);
}
}
}

View File

@@ -0,0 +1,62 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Windows.Forms;
namespace ICSharpCode.TextEditor.Gui.CompletionWindow
{
public interface ICompletionDataProvider
{
ImageList ImageList {
get;
}
string PreSelection {
get;
}
/// <summary>
/// Gets the index of the element in the list that is chosen by default.
/// </summary>
int DefaultIndex {
get;
}
/// <summary>
/// Processes a keypress. Returns the action to be run with the key.
/// </summary>
CompletionDataProviderKeyResult ProcessKey(char key);
/// <summary>
/// Executes the insertion. The provider should set the caret position and then
/// call data.InsertAction.
/// </summary>
bool InsertAction(ICompletionData data, TextArea textArea, int insertionOffset, char key);
/// <summary>
/// Generates the completion data. This method is called by the text editor control.
/// </summary>
ICompletionData[] GenerateCompletionData(string fileName, TextArea textArea, char charTyped);
}
public enum CompletionDataProviderKeyResult
{
/// <summary>
/// Normal key, used to choose an entry from the completion list
/// </summary>
NormalKey,
/// <summary>
/// This key triggers insertion of the completed expression
/// </summary>
InsertionKey,
/// <summary>
/// Increment both start and end offset of completion region when inserting this
/// key. Can be used to insert whitespace (or other characters) in front of the expression
/// while the completion window is open.
/// </summary>
BeforeStartKey
}
}