Skip to content

Commit 6e5eeaa

Browse files
committed
Add save prompt and modify CodeEditor and TextEditor
1 parent cf4b149 commit 6e5eeaa

File tree

4 files changed

+138
-19
lines changed

4 files changed

+138
-19
lines changed

src/Gemini.Demo/Modules/TextEditor/ViewModels/EditorViewModel.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,19 @@
44
using Gemini.Demo.Modules.TextEditor.Views;
55
using Gemini.Framework;
66
using Gemini.Framework.Threading;
7+
using System.ComponentModel.Composition;
78

89
namespace Gemini.Demo.Modules.TextEditor.ViewModels
910
{
11+
[Export(typeof(EditorViewModel))]
12+
[PartCreationPolicy(CreationPolicy.NonShared)]
1013
#pragma warning disable 659
1114
public class EditorViewModel : PersistedDocument
1215
#pragma warning restore 659
1316
{
1417
private EditorView _view;
1518
private string _originalText;
19+
private bool notYetLoaded = false;
1620

1721
protected override Task DoNew()
1822
{
@@ -38,6 +42,12 @@ protected override Task DoSave(string filePath)
3842

3943
private void ApplyOriginalText()
4044
{
45+
// At StartUp, _view is null, so notYetLoaded flag is added
46+
if (_view == null)
47+
{
48+
notYetLoaded = true;
49+
return;
50+
}
4151
_view.textBox.Text = _originalText;
4252

4353
_view.textBox.TextChanged += delegate
@@ -49,6 +59,12 @@ private void ApplyOriginalText()
4959
protected override void OnViewLoaded(object view)
5060
{
5161
_view = (EditorView) view;
62+
63+
if (notYetLoaded)
64+
{
65+
ApplyOriginalText();
66+
notYetLoaded = false;
67+
}
5268
}
5369

5470
public override bool Equals(object obj)

src/Gemini.Demo/Modules/TextEditor/Views/EditorView.xaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@
1111
FontFamily="Consolas"
1212
FontSize="14"
1313
ScrollViewer.VerticalScrollBarVisibility="Visible"
14-
ScrollViewer.HorizontalScrollBarVisibility="Visible"/>
14+
ScrollViewer.HorizontalScrollBarVisibility="Visible" TextWrapping="Wrap" AcceptsReturn="True"/>
1515
</UserControl>

src/Gemini.Modules.CodeEditor/ViewModels/CodeEditorViewModel.cs

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
using Gemini.Framework;
66
using Gemini.Framework.Threading;
77
using Gemini.Modules.CodeEditor.Views;
8+
using Gemini.Modules.StatusBar;
9+
using System.ComponentModel;
10+
using Caliburn.Micro;
811

912
namespace Gemini.Modules.CodeEditor.ViewModels
1013
{
@@ -17,31 +20,25 @@ public class CodeEditorViewModel : PersistedDocument
1720
private readonly LanguageDefinitionManager _languageDefinitionManager;
1821
private string _originalText;
1922
private ICodeEditorView _view;
23+
private IStatusBar _statusBar;
24+
private bool notYetLoaded = false;
2025

2126
[ImportingConstructor]
2227
public CodeEditorViewModel(LanguageDefinitionManager languageDefinitionManager)
2328
{
2429
_languageDefinitionManager = languageDefinitionManager;
2530
}
2631

27-
public override bool ShouldReopenOnStart
28-
{
29-
get { return true; }
30-
}
31-
32-
public override void SaveState(BinaryWriter writer)
33-
{
34-
writer.Write(FilePath);
35-
}
36-
37-
public override void LoadState(BinaryReader reader)
38-
{
39-
Load(reader.ReadString());
40-
}
41-
4232
protected override void OnViewLoaded(object view)
4333
{
4434
_view = (ICodeEditorView) view;
35+
_statusBar = IoC.Get<IStatusBar>();
36+
37+
if (notYetLoaded)
38+
{
39+
ApplyOriginalText();
40+
notYetLoaded = false;
41+
}
4542
}
4643

4744
public override bool Equals(object obj)
@@ -76,20 +73,52 @@ protected override Task DoSave(string filePath)
7673

7774
private void ApplyOriginalText()
7875
{
76+
// At StartUp, _view is null, so notYetLoaded flag is added
77+
if (_view == null)
78+
{
79+
notYetLoaded = true;
80+
return;
81+
}
7982
_view.TextEditor.Text = _originalText;
8083

8184
_view.TextEditor.TextChanged += delegate
8285
{
8386
IsDirty = string.Compare(_originalText, _view.TextEditor.Text) != 0;
8487
};
8588

89+
UpdateStatusBar();
90+
91+
// To update status bar items, Caret PositionChanged event is added
92+
_view.TextEditor.TextArea.Caret.PositionChanged += delegate
93+
{
94+
UpdateStatusBar();
95+
};
96+
8697
var fileExtension = Path.GetExtension(FileName).ToLower();
8798

8899
ILanguageDefinition languageDefinition = _languageDefinitionManager.GetDefinitionByExtension(fileExtension);
89100

90101
SetLanguage(languageDefinition);
91102
}
92103

104+
/// <summary>
105+
/// Update Column and Line position properties when caret position is changed
106+
/// </summary>
107+
private void UpdateStatusBar()
108+
{
109+
int lineNumber = _view.TextEditor.Document.GetLineByOffset(_view.TextEditor.CaretOffset).LineNumber;
110+
int colPosition = _view.TextEditor.TextArea.Caret.VisualColumn + 1;
111+
112+
// TODO: Now I don't know about Ch#
113+
//int charPosition = _view.TextEditor.CaretOffset;
114+
115+
if (_statusBar != null && _statusBar.Items.Count >= 3)
116+
{
117+
_statusBar.Items[1].Message = string.Format("Ln {0}", lineNumber);
118+
_statusBar.Items[2].Message = string.Format("Col {0}", colPosition);
119+
}
120+
}
121+
93122
private void SetLanguage(ILanguageDefinition languageDefinition)
94123
{
95124
_view.TextEditor.SyntaxHighlighting = (languageDefinition != null)

src/Gemini/Framework/PersistedDocument.cs

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1+
using Caliburn.Micro;
2+
using Gemini.Framework.Services;
3+
using Gemini.Properties;
4+
using Microsoft.Win32;
15
using System.IO;
6+
using System.Linq;
27
using System.Threading.Tasks;
8+
using System.Windows;
39

410
namespace Gemini.Framework
511
{
@@ -25,10 +31,78 @@ public bool IsDirty
2531
}
2632
}
2733

28-
public override void CanClose(System.Action<bool> callback)
34+
// ShouldReopenOnStart, SaveState and LoadState are default methods of PersistedDocument.
35+
public override bool ShouldReopenOnStart
2936
{
30-
// TODO: Show save prompt.
31-
callback(!IsDirty);
37+
get { return (FilePath != null); } // if FilePath is null, SaveState() will generate an NullExceptionError
38+
}
39+
40+
public override void SaveState(BinaryWriter writer)
41+
{
42+
writer.Write(FilePath);
43+
}
44+
45+
public override async void LoadState(BinaryReader reader)
46+
{
47+
await Load(reader.ReadString());
48+
}
49+
50+
public override async void CanClose(System.Action<bool> callback)
51+
{
52+
if (IsDirty)
53+
{
54+
// Show save prompt.
55+
// Note that CanClose method of Demo ShellViewModel blocks this.
56+
string title = IoC.Get<IMainWindow>().Title;
57+
string fileName = Path.GetFileNameWithoutExtension(FileName);
58+
string fileExtension = Path.GetExtension(FileName);
59+
var fileType = IoC.GetAll<IEditorProvider>()
60+
.SelectMany(x => x.FileTypes)
61+
.SingleOrDefault(x => x.FileExtension == fileExtension);
62+
63+
string message = string.Format(Resources.SaveChangesBeforeClosingMessage, fileType.Name, fileName);
64+
var result = MessageBox.Show(message, title, MessageBoxButton.YesNoCancel, MessageBoxImage.Warning);
65+
66+
if (result == MessageBoxResult.Yes)
67+
{
68+
if (IsNew)
69+
{
70+
// Ask new file path.
71+
var filter = string.Empty;
72+
if (fileType != null)
73+
filter = fileType.Name + "|*" + fileType.FileExtension + "|";
74+
filter += Properties.Resources.AllFiles + "|*.*";
75+
76+
var dialog = new SaveFileDialog() { FileName = this.FileName, Filter = filter };
77+
if (dialog.ShowDialog() == true)
78+
{
79+
// Save file.
80+
await Save(dialog.FileName);
81+
82+
// Add to recent files. Temporally, commented out.
83+
//IShell _shell = IoC.Get<IShell>();
84+
//_shell.RecentFiles.Update(dialog.FileName);
85+
}
86+
else
87+
{
88+
callback(false);
89+
return;
90+
}
91+
}
92+
else
93+
{
94+
// Save file.
95+
await Save(FilePath);
96+
}
97+
}
98+
else if (result == MessageBoxResult.Cancel)
99+
{
100+
callback(false);
101+
return;
102+
}
103+
}
104+
105+
callback(true);
32106
}
33107

34108
private void UpdateDisplayName()

0 commit comments

Comments
 (0)