Пример #1
0
        public void ReloadCollections()
        {
            lock (_bookCollectionLock)
            {
                _bookCollections = null;
                GetBookCollections();
            }

            _webSocketServer.SendEvent("editableCollectionList", "reload:" + _bookCollections[0].PathToDirectory);
        }
Пример #2
0
        protected override void OnSizeChanged(EventArgs e)
        {
            base.OnSizeChanged(e);
            // To correct a weird SplitPane behavior in CollectionsTabPane, we need
            // a notification when our window changes state from minimized to something else.
            bool minimized = ParentForm?.WindowState == FormWindowState.Minimized;

            if (!minimized && _minimized)
            {
                _webSocketServer.SendEvent("window", "restored");
            }

            _minimized = minimized;
        }
Пример #3
0
        /// <summary>
        /// Called when a file system watcher notices a new book (or some similar change) in our downloaded books folder.
        /// This will happen on a thread-pool thread.
        /// Since we are updating the UI in response we want to deal with it on the main thread.
        /// That also has the effect of a lock, preventing multiple threads trying to respond to changes.
        /// The main purpose of this method is to debounce such changes, since lots of them may
        /// happen in succession while downloading a book, and also some that we don't want
        /// to process may happen while we are selecting one. Debounced changes result in a websocket message
        /// that acts as an event for Javascript, and also raising a C# event.
        /// </summary>
        private void DebounceFolderChanged(string fullPath)
        {
            var shell = Application.OpenForms.Cast <Form>().FirstOrDefault(f => f is Shell);

            SafeInvoke.InvokeIfPossible("update downloaded books", shell, true, (Action)(() =>
            {
                // We may notice a change to the downloaded books directory before the other Bloom instance has finished
                // copying the new book there. Finishing should not take long, because the download is done...at worst
                // we have to copy the book on our own filesystem. Typically we only have to move the directory.
                // As a safeguard, wait half a second before we update things.
                if (_folderChangeDebounceTimer != null)
                {
                    // Things changed again before we started the update! Forget the old update and wait until things
                    // are stable for the required interval.
                    _folderChangeDebounceTimer.Stop();
                    _folderChangeDebounceTimer.Dispose();
                }
                _folderChangeDebounceTimer = new Timer();
                _folderChangeDebounceTimer.Tick += (o, args) =>
                {
                    _folderChangeDebounceTimer.Stop();
                    _folderChangeDebounceTimer.Dispose();
                    _folderChangeDebounceTimer = null;

                    // Updating the books involves selecting the modified book, which might involve changing
                    // some files (e.g., adding a branding image, BL-4914), which could trigger this again.
                    // So don't allow it to be triggered by changes to a folder we're already sending
                    // notifications about.
                    // (It's PROBABLY overkill to maintain a set of these...don't expect a notification about
                    // one folder to trigger a change to another...but better safe than sorry.)
                    // (Note that we don't need synchronized access to this dictionary, because all this
                    // happens only on the UI thread.)
                    if (!_changingFolders.Contains(fullPath))
                    {
                        try
                        {
                            _changingFolders.Add(fullPath);
                            _webSocketServer.SendEvent("editableCollectionList", "reload:" + PathToDirectory);
                            if (FolderContentChanged != null)
                            {
                                FolderContentChanged(this, new ProjectChangedEventArgs()
                                {
                                    Path = fullPath
                                });
                            }
                        }
                        finally
                        {
                            // Now we need to arrange to remove it again. Not right now, because
                            // whatever changes might be made during event handling may get noticed slightly later.
                            // But we don't want to miss it if the book gets downloaded again.
                            RemoveFromChangingFoldersLater(fullPath);
                        }
                    }
                };
                _folderChangeDebounceTimer.Interval = 500;
                _folderChangeDebounceTimer.Start();
            }));
        }
Пример #4
0
        private void ShowBook(bool updatePreview = true)
        {
            if (_bookSelection.CurrentSelection == null || !_visible)
            {
                HidePreview();
            }
            else
            {
                Debug.WriteLine("LibraryBookView.ShowBook() currentselection ok");

                _readmeBrowser.Visible = false;
                _splitContainerForPreviewAndAboutBrowsers.Visible = true;
                if (updatePreview && !TroubleShooterDialog.SuppressBookPreview)
                {
                    var previewDom = _bookSelection.CurrentSelection.GetPreviewHtmlFileForWholeBook();
                    XmlHtmlConverter.MakeXmlishTagsSafeForInterpretationAsHtml(previewDom.RawDom);
                    var fakeTempFile = BloomServer.MakeSimulatedPageFileInBookFolder(previewDom, setAsCurrentPageForDebugging: false, source: BloomServer.SimulatedPageFileSource.Preview);
                    _reactBookPreviewControl.Props = new { initialBookPreviewUrl = fakeTempFile.Key }; // need this for initial selection
                    _webSocketServer.SendString("bookStatus", "changeBook", fakeTempFile.Key);         // need this for changing selection display
                    _webSocketServer.SendEvent("bookStatus", "reload");                                // need this for changing selection's book info display if team collection
                    _reactBookPreviewControl.Visible = true;
                    RecordAndCleanupFakeFiles(fakeTempFile);
                }
                _splitContainerForPreviewAndAboutBrowsers.Panel2Collapsed = true;
                if (_bookSelection.CurrentSelection.HasAboutBookInformationToShow)
                {
                    if (RobustFile.Exists(_bookSelection.CurrentSelection.AboutBookHtmlPath))
                    {
                        _splitContainerForPreviewAndAboutBrowsers.Panel2Collapsed = false;
                        _readmeBrowser.Navigate(_bookSelection.CurrentSelection.AboutBookHtmlPath, false);
                        _readmeBrowser.Visible = true;
                    }
                    else if (RobustFile.Exists(_bookSelection.CurrentSelection.AboutBookMdPath))
                    {
                        _splitContainerForPreviewAndAboutBrowsers.Panel2Collapsed = false;
                        var md       = new Markdown();
                        var contents = RobustFile.ReadAllText(_bookSelection.CurrentSelection.AboutBookMdPath);
                        _readmeBrowser.NavigateRawHtml(string.Format("<html><head><meta charset=\"utf-8\"/></head><body>{0}</body></html>", md.Transform(contents)));
                        _readmeBrowser.Visible = true;
                    }
                }
                _reshowPending = false;
            }
        }
        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();
            });
        }
Пример #6
0
        public void RegisterWithApiHandler(BloomApiHandler apiHandler)
        {
            // This is just for storing the user preference of method
            // If we had a couple of these, we could just have a generic preferences api
            // that browser-side code could use.
            apiHandler.RegisterEndpointLegacy(kApiUrlPart + "method", request =>
            {
                if (request.HttpMethod == HttpMethods.Get)
                {
                    var method = Settings.Default.PublishAndroidMethod;
                    if (!new string[] { "wifi", "usb", "file" }.Contains(method))
                    {
                        method = "wifi";
                    }
                    request.ReplyWithText(method);
                }
                else                 // post
                {
                    Settings.Default.PublishAndroidMethod = request.RequiredPostString();
#if __MonoCS__
                    if (Settings.Default.PublishAndroidMethod == "usb")
                    {
                        _progress.MessageWithoutLocalizing("Sorry, this method is not available on Linux yet.");
                    }
#endif
                    request.PostSucceeded();
                }
            }, true);

            apiHandler.RegisterEndpointLegacy(kApiUrlPart + "backColor", request =>
            {
                if (request.HttpMethod == HttpMethods.Get)
                {
                    if (request.CurrentBook != _coverColorSourceBook)
                    {
                        _coverColorSourceBook = request.CurrentBook;
                        ImageUtils.TryCssColorFromString(request.CurrentBook?.GetCoverColor() ?? "", out _thumbnailBackgroundColor);
                    }
                    request.ReplyWithText(ToCssColorString(_thumbnailBackgroundColor));
                }
                else                 // post
                {
                    // ignore invalid colors (very common while user is editing hex)
                    Color newColor;
                    var newColorAsString = request.RequiredPostString();
                    if (ImageUtils.TryCssColorFromString(newColorAsString, out newColor))
                    {
                        _thumbnailBackgroundColor = newColor;
                        request.CurrentBook.SetCoverColor(newColorAsString);
                    }
                    request.PostSucceeded();
                }
            }, true);

            apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "motionBookMode",
                                                      readRequest =>
            {
                // If the user has taken off all possible motion, force not having motion in the
                // Bloom Reader book.  See https://issues.bloomlibrary.org/youtrack/issue/BL-7680.
                if (!readRequest.CurrentBook.HasMotionPages)
                {
                    readRequest.CurrentBook.BookInfo.PublishSettings.BloomPub.Motion = false;
                }
                return(readRequest.CurrentBook.BookInfo.PublishSettings.BloomPub.Motion);
            },
                                                      (writeRequest, value) =>
            {
                writeRequest.CurrentBook.BookInfo.PublishSettings.BloomPub.Motion = value;
                writeRequest.CurrentBook.BookInfo.SavePublishSettings();
                _webSocketServer.SendEvent("publish", "motionChanged");
            }
                                                      , true);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "updatePreview", request =>
            {
                MakeBloompubPreview(request, false);
            }, false);


            apiHandler.RegisterEndpointLegacy(kApiUrlPart + "thumbnail", request =>
            {
                var coverImage = request.CurrentBook.GetCoverImagePath();
                if (coverImage == null)
                {
                    request.Failed("no cover image");
                }
                else
                {
                    // We don't care as much about making it resized as making its background transparent.
                    using (var thumbnail = TempFile.CreateAndGetPathButDontMakeTheFile())
                    {
                        if (_thumbnailBackgroundColor == Color.Transparent)
                        {
                            ImageUtils.TryCssColorFromString(request.CurrentBook?.GetCoverColor(), out _thumbnailBackgroundColor);
                        }
                        RuntimeImageProcessor.GenerateEBookThumbnail(coverImage, thumbnail.Path, 256, 256, _thumbnailBackgroundColor);
                        request.ReplyWithImage(thumbnail.Path);
                    }
                }
            }, true);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "usb/start", request =>
            {
#if !__MonoCS__
                SetState("UsbStarted");
                UpdatePreviewIfNeeded(request);
                _usbPublisher.Connect(request.CurrentBook, _thumbnailBackgroundColor, GetSettings());
#endif
                request.PostSucceeded();
            }, true);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "usb/stop", request =>
            {
#if !__MonoCS__
                _usbPublisher.Stop(disposing: false);
                SetState("stopped");
#endif
                request.PostSucceeded();
            }, true);
            apiHandler.RegisterEndpointLegacy(kApiUrlPart + "wifi/start", request =>
            {
                SetState("ServingOnWifi");
                UpdatePreviewIfNeeded(request);
                _wifiPublisher.Start(request.CurrentBook, request.CurrentCollectionSettings, _thumbnailBackgroundColor, GetSettings());

                request.PostSucceeded();
            }, true);

            apiHandler.RegisterEndpointLegacy(kApiUrlPart + "wifi/stop", request =>
            {
                _wifiPublisher.Stop();
                SetState("stopped");
                request.PostSucceeded();
            }, true);

            apiHandler.RegisterEndpointLegacy(kApiUrlPart + "file/save", request =>
            {
                UpdatePreviewIfNeeded(request);
                FilePublisher.Save(request.CurrentBook, _bookServer, _thumbnailBackgroundColor, _progress, GetSettings());
                SetState("stopped");
                request.PostSucceeded();
            }, true);

            apiHandler.RegisterEndpointLegacy(kApiUrlPart + "file/bulkSaveBloomPubsParams", request =>
            {
                request.ReplyWithJson(JsonConvert.SerializeObject(_collectionSettings.BulkPublishBloomPubSettings));
            }, true);

            apiHandler.RegisterEndpointLegacy(kApiUrlPart + "file/bulkSaveBloomPubs", request =>
            {
                // update what's in the collection so that we remember for next time
                _collectionSettings.BulkPublishBloomPubSettings = request.RequiredPostObject <BulkBloomPubPublishSettings>();
                _collectionSettings.Save();

                _bulkBloomPubCreator.PublishAllBooks(_collectionSettings.BulkPublishBloomPubSettings);
                SetState("stopped");
                request.PostSucceeded();
            }, true);

            apiHandler.RegisterEndpointLegacy(kApiUrlPart + "textToClipboard", request =>
            {
                PortableClipboard.SetText(request.RequiredPostString());
                request.PostSucceeded();
            }, true);

            apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "canHaveMotionMode",
                                                      request =>
            {
                return(request.CurrentBook.HasMotionPages);
            },
                                                      null,  // no write action
                                                      false,
                                                      true); // we don't really know, just safe default

            apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "canRotate",
                                                      request =>
            {
                return(request.CurrentBook.BookInfo.PublishSettings.BloomPub.Motion && request.CurrentBook.HasMotionPages);
            },
                                                      null,  // no write action
                                                      false,
                                                      true); // we don't really know, just safe default

            apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "defaultLandscape",
                                                      request =>
            {
                return(request.CurrentBook.GetLayout().SizeAndOrientation.IsLandScape);
            },
                                                      null,  // no write action
                                                      false,
                                                      true); // we don't really know, just safe default
            apiHandler.RegisterEndpointLegacy(kApiUrlPart + "languagesInBook", request =>
            {
                try
                {
                    InitializeLanguagesInBook(request);

                    Dictionary <string, InclusionSetting> textLangsToPublish  = request.CurrentBook.BookInfo.PublishSettings.BloomPub.TextLangs;
                    Dictionary <string, InclusionSetting> audioLangsToPublish = request.CurrentBook.BookInfo.PublishSettings.BloomPub.AudioLangs;

                    var result = "[" + string.Join(",", _allLanguages.Select(kvp =>
                    {
                        string langCode = kvp.Key;

                        bool includeText = false;
                        if (textLangsToPublish != null && textLangsToPublish.TryGetValue(langCode, out InclusionSetting includeTextSetting))
                        {
                            includeText = includeTextSetting.IsIncluded();
                        }

                        bool includeAudio = false;
                        if (audioLangsToPublish != null && audioLangsToPublish.TryGetValue(langCode, out InclusionSetting includeAudioSetting))
                        {
                            includeAudio = includeAudioSetting.IsIncluded();
                        }

                        var value = new LanguagePublishInfo()
                        {
                            code             = kvp.Key,
                            name             = request.CurrentBook.PrettyPrintLanguage(langCode),
                            complete         = kvp.Value,
                            includeText      = includeText,
                            containsAnyAudio = _languagesWithAudio.Contains(langCode),
                            includeAudio     = includeAudio
                        };
                        var json = JsonConvert.SerializeObject(value);
                        return(json);
                    })) + "]";

                    request.ReplyWithText(result);
                }
Пример #7
0
        public bool UpdatePreview(EpubPublishUiSettings newSettings, bool force, WebSocketProgress progress = null)
        {
            _progress = progress ?? _standardProgress.WithL10NPrefix("PublishTab.Epub.");
            if (Program.RunningOnUiThread)
            {
                // There's some stuff inside this lock that has to run on the UI thread.
                // If we lock the UI thread here, we can deadlock the whole program.
                throw new ApplicationException(@"Must not attempt to make epubs on UI thread...will produce deadlocks");
            }

            lock (_epubMakerLock)
            {
                if (EpubMaker != null)
                {
                    EpubMaker.AbortRequested = false;
                }
                _stagingEpub = true;
            }

            // For some unknown reason, if the accessibility window is showing, some of the browser navigation
            // that is needed to accurately determine which content is visible simply doesn't happen.
            // It would be disconcerting if it popped to the top after we close it and reopen it.
            // So, we just close the window if it is showing when we do this. See BL-7807.
            // Except that opening the Ace Checker tab invokes this code path in a way that works without the
            // deadlock (or whatever causes the failure).  This call can be detected by the progress argument not
            // being null.  The Refresh button on the AccessibilityCheckWindow also uses this code path in the
            // same way, so the next two lines also allow that Refresh button to work.  See BL-9341 for why
            // the original fix is inadequate.
            if (progress == null)
            {
                AccessibilityChecker.AccessibilityCheckWindow.StaticClose();
            }

            try
            {
                _webSocketServer.SendString(kWebsocketContext, "startingEbookCreation", _previewSrc);

                var  htmlPath   = _bookSelection.CurrentSelection.GetPathHtmlFile();
                var  newVersion = Book.Book.ComputeHashForAllBookRelatedFiles(htmlPath);
                bool previewIsAlreadyCurrent;
                lock (_epubMakerLock)
                {
                    previewIsAlreadyCurrent = _desiredEpubSettings == newSettings && EpubMaker != null && newVersion == _bookVersion &&
                                              !EpubMaker.AbortRequested && !force;
                }

                if (previewIsAlreadyCurrent)
                {
                    SaveAsEpub();                    // just in case there's a race condition where we haven't already saved it.
                    return(true);                    // preview is already up to date.
                }

                _desiredEpubSettings = newSettings;

                // clear the obsolete preview, if any; this also ensures that when the new one gets done,
                // we will really be changing the src attr in the preview iframe so the display will update.
                _webSocketServer.SendEvent(kWebsocketContext, kWebsocketEventId_epubReady);
                _bookVersion = newVersion;
                ReportProgress(LocalizationManager.GetString("PublishTab.Epub.PreparingPreview", "Preparing Preview"));

                // This three-tries loop is an attempt to recover from a weird state the system sometimes gets into
                // where a browser won't navigate to a temporary page that the EpubMaker uses. I'm not sure it actually
                // helps, once the system gets into this state even a brand new browser seems to have the same problem.
                // Usually there will be no exception, and the loop breaks at the end of the first iteration.
                for (int i = 0; i < 3; i++)
                {
                    try
                    {
                        if (!PublishHelper.InPublishTab)
                        {
                            return(false);
                        }
                        _previewSrc = UpdateEpubControlContent();
                    }
                    catch (ApplicationException ex)
                    {
                        if (i >= 2)
                        {
                            throw;
                        }
                        ReportProgress("Something went wrong, trying again");
                        continue;
                    }

                    break;                     // normal case, no exception
                }

                lock (_epubMakerLock)
                {
                    if (EpubMaker.AbortRequested)
                    {
                        return(false);                        // the code that set the abort flag will request a new preview.
                    }
                }
            }
            finally
            {
                lock (_epubMakerLock)
                {
                    _stagingEpub = false;
                }
            }

            // Do pending save if the user requested it while the preview was still in progress.
            SaveAsEpub();
            ReportProgress(LocalizationManager.GetString("PublishTab.Epub.Done", "Done"));
            return(true);
        }
        public void RegisterWithApiHandler(BloomApiHandler apiHandler)
        {
            apiHandler.RegisterEndpointHandler(kApiUrlPart + "bookName", request =>
            {
                request.ReplyWithText(request.CurrentBook.TitleBestForUserDisplay);
            }, false);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "showAccessibilityChecker", request =>
            {
                AccessibilityCheckWindow.StaticShow(() => _webSocketServer.SendEvent(kWebSocketContext, kWindowActivated));
                request.PostSucceeded();
            }, true);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "descriptionsForAllImages", request =>
            {
                var problems    = AccessibilityCheckers.CheckDescriptionsForAllImages(request.CurrentBook);
                var resultClass = problems.Any() ? "failed" : "passed";
                request.ReplyWithJson(new { resultClass = resultClass, problems = problems });
            }, false);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "audioForAllImageDescriptions", request =>
            {
                var problems    = AccessibilityCheckers.CheckAudioForAllImageDescriptions(request.CurrentBook);
                var resultClass = problems.Any() ? "failed" : "passed";
                request.ReplyWithJson(new { resultClass = resultClass, problems = problems });
            }, false);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "audioForAllText", request =>
            {
                var problems    = AccessibilityCheckers.CheckAudioForAllText(request.CurrentBook);
                var resultClass = problems.Any() ? "failed" : "passed";
                request.ReplyWithJson(new { resultClass = resultClass, problems = problems });
            }, false);

            // Just a checkbox that the user ticks to say "yes, I checked this"
            // At this point, we don't have a way to clear that when the book changes.
            apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "noEssentialInfoByColor",
                                                      request => request.CurrentBook.BookInfo.MetaData.A11y_NoEssentialInfoByColor,
                                                      (request, b) => {
                request.CurrentBook.BookInfo.MetaData.A11y_NoEssentialInfoByColor = b;
                request.CurrentBook.Save();
            },
                                                      false);

            // Just a checkbox that the user ticks to say "yes, I checked this"
            // At this point, we don't have a way to clear that when the book changes.
            apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "noTextIncludedInAnyImages",
                                                      request => request.CurrentBook.BookInfo.MetaData.A11y_NoTextIncludedInAnyImages,
                                                      (request, b) => {
                request.CurrentBook.BookInfo.MetaData.A11y_NoTextIncludedInAnyImages = b;
                request.CurrentBook.Save();
            },
                                                      false);

            //enhance: this might have to become async to work on large books on slow computers
            apiHandler.RegisterEndpointHandler(kApiUrlPart + "aceByDaisyReportUrl", request => { MakeAceByDaisyReport(request); },
                                               false, false
                                               );

            // A checkbox that the user ticks in the Accessible Image tool to request a preview
            // of how things might look with cataracts.
            // For now this doesn't seem worth persisting, except for the session so it sticks from page to page.
            apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "cataracts",
                                                      request => _simulateCataracts,
                                                      (request, b) => { _simulateCataracts = b; },
                                                      false);
            // A checkbox that the user ticks in the Accessible Image tool to request a preview
            // of how things might look with color-blindness, and a set of radio buttons
            // for choosing different kinds of color-blindness.
            // For now these doesn't seem worth persisting, except for the session so it sticks from page to page.
            apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "colorBlindness",
                                                      request => _simulateColorBlindness,
                                                      (request, b) => { _simulateColorBlindness = b; },
                                                      false);
            apiHandler.RegisterEnumEndpointHandler(kApiUrlPart + "kindOfColorBlindness",
                                                   request => _kindOfColorBlindness,
                                                   (request, kind) => _kindOfColorBlindness = kind, false);
        }
        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
            }
        }
Пример #10
0
        public bool UpdatePreview(EpubPublishUiSettings newSettings, bool force, WebSocketProgress progress = null)
        {
            _progress = progress ?? _standardProgress.WithL10NPrefix("PublishTab.Epub.");
            if (Program.RunningOnUiThread)
            {
                // There's some stuff inside this lock that has to run on the UI thread.
                // If we lock the UI thread here, we can deadlock the whole program.
                throw new ApplicationException(@"Must not attempt to make epubs on UI thread...will produce deadlocks");
            }

            lock (_epubMakerLock)
            {
                if (EpubMaker != null)
                {
                    EpubMaker.AbortRequested = false;
                }
                _stagingEpub = true;
            }

            try
            {
                _webSocketServer.SendString(kWebsocketContext, "startingEbookCreation", _previewSrc);

                var  htmlPath   = _bookSelection.CurrentSelection.GetPathHtmlFile();
                var  newVersion = Book.Book.MakeVersionCode(File.ReadAllText(htmlPath), htmlPath);
                bool previewIsAlreadyCurrent;
                lock (_epubMakerLock)
                {
                    previewIsAlreadyCurrent = _desiredEpubSettings == newSettings && EpubMaker != null && newVersion == _bookVersion &&
                                              !EpubMaker.AbortRequested && !force;
                }

                if (previewIsAlreadyCurrent)
                {
                    SaveAsEpub();                    // just in case there's a race condition where we haven't already saved it.
                    return(true);                    // preview is already up to date.
                }

                _desiredEpubSettings = newSettings;

                // clear the obsolete preview, if any; this also ensures that when the new one gets done,
                // we will really be changing the src attr in the preview iframe so the display will update.
                _webSocketServer.SendEvent(kWebsocketContext, kWebsocketEventId_epubReady);
                _bookVersion = newVersion;
                ReportProgress(LocalizationManager.GetString("PublishTab.Epub.PreparingPreview", "Preparing Preview"));

                // This three-tries loop is an attempt to recover from a weird state the system sometimes gets into
                // where a browser won't navigate to a temporary page that the EpubMaker uses. I'm not sure it actually
                // helps, once the system gets into this state even a brand new browser seems to have the same problem.
                // Usually there will be no exception, and the loop breaks at the end of the first iteration.
                for (int i = 0; i < 3; i++)
                {
                    try
                    {
                        if (!PublishHelper.InPublishTab)
                        {
                            return(false);
                        }
                        _previewSrc = UpdateEpubControlContent();
                    }
                    catch (ApplicationException ex)
                    {
                        if (i >= 2)
                        {
                            throw;
                        }
                        ReportProgress("Something went wrong, trying again");
                        continue;
                    }

                    break;                     // normal case, no exception
                }

                lock (_epubMakerLock)
                {
                    if (EpubMaker.AbortRequested)
                    {
                        return(false);                        // the code that set the abort flag will request a new preview.
                    }
                }
            }
            finally
            {
                lock (_epubMakerLock)
                {
                    _stagingEpub = false;
                }
            }

            // Do pending save if the user requested it while the preview was still in progress.
            SaveAsEpub();
            ReportProgress(LocalizationManager.GetString("PublishTab.Epub.Done", "Done"));
            return(true);
        }