1
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
2
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
5
using System.Collections.Generic;
6
using System.Collections.ObjectModel;
7
using System.Diagnostics;
10
using System.Windows.Controls;
11
using System.Windows.Input;
12
using System.Windows.Threading;
15
using ICSharpCode.Core;
16
using ICSharpCode.Core.Presentation;
18
namespace ICSharpCode.SharpDevelop.Gui
20
sealed class AvalonWorkbenchWindow : DocumentContent, IWorkbenchWindow, IOwnerState
22
readonly static string contextMenuPath = "/SharpDevelop/Workbench/OpenFileTab/ContextMenu";
24
AvalonDockLayout dockLayout;
26
public AvalonWorkbenchWindow(AvalonDockLayout dockLayout)
28
if (dockLayout == null)
29
throw new ArgumentNullException("dockLayout");
31
CustomFocusManager.SetRememberFocusedChild(this, true);
32
this.IsFloatingAllowed = true;
33
this.dockLayout = dockLayout;
34
viewContents = new ViewContentCollection(this);
36
ResourceService.LanguageChanged += OnTabPageTextChanged;
39
protected override void FocusContent()
41
WpfWorkbench.FocusDebug("{0}.FocusContent() IsActiveContent={1} IsKeyboardFocusWithin={2} Keyboard.FocusedElement={3}",
42
Title, IsActiveContent, IsKeyboardFocusWithin, Keyboard.FocusedElement);
43
if (!(IsActiveContent && !IsKeyboardFocusWithin)) {
46
IInputElement activeChild = CustomFocusManager.GetFocusedChild(this);
47
WpfWorkbench.FocusDebug("{0}.FocusContent() - Will move focus (activeChild={1})", this.Title, activeChild);
48
// use lambda for fetching the active child - this is necessary because the ActiveViewContent might change until the background
50
AvalonWorkbenchWindow.SetFocus(this, () => activeChild ?? (ActiveViewContent != null ? ActiveViewContent.InitiallyFocusedControl as IInputElement : null));
53
internal static void SetFocus(ManagedContent m, Func<IInputElement> activeChildFunc, bool forceSetFocus = false)
55
m.Dispatcher.BeginInvoke(
56
DispatcherPriority.Background,
59
// ensure that condition for FocusContent() is still fulfilled
60
// (necessary to avoid focus switching loops when changing layouts)
61
if (!forceSetFocus && !(m.IsActiveContent && !m.IsKeyboardFocusWithin)) {
62
WpfWorkbench.FocusDebug("{0} - not moving focus (IsActiveContent={1}, IsKeyboardFocusWithin={2})",
63
m.Title, m.IsActiveContent, m.IsKeyboardFocusWithin);
66
IInputElement activeChild = activeChildFunc();
67
WpfWorkbench.FocusDebug("{0} - moving focus to: {1}", m.Title, activeChild != null ? activeChild.ToString() : "<null>");
68
if (activeChild != null) {
69
Keyboard.Focus(activeChild);
74
public bool IsDisposed { get { return false; } }
78
public enum OpenFileTabStates {
83
ViewContentWithoutFile = 8
86
public System.Enum InternalState {
88
IViewContent content = this.ActiveViewContent;
89
OpenFileTabStates state = OpenFileTabStates.Nothing;
90
if (content != null) {
92
state |= OpenFileTabStates.FileDirty;
93
if (content.IsReadOnly)
94
state |= OpenFileTabStates.FileReadOnly;
95
if (content.PrimaryFile != null && content.PrimaryFile.IsUntitled)
96
state |= OpenFileTabStates.FileUntitled;
97
if (content.PrimaryFile == null)
98
state |= OpenFileTabStates.ViewContentWithoutFile;
105
TabControl viewTabControl;
108
/// The current view content which is shown inside this window.
110
public IViewContent ActiveViewContent {
112
WorkbenchSingleton.DebugAssertMainThread();
113
if (viewTabControl != null && viewTabControl.SelectedIndex >= 0 && viewTabControl.SelectedIndex < ViewContents.Count) {
114
return ViewContents[viewTabControl.SelectedIndex];
115
} else if (ViewContents.Count == 1) {
116
return ViewContents[0];
122
int pos = ViewContents.IndexOf(value);
124
throw new ArgumentException();
129
SDWindowsFormsHost GetActiveWinFormsHost()
131
if (viewTabControl != null && viewTabControl.SelectedIndex >= 0 && viewTabControl.SelectedIndex < ViewContents.Count) {
132
TabItem page = (TabItem)viewTabControl.Items[viewTabControl.SelectedIndex];
133
return page.Content as SDWindowsFormsHost;
135
return this.Content as SDWindowsFormsHost;
139
public event EventHandler ActiveViewContentChanged;
141
IViewContent oldActiveViewContent;
143
void UpdateActiveViewContent()
145
UpdateTitleAndInfoTip();
147
IViewContent newActiveViewContent = this.ActiveViewContent;
148
if (newActiveViewContent != null)
149
IsLocked = newActiveViewContent.IsReadOnly;
151
if (oldActiveViewContent != newActiveViewContent && ActiveViewContentChanged != null) {
152
ActiveViewContentChanged(this, EventArgs.Empty);
154
oldActiveViewContent = newActiveViewContent;
155
CommandManager.InvalidateRequerySuggested();
158
sealed class ViewContentCollection : Collection<IViewContent>
160
readonly AvalonWorkbenchWindow window;
162
internal ViewContentCollection(AvalonWorkbenchWindow window)
164
this.window = window;
167
protected override void ClearItems()
169
foreach (IViewContent vc in this) {
170
window.UnregisterContent(vc);
174
window.ClearContent();
175
window.UpdateActiveViewContent();
178
protected override void InsertItem(int index, IViewContent item)
180
base.InsertItem(index, item);
182
window.RegisterNewContent(item);
185
window.SetContent(item.Control, item);
188
window.CreateViewTabControl();
189
IViewContent oldItem = this[0];
190
if (oldItem == item) oldItem = this[1];
192
TabItem oldPage = new TabItem();
193
oldPage.Header = StringParser.Parse(oldItem.TabPageText);
194
oldPage.SetContent(oldItem.Control, oldItem);
195
window.viewTabControl.Items.Add(oldPage);
198
TabItem newPage = new TabItem();
199
newPage.Header = StringParser.Parse(item.TabPageText);
200
newPage.SetContent(item.Control, item);
202
window.viewTabControl.Items.Insert(index, newPage);
204
window.UpdateActiveViewContent();
207
protected override void RemoveItem(int index)
209
window.UnregisterContent(this[index]);
211
base.RemoveItem(index);
214
window.ClearContent();
216
window.SetContent(this[0].Control, this[0]);
219
window.viewTabControl.Items.RemoveAt(index);
221
window.UpdateActiveViewContent();
224
protected override void SetItem(int index, IViewContent item)
226
window.UnregisterContent(this[index]);
228
base.SetItem(index, item);
230
window.RegisterNewContent(item);
233
window.ClearContent();
234
window.SetContent(item.Control, item);
236
TabItem page = (TabItem)window.viewTabControl.Items[index];
237
page.SetContent(item.Control, item);
238
page.Header = StringParser.Parse(item.TabPageText);
240
window.UpdateActiveViewContent();
244
readonly ViewContentCollection viewContents;
246
public IList<IViewContent> ViewContents {
247
get { return viewContents; }
251
/// Gets whether any contained view content has changed
252
/// since the last save/load operation.
254
public bool IsDirty {
255
get { return this.ViewContents.Any(vc => vc.IsDirty); }
258
public void SwitchView(int viewNumber)
260
if (viewTabControl != null) {
261
this.viewTabControl.SelectedIndex = viewNumber;
265
public void SelectWindow()
267
Activate();//this.SetAsActive();
270
public override void OnApplyTemplate()
272
base.OnApplyTemplate();
274
if (this.DragEnabledArea != null) {
275
this.DragEnabledArea.ContextMenu = MenuService.CreateContextMenu(this, contextMenuPath);
276
UpdateInfoTip(); // set tooltip
282
ResourceService.LanguageChanged -= OnTabPageTextChanged;
283
// DetachContent must be called before the controls are disposed
284
List<IViewContent> viewContents = this.ViewContents.ToList();
285
this.ViewContents.Clear();
286
viewContents.ForEach(vc => vc.Dispose());
289
sealed class TabControlWithModifiedShortcuts : TabControl
291
readonly AvalonWorkbenchWindow parentWindow;
293
public TabControlWithModifiedShortcuts(AvalonWorkbenchWindow parentWindow)
295
this.parentWindow = parentWindow;
298
protected override void OnKeyDown(KeyEventArgs e)
300
// We don't call base.KeyDown to prevent the TabControl from handling Ctrl+Tab.
301
// Instead, we let the key press bubble up to the DocumentPane.
304
protected override void OnPreviewKeyDown(KeyEventArgs e)
306
base.OnPreviewKeyDown(e);
310
// However, we do want to handle Ctrl+PgUp / Ctrl+PgDown (SD-1735)
311
if ((e.Key == Key.PageUp || e.Key == Key.PageDown) && e.KeyboardDevice.Modifiers == ModifierKeys.Control) {
312
int index = this.SelectedIndex;
313
if (e.Key == Key.PageUp) {
314
if (++index >= this.Items.Count)
318
index = this.Items.Count - 1;
320
this.SelectedIndex = index;
322
IViewContent vc = parentWindow.ActiveViewContent;
324
SetFocus(parentWindow, () => vc.InitiallyFocusedControl as IInputElement, true);
331
private void CreateViewTabControl()
333
if (viewTabControl == null) {
334
viewTabControl = new TabControlWithModifiedShortcuts(this);
335
viewTabControl.TabStripPlacement = Dock.Bottom;
336
this.SetContent(viewTabControl);
338
viewTabControl.SelectionChanged += delegate {
339
UpdateActiveViewContent();
347
if (viewTabControl != null) {
348
foreach (TabItem page in viewTabControl.Items) {
349
page.SetContent(null);
351
viewTabControl = null;
355
void OnTitleNameChanged(object sender, EventArgs e)
357
if (sender == ActiveViewContent) {
362
void OnInfoTipChanged(object sender, EventArgs e)
364
if (sender == ActiveViewContent) {
369
void OnIsDirtyChanged(object sender, EventArgs e)
372
CommandManager.InvalidateRequerySuggested();
375
void UpdateTitleAndInfoTip()
383
IViewContent content = ActiveViewContent;
386
string newInfoTip = content.InfoTip;
388
if (newInfoTip != this.InfoTip) {
389
this.InfoTip = newInfoTip;
390
if (DragEnabledArea != null)
391
DragEnabledArea.ToolTip = this.InfoTip;
400
IViewContent content = ActiveViewContent;
401
if (content != null) {
402
string newTitle = content.TitleName;
405
if (newTitle != Title) {
413
void RegisterNewContent(IViewContent content)
415
Debug.Assert(content.WorkbenchWindow == null);
416
content.WorkbenchWindow = this;
418
content.TabPageTextChanged += OnTabPageTextChanged;
419
content.TitleNameChanged += OnTitleNameChanged;
420
content.InfoTipChanged += OnInfoTipChanged;
421
content.IsDirtyChanged += OnIsDirtyChanged;
423
this.dockLayout.Workbench.OnViewOpened(new ViewContentEventArgs(content));
426
void UnregisterContent(IViewContent content)
428
content.WorkbenchWindow = null;
430
content.TabPageTextChanged -= OnTabPageTextChanged;
431
content.TitleNameChanged -= OnTitleNameChanged;
432
content.InfoTipChanged -= OnInfoTipChanged;
433
content.IsDirtyChanged -= OnIsDirtyChanged;
435
this.dockLayout.Workbench.OnViewClosed(new ViewContentEventArgs(content));
438
void OnTabPageTextChanged(object sender, EventArgs e)
440
RefreshTabPageTexts();
445
public bool CloseWindow(bool force)
447
WorkbenchSingleton.AssertMainThread();
451
return this.ViewContents.Count == 0;
454
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
457
if (!e.Cancel && !forceClose && this.IsDirty) {
458
MessageBoxResult dr = MessageBox.Show(
459
ResourceService.GetString("MainWindow.SaveChangesMessage"),
460
ResourceService.GetString("MainWindow.SaveChangesMessageHeader") + " " + Title + " ?",
461
MessageBoxButton.YesNoCancel, MessageBoxImage.Question,
462
MessageBoxResult.Yes);
464
case MessageBoxResult.Yes:
465
foreach (IViewContent vc in this.ViewContents) {
467
ICSharpCode.SharpDevelop.Commands.SaveFile.Save(vc);
469
if (MessageService.AskQuestion("${res:MainWindow.DiscardChangesMessage}")) {
476
case MessageBoxResult.No:
478
case MessageBoxResult.Cancel:
484
foreach (IViewContent vc in this.viewContents) {
485
dockLayout.Workbench.StoreMemento(vc);
490
protected override void OnClosed()
494
CommandManager.InvalidateRequerySuggested();
497
void RefreshTabPageTexts()
499
if (viewTabControl != null) {
500
for (int i = 0; i < viewTabControl.Items.Count; ++i) {
501
TabItem tabPage = (TabItem)viewTabControl.Items[i];
502
tabPage.Header = StringParser.Parse(ViewContents[i].TabPageText);
507
void OnTitleChanged()
509
if (TitleChanged != null)
511
TitleChanged(this, EventArgs.Empty);
515
public event EventHandler TitleChanged;
517
void OnInfoTipChanged()
519
if (InfoTipChanged != null)
521
InfoTipChanged(this, EventArgs.Empty);
525
public event EventHandler InfoTipChanged;
527
public override string ToString()
529
return "[AvalonWorkbenchWindow: " + this.Title + "]";
533
/// Gets the target for re-routing commands to this window.
535
internal IInputElement GetCommandTarget()
537
return CustomFocusManager.GetFocusedChild(this) ?? GetActiveWinFormsHost();