summaryrefslogtreecommitdiff
path: root/src/Notepad/Presentation
diff options
context:
space:
mode:
authormo <mo.khan@gmail.com>2018-11-04 15:22:16 -0700
committermo <mo.khan@gmail.com>2018-11-04 15:22:16 -0700
commit5ee1f55497a4e30322a56f133f897ecde1612967 (patch)
treebf544e0879234c3623869627d8786776cb19b8e9 /src/Notepad/Presentation
initial commit.HEADmaster
Diffstat (limited to 'src/Notepad/Presentation')
-rw-r--r--src/Notepad/Presentation/Context/NotepadApplicationContext.cs18
-rw-r--r--src/Notepad/Presentation/Context/NotepadApplicationContextSpecs.cs80
-rw-r--r--src/Notepad/Presentation/Core/ApplicationController.cs13
-rw-r--r--src/Notepad/Presentation/Core/ApplicationControllerSpecs.cs54
-rw-r--r--src/Notepad/Presentation/Core/IApplicationController.cs5
-rw-r--r--src/Notepad/Presentation/Core/IPresenter.cs5
-rw-r--r--src/Notepad/Presentation/Core/IPresenterRegistry.cs13
-rw-r--r--src/Notepad/Presentation/Model/Menu/File/Commands/ExitCommandSpecs.cs34
-rw-r--r--src/Notepad/Presentation/Model/Menu/File/Commands/IExitCommand.cs18
-rw-r--r--src/Notepad/Presentation/Model/Menu/File/Commands/ISaveCommand.cs27
-rw-r--r--src/Notepad/Presentation/Model/Menu/File/Commands/SaveCommandSpecs.cs95
-rw-r--r--src/Notepad/Presentation/Model/Menu/File/ExitMenuItem.cs23
-rw-r--r--src/Notepad/Presentation/Model/Menu/File/ExitMenuItemSpecs.cs72
-rw-r--r--src/Notepad/Presentation/Model/Menu/File/FileMenu.cs26
-rw-r--r--src/Notepad/Presentation/Model/Menu/File/FileMenuSpecs.cs105
-rw-r--r--src/Notepad/Presentation/Model/Menu/File/NewMenuItem.cs17
-rw-r--r--src/Notepad/Presentation/Model/Menu/File/NewMenuItemSpecs.cs46
-rw-r--r--src/Notepad/Presentation/Model/Menu/File/SaveAsMenuItem.cs24
-rw-r--r--src/Notepad/Presentation/Model/Menu/File/SaveAsMenuItemSpecs.cs102
-rw-r--r--src/Notepad/Presentation/Model/Menu/File/SaveMenuItem.cs23
-rw-r--r--src/Notepad/Presentation/Model/Menu/File/SaveMenuItemSpecs.cs99
-rw-r--r--src/Notepad/Presentation/Model/Menu/Help/AboutMenuItem.cs24
-rw-r--r--src/Notepad/Presentation/Model/Menu/Help/AboutMenuItemSpecs.cs101
-rw-r--r--src/Notepad/Presentation/Model/Menu/Help/HelpMenu.cs21
-rw-r--r--src/Notepad/Presentation/Model/Menu/Help/HelpMenuSpecs.cs76
-rw-r--r--src/Notepad/Presentation/Model/Menu/IMenuItem.cs7
-rw-r--r--src/Notepad/Presentation/Model/Menu/IMenuItemComparer.cs23
-rw-r--r--src/Notepad/Presentation/Model/Menu/ISubMenu.cs9
-rw-r--r--src/Notepad/Presentation/Model/Menu/ISubMenuItemComparer.cs20
-rw-r--r--src/Notepad/Presentation/Model/Menu/MenuNames.cs14
-rw-r--r--src/Notepad/Presentation/Presenters/Commands/IRunPresenterCommand.cs18
-rw-r--r--src/Notepad/Presentation/Presenters/Commands/RunPresenterCommandSpecs.cs34
-rw-r--r--src/Notepad/Presentation/Presenters/Menu/File/ISaveAsPresenter.cs28
-rw-r--r--src/Notepad/Presentation/Presenters/Menu/File/SaveAsPresenterSpecs.cs69
-rw-r--r--src/Notepad/Presentation/Presenters/Menu/Help/AboutApplicationPresenterSpecs.cs34
-rw-r--r--src/Notepad/Presentation/Presenters/Menu/Help/IAboutApplicationPresenter.cs18
-rw-r--r--src/Notepad/Presentation/Presenters/Menu/IMainMenuPresenter.cs29
-rw-r--r--src/Notepad/Presentation/Presenters/Menu/MainMenuPresenterSpecs.cs86
-rw-r--r--src/Notepad/Presentation/Presenters/Shell/IMainShellPresenter.cs5
-rw-r--r--src/Notepad/Presentation/Presenters/Shell/MainShellPresenter.cs17
-rw-r--r--src/Notepad/Presentation/Presenters/Shell/MainShellPresenterSpecs.cs36
-rw-r--r--src/Notepad/Presentation/Views/Menu/File/ISaveAsView.cs17
-rw-r--r--src/Notepad/Presentation/Views/Menu/Help/AboutApplicationView.Designer.cs184
-rw-r--r--src/Notepad/Presentation/Views/Menu/Help/AboutApplicationView.cs99
-rw-r--r--src/Notepad/Presentation/Views/Menu/Help/AboutApplicationView.resx120
-rw-r--r--src/Notepad/Presentation/Views/Menu/Help/IAboutApplicationView.cs5
-rw-r--r--src/Notepad/Presentation/Views/Menu/IMainMenuView.cs23
-rw-r--r--src/Notepad/Presentation/Views/Menu/MainMenuViewSpecs.cs69
-rw-r--r--src/Notepad/Presentation/Views/Menu/Mappers/IMenuItemToToolStripMenuItemMapper.cs15
-rw-r--r--src/Notepad/Presentation/Views/Menu/Mappers/ISubMenuToToolStripMenuItemMapper.cs10
-rw-r--r--src/Notepad/Presentation/Views/Menu/Mappers/MenuItemToToolStripMenuItemMapperSpecs.cs47
-rw-r--r--src/Notepad/Presentation/Views/Menu/Mappers/SubMenuToToolStripMenuItemMapper.cs21
-rw-r--r--src/Notepad/Presentation/Views/Menu/Mappers/SubMenuToToolStripMenuItemMapperSpecs.cs90
-rw-r--r--src/Notepad/Presentation/Views/Shell/WindowShell.Designer.cs82
-rw-r--r--src/Notepad/Presentation/Views/Shell/WindowShell.cs13
-rw-r--r--src/Notepad/Presentation/Views/Shell/WindowShell.resx126
56 files changed, 2489 insertions, 0 deletions
diff --git a/src/Notepad/Presentation/Context/NotepadApplicationContext.cs b/src/Notepad/Presentation/Context/NotepadApplicationContext.cs
new file mode 100644
index 0000000..8fda742
--- /dev/null
+++ b/src/Notepad/Presentation/Context/NotepadApplicationContext.cs
@@ -0,0 +1,18 @@
+using System.Windows.Forms;
+using Notepad.Presentation.Core;
+using Notepad.Presentation.Model.Menu.File.Commands;
+using Notepad.Presentation.Presenters.Shell;
+using Notepad.Presentation.Views.Shell;
+
+namespace Notepad.Presentation.Context {
+ public class NotepadApplicationContext : ApplicationContext {
+ public NotepadApplicationContext(
+ WindowShell shellView,
+ IExitCommand exitCommand,
+ IApplicationController applicationController) {
+ shellView.Closed += delegate { exitCommand.Execute(); };
+ applicationController.Run<IMainShellPresenter>();
+ MainForm = shellView;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Context/NotepadApplicationContextSpecs.cs b/src/Notepad/Presentation/Context/NotepadApplicationContextSpecs.cs
new file mode 100644
index 0000000..f8b9765
--- /dev/null
+++ b/src/Notepad/Presentation/Context/NotepadApplicationContextSpecs.cs
@@ -0,0 +1,80 @@
+using MbUnit.Framework;
+using Notepad.Presentation.Context;
+using Notepad.Presentation.Core;
+using Notepad.Presentation.Model.Menu.File.Commands;
+using Notepad.Presentation.Presenters.Shell;
+using Notepad.Presentation.Views.Shell;
+using Notepad.Test.Extensions;
+using Rhino.Mocks;
+using Rhino.Mocks.Constraints;
+using Rhino.Mocks.Interfaces;
+
+namespace Notepad.Presentation.Context {
+ public class NotepadApplicationContextSpecs {}
+
+ [TestFixture]
+ public class when_creating_the_application_context_ {
+ private MockRepository mockery;
+ private WindowShell shellView;
+ private IExitCommand exitCommand;
+ private IApplicationController applicationController;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ shellView = mockery.DynamicMock<WindowShell>();
+ exitCommand = mockery.DynamicMock<IExitCommand>();
+ applicationController = mockery.DynamicMock<IApplicationController>();
+ }
+
+ [Test]
+ public void should_register_for_the_main_shell_views_closing_event() {
+ using (mockery.Record()) {
+ shellView.Closed += null;
+ LastCall.Constraints(Is.NotNull());
+ }
+ using (mockery.Playback()) {
+ CreateSUT();
+ }
+ }
+
+ [Test]
+ public void should_execute_the_exit_application_command() {
+ IEventRaiser raiser;
+ using (mockery.Record()) {
+ shellView.Closed += null;
+ raiser = LastCall.Constraints(Is.NotNull()).GetEventRaiser();
+
+ exitCommand.Execute();
+ }
+ using (mockery.Playback()) {
+ CreateSUT();
+ raiser.Raise(null, null);
+ }
+ }
+
+ [Test]
+ public void should_specify_the_main_shell_view_as_the_main_form() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().MainForm.ShouldBeEqualTo(shellView);
+ }
+ }
+
+ [Test]
+ public void should_run_the_main_shell_presenter() {
+ using (mockery.Record()) {
+ applicationController.Run<IMainShellPresenter>();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT();
+ }
+ }
+
+ private NotepadApplicationContext CreateSUT() {
+ return new NotepadApplicationContext(shellView, exitCommand, applicationController);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Core/ApplicationController.cs b/src/Notepad/Presentation/Core/ApplicationController.cs
new file mode 100644
index 0000000..4970593
--- /dev/null
+++ b/src/Notepad/Presentation/Core/ApplicationController.cs
@@ -0,0 +1,13 @@
+namespace Notepad.Presentation.Core {
+ public class ApplicationController : IApplicationController {
+ private readonly IPresenterRegistry registeredPresenters;
+
+ public ApplicationController(IPresenterRegistry presenterRegistry) {
+ registeredPresenters = presenterRegistry;
+ }
+
+ public void Run<Presenter>() where Presenter : IPresenter {
+ registeredPresenters.FindAnImplementationOf<Presenter>().Initialize();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Core/ApplicationControllerSpecs.cs b/src/Notepad/Presentation/Core/ApplicationControllerSpecs.cs
new file mode 100644
index 0000000..834ec7c
--- /dev/null
+++ b/src/Notepad/Presentation/Core/ApplicationControllerSpecs.cs
@@ -0,0 +1,54 @@
+using MbUnit.Framework;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Core {
+ public class ApplicationControllerSpecs {}
+
+ [TestFixture]
+ public class when_the_application_controller_is_asked_to_run_a_presenter_ {
+ private MockRepository mockery;
+ private IPresenterRegistry presenterRegistry;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ presenterRegistry = mockery.DynamicMock<IPresenterRegistry>();
+ }
+
+ [Test]
+ public void should_ask_the_registered_presenters_for_an_instance_of_the_presenter_to_run() {
+ var implementationOfThePresenter = mockery.DynamicMock<IPresenter>();
+ using (mockery.Record()) {
+ Expect
+ .Call(presenterRegistry.FindAnImplementationOf<IPresenter>())
+ .Return(implementationOfThePresenter)
+ .Repeat
+ .AtLeastOnce();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Run<IPresenter>();
+ }
+ }
+
+ [Test]
+ public void should_initialize_the_presenter_to_run() {
+ var presenterToInitialize = mockery.DynamicMock<IPresenter>();
+ using (mockery.Record()) {
+ SetupResult
+ .For(presenterRegistry.FindAnImplementationOf<IPresenter>())
+ .Return(presenterToInitialize);
+
+ presenterToInitialize.Initialize();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Run<IPresenter>();
+ }
+ }
+
+ private IApplicationController CreateSUT() {
+ return new ApplicationController(presenterRegistry);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Core/IApplicationController.cs b/src/Notepad/Presentation/Core/IApplicationController.cs
new file mode 100644
index 0000000..735ef04
--- /dev/null
+++ b/src/Notepad/Presentation/Core/IApplicationController.cs
@@ -0,0 +1,5 @@
+namespace Notepad.Presentation.Core {
+ public interface IApplicationController {
+ void Run<Presenter>() where Presenter : IPresenter;
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Core/IPresenter.cs b/src/Notepad/Presentation/Core/IPresenter.cs
new file mode 100644
index 0000000..45d40b2
--- /dev/null
+++ b/src/Notepad/Presentation/Core/IPresenter.cs
@@ -0,0 +1,5 @@
+namespace Notepad.Presentation.Core {
+ public interface IPresenter {
+ void Initialize();
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Core/IPresenterRegistry.cs b/src/Notepad/Presentation/Core/IPresenterRegistry.cs
new file mode 100644
index 0000000..9117489
--- /dev/null
+++ b/src/Notepad/Presentation/Core/IPresenterRegistry.cs
@@ -0,0 +1,13 @@
+using Notepad.Infrastructure.Container;
+
+namespace Notepad.Presentation.Core {
+ public interface IPresenterRegistry {
+ Presenter FindAnImplementationOf<Presenter>() where Presenter : IPresenter;
+ }
+
+ public class RegisteredPresenter : IPresenterRegistry {
+ public Presenter FindAnImplementationOf<Presenter>() where Presenter : IPresenter {
+ return Resolve.DependencyFor<Presenter>();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Model/Menu/File/Commands/ExitCommandSpecs.cs b/src/Notepad/Presentation/Model/Menu/File/Commands/ExitCommandSpecs.cs
new file mode 100644
index 0000000..00855b6
--- /dev/null
+++ b/src/Notepad/Presentation/Model/Menu/File/Commands/ExitCommandSpecs.cs
@@ -0,0 +1,34 @@
+using MbUnit.Framework;
+using Notepad.Infrastructure.System;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Model.Menu.File.Commands {
+ public class ExitCommandSpecs {}
+
+ [TestFixture]
+ public class when_executing_the_exit_command_specs_ {
+ private MockRepository mockery;
+ private IApplicationEnvironment application;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ application = mockery.DynamicMock<IApplicationEnvironment>();
+ }
+
+ [Test]
+ public void should_ask_the_application_environment_to_shut_down() {
+ using (mockery.Record()) {
+ application.ShutDown();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Execute();
+ }
+ }
+
+ private IExitCommand CreateSUT() {
+ return new ExitCommand(application);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Model/Menu/File/Commands/IExitCommand.cs b/src/Notepad/Presentation/Model/Menu/File/Commands/IExitCommand.cs
new file mode 100644
index 0000000..a78c32b
--- /dev/null
+++ b/src/Notepad/Presentation/Model/Menu/File/Commands/IExitCommand.cs
@@ -0,0 +1,18 @@
+using Notepad.Infrastructure.Core;
+using Notepad.Infrastructure.System;
+
+namespace Notepad.Presentation.Model.Menu.File.Commands {
+ public interface IExitCommand : ICommand {}
+
+ public class ExitCommand : IExitCommand {
+ private readonly IApplicationEnvironment application;
+
+ public ExitCommand(IApplicationEnvironment application) {
+ this.application = application;
+ }
+
+ public void Execute() {
+ application.ShutDown();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Model/Menu/File/Commands/ISaveCommand.cs b/src/Notepad/Presentation/Model/Menu/File/Commands/ISaveCommand.cs
new file mode 100644
index 0000000..ea7c396
--- /dev/null
+++ b/src/Notepad/Presentation/Model/Menu/File/Commands/ISaveCommand.cs
@@ -0,0 +1,27 @@
+using Notepad.Infrastructure.Core;
+using Notepad.Presentation.Core;
+using Notepad.Presentation.Presenters.Menu.File;
+using Notepad.Tasks;
+
+namespace Notepad.Presentation.Model.Menu.File.Commands {
+ public interface ISaveCommand : ICommand {}
+
+ public class SaveCommand : ISaveCommand {
+ private readonly IApplicationController controller;
+ private readonly IDocumentTasks tasks;
+
+ public SaveCommand(IApplicationController controller, IDocumentTasks tasks) {
+ this.controller = controller;
+ this.tasks = tasks;
+ }
+
+ public void Execute() {
+ if (!tasks.HasAPathToSaveToBeenSpecified()) {
+ controller.Run<ISaveAsPresenter>();
+ }
+ else {
+ tasks.SaveTheActiveDocument();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Model/Menu/File/Commands/SaveCommandSpecs.cs b/src/Notepad/Presentation/Model/Menu/File/Commands/SaveCommandSpecs.cs
new file mode 100644
index 0000000..7e4d827
--- /dev/null
+++ b/src/Notepad/Presentation/Model/Menu/File/Commands/SaveCommandSpecs.cs
@@ -0,0 +1,95 @@
+using MbUnit.Framework;
+using Notepad.Presentation.Core;
+using Notepad.Presentation.Presenters.Menu.File;
+using Notepad.Tasks;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Model.Menu.File.Commands {
+ public class SaveCommandSpecs {}
+
+ [TestFixture]
+ public class when_executing_the_save_command_and_a_file_path_to_save_to_has_not_been_specified_ {
+ private MockRepository mockery;
+ private IApplicationController applicationController;
+ private IDocumentTasks tasks;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ applicationController = mockery.DynamicMock<IApplicationController>();
+ tasks = mockery.DynamicMock<IDocumentTasks>();
+
+ SetupResult.For(tasks.HasAPathToSaveToBeenSpecified()).Return(false);
+ }
+
+ [Test]
+ public void should_run_the_save_as_presenter() {
+ using (mockery.Record()) {
+ applicationController.Run<ISaveAsPresenter>();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Execute();
+ }
+ }
+
+ [Test]
+ public void should_not_save_the_active_document() {
+ using (mockery.Record()) {
+ tasks.SaveTheActiveDocument();
+ LastCall.Repeat.Never();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Execute();
+ }
+ }
+
+ private ISaveCommand CreateSUT() {
+ return new SaveCommand(applicationController, tasks);
+ }
+ }
+
+ [TestFixture]
+ public class when_executing_the_save_command_and_a_path_to_save_to_has_already_been_specified_ {
+ private MockRepository mockery;
+ private IApplicationController applicationController;
+ private IDocumentTasks tasks;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ applicationController = mockery.DynamicMock<IApplicationController>();
+ tasks = mockery.DynamicMock<IDocumentTasks>();
+
+ SetupResult.For(tasks.HasAPathToSaveToBeenSpecified()).Return(true);
+ }
+
+ [Test]
+ public void should_not_run_the_save_as_presenter() {
+ using (mockery.Record()) {
+ applicationController.Run<ISaveAsPresenter>();
+ LastCall.Repeat.Never();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Execute();
+ }
+ }
+
+ [Test]
+ public void should_save_the_active_document() {
+ using (mockery.Record()) {
+ tasks.SaveTheActiveDocument();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Execute();
+ }
+ }
+
+ private ISaveCommand CreateSUT() {
+ return new SaveCommand(applicationController, tasks);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Model/Menu/File/ExitMenuItem.cs b/src/Notepad/Presentation/Model/Menu/File/ExitMenuItem.cs
new file mode 100644
index 0000000..c926dac
--- /dev/null
+++ b/src/Notepad/Presentation/Model/Menu/File/ExitMenuItem.cs
@@ -0,0 +1,23 @@
+using Notepad.Presentation.Model.Menu.File.Commands;
+
+namespace Notepad.Presentation.Model.Menu.File {
+ public class ExitMenuItem : IMenuItem {
+ private readonly IExitCommand exitCommand;
+
+ public ExitMenuItem(IExitCommand exitCommand) {
+ this.exitCommand = exitCommand;
+ }
+
+ public string Name() {
+ return "E&xit";
+ }
+
+ public void Click() {
+ exitCommand.Execute();
+ }
+
+ public bool BelongsTo(ISubMenu menu) {
+ return menu.Name().Equals(MenuNames.File);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Model/Menu/File/ExitMenuItemSpecs.cs b/src/Notepad/Presentation/Model/Menu/File/ExitMenuItemSpecs.cs
new file mode 100644
index 0000000..3220ae5
--- /dev/null
+++ b/src/Notepad/Presentation/Model/Menu/File/ExitMenuItemSpecs.cs
@@ -0,0 +1,72 @@
+using MbUnit.Framework;
+using Notepad.Presentation.Model.Menu.File.Commands;
+using Notepad.Test.Extensions;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Model.Menu.File {
+ public class ExitMenuItemSpecs {}
+
+ [TestFixture]
+ public class when_asking_the_exit_menu_item_for_its_name_ {
+ [Test]
+ public void should_return_the_correct_name() {
+ CreateSUT().Name().ShouldBeEqualTo("E&xit");
+ }
+
+ private IMenuItem CreateSUT() {
+ return new ExitMenuItem(null);
+ }
+ }
+
+ [TestFixture]
+ public class when_clicking_on_the_exit_menu_item_ {
+ private MockRepository mockery;
+ private IExitCommand exitCommand;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ exitCommand = mockery.DynamicMock<IExitCommand>();
+ }
+
+ [Test]
+ public void should_execute_the_exit_command() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().Click();
+ }
+ }
+
+ private IMenuItem CreateSUT() {
+ return new ExitMenuItem(exitCommand);
+ }
+ }
+
+ [TestFixture]
+ public class when_asking_the_exit_menu_item_if_it_belongs_to_a_menu_that_it_does {
+ private MockRepository mockery;
+ private ISubMenu fileMenu;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ fileMenu = mockery.DynamicMock<ISubMenu>();
+
+ SetupResult.For(fileMenu.Name()).Return(MenuNames.File);
+ }
+
+ [Test]
+ public void should_return_true() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().BelongsTo(fileMenu).ShouldBeEqualTo(true);
+ }
+ }
+
+ private IMenuItem CreateSUT() {
+ return new ExitMenuItem(null);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Model/Menu/File/FileMenu.cs b/src/Notepad/Presentation/Model/Menu/File/FileMenu.cs
new file mode 100644
index 0000000..98b5a47
--- /dev/null
+++ b/src/Notepad/Presentation/Model/Menu/File/FileMenu.cs
@@ -0,0 +1,26 @@
+using System.Collections.Generic;
+using Notepad.Domain.Repositories;
+using Notepad.Infrastructure.Extensions;
+
+namespace Notepad.Presentation.Model.Menu.File {
+ public class FileMenu : ISubMenu {
+ private readonly IRepository<IMenuItem> repository;
+ private readonly IMenuItemComparer menuItemComparer;
+
+ public FileMenu(IRepository<IMenuItem> repository, IMenuItemComparer menuItemComparer) {
+ this.repository = repository;
+ this.menuItemComparer = menuItemComparer;
+ }
+
+ public IEnumerable<IMenuItem> AllMenuItems() {
+ return repository
+ .All()
+ .ThatSatisfy(menuItem => menuItem.BelongsTo(this))
+ .SortedUsing(menuItemComparer);
+ }
+
+ public string Name() {
+ return MenuNames.File;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Model/Menu/File/FileMenuSpecs.cs b/src/Notepad/Presentation/Model/Menu/File/FileMenuSpecs.cs
new file mode 100644
index 0000000..8630ad3
--- /dev/null
+++ b/src/Notepad/Presentation/Model/Menu/File/FileMenuSpecs.cs
@@ -0,0 +1,105 @@
+using System.Collections.Generic;
+using MbUnit.Framework;
+using Notepad.Domain.Repositories;
+using Notepad.Infrastructure.Extensions;
+using Notepad.Test.Extensions;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Model.Menu.File {
+ public class FileMenuSpecs {}
+
+ [TestFixture]
+ public class when_asking_the_file_menu_for_its_name_ {
+ [Test]
+ public void should_return_the_correct_name() {
+ CreateSUT().Name().ShouldBeEqualTo(MenuNames.File);
+ }
+
+ private ISubMenu CreateSUT() {
+ return new FileMenu(null, null);
+ }
+ }
+
+ [TestFixture]
+ public class when_asking_the_file_menu_for_its_menu_items_ {
+ private MockRepository mockery;
+ private IRepository<IMenuItem> repository;
+ private ISubMenu sut;
+ private IMenuItemComparer menuItemComparer;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ repository = mockery.DynamicMock<IRepository<IMenuItem>>();
+ menuItemComparer = mockery.DynamicMock<IMenuItemComparer>();
+
+ sut = CreateSUT();
+ }
+
+ [Test]
+ public void should_ask_the_repository_for_all_the_menu_items() {
+ using (mockery.Record()) {
+ Expect
+ .Call(repository.All())
+ .Return(new List<IMenuItem>())
+ .Repeat
+ .AtLeastOnce();
+ }
+
+ using (mockery.Playback()) {
+ sut.AllMenuItems().Walk();
+ }
+ }
+
+ [Test]
+ public void should_return_the_menu_items_that_belong_to_the_file_menu() {
+ var saveMenuItem = mockery.DynamicMock<IMenuItem>();
+ var helpMenuItem = mockery.DynamicMock<IMenuItem>();
+
+ var allMenuItems = new List<IMenuItem> {saveMenuItem, helpMenuItem};
+
+ using (mockery.Record()) {
+ SetupResult.For(repository.All()).Return(allMenuItems);
+ SetupResult.For(saveMenuItem.BelongsTo(sut)).Return(true);
+ SetupResult.For(helpMenuItem.BelongsTo(sut)).Return(false);
+ }
+
+ using (mockery.Playback()) {
+ var returnedItems = sut.AllMenuItems();
+ returnedItems.ShouldContain(saveMenuItem);
+ returnedItems.ShouldNotContain(helpMenuItem);
+ }
+ }
+
+ [Test]
+ public void should_sort_the_items_in_the_file_menu() {
+ var firstItem = mockery.DynamicMock<IMenuItem>();
+ var secondItem = mockery.DynamicMock<IMenuItem>();
+
+ var allMenuItems = new List<IMenuItem> {firstItem, secondItem};
+
+ using (mockery.Record()) {
+ SetupResult.For(repository.All()).Return(allMenuItems);
+ SetupResult.For(firstItem.BelongsTo(null))
+ .IgnoreArguments()
+ .Return(true);
+ SetupResult.For(secondItem.BelongsTo(null))
+ .IgnoreArguments()
+ .Return(true);
+ Expect
+ .Call(menuItemComparer.Compare(firstItem, secondItem))
+ .Return(1)
+ .Repeat
+ .AtLeastOnce();
+ }
+
+ using (mockery.Playback()) {
+ sut.AllMenuItems().Walk();
+ }
+ }
+
+ private ISubMenu CreateSUT() {
+ return new FileMenu(repository, menuItemComparer);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Model/Menu/File/NewMenuItem.cs b/src/Notepad/Presentation/Model/Menu/File/NewMenuItem.cs
new file mode 100644
index 0000000..ab527d8
--- /dev/null
+++ b/src/Notepad/Presentation/Model/Menu/File/NewMenuItem.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace Notepad.Presentation.Model.Menu.File {
+ public class NewMenuItem : IMenuItem {
+ public void Click() {
+ throw new NotImplementedException();
+ }
+
+ public bool BelongsTo(ISubMenu menu) {
+ return menu.Name().Equals(MenuNames.File);
+ }
+
+ public string Name() {
+ return "&New";
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Model/Menu/File/NewMenuItemSpecs.cs b/src/Notepad/Presentation/Model/Menu/File/NewMenuItemSpecs.cs
new file mode 100644
index 0000000..54a7b20
--- /dev/null
+++ b/src/Notepad/Presentation/Model/Menu/File/NewMenuItemSpecs.cs
@@ -0,0 +1,46 @@
+using MbUnit.Framework;
+using Notepad.Test.Extensions;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Model.Menu.File {
+ public class NewMenuItemSpecs {}
+
+ [TestFixture]
+ public class when_asking_the_new_file_menu_if_it_belongs_to_the_file_menu_ {
+ private MockRepository mockery;
+ private ISubMenu fileMenu;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ fileMenu = mockery.DynamicMock<ISubMenu>();
+
+ SetupResult.For(fileMenu.Name()).Return("&File");
+ }
+
+ [Test]
+ public void should_return_true() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().BelongsTo(fileMenu).ShouldBeEqualTo(true);
+ }
+ }
+
+ private IMenuItem CreateSUT() {
+ return new NewMenuItem();
+ }
+ }
+
+ [TestFixture]
+ public class when_asking_the_new_file_menu_item_for_its_name_ {
+ [Test]
+ public void should_return_the_correct_name() {
+ CreateSUT().Name().ShouldBeEqualTo("&New");
+ }
+
+ private IMenuItem CreateSUT() {
+ return new NewMenuItem();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Model/Menu/File/SaveAsMenuItem.cs b/src/Notepad/Presentation/Model/Menu/File/SaveAsMenuItem.cs
new file mode 100644
index 0000000..859ad32
--- /dev/null
+++ b/src/Notepad/Presentation/Model/Menu/File/SaveAsMenuItem.cs
@@ -0,0 +1,24 @@
+using Notepad.Presentation.Presenters.Commands;
+using Notepad.Presentation.Presenters.Menu.File;
+
+namespace Notepad.Presentation.Model.Menu.File {
+ public class SaveAsMenuItem : IMenuItem {
+ private readonly IRunPresenterCommand<ISaveAsPresenter> saveAsCommand;
+
+ public SaveAsMenuItem(IRunPresenterCommand<ISaveAsPresenter> saveAsCommand) {
+ this.saveAsCommand = saveAsCommand;
+ }
+
+ public string Name() {
+ return "Save &As...";
+ }
+
+ public void Click() {
+ saveAsCommand.Execute();
+ }
+
+ public bool BelongsTo(ISubMenu menu) {
+ return menu.Name().Equals(MenuNames.File);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Model/Menu/File/SaveAsMenuItemSpecs.cs b/src/Notepad/Presentation/Model/Menu/File/SaveAsMenuItemSpecs.cs
new file mode 100644
index 0000000..a7c7fd0
--- /dev/null
+++ b/src/Notepad/Presentation/Model/Menu/File/SaveAsMenuItemSpecs.cs
@@ -0,0 +1,102 @@
+using MbUnit.Framework;
+using Notepad.Presentation.Model.Menu.File.Commands;
+using Notepad.Presentation.Presenters.Commands;
+using Notepad.Presentation.Presenters.Menu.File;
+using Notepad.Test.Extensions;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Model.Menu.File {
+ public class SaveAsMenuItemSpecs {}
+
+ [TestFixture]
+ public class when_asking_the_save_as_menu_item_for_its_name_ {
+ [Test]
+ public void should_return_the_correct_name() {
+ CreateSUT().Name().ShouldBeEqualTo("Save &As...");
+ }
+
+ private IMenuItem CreateSUT() {
+ return new SaveAsMenuItem(null);
+ }
+ }
+
+ [TestFixture]
+ public class when_clicking_on_the_save_as_menu_item_ {
+ private MockRepository mockery;
+ private IRunPresenterCommand<ISaveAsPresenter> saveAsCommand;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ saveAsCommand = mockery.DynamicMock<IRunPresenterCommand<ISaveAsPresenter>>();
+ }
+
+ [Test]
+ public void should_execute_the_save_as_command() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().Click();
+ }
+ }
+
+ private IMenuItem CreateSUT() {
+ return new SaveAsMenuItem(saveAsCommand);
+ }
+ }
+
+ [TestFixture]
+ public class when_asking_the_save_as_menu_item_if_it_belongs_to_a_menu_that_it_does {
+ private MockRepository mockery;
+ private ISubMenu fileMenu;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ fileMenu = mockery.DynamicMock<ISubMenu>();
+
+ SetupResult.For(fileMenu.Name()).Return(MenuNames.File);
+ }
+
+ [Test]
+ public void should_return_true() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().BelongsTo(fileMenu).ShouldBeEqualTo(true);
+ }
+ }
+
+ private IMenuItem CreateSUT() {
+ return new SaveAsMenuItem(null);
+ }
+ }
+
+ [TestFixture]
+ public class when_asking_the_save_as_menu_item_if_it_belongs_to_a_menu_item_that_it_does_not {
+ private MockRepository mockery;
+ private ISubMenu unknownMenu;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ unknownMenu = mockery.DynamicMock<ISubMenu>();
+
+ SetupResult.For(unknownMenu.Name()).Return("blah");
+ }
+
+ [Test]
+ public void should_return_false() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().BelongsTo(unknownMenu).ShouldBeEqualTo(false);
+ }
+ }
+
+
+ private IMenuItem CreateSUT() {
+ return new SaveAsMenuItem(null);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Model/Menu/File/SaveMenuItem.cs b/src/Notepad/Presentation/Model/Menu/File/SaveMenuItem.cs
new file mode 100644
index 0000000..f99c179
--- /dev/null
+++ b/src/Notepad/Presentation/Model/Menu/File/SaveMenuItem.cs
@@ -0,0 +1,23 @@
+using Notepad.Presentation.Model.Menu.File.Commands;
+
+namespace Notepad.Presentation.Model.Menu.File {
+ public class SaveMenuItem : IMenuItem {
+ private readonly ISaveCommand saveCommand;
+
+ public SaveMenuItem(ISaveCommand saveCommand) {
+ this.saveCommand = saveCommand;
+ }
+
+ public bool BelongsTo(ISubMenu menu) {
+ return menu.Name().Equals(MenuNames.File);
+ }
+
+ public void Click() {
+ saveCommand.Execute();
+ }
+
+ public string Name() {
+ return "&Save";
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Model/Menu/File/SaveMenuItemSpecs.cs b/src/Notepad/Presentation/Model/Menu/File/SaveMenuItemSpecs.cs
new file mode 100644
index 0000000..071adf8
--- /dev/null
+++ b/src/Notepad/Presentation/Model/Menu/File/SaveMenuItemSpecs.cs
@@ -0,0 +1,99 @@
+using MbUnit.Framework;
+using Notepad.Presentation.Model.Menu.File.Commands;
+using Notepad.Test.Extensions;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Model.Menu.File {
+ public class SaveMenuItemSpecs {}
+
+ [TestFixture]
+ public class when_asking_the_save_menu_item_for_its_name_ {
+ [Test]
+ public void should_return_the_correct_name() {
+ CreateSUT().Name().ShouldBeEqualTo("&Save");
+ }
+
+ private IMenuItem CreateSUT() {
+ return new SaveMenuItem(null);
+ }
+ }
+
+ [TestFixture]
+ public class when_clicking_on_the_save_menu_item_ {
+ private MockRepository mockery;
+ private ISaveCommand saveCommand;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ saveCommand = mockery.DynamicMock<ISaveCommand>();
+ }
+
+ [Test]
+ public void should_execute_the_save_command() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().Click();
+ }
+ }
+
+ private IMenuItem CreateSUT() {
+ return new SaveMenuItem(saveCommand);
+ }
+ }
+
+ [TestFixture]
+ public class when_asking_the_save_menu_item_if_it_belongs_to_a_menu_that_it_does {
+ private MockRepository mockery;
+ private ISubMenu fileMenu;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ fileMenu = mockery.DynamicMock<ISubMenu>();
+
+ SetupResult.For(fileMenu.Name()).Return(MenuNames.File);
+ }
+
+ [Test]
+ public void should_return_true() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().BelongsTo(fileMenu).ShouldBeEqualTo(true);
+ }
+ }
+
+ private IMenuItem CreateSUT() {
+ return new SaveMenuItem(null);
+ }
+ }
+
+ [TestFixture]
+ public class when_asking_the_save_menu_item_if_it_belongs_to_a_menu_item_that_it_does_not {
+ private MockRepository mockery;
+ private ISubMenu unknownMenu;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ unknownMenu = mockery.DynamicMock<ISubMenu>();
+
+ SetupResult.For(unknownMenu.Name()).Return("blah");
+ }
+
+ [Test]
+ public void should_return_false() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().BelongsTo(unknownMenu).ShouldBeEqualTo(false);
+ }
+ }
+
+ private IMenuItem CreateSUT() {
+ return new SaveMenuItem(null);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Model/Menu/Help/AboutMenuItem.cs b/src/Notepad/Presentation/Model/Menu/Help/AboutMenuItem.cs
new file mode 100644
index 0000000..82a2839
--- /dev/null
+++ b/src/Notepad/Presentation/Model/Menu/Help/AboutMenuItem.cs
@@ -0,0 +1,24 @@
+using Notepad.Presentation.Presenters.Commands;
+using Notepad.Presentation.Presenters.Menu.Help;
+
+namespace Notepad.Presentation.Model.Menu.Help {
+ public class AboutMenuItem : IMenuItem {
+ private readonly IRunPresenterCommand<IAboutApplicationPresenter> displayAboutCommand;
+
+ public AboutMenuItem(IRunPresenterCommand<IAboutApplicationPresenter> displayAboutCommand) {
+ this.displayAboutCommand = displayAboutCommand;
+ }
+
+ public bool BelongsTo(ISubMenu menu) {
+ return menu.Name().Equals(MenuNames.Help);
+ }
+
+ public void Click() {
+ displayAboutCommand.Execute();
+ }
+
+ public string Name() {
+ return MenuItemNames.About;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Model/Menu/Help/AboutMenuItemSpecs.cs b/src/Notepad/Presentation/Model/Menu/Help/AboutMenuItemSpecs.cs
new file mode 100644
index 0000000..74d7663
--- /dev/null
+++ b/src/Notepad/Presentation/Model/Menu/Help/AboutMenuItemSpecs.cs
@@ -0,0 +1,101 @@
+using MbUnit.Framework;
+using Notepad.Presentation.Presenters.Commands;
+using Notepad.Presentation.Presenters.Menu.Help;
+using Notepad.Test.Extensions;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Model.Menu.Help {
+ public class AboutMenuItemSpecs {}
+
+ [TestFixture]
+ public class when_asking_the_about_menu_item_for_its_name_ {
+ [Test]
+ public void should_return_the_correct_name() {
+ CreateSUT().Name().ShouldBeEqualTo(MenuItemNames.About);
+ }
+
+ private IMenuItem CreateSUT() {
+ return new AboutMenuItem(null);
+ }
+ }
+
+ [TestFixture]
+ public class when_clicking_on_the_about_menu_item_ {
+ private MockRepository mockery;
+ private IRunPresenterCommand<IAboutApplicationPresenter> displayAboutCommand;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ displayAboutCommand = mockery.DynamicMock<IRunPresenterCommand<IAboutApplicationPresenter>>();
+ }
+
+ [Test]
+ public void should_execute_the_display_about_command() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().Click();
+ }
+ }
+
+ private IMenuItem CreateSUT() {
+ return new AboutMenuItem(displayAboutCommand);
+ }
+ }
+
+ [TestFixture]
+ public class when_asking_the_about_menu_item_if_it_belongs_to_a_menu_that_it_does {
+ private MockRepository mockery;
+ private ISubMenu menuThatThisItemBelongsTo;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ menuThatThisItemBelongsTo = mockery.DynamicMock<ISubMenu>();
+
+ SetupResult.For(menuThatThisItemBelongsTo.Name()).Return(MenuNames.Help);
+ }
+
+ [Test]
+ public void should_return_true() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().BelongsTo(menuThatThisItemBelongsTo).ShouldBeEqualTo(true);
+ }
+ }
+
+ private IMenuItem CreateSUT() {
+ return new AboutMenuItem(null);
+ }
+ }
+
+ [TestFixture]
+ public class when_asking_the_about_menu_item_if_it_belongs_to_a_menu_item_that_it_does_not {
+ private MockRepository mockery;
+ private ISubMenu unknownMenu;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ unknownMenu = mockery.DynamicMock<ISubMenu>();
+
+ SetupResult.For(unknownMenu.Name()).Return("blah");
+ }
+
+ [Test]
+ public void should_return_false() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().BelongsTo(unknownMenu).ShouldBeEqualTo(false);
+ }
+ }
+
+
+ private IMenuItem CreateSUT() {
+ return new AboutMenuItem(null);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Model/Menu/Help/HelpMenu.cs b/src/Notepad/Presentation/Model/Menu/Help/HelpMenu.cs
new file mode 100644
index 0000000..ace9696
--- /dev/null
+++ b/src/Notepad/Presentation/Model/Menu/Help/HelpMenu.cs
@@ -0,0 +1,21 @@
+using System.Collections.Generic;
+using Notepad.Domain.Repositories;
+using Notepad.Infrastructure.Extensions;
+
+namespace Notepad.Presentation.Model.Menu.Help {
+ public class HelpMenu : ISubMenu {
+ private readonly IRepository<IMenuItem> menuItems;
+
+ public HelpMenu(IRepository<IMenuItem> repository) {
+ menuItems = repository;
+ }
+
+ public IEnumerable<IMenuItem> AllMenuItems() {
+ return menuItems.All().ThatSatisfy(m => m.BelongsTo(this));
+ }
+
+ public string Name() {
+ return MenuNames.Help;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Model/Menu/Help/HelpMenuSpecs.cs b/src/Notepad/Presentation/Model/Menu/Help/HelpMenuSpecs.cs
new file mode 100644
index 0000000..c716376
--- /dev/null
+++ b/src/Notepad/Presentation/Model/Menu/Help/HelpMenuSpecs.cs
@@ -0,0 +1,76 @@
+using System.Collections.Generic;
+using MbUnit.Framework;
+using Notepad.Domain.Repositories;
+using Notepad.Infrastructure.Extensions;
+using Notepad.Presentation.Model.Menu.Help;
+using Notepad.Test.Extensions;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Model.Menu.Help {
+ public class HelpMenuSpecs {}
+
+ [TestFixture]
+ public class when_asking_the_help_menu_for_its_name_ {
+ [Test]
+ public void should_return_the_correct_name() {
+ CreateSUT().Name().ShouldBeEqualTo(MenuNames.Help);
+ }
+
+ private ISubMenu CreateSUT() {
+ return new HelpMenu(null);
+ }
+ }
+
+ [TestFixture]
+ public class when_asking_the_help_menu_for_its_menu_items_ {
+ private MockRepository mockery;
+ private IRepository<IMenuItem> repository;
+ private ISubMenu sut;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ repository = mockery.DynamicMock<IRepository<IMenuItem>>();
+ sut = CreateSUT();
+ }
+
+ [Test]
+ public void should_ask_the_repository_for_all_the_menu_items() {
+ using (mockery.Record()) {
+ Expect
+ .Call(repository.All())
+ .Return(new List<IMenuItem>())
+ .Repeat
+ .AtLeastOnce();
+ }
+
+ using (mockery.Playback()) {
+ sut.AllMenuItems().Walk();
+ }
+ }
+
+ [Test]
+ public void should_return_the_menu_items_that_belong_to_the_help_menu() {
+ var saveMenuItem = mockery.DynamicMock<IMenuItem>();
+ var helpMenuItem = mockery.DynamicMock<IMenuItem>();
+
+ var allMenuItems = new List<IMenuItem> {saveMenuItem, helpMenuItem};
+
+ using (mockery.Record()) {
+ SetupResult.For(repository.All()).Return(allMenuItems);
+ SetupResult.For(saveMenuItem.BelongsTo(sut)).Return(false);
+ SetupResult.For(helpMenuItem.BelongsTo(sut)).Return(true);
+ }
+
+ using (mockery.Playback()) {
+ var returnedItems = sut.AllMenuItems();
+ returnedItems.ShouldNotContain(saveMenuItem);
+ returnedItems.ShouldContain(helpMenuItem);
+ }
+ }
+
+ private ISubMenu CreateSUT() {
+ return new HelpMenu(repository);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Model/Menu/IMenuItem.cs b/src/Notepad/Presentation/Model/Menu/IMenuItem.cs
new file mode 100644
index 0000000..26483a0
--- /dev/null
+++ b/src/Notepad/Presentation/Model/Menu/IMenuItem.cs
@@ -0,0 +1,7 @@
+namespace Notepad.Presentation.Model.Menu {
+ public interface IMenuItem {
+ string Name();
+ void Click();
+ bool BelongsTo(ISubMenu menu);
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Model/Menu/IMenuItemComparer.cs b/src/Notepad/Presentation/Model/Menu/IMenuItemComparer.cs
new file mode 100644
index 0000000..2fd15c7
--- /dev/null
+++ b/src/Notepad/Presentation/Model/Menu/IMenuItemComparer.cs
@@ -0,0 +1,23 @@
+using System.Collections.Generic;
+
+namespace Notepad.Presentation.Model.Menu {
+ public interface IMenuItemComparer : IComparer<IMenuItem> {}
+
+ public class MenuItemComparer : IMenuItemComparer {
+ private IList<string> rankedMenuItems;
+
+ public MenuItemComparer() {
+ rankedMenuItems = new List<string> {
+ MenuItemNames.New,
+ MenuItemNames.Save,
+ MenuItemNames.SaveAs,
+ MenuItemNames.Exit,
+ MenuItemNames.About
+ };
+ }
+
+ public int Compare(IMenuItem x, IMenuItem y) {
+ return rankedMenuItems.IndexOf(x.Name()).CompareTo(rankedMenuItems.IndexOf(y.Name()));
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Model/Menu/ISubMenu.cs b/src/Notepad/Presentation/Model/Menu/ISubMenu.cs
new file mode 100644
index 0000000..f06a0b9
--- /dev/null
+++ b/src/Notepad/Presentation/Model/Menu/ISubMenu.cs
@@ -0,0 +1,9 @@
+using System.Collections.Generic;
+using Notepad.Presentation.Model.Menu;
+
+namespace Notepad.Presentation.Model.Menu {
+ public interface ISubMenu {
+ string Name();
+ IEnumerable<IMenuItem> AllMenuItems();
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Model/Menu/ISubMenuItemComparer.cs b/src/Notepad/Presentation/Model/Menu/ISubMenuItemComparer.cs
new file mode 100644
index 0000000..74f30db
--- /dev/null
+++ b/src/Notepad/Presentation/Model/Menu/ISubMenuItemComparer.cs
@@ -0,0 +1,20 @@
+using System.Collections.Generic;
+
+namespace Notepad.Presentation.Model.Menu {
+ public interface ISubMenuItemComparer : IComparer<ISubMenu> {}
+
+ public class SubMenuItemComparer : ISubMenuItemComparer {
+ private List<string> rankings;
+
+ public SubMenuItemComparer() {
+ rankings = new List<string> {
+ MenuNames.File,
+ MenuNames.Help
+ };
+ }
+
+ public int Compare(ISubMenu x, ISubMenu y) {
+ return rankings.IndexOf(x.Name()).CompareTo(rankings.IndexOf(y.Name()));
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Model/Menu/MenuNames.cs b/src/Notepad/Presentation/Model/Menu/MenuNames.cs
new file mode 100644
index 0000000..f5ef21f
--- /dev/null
+++ b/src/Notepad/Presentation/Model/Menu/MenuNames.cs
@@ -0,0 +1,14 @@
+namespace Notepad.Presentation.Model.Menu {
+ public static class MenuNames {
+ public static readonly string File = "&File";
+ public static readonly string Help = "&Help";
+ }
+
+ public static class MenuItemNames {
+ public static readonly string New = "&New";
+ public static readonly string Save = "&Save";
+ public static readonly string SaveAs = "Save &As...";
+ public static readonly string Exit = "E&xit";
+ public static readonly string About = "&About";
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Presenters/Commands/IRunPresenterCommand.cs b/src/Notepad/Presentation/Presenters/Commands/IRunPresenterCommand.cs
new file mode 100644
index 0000000..9d7458e
--- /dev/null
+++ b/src/Notepad/Presentation/Presenters/Commands/IRunPresenterCommand.cs
@@ -0,0 +1,18 @@
+using Notepad.Infrastructure.Core;
+using Notepad.Presentation.Core;
+
+namespace Notepad.Presentation.Presenters.Commands {
+ public interface IRunPresenterCommand<Presenter> : ICommand where Presenter : IPresenter {}
+
+ public class RunPresenterCommand<Presenter> : IRunPresenterCommand<Presenter> where Presenter : IPresenter {
+ private readonly IApplicationController applicationController;
+
+ public RunPresenterCommand(IApplicationController applicationController) {
+ this.applicationController = applicationController;
+ }
+
+ public void Execute() {
+ applicationController.Run<Presenter>();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Presenters/Commands/RunPresenterCommandSpecs.cs b/src/Notepad/Presentation/Presenters/Commands/RunPresenterCommandSpecs.cs
new file mode 100644
index 0000000..5ec7002
--- /dev/null
+++ b/src/Notepad/Presentation/Presenters/Commands/RunPresenterCommandSpecs.cs
@@ -0,0 +1,34 @@
+using MbUnit.Framework;
+using Notepad.Presentation.Core;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Presenters.Commands {
+ public class RunPresenterCommandSpecs {}
+
+ [TestFixture]
+ public class when_executing_the_run_presenter_command_ {
+ private MockRepository mockery;
+ private IApplicationController applicationController;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ applicationController = mockery.DynamicMock<IApplicationController>();
+ }
+
+ [Test]
+ public void should_run_the_display_about_presenter() {
+ using (mockery.Record()) {
+ applicationController.Run<IPresenter>();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Execute();
+ }
+ }
+
+ private IRunPresenterCommand<IPresenter> CreateSUT() {
+ return new RunPresenterCommand<IPresenter>(applicationController);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Presenters/Menu/File/ISaveAsPresenter.cs b/src/Notepad/Presentation/Presenters/Menu/File/ISaveAsPresenter.cs
new file mode 100644
index 0000000..509d21c
--- /dev/null
+++ b/src/Notepad/Presentation/Presenters/Menu/File/ISaveAsPresenter.cs
@@ -0,0 +1,28 @@
+using Notepad.Infrastructure.Extensions;
+using Notepad.Presentation.Core;
+using Notepad.Presentation.Views.Menu.File;
+using Notepad.Tasks;
+
+namespace Notepad.Presentation.Presenters.Menu.File {
+ public interface ISaveAsPresenter : IPresenter {
+ void SaveToFileAt(string pathToSaveDocumentTo);
+ }
+
+ public class SaveAsPresenter : ISaveAsPresenter {
+ private readonly ISaveAsView view;
+ private readonly IDocumentTasks tasks;
+
+ public SaveAsPresenter(ISaveAsView view, IDocumentTasks tasks) {
+ this.view = view;
+ this.tasks = tasks;
+ }
+
+ public void Initialize() {
+ view.AttachTo(this);
+ }
+
+ public void SaveToFileAt(string pathToSaveDocumentTo) {
+ tasks.SaveActiveDocumentTo(pathToSaveDocumentTo.AsAnAbsoluteFilePath());
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Presenters/Menu/File/SaveAsPresenterSpecs.cs b/src/Notepad/Presentation/Presenters/Menu/File/SaveAsPresenterSpecs.cs
new file mode 100644
index 0000000..da5032f
--- /dev/null
+++ b/src/Notepad/Presentation/Presenters/Menu/File/SaveAsPresenterSpecs.cs
@@ -0,0 +1,69 @@
+using MbUnit.Framework;
+using Notepad.Infrastructure.Extensions;
+using Notepad.Presentation.Model.Menu.File.Commands;
+using Notepad.Presentation.Views.Menu.File;
+using Notepad.Tasks;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Presenters.Menu.File {
+ public class SaveAsPresenterSpecs {}
+
+ [TestFixture]
+ public class when_initializing_the_save_as_presenter_ {
+ private MockRepository mockery;
+ private ISaveAsView view;
+ private ISaveAsPresenter sut;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ view = mockery.DynamicMock<ISaveAsView>();
+
+ sut = CreateSUT();
+ }
+
+ private ISaveAsPresenter CreateSUT() {
+ return new SaveAsPresenter(view, null);
+ }
+
+ [Test]
+ public void should_initialize_the_view_with_itself() {
+ using (mockery.Record()) {
+ view.AttachTo(sut);
+ }
+
+ using (mockery.Playback()) {
+ sut.Initialize();
+ }
+ }
+ }
+
+ [TestFixture]
+ public class when_selecting_a_file_path_to_save_to_ {
+ private MockRepository mockery;
+ private ISaveCommand saveCommand;
+ private IDocumentTasks tasks;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ tasks = mockery.DynamicMock<IDocumentTasks>();
+ }
+
+ [Test]
+ public void should_ask_the_active_document_to_save_the_path_specified() {
+ var pathToSaveFileTo = "some path";
+ using (mockery.Record()) {
+ tasks.SaveActiveDocumentTo(pathToSaveFileTo.AsAnAbsoluteFilePath());
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().SaveToFileAt(pathToSaveFileTo);
+ }
+ }
+
+ private ISaveAsPresenter CreateSUT() {
+ return new SaveAsPresenter(null, tasks);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Presenters/Menu/Help/AboutApplicationPresenterSpecs.cs b/src/Notepad/Presentation/Presenters/Menu/Help/AboutApplicationPresenterSpecs.cs
new file mode 100644
index 0000000..26d7148
--- /dev/null
+++ b/src/Notepad/Presentation/Presenters/Menu/Help/AboutApplicationPresenterSpecs.cs
@@ -0,0 +1,34 @@
+using MbUnit.Framework;
+using Notepad.Presentation.Views.Menu.Help;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Presenters.Menu.Help {
+ public class AboutApplicationPresenterSpecs {}
+
+ [TestFixture]
+ public class when_initializing_the_application_information_presenter_ {
+ private MockRepository mockery;
+ private IAboutApplicationView view;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ view = mockery.DynamicMock<IAboutApplicationView>();
+ }
+
+ [Test]
+ public void should_display_the_view() {
+ using (mockery.Record()) {
+ view.Display();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Initialize();
+ }
+ }
+
+ private IAboutApplicationPresenter CreateSUT() {
+ return new AboutApplicationPresenter(view);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Presenters/Menu/Help/IAboutApplicationPresenter.cs b/src/Notepad/Presentation/Presenters/Menu/Help/IAboutApplicationPresenter.cs
new file mode 100644
index 0000000..f4c593f
--- /dev/null
+++ b/src/Notepad/Presentation/Presenters/Menu/Help/IAboutApplicationPresenter.cs
@@ -0,0 +1,18 @@
+using Notepad.Presentation.Core;
+using Notepad.Presentation.Views.Menu.Help;
+
+namespace Notepad.Presentation.Presenters.Menu.Help {
+ public interface IAboutApplicationPresenter : IPresenter {}
+
+ public class AboutApplicationPresenter : IAboutApplicationPresenter {
+ private readonly IAboutApplicationView view;
+
+ public AboutApplicationPresenter(IAboutApplicationView view) {
+ this.view = view;
+ }
+
+ public void Initialize() {
+ view.Display();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Presenters/Menu/IMainMenuPresenter.cs b/src/Notepad/Presentation/Presenters/Menu/IMainMenuPresenter.cs
new file mode 100644
index 0000000..b40507d
--- /dev/null
+++ b/src/Notepad/Presentation/Presenters/Menu/IMainMenuPresenter.cs
@@ -0,0 +1,29 @@
+using Notepad.Domain.Repositories;
+using Notepad.Infrastructure.Extensions;
+using Notepad.Presentation.Core;
+using Notepad.Presentation.Model.Menu;
+using Notepad.Presentation.Views.Menu;
+
+namespace Notepad.Presentation.Presenters.Menu {
+ public interface IMainMenuPresenter : IPresenter {}
+
+ public class MainMenuPresenter : IMainMenuPresenter {
+ private readonly IMainMenuView mainMenu;
+ private readonly IRepository<ISubMenu> repository;
+ private readonly ISubMenuItemComparer comparer;
+
+ public MainMenuPresenter(IMainMenuView mainMenu,
+ IRepository<ISubMenu> repository,
+ ISubMenuItemComparer comparer) {
+ this.mainMenu = mainMenu;
+ this.repository = repository;
+ this.comparer = comparer;
+ }
+
+ public void Initialize() {
+ foreach (var subMenuToAddToMainMenu in repository.All().SortedUsing(comparer)) {
+ mainMenu.Add(subMenuToAddToMainMenu);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Presenters/Menu/MainMenuPresenterSpecs.cs b/src/Notepad/Presentation/Presenters/Menu/MainMenuPresenterSpecs.cs
new file mode 100644
index 0000000..a1b708b
--- /dev/null
+++ b/src/Notepad/Presentation/Presenters/Menu/MainMenuPresenterSpecs.cs
@@ -0,0 +1,86 @@
+using System.Collections.Generic;
+using MbUnit.Framework;
+using Notepad.Domain.Repositories;
+using Notepad.Presentation.Model.Menu;
+using Notepad.Presentation.Views.Menu;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Presenters.Menu {
+ public class MainMenuPresenterSpecs {}
+
+ [TestFixture]
+ public class when_initializing_the_main_menu_presenter_ {
+ private MockRepository mockery;
+ private IRepository<ISubMenu> repository;
+ private IMainMenuView mainMenu;
+ private ISubMenuItemComparer comparer;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ repository = mockery.DynamicMock<IRepository<ISubMenu>>();
+ mainMenu = mockery.DynamicMock<IMainMenuView>();
+ comparer = mockery.DynamicMock<ISubMenuItemComparer>();
+ }
+
+ [Test]
+ public void should_ask_the_repository_for_all_the_sub_menus() {
+ var subMenus = new List<ISubMenu>();
+ using (mockery.Record()) {
+ Expect
+ .Call(repository.All())
+ .Return(subMenus)
+ .Repeat
+ .AtLeastOnce();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Initialize();
+ }
+ }
+
+ [Test]
+ public void should_add_each_of_the_sub_menus_to_the_main_menu() {
+ var fileMenu = mockery.DynamicMock<ISubMenu>();
+ var subMenus = new List<ISubMenu> {fileMenu};
+
+ using (mockery.Record()) {
+ SetupResult
+ .For(repository.All())
+ .Return(subMenus);
+ mainMenu.Add(fileMenu);
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Initialize();
+ }
+ }
+
+ [Test]
+ public void should_sort_the_sub_menus_using_the_comparer() {
+ var firstMenu = mockery.DynamicMock<ISubMenu>();
+ var secondMenu = mockery.DynamicMock<ISubMenu>();
+
+ var subMenus = new List<ISubMenu> {firstMenu, secondMenu};
+
+ using (mockery.Record()) {
+ SetupResult
+ .For(repository.All())
+ .Return(subMenus);
+ Expect
+ .Call(comparer.Compare(firstMenu, secondMenu))
+ .Return(0)
+ .Repeat
+ .AtLeastOnce();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Initialize();
+ }
+ }
+
+ private IMainMenuPresenter CreateSUT() {
+ return new MainMenuPresenter(mainMenu, repository, comparer);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Presenters/Shell/IMainShellPresenter.cs b/src/Notepad/Presentation/Presenters/Shell/IMainShellPresenter.cs
new file mode 100644
index 0000000..c0ed3e9
--- /dev/null
+++ b/src/Notepad/Presentation/Presenters/Shell/IMainShellPresenter.cs
@@ -0,0 +1,5 @@
+using Notepad.Presentation.Core;
+
+namespace Notepad.Presentation.Presenters.Shell {
+ public interface IMainShellPresenter : IPresenter {}
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Presenters/Shell/MainShellPresenter.cs b/src/Notepad/Presentation/Presenters/Shell/MainShellPresenter.cs
new file mode 100644
index 0000000..1406be0
--- /dev/null
+++ b/src/Notepad/Presentation/Presenters/Shell/MainShellPresenter.cs
@@ -0,0 +1,17 @@
+using Notepad.Presentation.Core;
+using Notepad.Presentation.Presenters.Menu;
+using Notepad.Presentation.Presenters.Shell;
+
+namespace Notepad.Presentation.Presenters.Shell {
+ public class MainShellPresenter : IMainShellPresenter {
+ private readonly IApplicationController applicationController;
+
+ public MainShellPresenter(IApplicationController applicationController) {
+ this.applicationController = applicationController;
+ }
+
+ public void Initialize() {
+ applicationController.Run<IMainMenuPresenter>();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Presenters/Shell/MainShellPresenterSpecs.cs b/src/Notepad/Presentation/Presenters/Shell/MainShellPresenterSpecs.cs
new file mode 100644
index 0000000..681ce58
--- /dev/null
+++ b/src/Notepad/Presentation/Presenters/Shell/MainShellPresenterSpecs.cs
@@ -0,0 +1,36 @@
+using MbUnit.Framework;
+using Notepad.Presentation.Core;
+using Notepad.Presentation.Presenters.Menu;
+using Notepad.Presentation.Presenters.Shell;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Presenters.Shell {
+ public class MainShellPresenterSpecs {}
+
+ [TestFixture]
+ public class when_initializing_the_main_shell_presenter_ {
+ private MockRepository mockery;
+ private IApplicationController applicationController;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ applicationController = mockery.DynamicMock<IApplicationController>();
+ }
+
+ [Test]
+ public void should_initialize_the_main_menu_presenter() {
+ using (mockery.Record()) {
+ applicationController.Run<IMainMenuPresenter>();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Initialize();
+ }
+ }
+
+ private IMainShellPresenter CreateSUT() {
+ return new MainShellPresenter(applicationController);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Views/Menu/File/ISaveAsView.cs b/src/Notepad/Presentation/Views/Menu/File/ISaveAsView.cs
new file mode 100644
index 0000000..e66de04
--- /dev/null
+++ b/src/Notepad/Presentation/Views/Menu/File/ISaveAsView.cs
@@ -0,0 +1,17 @@
+using System.Windows.Forms;
+using Notepad.Presentation.Presenters.Menu.File;
+
+namespace Notepad.Presentation.Views.Menu.File {
+ public interface ISaveAsView {
+ void AttachTo(ISaveAsPresenter presenterToDelegateWorkToo);
+ }
+
+ public class SaveAsView : ISaveAsView {
+ public void AttachTo(ISaveAsPresenter presenterToDelegateWorkToo) {
+ var saveFileDialog = new SaveFileDialog();
+ if (saveFileDialog.ShowDialog() == DialogResult.OK) {
+ presenterToDelegateWorkToo.SaveToFileAt(saveFileDialog.FileName);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Views/Menu/Help/AboutApplicationView.Designer.cs b/src/Notepad/Presentation/Views/Menu/Help/AboutApplicationView.Designer.cs
new file mode 100644
index 0000000..6059d18
--- /dev/null
+++ b/src/Notepad/Presentation/Views/Menu/Help/AboutApplicationView.Designer.cs
@@ -0,0 +1,184 @@
+namespace Notepad.Presentation.Views.Menu.Help
+{
+ public partial class AboutApplicationView
+ {
+ /// <summary>
+ /// Required designer variable.
+ /// </summary>
+ private System.ComponentModel.IContainer components = null;
+
+ /// <summary>
+ /// Clean up any resources being used.
+ /// </summary>
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ /// <summary>
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ /// </summary>
+ private void InitializeComponent()
+ {
+ this.tableLayoutPanel = new System.Windows.Forms.TableLayoutPanel();
+ this.logoPictureBox = new System.Windows.Forms.PictureBox();
+ this.labelProductName = new System.Windows.Forms.Label();
+ this.labelVersion = new System.Windows.Forms.Label();
+ this.uxCopyright = new System.Windows.Forms.Label();
+ this.uxCompanyName = new System.Windows.Forms.Label();
+ this.uxDescription = new System.Windows.Forms.TextBox();
+ this.okButton = new System.Windows.Forms.Button();
+ this.tableLayoutPanel.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.logoPictureBox)).BeginInit();
+ this.SuspendLayout();
+ //
+ // tableLayoutPanel
+ //
+ this.tableLayoutPanel.ColumnCount = 2;
+ this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33F));
+ this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 67F));
+ this.tableLayoutPanel.Controls.Add(this.logoPictureBox, 0, 0);
+ this.tableLayoutPanel.Controls.Add(this.labelProductName, 1, 0);
+ this.tableLayoutPanel.Controls.Add(this.labelVersion, 1, 1);
+ this.tableLayoutPanel.Controls.Add(this.uxCopyright, 1, 2);
+ this.tableLayoutPanel.Controls.Add(this.uxCompanyName, 1, 3);
+ this.tableLayoutPanel.Controls.Add(this.uxDescription, 1, 4);
+ this.tableLayoutPanel.Controls.Add(this.okButton, 1, 5);
+ this.tableLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.tableLayoutPanel.Location = new System.Drawing.Point(9, 9);
+ this.tableLayoutPanel.Name = "tableLayoutPanel";
+ this.tableLayoutPanel.RowCount = 6;
+ this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10F));
+ this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10F));
+ this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10F));
+ this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10F));
+ this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
+ this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10F));
+ this.tableLayoutPanel.Size = new System.Drawing.Size(417, 265);
+ this.tableLayoutPanel.TabIndex = 0;
+ //
+ // logoPictureBox
+ //
+ this.logoPictureBox.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.logoPictureBox.Location = new System.Drawing.Point(3, 3);
+ this.logoPictureBox.Name = "logoPictureBox";
+ this.tableLayoutPanel.SetRowSpan(this.logoPictureBox, 6);
+ this.logoPictureBox.Size = new System.Drawing.Size(131, 259);
+ this.logoPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
+ this.logoPictureBox.TabIndex = 12;
+ this.logoPictureBox.TabStop = false;
+ //
+ // labelProductName
+ //
+ this.labelProductName.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.labelProductName.Location = new System.Drawing.Point(143, 0);
+ this.labelProductName.Margin = new System.Windows.Forms.Padding(6, 0, 3, 0);
+ this.labelProductName.MaximumSize = new System.Drawing.Size(0, 17);
+ this.labelProductName.Name = "labelProductName";
+ this.labelProductName.Size = new System.Drawing.Size(271, 17);
+ this.labelProductName.TabIndex = 19;
+ this.labelProductName.Text = "Product Name";
+ this.labelProductName.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // labelVersion
+ //
+ this.labelVersion.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.labelVersion.Location = new System.Drawing.Point(143, 26);
+ this.labelVersion.Margin = new System.Windows.Forms.Padding(6, 0, 3, 0);
+ this.labelVersion.MaximumSize = new System.Drawing.Size(0, 17);
+ this.labelVersion.Name = "labelVersion";
+ this.labelVersion.Size = new System.Drawing.Size(271, 17);
+ this.labelVersion.TabIndex = 0;
+ this.labelVersion.Text = "Version";
+ this.labelVersion.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // uxCopyright
+ //
+ this.uxCopyright.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.uxCopyright.Location = new System.Drawing.Point(143, 52);
+ this.uxCopyright.Margin = new System.Windows.Forms.Padding(6, 0, 3, 0);
+ this.uxCopyright.MaximumSize = new System.Drawing.Size(0, 17);
+ this.uxCopyright.Name = "uxCopyright";
+ this.uxCopyright.Size = new System.Drawing.Size(271, 17);
+ this.uxCopyright.TabIndex = 21;
+ this.uxCopyright.Text = "Copyright";
+ this.uxCopyright.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // uxCompanyName
+ //
+ this.uxCompanyName.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.uxCompanyName.Location = new System.Drawing.Point(143, 78);
+ this.uxCompanyName.Margin = new System.Windows.Forms.Padding(6, 0, 3, 0);
+ this.uxCompanyName.MaximumSize = new System.Drawing.Size(0, 17);
+ this.uxCompanyName.Name = "uxCompanyName";
+ this.uxCompanyName.Size = new System.Drawing.Size(271, 17);
+ this.uxCompanyName.TabIndex = 22;
+ this.uxCompanyName.Text = "Company Name";
+ this.uxCompanyName.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // uxDescription
+ //
+ this.uxDescription.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.uxDescription.Location = new System.Drawing.Point(143, 107);
+ this.uxDescription.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
+ this.uxDescription.Multiline = true;
+ this.uxDescription.Name = "uxDescription";
+ this.uxDescription.ReadOnly = true;
+ this.uxDescription.ScrollBars = System.Windows.Forms.ScrollBars.Both;
+ this.uxDescription.Size = new System.Drawing.Size(271, 126);
+ this.uxDescription.TabIndex = 23;
+ this.uxDescription.TabStop = false;
+ this.uxDescription.Text = "Description";
+ //
+ // okButton
+ //
+ this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.okButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ this.okButton.Location = new System.Drawing.Point(339, 239);
+ this.okButton.Name = "okButton";
+ this.okButton.Size = new System.Drawing.Size(75, 23);
+ this.okButton.TabIndex = 24;
+ this.okButton.Text = "&OK";
+ //
+ // AboutApplicationView
+ //
+ this.AcceptButton = this.okButton;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(435, 283);
+ this.Controls.Add(this.tableLayoutPanel);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "AboutApplicationView";
+ this.Padding = new System.Windows.Forms.Padding(9);
+ this.ShowIcon = false;
+ this.ShowInTaskbar = false;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+ this.Text = "About Notepad";
+ this.tableLayoutPanel.ResumeLayout(false);
+ this.tableLayoutPanel.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.logoPictureBox)).EndInit();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.TableLayoutPanel tableLayoutPanel;
+ private System.Windows.Forms.PictureBox logoPictureBox;
+ private System.Windows.Forms.Label labelProductName;
+ private System.Windows.Forms.Label labelVersion;
+ private System.Windows.Forms.Label uxCopyright;
+ private System.Windows.Forms.Label uxCompanyName;
+ private System.Windows.Forms.TextBox uxDescription;
+ private System.Windows.Forms.Button okButton;
+ }
+}
diff --git a/src/Notepad/Presentation/Views/Menu/Help/AboutApplicationView.cs b/src/Notepad/Presentation/Views/Menu/Help/AboutApplicationView.cs
new file mode 100644
index 0000000..515a493
--- /dev/null
+++ b/src/Notepad/Presentation/Views/Menu/Help/AboutApplicationView.cs
@@ -0,0 +1,99 @@
+using System.IO;
+using System.Reflection;
+using System.Windows.Forms;
+
+namespace Notepad.Presentation.Views.Menu.Help {
+ public partial class AboutApplicationView : Form, IAboutApplicationView {
+ public AboutApplicationView() {
+ InitializeComponent();
+ labelProductName.Text = AssemblyProduct;
+ labelVersion.Text = string.Format("Version {0} {0}", AssemblyVersion);
+ uxCopyright.Text = AssemblyCopyright;
+ uxCompanyName.Text = AssemblyCompany;
+ uxDescription.Text = AssemblyDescription;
+ }
+
+ public void Display() {
+ ShowDialog();
+ }
+
+ public string AssemblyTitle
+ {
+ get
+ {
+ object[] attributes =
+ Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyTitleAttribute), false);
+ if (attributes.Length > 0)
+ {
+ AssemblyTitleAttribute titleAttribute = (AssemblyTitleAttribute)attributes[0];
+ if (titleAttribute.Title != "")
+ {
+ return titleAttribute.Title;
+ }
+ }
+ return Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().CodeBase);
+ }
+ }
+
+ public string AssemblyVersion
+ {
+ get { return Assembly.GetExecutingAssembly().GetName().Version.ToString(); }
+ }
+
+ public string AssemblyDescription
+ {
+ get
+ {
+ object[] attributes =
+ Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false);
+ if (attributes.Length == 0)
+ {
+ return "";
+ }
+ return ((AssemblyDescriptionAttribute)attributes[0]).Description;
+ }
+ }
+
+ public string AssemblyProduct
+ {
+ get
+ {
+ object[] attributes =
+ Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), false);
+ if (attributes.Length == 0)
+ {
+ return "";
+ }
+ return ((AssemblyProductAttribute)attributes[0]).Product;
+ }
+ }
+
+ public string AssemblyCopyright
+ {
+ get
+ {
+ object[] attributes =
+ Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false);
+ if (attributes.Length == 0)
+ {
+ return "";
+ }
+ return ((AssemblyCopyrightAttribute)attributes[0]).Copyright;
+ }
+ }
+
+ public string AssemblyCompany
+ {
+ get
+ {
+ object[] attributes =
+ Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), false);
+ if (attributes.Length == 0)
+ {
+ return "";
+ }
+ return ((AssemblyCompanyAttribute)attributes[0]).Company;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Views/Menu/Help/AboutApplicationView.resx b/src/Notepad/Presentation/Views/Menu/Help/AboutApplicationView.resx
new file mode 100644
index 0000000..5ea0895
--- /dev/null
+++ b/src/Notepad/Presentation/Views/Menu/Help/AboutApplicationView.resx
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+</root> \ No newline at end of file
diff --git a/src/Notepad/Presentation/Views/Menu/Help/IAboutApplicationView.cs b/src/Notepad/Presentation/Views/Menu/Help/IAboutApplicationView.cs
new file mode 100644
index 0000000..6726cba
--- /dev/null
+++ b/src/Notepad/Presentation/Views/Menu/Help/IAboutApplicationView.cs
@@ -0,0 +1,5 @@
+namespace Notepad.Presentation.Views.Menu.Help {
+ public interface IAboutApplicationView {
+ void Display();
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Views/Menu/IMainMenuView.cs b/src/Notepad/Presentation/Views/Menu/IMainMenuView.cs
new file mode 100644
index 0000000..6b6c717
--- /dev/null
+++ b/src/Notepad/Presentation/Views/Menu/IMainMenuView.cs
@@ -0,0 +1,23 @@
+using Notepad.Presentation.Model.Menu;
+using Notepad.Presentation.Views.Menu.Mappers;
+using Notepad.Presentation.Views.Shell;
+
+namespace Notepad.Presentation.Views.Menu {
+ public interface IMainMenuView {
+ void Add(ISubMenu menuToAddToTheMainMenu);
+ }
+
+ public class MainMenuView : IMainMenuView {
+ private readonly ISubMenuToToolStripMenuItemMapper mapper;
+ private readonly WindowShell mainShell;
+
+ public MainMenuView(WindowShell mainShell, ISubMenuToToolStripMenuItemMapper mapper) {
+ this.mapper = mapper;
+ this.mainShell = mainShell;
+ }
+
+ public void Add(ISubMenu menuToAddToTheMainMenu) {
+ mainShell.MenuStrip().Items.Add(mapper.MapFrom(menuToAddToTheMainMenu));
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Views/Menu/MainMenuViewSpecs.cs b/src/Notepad/Presentation/Views/Menu/MainMenuViewSpecs.cs
new file mode 100644
index 0000000..f4d5a57
--- /dev/null
+++ b/src/Notepad/Presentation/Views/Menu/MainMenuViewSpecs.cs
@@ -0,0 +1,69 @@
+using System.Windows.Forms;
+using MbUnit.Framework;
+using Notepad.Presentation.Model.Menu;
+using Notepad.Presentation.Views.Menu.Mappers;
+using Notepad.Presentation.Views.Shell;
+using Notepad.Test.Extensions;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Views.Menu {
+ public class MainMenuViewSpecs {}
+
+ [TestFixture]
+ public class when_adding_sub_menus_to_the_main_menu_ {
+ private MockRepository mockery;
+ private ISubMenuToToolStripMenuItemMapper mapper;
+ private MenuStrip mainMenuStrip;
+ private WindowShell mainShell;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ mapper = mockery.DynamicMock<ISubMenuToToolStripMenuItemMapper>();
+ mainShell = mockery.DynamicMock<WindowShell>();
+ mainMenuStrip = new MenuStrip();
+
+ SetupResult.For(mainShell.MenuStrip()).Return(mainMenuStrip);
+ }
+
+ [Test]
+ public void should_leverage_the_mapper_to_map_the_sub_menu_to_a_tool_strip_menu_item() {
+ var subMenu = mockery.DynamicMock<ISubMenu>();
+ var subMenuToolStripMenuItem = new ToolStripMenuItem();
+
+ using (mockery.Record()) {
+ Expect
+ .Call(mapper.MapFrom(subMenu))
+ .Return(subMenuToolStripMenuItem)
+ .Repeat
+ .AtLeastOnce();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Add(subMenu);
+ }
+ }
+
+ [Test]
+ public void should_add_the_mapped_menu_strip_item_to_the_main_menu_strip() {
+ var subMenu = mockery.DynamicMock<ISubMenu>();
+ var subMenuToolStripMenuItem = new ToolStripMenuItem();
+
+ using (mockery.Record()) {
+ SetupResult
+ .For(mapper.MapFrom(subMenu))
+ .Return(subMenuToolStripMenuItem);
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().Add(subMenu);
+ mainMenuStrip.Items.Contains(subMenuToolStripMenuItem)
+ .ShouldBeEqualTo(true);
+ }
+ }
+
+ private IMainMenuView CreateSUT() {
+ return new MainMenuView(mainShell, mapper);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Views/Menu/Mappers/IMenuItemToToolStripMenuItemMapper.cs b/src/Notepad/Presentation/Views/Menu/Mappers/IMenuItemToToolStripMenuItemMapper.cs
new file mode 100644
index 0000000..a8214a8
--- /dev/null
+++ b/src/Notepad/Presentation/Views/Menu/Mappers/IMenuItemToToolStripMenuItemMapper.cs
@@ -0,0 +1,15 @@
+using System.Windows.Forms;
+using Notepad.Infrastructure.Core;
+using Notepad.Presentation.Model.Menu;
+
+namespace Notepad.Presentation.Views.Menu.Mappers {
+ public interface IMenuItemToToolStripMenuItemMapper : IMapper<IMenuItem, ToolStripMenuItem> {}
+
+ public class MenuItemToToolStripMenuItemMapper : IMenuItemToToolStripMenuItemMapper {
+ public ToolStripMenuItem MapFrom(IMenuItem item) {
+ var toolStripMenuItem = new ToolStripMenuItem(item.Name());
+ toolStripMenuItem.Click += delegate { item.Click(); };
+ return toolStripMenuItem;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Views/Menu/Mappers/ISubMenuToToolStripMenuItemMapper.cs b/src/Notepad/Presentation/Views/Menu/Mappers/ISubMenuToToolStripMenuItemMapper.cs
new file mode 100644
index 0000000..8c00cee
--- /dev/null
+++ b/src/Notepad/Presentation/Views/Menu/Mappers/ISubMenuToToolStripMenuItemMapper.cs
@@ -0,0 +1,10 @@
+using System.Windows.Forms;
+using Notepad.Infrastructure.Core;
+using Notepad.Presentation.Model.Menu;
+
+namespace Notepad.Presentation.Views.Menu.Mappers {
+ public interface ISubMenuToToolStripMenuItemMapper : IMapper<ISubMenu, ToolStripMenuItem>
+ {
+
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Views/Menu/Mappers/MenuItemToToolStripMenuItemMapperSpecs.cs b/src/Notepad/Presentation/Views/Menu/Mappers/MenuItemToToolStripMenuItemMapperSpecs.cs
new file mode 100644
index 0000000..8d8b29c
--- /dev/null
+++ b/src/Notepad/Presentation/Views/Menu/Mappers/MenuItemToToolStripMenuItemMapperSpecs.cs
@@ -0,0 +1,47 @@
+using MbUnit.Framework;
+using Notepad.Presentation.Model.Menu;
+using Notepad.Presentation.Views.Menu.Mappers;
+using Notepad.Test.Extensions;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Views.Menu.Mappers {
+ public class MenuItemToToolStripMenuItemMapperSpecs {}
+
+ [TestFixture]
+ public class when_mapping_a_menu_item_to_a_tool_strip_menu_item_ {
+ private MockRepository mockery;
+ private IMenuItem menuItem;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ menuItem = mockery.DynamicMock<IMenuItem>();
+
+ SetupResult.For(menuItem.Name()).Return("&Save");
+ }
+
+ [Test]
+ public void should_return_a_tool_strip_menu_item_with_the_menu_items_name_applied_as_its_text() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().MapFrom(menuItem).Text.ShouldBeEqualTo("&Save");
+ }
+ }
+
+ [Test]
+ public void should_invoke_the_menu_items_click_method_when_the_tool_strip_menu_item_is_clicked() {
+ using (mockery.Record()) {
+ menuItem.Click();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().MapFrom(menuItem).PerformClick();
+ }
+ }
+
+ private IMenuItemToToolStripMenuItemMapper CreateSUT() {
+ return new MenuItemToToolStripMenuItemMapper();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Views/Menu/Mappers/SubMenuToToolStripMenuItemMapper.cs b/src/Notepad/Presentation/Views/Menu/Mappers/SubMenuToToolStripMenuItemMapper.cs
new file mode 100644
index 0000000..74efbbd
--- /dev/null
+++ b/src/Notepad/Presentation/Views/Menu/Mappers/SubMenuToToolStripMenuItemMapper.cs
@@ -0,0 +1,21 @@
+using System.Windows.Forms;
+using Notepad.Presentation.Model.Menu;
+using Notepad.Presentation.Views.Menu.Mappers;
+
+namespace Notepad.Presentation.Views.Menu.Mappers {
+ public class SubMenuToToolStripMenuItemMapper : ISubMenuToToolStripMenuItemMapper {
+ private readonly IMenuItemToToolStripMenuItemMapper mapper;
+
+ public SubMenuToToolStripMenuItemMapper(IMenuItemToToolStripMenuItemMapper mapper) {
+ this.mapper = mapper;
+ }
+
+ public ToolStripMenuItem MapFrom(ISubMenu item) {
+ var toolStripMenuItem = new ToolStripMenuItem(item.Name());
+ foreach (var menuItem in item.AllMenuItems()) {
+ toolStripMenuItem.DropDownItems.Add(mapper.MapFrom(menuItem));
+ }
+ return toolStripMenuItem;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Views/Menu/Mappers/SubMenuToToolStripMenuItemMapperSpecs.cs b/src/Notepad/Presentation/Views/Menu/Mappers/SubMenuToToolStripMenuItemMapperSpecs.cs
new file mode 100644
index 0000000..d26391c
--- /dev/null
+++ b/src/Notepad/Presentation/Views/Menu/Mappers/SubMenuToToolStripMenuItemMapperSpecs.cs
@@ -0,0 +1,90 @@
+using System.Collections.Generic;
+using System.Windows.Forms;
+using MbUnit.Framework;
+using Notepad.Infrastructure.Core;
+using Notepad.Presentation.Model.Menu;
+using Notepad.Test.Extensions;
+using Rhino.Mocks;
+
+namespace Notepad.Presentation.Views.Menu.Mappers {
+ public class SubMenuToToolStripMenuItemMapperSpecs {}
+
+ [TestFixture]
+ public class when_mapping_a_sub_menu_to_a_tool_strip_menu_item_ {
+ private MockRepository mockery;
+ private ISubMenu subMenu;
+ private IList<IMenuItem> menuItems;
+ private IMenuItemToToolStripMenuItemMapper mapper;
+
+ [SetUp]
+ public void SetUp() {
+ mockery = new MockRepository();
+ subMenu = mockery.DynamicMock<ISubMenu>();
+ mapper = mockery.DynamicMock<IMenuItemToToolStripMenuItemMapper>();
+ menuItems = new List<IMenuItem>();
+
+ SetupResult.For(subMenu.Name()).Return("&File");
+ SetupResult.For(subMenu.AllMenuItems()).Return(menuItems);
+ }
+
+ [Test]
+ public void should_return_a_non_null_value() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().MapFrom(subMenu).ShouldNotBeNull();
+ }
+ }
+
+ [Test]
+ public void should_return_a_menu_item_with_the_sub_menus_name_applied_as_its_text() {
+ using (mockery.Record()) {}
+
+ using (mockery.Playback()) {
+ CreateSUT().MapFrom(subMenu).Text.ShouldBeEqualTo("&File");
+ }
+ }
+
+ [Test]
+ public void should_map_each_of_the_sub_menus_menu_items_to_tool_strip_menu_items() {
+ var firstMenuItem = mockery.DynamicMock<IMenuItem>();
+
+ menuItems.Add(firstMenuItem);
+ using (mockery.Record()) {
+ Expect
+ .Call(mapper.MapFrom(firstMenuItem))
+ .Return(new ToolStripMenuItem())
+ .Repeat
+ .AtLeastOnce();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().MapFrom(subMenu);
+ }
+ }
+
+ [Test]
+ public void should_add_all_the_mapped_menu_items_to_the_menu_item_representing_the_sub_menu() {
+ var firstMenuItem = mockery.DynamicMock<IMenuItem>();
+ var mappedMenuItem = new ToolStripMenuItem();
+
+ menuItems.Add(firstMenuItem);
+ using (mockery.Record()) {
+ SetupResult
+ .For(mapper.MapFrom(firstMenuItem))
+ .Return(mappedMenuItem)
+ .Repeat
+ .AtLeastOnce();
+ }
+
+ using (mockery.Playback()) {
+ CreateSUT().MapFrom(subMenu).DropDownItems.Contains(mappedMenuItem)
+ .ShouldBeEqualTo(true);
+ }
+ }
+
+ private IMapper<ISubMenu, ToolStripMenuItem> CreateSUT() {
+ return new SubMenuToToolStripMenuItemMapper(mapper);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Views/Shell/WindowShell.Designer.cs b/src/Notepad/Presentation/Views/Shell/WindowShell.Designer.cs
new file mode 100644
index 0000000..32282d8
--- /dev/null
+++ b/src/Notepad/Presentation/Views/Shell/WindowShell.Designer.cs
@@ -0,0 +1,82 @@
+namespace Notepad.Presentation.Views.Shell {
+ partial class WindowShell
+ {
+ /// <summary>
+ /// Required designer variable.
+ /// </summary>
+ private System.ComponentModel.IContainer components = null;
+
+ /// <summary>
+ /// Clean up any resources being used.
+ /// </summary>
+ /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ /// <summary>
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ /// </summary>
+ private void InitializeComponent()
+ {
+ this.uxMainMenuStrip = new System.Windows.Forms.MenuStrip();
+ this.uxEditorTextBox = new System.Windows.Forms.RichTextBox();
+ this.uxStatusBar = new System.Windows.Forms.StatusStrip();
+ this.SuspendLayout();
+ //
+ // uxMainMenuStrip
+ //
+ this.uxMainMenuStrip.Location = new System.Drawing.Point(0, 0);
+ this.uxMainMenuStrip.Name = "uxMainMenuStrip";
+ this.uxMainMenuStrip.Size = new System.Drawing.Size(292, 24);
+ this.uxMainMenuStrip.TabIndex = 0;
+ this.uxMainMenuStrip.Text = "menuStrip1";
+ //
+ // uxEditorTextBox
+ //
+ this.uxEditorTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.uxEditorTextBox.Location = new System.Drawing.Point(0, 24);
+ this.uxEditorTextBox.Name = "uxEditorTextBox";
+ this.uxEditorTextBox.Size = new System.Drawing.Size(292, 249);
+ this.uxEditorTextBox.TabIndex = 1;
+ this.uxEditorTextBox.Text = "";
+ //
+ // uxStatusBar
+ //
+ this.uxStatusBar.Location = new System.Drawing.Point(0, 251);
+ this.uxStatusBar.Name = "uxStatusBar";
+ this.uxStatusBar.Size = new System.Drawing.Size(292, 22);
+ this.uxStatusBar.TabIndex = 2;
+ this.uxStatusBar.Text = "statusStrip1";
+ //
+ // MainShellView
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(292, 273);
+ this.Controls.Add(this.uxStatusBar);
+ this.Controls.Add(this.uxEditorTextBox);
+ this.Controls.Add(this.uxMainMenuStrip);
+ this.MainMenuStrip = this.uxMainMenuStrip;
+ this.Name = "MainShellView";
+ this.Text = "Notepad";
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.MenuStrip uxMainMenuStrip;
+ private System.Windows.Forms.RichTextBox uxEditorTextBox;
+ private System.Windows.Forms.StatusStrip uxStatusBar;
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Views/Shell/WindowShell.cs b/src/Notepad/Presentation/Views/Shell/WindowShell.cs
new file mode 100644
index 0000000..e90b11d
--- /dev/null
+++ b/src/Notepad/Presentation/Views/Shell/WindowShell.cs
@@ -0,0 +1,13 @@
+using System.Windows.Forms;
+
+namespace Notepad.Presentation.Views.Shell {
+ public partial class WindowShell : Form {
+ public WindowShell() {
+ InitializeComponent();
+ }
+
+ public MenuStrip MenuStrip() {
+ return uxMainMenuStrip;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Notepad/Presentation/Views/Shell/WindowShell.resx b/src/Notepad/Presentation/Views/Shell/WindowShell.resx
new file mode 100644
index 0000000..c61a0c7
--- /dev/null
+++ b/src/Notepad/Presentation/Views/Shell/WindowShell.resx
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <metadata name="uxMainMenuStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <value>17, 17</value>
+ </metadata>
+ <metadata name="uxStatusBar.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <value>126, 17</value>
+ </metadata>
+</root> \ No newline at end of file