コード例 #1
0
        public CollectionModel(string pathToCollection, CollectionSettings collectionSettings,
                               BookSelection bookSelection,
                               SourceCollectionsList sourceCollectionsList,
                               BookCollection.Factory bookCollectionFactory,
                               EditBookCommand editBookCommand,
                               CreateFromSourceBookCommand createFromSourceBookCommand,
                               BookServer bookServer,
                               CurrentEditableCollectionSelection currentEditableCollectionSelection,
                               BookThumbNailer thumbNailer,
                               TeamCollectionManager tcManager,
                               BloomWebSocketServer webSocketServer,
                               BookCollectionHolder bookCollectionHolder,
                               LocalizationChangedEvent localizationChangedEvent)
        {
            _bookSelection         = bookSelection;
            _pathToCollection      = pathToCollection;
            _collectionSettings    = collectionSettings;
            _sourceCollectionsList = sourceCollectionsList;
            _bookCollectionFactory = bookCollectionFactory;
            _editBookCommand       = editBookCommand;
            _bookServer            = bookServer;
            _currentEditableCollectionSelection = currentEditableCollectionSelection;
            _thumbNailer              = thumbNailer;
            _tcManager                = tcManager;
            _webSocketServer          = webSocketServer;
            _bookCollectionHolder     = bookCollectionHolder;
            _localizationChangedEvent = localizationChangedEvent;

            createFromSourceBookCommand.Subscribe(CreateFromSourceBook);
        }
コード例 #2
0
        public static void DoWorkWithProgressDialog(
            BloomWebSocketServer socketServer,
            string title,
            Func <IWebSocketProgress, BackgroundWorker, bool> doWhat,
            Action <Form> doWhenMainActionFalse = null,
            IWin32Window owner    = null,
            int width             = 620,
            int height            = 550,
            bool showCancelButton = true)
        {
            var kProgressContextName = "progress";

            DoWorkWithProgressDialog(
                socketServer,
                kProgressContextName,
                () => new ReactDialog("progressDialogBundle",
                                      // props to send to the react component
                                      new
            {
                title,
                titleColor           = "white",
                titleBackgroundColor = Palette.kBloomBlueHex,
                webSocketContext     = kProgressContextName,
                showReportButton     = "if-error",
                showCancelButton     = showCancelButton
            }, title)
            {
                Width = width, Height = height
            },                                                            // winforms dialog properties
                doWhat,
                doWhenMainActionFalse,
                owner);
        }
コード例 #3
0
                                                    BloomWebSocketServer webSocketServer); //autofac uses this

        public BulkBloomPubCreator(BookServer bookServer, CollectionModel collectionModel,
                                   BloomWebSocketServer webSocketServer)
        {
            _bookServer      = bookServer;
            _collectionModel = collectionModel;
            _webSocketServer = webSocketServer;
        }
コード例 #4
0
        private string _previousTargetSaveAs;         // enhance: should this be shared with CollectionApi or other save as locations?

        public BookCommandsApi(CollectionModel collectionModel, BloomWebSocketServer webSocketServer, BookSelection bookSelection, SpreadsheetApi spreadsheetApi)
        {
            _collectionModel              = collectionModel;
            _webSocketServer              = webSocketServer;
            _bookSelection                = bookSelection;
            this._spreadsheetApi          = spreadsheetApi;
            _collectionModel.BookCommands = this;
        }
コード例 #5
0
 public void WithDefaultValues()
 {
     this._collectionSettings    = new CollectionSettings();
     this._bookSelection         = new BookSelection();
     this._teamCollectionManager = null;                 // ENHANCE: This would be better off calling the builder for TeamCollectionManager, when it's implemented.
     this._bookServer            = null;
     this._bloomWebSocketServer  = null;
 }
コード例 #6
0
 public TeamCollectionApiBuilder WithDefaultMocks()
 {
     this.MockCollectionSettings    = new Mock <CollectionSettings>();
     this.MockBookSelection         = new Mock <BookSelection>();
     this.MockTeamCollectionManager = new Mock <ITeamCollectionManager>();
     this._bookServer           = null;
     this._bloomWebSocketServer = null;
     return(this);
 }
コード例 #7
0
ファイル: CollectionApi.cs プロジェクト: gmartin7/myBloomFork
 public CollectionApi(CollectionSettings settings, CollectionModel collectionModel, BookSelection bookSelection, EditBookCommand editBookCommand, BookThumbNailer thumbNailer, BloomWebSocketServer webSocketServer)
 {
     _settings        = settings;
     _collectionModel = collectionModel;
     _bookSelection   = bookSelection;
     _editBookCommand = editBookCommand;
     _thumbNailer     = thumbNailer;
     _webSocketServer = webSocketServer;
 }
コード例 #8
0
 public PublishEpubApi(BookThumbNailer thumbNailer, NavigationIsolator isolator, BookServer bookServer,
                       BookSelection bookSelection, CollectionSettings collectionSettings, BloomWebSocketServer webSocketServer)
 {
     _thumbNailer        = thumbNailer;
     _bookServer         = bookServer;
     _bookSelection      = bookSelection;
     _collectionSettings = collectionSettings;
     _webSocketServer    = webSocketServer;
     _standardProgress   = new WebSocketProgress(_webSocketServer, kWebsocketContext);
 }
コード例 #9
0
 public AudioRecording(BookSelection bookSelection, BloomWebSocketServer bloomWebSocketServer)
 {
     _bookSelection                = bookSelection;
     _startRecordingTimer          = new Timer();
     _startRecordingTimer.Interval = 300;             //  ms from click to actual recording
     _startRecordingTimer.Tick    += OnStartRecordingTimer_Elapsed;
     _backupPath      = System.IO.Path.GetTempFileName();
     CurrentRecording = this;
     _webSocketServer = bloomWebSocketServer;
 }
コード例 #10
0
 // Called by autofac, which creates the one instance and registers it with the server.
 public TeamCollectionApi(CurrentEditableCollectionSelection currentBookCollectionSelection, CollectionSettings settings, BookSelection bookSelection, ITeamCollectionManager tcManager, BookServer bookServer, BloomWebSocketServer socketServer)
 {
     _currentBookCollectionSelection = currentBookCollectionSelection;
     _settings  = settings;
     _tcManager = tcManager;
     _tcManager.CurrentCollection?.SetupMonitoringBehavior();
     _bookSelection = bookSelection;
     _socketServer  = socketServer;
     _bookServer    = bookServer;
     TheOneInstance = this;
 }
コード例 #11
0
 public AudioRecording(BookSelection bookSelection, BloomWebSocketServer bloomWebSocketServer)
 {
     _bookSelection                 = bookSelection;
     _startRecordingTimer           = new Timer();
     _startRecordingTimer.Interval  = 300;            //  ms from click to actual recording
     _startRecordingTimer.Tick     += OnStartRecordingTimer_Elapsed;
     _backupPathForRecordableAudio  = Path.GetTempFileName();
     _backupPathForPublishableAudio = Path.GetTempFileName();
     CurrentRecording               = this;
     _webSocketServer               = bloomWebSocketServer;
     // We create the ManualResetEvent in the "set" (non-blocking) state initially. The idea is to allow HandleEndRecord() to run,
     // but then block functions like HandleAudioFileRequest() which relies on the contents of the audio folder until Recorder_Stopped() has reported finishing saving the audio file.
     _completingRecording = new ManualResetEvent(true);
 }
コード例 #12
0
        public PublishToAndroidApi(BloomWebSocketServer bloomWebSocketServer, BookServer bookServer, RuntimeImageProcessor imageProcessor)
        {
            _webSocketServer = bloomWebSocketServer;
            _bookServer      = bookServer;
            _imageProcessor  = imageProcessor;
            _progress        = new WebSocketProgress(_webSocketServer, kWebSocketContext);
            _wifiPublisher   = new WiFiPublisher(_progress, _bookServer);
#if !__MonoCS__
            _usbPublisher = new UsbPublisher(_progress, _bookServer)
            {
                Stopped = () => SetState("stopped")
            };
#endif
        }
コード例 #13
0
 public TeamCollectionApiBuilder WithDefaultMocks(bool addFakeBookFolderPath = false)
 {
     this.MockCollectionSettings = new Mock <CollectionSettings>();
     this.MockBookSelection      = new Mock <BookSelection>();
     if (addFakeBookFolderPath)
     {
         var book = new Mock <Bloom.Book.Book>();
         book.Setup(m => m.FolderPath).Returns("");
         MockBookSelection.Setup(m => m.CurrentSelection).Returns(book.Object);
     }
     this.MockTeamCollectionManager = new Mock <ITeamCollectionManager>();
     this._bookServer           = null;
     this._bloomWebSocketServer = null;
     return(this);
 }
コード例 #14
0
        public PublishToAndroidApi(CollectionSettings collectionSettings, BloomWebSocketServer bloomWebSocketServer, BookServer bookServer, BulkBloomPubCreator bulkBloomPubCreator)
        {
            _collectionSettings  = collectionSettings;
            _webSocketServer     = bloomWebSocketServer;
            _bookServer          = bookServer;
            _bulkBloomPubCreator = bulkBloomPubCreator;
            _progress            = new WebSocketProgress(_webSocketServer, kWebSocketContext);
            _wifiPublisher       = new WiFiPublisher(_progress, _bookServer);
#if !__MonoCS__
            _usbPublisher = new UsbPublisher(_progress, _bookServer)
            {
                Stopped = () => SetState("stopped")
            };
#endif
        }
コード例 #15
0
        public delegate LibraryBookView Factory();        //autofac uses this

        public LibraryBookView(BookSelection bookSelection,
                               //SendReceiver sendReceiver,
                               CreateFromSourceBookCommand createFromSourceBookCommand,
                               EditBookCommand editBookCommand,
                               SelectedTabChangedEvent selectedTabChangedEvent,
                               SelectedTabAboutToChangeEvent selectedTabAboutToChangeEvent,
                               BloomWebSocketServer webSocketServer)
        {
            InitializeComponent();
            _bookSelection = bookSelection;
            //_sendReceiver = sendReceiver;
            _createFromSourceBookCommand = createFromSourceBookCommand;
            _editBookCommand             = editBookCommand;
            _webSocketServer             = webSocketServer;
            if (!Bloom.CLI.UploadCommand.IsUploading)
            {
                bookSelection.SelectionChanged += OnBookSelectionChanged;
            }

            selectedTabAboutToChangeEvent.Subscribe(c =>
            {
                if (!(c.To is LibraryView))
                {
                    // We're becoming invisible. Stop any work in progress to generate a preview
                    // (thus allowing other browsers, like the ones in the Edit view, to navigate
                    // to their destinations.)
                    HidePreview();
                }
            });

            selectedTabChangedEvent.Subscribe(c =>
            {
                var wasVisible = _visible;
                _visible       = c.To is LibraryView || c is ReactCollectionTabView;
                if (_reshowPending || wasVisible != _visible)
                {
                    ShowBook();
                }
            });
        }
コード例 #16
0
        public BookCollection(string path, CollectionType collectionType,
                              BookSelection bookSelection, TeamCollectionManager tcm = null, BloomWebSocketServer webSocketServer = null)
        {
            _path            = path;
            _bookSelection   = bookSelection;
            _tcManager       = tcm;
            _webSocketServer = webSocketServer;

            Type = collectionType;

            if (collectionType == CollectionType.TheOneEditableCollection)
            {
                MakeCollectionCSSIfMissing();
            }

            CollectionCreated?.Invoke(this, new EventArgs());

            if (ContainsDownloadedBooks)
            {
                WatchDirectory();
            }
        }
コード例 #17
0
        public AccessibilityCheckApi(BloomWebSocketServer webSocketServer, BookSelection bookSelection,
                                     BookRenamedEvent bookRenamedEvent, BookSavedEvent bookSavedEvent, EpubMaker.Factory epubMakerFactory,
                                     PublishEpubApi epubApi)
        {
            _webSocketServer = webSocketServer;
            var progress = new WebSocketProgress(_webSocketServer, kWebSocketContext);

            _webSocketProgress              = progress.WithL10NPrefix("AccessibilityCheck.");
            _epubApi                        = epubApi;
            bookSelection.SelectionChanged += (unused1, unused2) =>
            {
                _webSocketServer.SendEvent(kWebSocketContext, kBookSelectionChanged);
            };
            // we get this when the book is renamed
            bookRenamedEvent.Subscribe((book) =>
            {
                RefreshClient();
            });
            // we get this when the contents of the page might have changed
            bookSavedEvent.Subscribe((book) =>
            {
                RefreshClient();
            });
        }
コード例 #18
0
        public static void DoWorkWithProgressDialog(BloomWebSocketServer socketServer, string socketContext, Func <Form> makeDialog,
                                                    Func <IWebSocketProgress, bool> doWhat, Action <Form> doWhenMainActionFalse = null)
        {
            var progress = new WebSocketProgress(socketServer, socketContext);

            // NOTE: This (specifically ShowDialog) blocks the main thread until the dialog is closed.
            // Be careful to avoid deadlocks.
            using (var dlg = makeDialog())
            {
                // For now let's not try to handle letting the user abort.
                dlg.ControlBox = false;
                var worker = new BackgroundWorker();
                worker.DoWork += (sender, args) =>
                {
                    // A way of waiting until the dialog is ready to receive progress messages
                    while (!socketServer.IsSocketOpen(socketContext))
                    {
                        Thread.Sleep(50);
                    }
                    bool waitForUserToCloseDialogOrReportProblems;
                    try
                    {
                        waitForUserToCloseDialogOrReportProblems = doWhat(progress);
                    }
                    catch (Exception ex)
                    {
                        // depending on the nature of the problem, we might want to do more or less than this.
                        // But at least this lets the dialog reach one of the states where it can be closed,
                        // and gives the user some idea things are not right.
                        socketServer.SendEvent(socketContext, "finished");
                        waitForUserToCloseDialogOrReportProblems = true;
                        progress.MessageWithoutLocalizing("Something went wrong: " + ex.Message, ProgressKind.Error);
                    }

                    // stop the spinner
                    socketServer.SendEvent(socketContext, "finished");
                    if (waitForUserToCloseDialogOrReportProblems)
                    {
                        // Now the user is allowed to close the dialog or report problems.
                        // (ProgressDialog in JS-land is watching for this message, which causes it to turn
                        // on the buttons that allow the dialog to be manually closed (or a problem to be reported).
                        socketServer.SendBundle(socketContext, "show-buttons", new DynamicJson());
                    }
                    else
                    {
                        // Just close the dialog
                        dlg.Invoke((Action)(() =>
                        {
                            if (doWhenMainActionFalse != null)
                            {
                                doWhenMainActionFalse(dlg);
                            }
                            else
                            {
                                dlg.Close();
                            }
                        }));
                    }
                };

                worker.RunWorkerAsync();
                dlg.ShowDialog();                 // returns when dialog closed
            }
        }
コード例 #19
0
 public TeamCollectionApiBuilder WithBloomWebSocketServer(BloomWebSocketServer bloomWebSocketServer)
 {
     _bloomWebSocketServer = bloomWebSocketServer;
     return(this);
 }
コード例 #20
0
        private DateTime _lastButtonClickedTime = DateTime.Now;         // initially, instance creation time

        public PageControlsApi(EditingModel model)
        {
            _editingModel    = model;
            _webSocketServer = _editingModel.EditModelSocketServer;
            _editingModel.PageSelectModelChangesComplete += PageSelectModelChangesCompleteHandler;
        }
コード例 #21
0
 /// <summary>
 /// Constructs a new Spreadsheet Exporter
 /// </summary>
 /// <param name="webSocketServer">The webSockerServer of the instance</param>
 /// <param name="collectionSettings">The collectionSettings of the book that will be exported. This is used to retrieve the language display names</param>
 public SpreadsheetExporter(BloomWebSocketServer webSocketServer, CollectionSettings collectionSettings)
     : this(webSocketServer, new CollectionSettingsLanguageDisplayNameResolver(collectionSettings))
 {
 }
コード例 #22
0
 /// <summary>
 /// Constructs a new Spreadsheet Exporter
 /// </summary>
 /// <param name="webSocketServer">The webSockerServer of the instance</param>
 /// <param name="langDisplayNameResolver">The object that will be used to  retrieve the language display names</param>
 public SpreadsheetExporter(BloomWebSocketServer webSocketServer, ILanguageDisplayNameResolver langDisplayNameResolver)
 {
     _webSocketServer        = webSocketServer;
     LangDisplayNameResolver = langDisplayNameResolver;
 }
コード例 #23
0
 public SpreadsheetApi(CollectionModel collectionModel, BloomWebSocketServer webSocketServer, BookSelection bookSelection)
 {
     _collectionModel = collectionModel;
     _webSocketServer = webSocketServer;
     _bookSelection   = bookSelection;
 }
コード例 #24
0
 public static void UpdateButtonTitle(BloomWebSocketServer server, BookInfo bookInfo, string bestTitle)
 {
     server.SendString("book", IdForBookButton(bookInfo), bestTitle);
 }
コード例 #25
0
        public PageListView(PageSelection pageSelection, RelocatePageEvent relocatePageEvent, EditingModel model,
                            HtmlThumbNailer thumbnailProvider, NavigationIsolator isolator, ControlKeyEvent controlKeyEvent, PageListApi pageListApi, BloomWebSocketServer webSocketServer)
        {
            _pageSelection = pageSelection;
            _model         = model;
            this.Font      = SystemFonts.MessageBoxFont;
            InitializeComponent();
            _thumbNailList.PageListApi     = pageListApi;
            _thumbNailList.WebSocketServer = webSocketServer;
            this.BackColor = Palette.SidePanelBackgroundColor;

            _thumbNailList.Thumbnailer          = thumbnailProvider;
            _thumbNailList.RelocatePageEvent    = relocatePageEvent;
            _thumbNailList.PageSelectedChanged += new EventHandler(OnPageSelectedChanged);
            _thumbNailList.ControlKeyEvent      = controlKeyEvent;
            _thumbNailList.Model = model;
            _thumbNailList.BringToFront();             // needed to get DockStyle.Fill to work right.
            // First action determines whether the menu item is enabled, second performs it.
            var menuItems = new List <WebThumbNailList.MenuItemSpec>();

            menuItems.Add(
                new WebThumbNailList.MenuItemSpec()
            {
                Label          = LocalizationManager.GetString("EditTab.DuplicatePageButton", "Duplicate Page"),
                EnableFunction = (page) => page != null && _model.CanDuplicatePage,
                ExecuteCommand = (page) => _model.DuplicatePage(page)
            });
            menuItems.Add(
                new WebThumbNailList.MenuItemSpec()
            {
                Label          = LocalizationManager.GetString("EditTab.CopyPage", "Copy Page"),
                EnableFunction = (page) => page != null && _model.CanCopyPage,
                ExecuteCommand = (page) => _model.CopyPage(page)
            });
            menuItems.Add(
                new WebThumbNailList.MenuItemSpec()
            {
                Label          = LocalizationManager.GetString("EditTab.PastePage", "Paste Page"),
                EnableFunction = (page) => page != null && _model.CanAddPages && _model.GetClipboardHasPage(),
                ExecuteCommand = (page) => _model.PastePage(page)
            });
            menuItems.Add(
                new WebThumbNailList.MenuItemSpec()
            {
                Label          = LocalizationManager.GetString("EditTab.DeletePageButton", "Remove Page"),
                EnableFunction = (page) => page != null && _model.CanDeletePage,
                ExecuteCommand = (page) =>
                {
                    if (ConfirmRemovePageDialog.Confirm())
                    {
                        _model.DeletePage(page);
                    }
                }
            });
            menuItems.Add(
                new WebThumbNailList.MenuItemSpec()
            {
                Label          = LocalizationManager.GetString("EditTab.ChooseLayoutButton", "Choose Different Layout"),
                EnableFunction = (page) => page != null && !page.Required && !_model.CurrentBook.LockedDown,
                ExecuteCommand = (page) => _model.ChangePageLayout(page)
            });
            // This adds the desired menu items to the Gecko context menu that happens when we right-click
            _thumbNailList.ContextMenuProvider = args =>
            {
                var page = _thumbNailList.GetPageContaining(args.TargetNode);
                if (page == null)
                {
                    return(true);                    // no page-related commands if we didn't click on one.
                }
                if (page != _pageSelection.CurrentSelection)
                {
                    return(true);                    //it's too dangerous to let users do thing to a page they aren't seeing
                }
                foreach (var item in menuItems)
                {
                    var menuItem = new MenuItem(item.Label, (sender, eventArgs) => item.ExecuteCommand(page));
                    args.ContextMenu.MenuItems.Add(menuItem);
                    menuItem.Enabled = item.EnableFunction(page);
                }
                return(true);
            };
            // This sets up the context menu items that will be shown when the user clicks the
            // arrow in the thumbnail list.
            _thumbNailList.ContextMenuItems = menuItems;
        }
コード例 #26
0
 public PublishAudioVideoAPI(BloomWebSocketServer bloomWebSocketServer, PublishToAndroidApi publishToAndroidApi)
 {
     _webSocketServer     = bloomWebSocketServer;
     _publishToAndroidApi = publishToAndroidApi;
 }
コード例 #27
0
 // The only instance of this is created by autofac
 public PerformanceMeasurement(BloomWebSocketServer webSocketServer)
 {
     _webSocketServer = webSocketServer;
     Global           = this;   // note, this is changed if we change collections and the ProjectContext makes a new one
 }
コード例 #28
0
 // Called by autofac, which creates the one instance and registers it with the server.
 public CommonApi(BookSelection bookSelection, BloomParseClient parseClient, BloomWebSocketServer webSocketServer)
 {
     _bookSelection   = bookSelection;
     _parseClient     = parseClient;
     _webSocketServer = webSocketServer;
 }
コード例 #29
0
        public delegate PublishView Factory();        //autofac uses this

        public PublishView(PublishModel model,
                           SelectedTabChangedEvent selectedTabChangedEvent, LocalizationChangedEvent localizationChangedEvent, BookTransfer bookTransferrer, LoginDialog login, NavigationIsolator isolator,
                           PublishToAndroidApi publishApi, PublishEpubApi publishEpubApi, BloomWebSocketServer webSocketServer)
        {
            _bookTransferrer = bookTransferrer;
            _loginDialog     = login;
            _isolator        = isolator;
            _publishApi      = publishApi;
            _publishEpubApi  = publishEpubApi;
            _webSocketServer = webSocketServer;

            InitializeComponent();

            if (this.DesignMode)
            {
                return;
            }

            _model      = model;
            _model.View = this;

            _makePdfBackgroundWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(_makePdfBackgroundWorker_RunWorkerCompleted);
            _pdfViewer.PrintProgress += new System.EventHandler <PdfPrintProgressEventArgs>(OnPrintProgress);

            // BL-625: With mono, if a RadioButton group has its AutoCheck properties set to true, the default RadioButton.OnEnter
            //         event checks to make sure one of the RadioButtons is checked. If none are checked, the one the mouse pointer
            //         is over is checked, causing the CheckChanged event to fire.
            if (SIL.PlatformUtilities.Platform.IsMono)
            {
                SetAutoCheck(false);
            }

            //NB: just triggering off "VisibilityChanged" was unreliable. So now we trigger
            //off the tab itself changing, either to us or away from us.
            selectedTabChangedEvent.Subscribe(c =>
            {
                if (c.To == this)
                {
                    Activate();
                }
                else if (c.To != this)
                {
                    Deactivate();
                }
            });

            //TODO: find a way to call this just once, at the right time:

            //			DeskAnalytics.Track("Publish");

//#if DEBUG
//          var linkLabel = new LinkLabel() {Text = "DEBUG"};
//			linkLabel.Click+=new EventHandler((x,y)=>_model.DebugCurrentPDFLayout());
//          tableLayoutPanel1.Controls.Add(linkLabel);
//#endif
            _menusToolStrip.BackColor = _layoutChoices.BackColor = tableLayoutPanel1.BackColor = Palette.GeneralBackground;
            if (SIL.PlatformUtilities.Platform.IsMono)
            {
                BackgroundColorsForLinux();
            }

            // Adding this renderer prevents a white line from showing up under the components.
            _menusToolStrip.Renderer = new EditingView.FixedToolStripRenderer();

            // As far as I can tell, this is not needed anymore, and its presence,
            // at least in this place in the code, causes errors when running command-line tools
            // like UploadCommand which needs a PublishView but must not have something fully initialized.
            //GeckoPreferences.Default["pdfjs.disabled"] = false;
            SetupLocalization();
            localizationChangedEvent.Subscribe(o =>
            {
                SetupLocalization();
                UpdateLayoutChoiceLabels();
                UpdateSaveButton();
            });

            // Make this extra box available to show when wanted.
            _previewBox         = new PictureBox();
            _previewBox.Visible = false;
            Controls.Add(_previewBox);
            _previewBox.BringToFront();
        }
コード例 #30
0
 // This one is created by the ProjectContext and is used for the global current book
 public BookSelection(BloomWebSocketServer webSocketServer)
 {
     _webSocketServer = webSocketServer;
 }