37
37
using ICSharpCode.NRefactory.Completion;
38
38
using Mono.TextEditor;
39
39
using MonoDevelop.Ide.Gui.Content;
40
using MonoDevelop.Components;
41
using Mono.TextEditor.Highlighting;
42
using MonoDevelop.Core;
41
44
namespace MonoDevelop.Ide.CodeCompletion
53
public class ListWindow : Gtk.Window
56
public class ListWindow : PopoverWindow
55
internal VScrollbar scrollbar;
58
const int WindowWidth = 300;
60
64
public CompletionTextEditorExtension Extension {
67
71
return list.filteredItems;
75
internal ScrolledWindow scrollbar;
71
public ListWindow () : base(Gtk.WindowType.Popup)
77
public ListWindow (Gtk.WindowType type) : base(type)
73
79
vbox = new VBox ();
74
HBox box = new HBox ();
75
80
list = new ListWidget (this);
76
81
list.SelectionChanged += new EventHandler (OnSelectionChanged);
77
82
list.ScrollEvent += new ScrollEventHandler (OnScrolled);
79
box.PackStart (list, true, true, 0);
82
scrollbar = new VScrollbar (null);
83
scrollbar.ValueChanged += new EventHandler (OnScrollChanged);
84
box.PackStart (scrollbar, false, false, 0);
84
scrollbar = new MonoDevelop.Components.CompactScrolledWindow ();
85
scrollbar.Child = list;
85
86
list.ButtonPressEvent += delegate(object o, ButtonPressEventArgs args) {
86
87
if (args.Event.Button == 1 && args.Event.Type == Gdk.EventType.TwoButtonPress)
89
list.WordsFiltered += delegate {
92
list.SizeAllocated += delegate {
95
vbox.PackStart (box, true, true, 0);
90
vbox.PackEnd (scrollbar, true, true, 0);
91
ContentBox.Add (vbox);
97
92
this.AutoSelect = true;
98
93
this.TypeHint = WindowTypeHint.Menu;
94
Theme.CornerRadius = 4;
95
var style = SyntaxModeService.GetColorStyle (IdeApp.Preferences.ColorScheme);
96
Theme.SetFlatColor (style.CompletionText.Background);
101
99
protected virtual void DoubleClick ()
138
136
if (IsRealized && !Visible)
141
int width = list.WidthRequest;
142
int height = list.HeightRequest + (footer != null ? footer.Allocation.Height : 0);
139
int width = Math.Max (Allocation.Width, list.WidthRequest + Theme.CornerRadius * 2);
140
int height = Math.Max (Allocation.Height, list.HeightRequest + 2 + (footer != null ? footer.Allocation.Height : 0) + Theme.CornerRadius * 2);
144
142
SetSizeRequest (width, height);
146
144
Resize (width, height);
149
void UpdateScrollBar ()
151
double pageSize = Math.Max (0, list.VisibleRows);
152
double upper = Math.Max (0, list.filteredItems.Count - 1);
153
scrollbar.Adjustment.SetBounds (0, upper, 1, pageSize, pageSize);
154
if (pageSize >= upper) {
155
this.scrollbar.Value = -1;
156
this.scrollbar.Visible = false;
158
this.scrollbar.Value = list.Page;
159
this.scrollbar.Visible = true;
164
148
public IListDataProvider DataProvider {
192
176
set { list.AutoCompleteEmptyMatch = value; }
179
public bool AutoCompleteEmptyMatchOnCurlyBrace {
180
get { return list.AutoCompleteEmptyMatchOnCurlyBrace; }
181
set { list.AutoCompleteEmptyMatchOnCurlyBrace = value; }
195
184
public string DefaultCompletionString {
197
186
return list.DefaultCompletionString;
213
protected int StartOffset {
202
internal int StartOffset {
218
207
public ICompletionWidget CompletionWidget {
209
return list.CompletionWidget;
212
list.CompletionWidget = value;
223
216
int endOffset = -1;
224
public string PartialWord {
217
public virtual string PartialWord {
226
219
return CompletionWidget.GetText (StartOffset, Math.Max (StartOffset, endOffset > 0 ? endOffset : CompletionWidget.CaretOffset));
299
287
UpdateWordSelection ();
300
288
return KeyActions.Process;
291
list.AutoSelect = list.AutoCompleteEmptyMatch = true;
303
292
endOffset = CompletionWidget.CaretOffset - 1;
304
if (CompleteWithSpaceOrPunctuation && list.SelectionEnabled) {
293
if (list.SelectionEnabled) {
294
if (keyChar == '{' && !list.AutoCompleteEmptyMatchOnCurlyBrace && string.IsNullOrEmpty (list.CompletionString))
295
return KeyActions.CloseWindow | KeyActions.Process;
305
296
return KeyActions.Complete | KeyActions.Process | KeyActions.CloseWindow;
307
return KeyActions.CloseWindow | KeyActions.Process;
298
return KeyActions.CloseWindow | KeyActions.Process;
310
301
return KeyActions.Process;
356
347
case Gdk.Key.KP_Enter:
357
348
endOffset = CompletionWidget.CaretOffset;
358
349
WasShiftPressed = (modifier & ModifierType.ShiftMask) == ModifierType.ShiftMask;
359
return (!AutoSelect ? KeyActions.Process : (KeyActions.Complete | KeyActions.Ignore)) | KeyActions.CloseWindow;
350
return KeyActions.Complete | KeyActions.Ignore | KeyActions.CloseWindow;
360
351
case Gdk.Key.Down:
361
352
if ((modifier & Gdk.ModifierType.ShiftMask) == Gdk.ModifierType.ShiftMask) {
362
353
if (!SelectionEnabled /*&& !CompletionWindowManager.ForceSuggestionMode*/)
383
374
return KeyActions.Process;
384
375
if (list.filteredItems.Count < 2)
385
376
return KeyActions.CloseWindow | KeyActions.Process;
386
list.MoveCursor (-(list.VisibleRows - 1));
377
list.MoveCursor (-8);
387
378
return KeyActions.Ignore;
389
380
case Gdk.Key.Page_Down:
391
382
return KeyActions.Process;
392
383
if (list.filteredItems.Count < 2)
393
384
return KeyActions.CloseWindow | KeyActions.Process;
394
list.MoveCursor (list.VisibleRows - 1);
395
386
return KeyActions.Ignore;
397
388
case Gdk.Key.Left:
429
416
if (keyChar == ' ' && (modifier & ModifierType.ShiftMask) == ModifierType.ShiftMask)
430
417
return KeyActions.CloseWindow | KeyActions.Process;
419
// special case end with punctuation like 'param:' -> don't input double punctuation, otherwise we would end up with 'param::'
420
if (char.IsPunctuation (keyChar) && keyChar != '_') {
421
foreach (var item in FilteredItems) {
422
if (DataProvider.GetText (item).EndsWith (keyChar.ToString (), StringComparison.Ordinal)) {
423
list.SelectedItem = item;
424
return KeyActions.Complete | KeyActions.CloseWindow | KeyActions.Ignore;
432
431
/* //don't input letters/punctuation etc when non-shift modifiers are active
433
432
bool nonShiftModifierActive = ((Gdk.ModifierType.ControlMask | Gdk.ModifierType.MetaMask
434
433
| Gdk.ModifierType.Mod1Mask | Gdk.ModifierType.SuperMask)
520
519
if (!string.IsNullOrEmpty (partialWord)) {
521
520
for (int i = 0; i < list.filteredItems.Count; i++) {
522
521
int index = list.filteredItems[i];
523
string text = DataProvider.GetCompletionText (index);
522
string text = DataProvider.GetText (index);
525
524
if (!matcher.CalcMatchRank (text, out rank))
600
599
bool hasMismatches;
602
601
int matchedIndex = FindMatchedEntry (s, out hasMismatches);
604
603
SelectEntry (matchedIndex);
607
void OnScrollChanged (object o, EventArgs args)
609
list.Page = (int)scrollbar.Value;
612
606
void OnScrolled (object o, ScrollEventArgs args)
614
608
if (!scrollbar.Visible)
617
var adj = scrollbar.Adjustment;
611
var adj = scrollbar.Vadjustment;
618
612
var alloc = Allocation;
620
614
//This widget is a special case because it's always aligned to items as it scrolls.
639
633
void OnSelectionChanged (object o, EventArgs args)
641
scrollbar.Value = list.Page;
642
635
OnSelectionChanged ();
645
638
protected virtual void OnSelectionChanged ()
649
642
protected override bool OnExposeEvent (Gdk.EventExpose args)
651
644
base.OnExposeEvent (args);
653
646
int winWidth, winHeight;
654
647
this.GetSize (out winWidth, out winHeight);
655
648
this.GdkWindow.DrawRectangle (this.Style.ForegroundGC (StateType.Insensitive), false, 0, 0, winWidth - 1, winHeight - 1);
659
652
public int TextOffset {
660
get { return list.TextOffset + (int)this.BorderWidth; }
653
get { return list.TextOffset + (int)Theme.CornerRadius; }