示例#1
0
        /// <summary>
        /// Start things up so all the collection-specific objects we need can be created.
        /// </summary>
        /// <param name="projectSettingsPath"></param>
        /// <param name="parentContainer"></param>
        /// <param name="justEnoughForHtmlDialog">If true, we're aiming for the minimum init that will
        /// let us display an HTML dialog. Currently this will be the JoinTeamCollection dialog.
        /// We need the BloomServer running (so we can make API requests, including for localization).
        /// We can avoid a lot of other expensive stuff.</param>
        public ProjectContext(string projectSettingsPath, IContainer parentContainer, bool justEnoughForHtmlDialog = false)
        {
            SettingsPath = projectSettingsPath;
            // BL-8019: A couple lines down, BuildSubContainerForThisProject() starts BloomServer with the new project.
            // While we are starting (or restarting, in the case of switching collections) BloomServer we need to use
            // the WinFormsExceptionHandler mechanism, which doesn't use a browser.
            // The ProblemReportApi, which uses the browser (and therefore BloomServer) isn't available to us
            // while BloomServer is starting up. By the time WorkspaceView comes online and sets the error reporting
            // to the ProblemReportApi mechanism, BloomServer will be up and running again.
            ErrorReport.OnShowDetails         = null;
            FatalExceptionHandler.UseFallback = true;

            BuildSubContainerForThisProject(projectSettingsPath, parentContainer);

            _scope.Resolve <CollectionSettings>().CheckAndFixDependencies(_scope.Resolve <BloomFileLocator>());

            if (!justEnoughForHtmlDialog)
            {
                ProjectWindow = _scope.Resolve <Shell>();
            }

            string collectionDirectory = Path.GetDirectoryName(projectSettingsPath);

            //should we save a link to this in the list of collections?
            var collectionSettings = _scope.Resolve <CollectionSettings>();

            if (!justEnoughForHtmlDialog && collectionSettings.IsSourceCollection)
            {
                AddShortCutInComputersBloomCollections(collectionDirectory);
            }

            ToolboxView.SetupToolboxForCollection(Settings);
            _scope.Resolve <TeamCollectionManager>().Settings = Settings;
        }
示例#2
0
        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            var container = ContainerHelper.I;

            IRegionManager     mgr  = container.Resolve <IRegionManager>();
            MessageConsoleView view = new MessageConsoleView();

            container.BuildUp(view);
            mgr.AddView(view, "MessageConsoleRegion");
            //
            ToolboxView tbView = new ToolboxView();

            container.BuildUp(tbView);
            container.RegisterInstance <IToolboxService>(tbView);
            mgr.AddView(tbView, "ToolboxRegion");

            _propGrid = (PropertyGrid)PropertyGridRegion.Content;
            container.BuildUp(_propGrid);
            container.RegisterInstance <IDesignEditorService>(_propGrid);

            DesignSurface surface = new DesignSurface();

            container.BuildUp(surface);
            container.RegisterInstance <IDesigner>(surface);
            mgr.AddView(surface, "DesignSurfaceRegion");

            _globalMouse   = EventAggregator.Get <GlobalMousePostionChangedEvent, Point>();
            _leftMouseDown = EventAggregator.Get <MouseLeftButtonDownEvent, MouseButtonEventArgs>();
            _leftMouseUp   = EventAggregator.Get <MouseLeftButtonUpEvent, MouseButtonEventArgs>();

            container.BuildUp(Presenter);
            Presenter.InitializeDesignableControls();
        }
        public ToolboxViewModel()
        {
            _view = new ToolboxView()
            {
                DataContext = this
            };

            _view.Show();
        }
示例#4
0
        /// <summary>
        /// Give the locations of the bedrock files/folders that come with Bloom. These will have priority.
        /// (But compare GetAfterXMatterFileLocations, for further paths that are searched after factory XMatter).
        /// </summary>
        public static IEnumerable <string> GetFactoryFileLocations()
        {
            //bookLayout has basepage.css. We have it first because it will find its way to many other folders, but this is the authoritative one
            yield return(FileLocationUtilities.GetDirectoryDistributedWithApplication(BloomFileLocator.BrowserRoot, "bookLayout"));

            yield return(FileLocationUtilities.GetDirectoryDistributedWithApplication(BloomFileLocator.BrowserRoot));

            //hack to get the distfiles folder itself
            yield return(Path.GetDirectoryName(FileLocationUtilities.GetDirectoryDistributedWithApplication("localization")));

            yield return(FileLocationUtilities.GetDirectoryDistributedWithApplication(BloomFileLocator.BrowserRoot));

            yield return(FileLocationUtilities.GetDirectoryDistributedWithApplication(Path.Combine(BloomFileLocator.BrowserRoot, "bookEdit/js")));

            yield return(FileLocationUtilities.GetDirectoryDistributedWithApplication(Path.Combine(BloomFileLocator.BrowserRoot, "bookEdit/js/toolbar")));

            yield return(FileLocationUtilities.GetDirectoryDistributedWithApplication(Path.Combine(BloomFileLocator.BrowserRoot, "bookEdit/css")));

            yield return(FileLocationUtilities.GetDirectoryDistributedWithApplication(Path.Combine(BloomFileLocator.BrowserRoot, "bookEdit/html")));

            yield return(FileLocationUtilities.GetDirectoryDistributedWithApplication(Path.Combine(BloomFileLocator.BrowserRoot, "bookEdit/html/font-awesome/css")));

            yield return(FileLocationUtilities.GetDirectoryDistributedWithApplication(Path.Combine(BloomFileLocator.BrowserRoot, "bookEdit/img")));

            yield return(FileLocationUtilities.GetDirectoryDistributedWithApplication(Path.Combine(BloomFileLocator.BrowserRoot, "images")));

            foreach (var dir in ToolboxView.GetToolboxServerDirectories())
            {
                yield return(dir);
            }
            yield return(FileLocationUtilities.GetDirectoryDistributedWithApplication(Path.Combine(BloomFileLocator.BrowserRoot, "bookEdit/StyleEditor")));

            yield return(FileLocationUtilities.GetDirectoryDistributedWithApplication(Path.Combine(BloomFileLocator.BrowserRoot, "bookEdit/TopicChooser")));

            yield return(FileLocationUtilities.GetDirectoryDistributedWithApplication(Path.Combine(BloomFileLocator.BrowserRoot, "collection")));

            var x = FileLocationUtilities.GetDirectoryDistributedWithApplication(Path.Combine(BloomFileLocator.BrowserRoot, "performance"));

            yield return(FileLocationUtilities.GetDirectoryDistributedWithApplication(Path.Combine(BloomFileLocator.BrowserRoot, "performance")));

            yield return(FileLocationUtilities.GetDirectoryDistributedWithApplication(Path.Combine(BloomFileLocator.BrowserRoot, "themes/bloom-jqueryui-theme")));

            yield return(FileLocationUtilities.GetDirectoryDistributedWithApplication(Path.Combine(BloomFileLocator.BrowserRoot, "lib")));

            // not needed: yield return FileLocationUtilities.GetDirectoryDistributedWithApplication(Path.Combine(BloomFileLocator.BrowserRoot,"lib/localizationManager"));
            yield return(FileLocationUtilities.GetDirectoryDistributedWithApplication(Path.Combine(BloomFileLocator.BrowserRoot, "lib/long-press")));

            yield return(FileLocationUtilities.GetDirectoryDistributedWithApplication(Path.Combine(BloomFileLocator.BrowserRoot, "lib/split-pane")));

            yield return(FileLocationUtilities.GetDirectoryDistributedWithApplication(Path.Combine(BloomFileLocator.BrowserRoot, "lib/ckeditor/skins/icy_orange")));

            yield return(FileLocationUtilities.GetDirectoryDistributedWithApplication(Path.Combine(BloomFileLocator.BrowserRoot, "bookEdit/toolbox/talkingBook")));

            yield return(BloomFileLocator.GetInstalledXMatterDirectory());

            yield return(FileLocationUtilities.GetDirectoryDistributedWithApplication(Path.Combine(BloomFileLocator.BrowserRoot, "publish/ePUBPublish")));
        }
示例#5
0
        public ProjectContext(string projectSettingsPath, IContainer parentContainer)
        {
            SettingsPath = projectSettingsPath;
            BuildSubContainerForThisProject(projectSettingsPath, parentContainer);

            _scope.Resolve <CollectionSettings>().CheckAndFixDependencies(_scope.Resolve <BloomFileLocator>());

            ProjectWindow = _scope.Resolve <Shell>();

            string collectionDirectory = Path.GetDirectoryName(projectSettingsPath);

            //should we save a link to this in the list of collections?
            var collectionSettings = _scope.Resolve <CollectionSettings>();

            if (collectionSettings.IsSourceCollection)
            {
                AddShortCutInComputersBloomCollections(collectionDirectory);
            }

            ToolboxView.SetupToolboxForCollection(Settings);
        }
示例#6
0
        /// ------------------------------------------------------------------------------------
        protected void BuildSubContainerForThisProject(string projectSettingsPath, IContainer parentContainer)
        {
            var commandTypes = new[]
            {
                typeof(DuplicatePageCommand),
                typeof(DeletePageCommand),
                typeof(CutCommand),
                typeof(CopyCommand),
                typeof(PasteCommand),
                typeof(UndoCommand)
            };

            var editableCollectionDirectory = Path.GetDirectoryName(projectSettingsPath);

            try
            {
                _scope = parentContainer.BeginLifetimeScope(builder =>
                {
                    //BloomEvents are by nature, singletons (InstancePerLifetimeScope)
                    builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
                    .InstancePerLifetimeScope()
                    // Didn't work .Where(t => t.GetInterfaces().Contains(typeof(Bloom.Event<>)));
                    .Where(t => t is IEvent);

                    //Other classes which are also  singletons
                    builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
                    .InstancePerLifetimeScope()
                    .Where(t => new[]
                    {
                        typeof(TemplateInsertionCommand),
                        typeof(EditBookCommand),
                        typeof(SendReceiveCommand),
                        typeof(SelectedTabAboutToChangeEvent),
                        typeof(SelectedTabChangedEvent),
                        typeof(LibraryClosing),
                        typeof(PageListChangedEvent),                                  // REMOVE+++++++++++++++++++++++++++
                        typeof(BookRefreshEvent),
                        typeof(BookSavedEvent),
                        typeof(PageRefreshEvent),
                        typeof(BookDownloadStartingEvent),
                        typeof(BookSelection),
                        typeof(CurrentEditableCollectionSelection),
                        typeof(RelocatePageEvent),
                        typeof(QueueRenameOfCollection),
                        typeof(PageSelection),
                        typeof(LocalizationChangedEvent),
                        typeof(ControlKeyEvent),
                        typeof(BookStatusChangeEvent),
                        typeof(EditingModel),
                        typeof(AudioRecording),
                        typeof(BookSettingsApi),
                        typeof(BookMetadataApi),
                        typeof(PublishToAndroidApi),
                        typeof(PublishEpubApi),
                        typeof(AccessibilityCheckApi),
                        typeof(CollectionSettingsApi),
                        typeof(CollectionApi),
                        typeof(PageControlsApi),
                        typeof(ReadersApi),
                        typeof(PageTemplatesApi),
                        typeof(AddOrChangePageApi),
                        typeof(BloomWebSocketServer),
                        typeof(PerformanceMeasurement),
                        typeof(KeyboardingConfigApi),
                        typeof(ImageApi),
                        typeof(MusicApi),
                        typeof(PageListApi),
                        typeof(TalkingBookApi),
                        typeof(ToolboxApi),
                        typeof(CommonApi),
                        typeof(TeamCollectionApi),
                        typeof(BrandingSettings),
                        typeof(AppApi),
                        typeof(I18NApi),
                        typeof(SignLanguageApi),
                        typeof(AudioSegmentationApi),
                        typeof(FileIOApi),
                        typeof(EditingViewApi),
                        typeof(BrowserDialogApi),
                        typeof(ProblemReportApi)
                    }.Contains(t));

                    builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
                    .InstancePerLifetimeScope()
                    .Where(commandTypes.Contains).As <ICommand>();

                    var bookRenameEvent = new BookRenamedEvent();
                    builder.Register(c => bookRenameEvent).AsSelf().InstancePerLifetimeScope();

                    try
                    {
#if Chorus                      //nb: we split out the ChorusSystem.Init() so that this won't ever fail, so we have something registered even if we aren't
                        //going to be able to do HG for some reason.
                        var chorusSystem = new ChorusSystem(Path.GetDirectoryName(projectSettingsPath));
                        builder.Register <ChorusSystem>(c => chorusSystem).InstancePerLifetimeScope();
                        builder.Register <SendReceiver>(c => new SendReceiver(chorusSystem, () => ProjectWindow))
                        .InstancePerLifetimeScope();

                        chorusSystem.Init(string.Empty /*user name*/);
#endif
                    }
                    catch (Exception error)
                    {
#if USING_CHORUS
#if !DEBUG
                        SIL.Reporting.ErrorReport.NotifyUserOfProblem(error,
                                                                      "There was a problem loading the Chorus Send/Receive system for this collection. Bloom will try to limp along, but you'll need technical help to resolve this. If you have no other choice, find this folder: {0}, move it somewhere safe, and restart Bloom.", Path.GetDirectoryName(projectSettingsPath).CombineForPath(".hg"));
#endif
                        //swallow for develoeprs, because this happens if you don't have the Mercurial and "Mercurial Extensions" folders in the root, and our
                        //getdependencies doesn't yet do that.
#endif
                    }


                    //This deserves some explanation:
                    //*every* collection has a "*.BloomCollection" settings file. But the one we make the most use of is the one editable collection
                    //That's why we're registering it... it gets used all over. At the moment (May 2012), we don't ever read the
                    //settings file of the collections we're using for sources.
                    try
                    {
                        // It's important to create the TC manager before we create CollectionSettings, as its constructor makes sure
                        // we have a current version of the file that CollectionSettings is built from.
                        builder.Register <TeamCollectionManager>(c => new TeamCollectionManager(projectSettingsPath,
                                                                                                c.Resolve <BloomWebSocketServer>(), c.Resolve <BookRenamedEvent>(),
                                                                                                c.Resolve <BookStatusChangeEvent>(),
                                                                                                c.Resolve <BookSelection>(),
                                                                                                c.Resolve <LibraryClosing>())).InstancePerLifetimeScope();
                        builder.Register <ITeamCollectionManager>(c => c.Resolve <TeamCollectionManager>()).InstancePerLifetimeScope();
                        builder.Register <CollectionSettings>(c =>
                        {
                            c.Resolve <TeamCollectionManager>();
                            return(GetCollectionSettings(projectSettingsPath));
                        }).InstancePerLifetimeScope();
                    }
                    catch (Exception)
                    {
                        return;
                    }


                    builder.Register <LibraryModel>(
                        c =>
                        new LibraryModel(editableCollectionDirectory, c.Resolve <CollectionSettings>(),
                                                        #if Chorus
                                         c.Resolve <SendReceiver>(),
                                                        #endif
                                         c.Resolve <BookSelection>(), c.Resolve <SourceCollectionsList>(), c.Resolve <BookCollection.Factory>(),
                                         c.Resolve <EditBookCommand>(), c.Resolve <CreateFromSourceBookCommand>(), c.Resolve <BookServer>(),
                                         c.Resolve <CurrentEditableCollectionSelection>(), c.Resolve <BookThumbNailer>(), c.Resolve <TeamCollectionManager>())).InstancePerLifetimeScope();

                    // Keep in sync with OptimizedFileLocator: it wants to return the object created here.
                    builder.Register <IChangeableFileLocator>(
                        c =>
                        new BloomFileLocator(c.Resolve <CollectionSettings>(), c.Resolve <XMatterPackFinder>(), GetFactoryFileLocations(),
                                             GetFoundFileLocations(), GetAfterXMatterFileLocations())).InstancePerLifetimeScope();

                    builder.Register <LanguageSettings>(c =>
                    {
                        var librarySettings = c.Resolve <CollectionSettings>();
                        var preferredSourceLanguagesInOrder = new List <string>();
                        preferredSourceLanguagesInOrder.Add(librarySettings.Language2.Iso639Code);
                        if (!String.IsNullOrEmpty(librarySettings.Language3.Iso639Code) &&
                            librarySettings.Language3.Iso639Code != librarySettings.Language2.Iso639Code)
                        {
                            preferredSourceLanguagesInOrder.Add(librarySettings.Language3.Iso639Code);
                        }

                        return(new LanguageSettings(librarySettings.Language1.Iso639Code, preferredSourceLanguagesInOrder));
                    });
                    builder.Register <XMatterPackFinder>(c =>
                    {
                        var locations = new List <string>();
                        locations.Add(BloomFileLocator.GetInstalledXMatterDirectory());
                        locations.Add(XMatterAppDataFolder);
                        locations.Add(XMatterCommonDataFolder);
                        return(new XMatterPackFinder(locations));
                    });

                    builder.Register <SourceCollectionsList>(c =>
                    {
                        var l = new SourceCollectionsList(c.Resolve <Book.Book.Factory>(), c.Resolve <BookStorage.Factory>(),
                                                          editableCollectionDirectory, new string[] { BloomFileLocator.FactoryCollectionsDirectory, GetInstalledCollectionsDirectory() });
                        return(l);
                    }).InstancePerLifetimeScope();

                    builder.Register <ITemplateFinder>(c =>
                    {
                        return(c.Resolve <SourceCollectionsList>());
                    }).InstancePerLifetimeScope();

                    builder.RegisterType <BloomParseClient>().AsSelf().SingleInstance();

                    // Enhance: may need some way to test a release build in the sandbox.
                    builder.Register(c => CreateBloomS3Client()).AsSelf().SingleInstance();
                    builder.RegisterType <BookUpload>().AsSelf().SingleInstance();

                    //TODO: this gave a stackoverflow exception
//				builder.Register<WorkspaceModel>(c => c.Resolve<WorkspaceModel.Factory>()(rootDirectoryPath)).InstancePerLifetimeScope();
                    //so we're doing this
                    builder.Register(c => editableCollectionDirectory).InstancePerLifetimeScope();

                    builder.RegisterType <CreateFromSourceBookCommand>().InstancePerLifetimeScope();

                    // See related comment below for BL-688
//				string collectionDirectory = Path.GetDirectoryName(projectSettingsPath);
//				if (Path.GetFileNameWithoutExtension(projectSettingsPath).ToLower().Contains("web"))
//				{
//					// REVIEW: This seems to be used only for testing purposes
//					BookCollection editableCollection = _scope.Resolve<BookCollection.Factory>()(collectionDirectory, BookCollection.CollectionType.TheOneEditableCollection);
//					var sourceCollectionsList = _scope.Resolve<SourceCollectionsList>();
//					_httpServer = new BloomServer(_scope.Resolve<CollectionSettings>(), editableCollection, sourceCollectionsList, parentContainer.Resolve<HtmlThumbNailer>());
//				}
//				else
//				{
                    builder.Register <BloomServer>(
                        c =>
                        new BloomServer(new RuntimeImageProcessor(bookRenameEvent), c.Resolve <BookSelection>(), c.Resolve <CollectionSettings>())).SingleInstance();

                    builder.Register <Func <WorkspaceView> >(c => () =>
                    {
                        var factory = c.Resolve <WorkspaceView.Factory>();

                        // Removing this check because finding "web" anywhere in the path is problematic.
                        // This was discovered by a user whose username included "web" (https://jira.sil.org/browse/BL-688)
                        // It appears this code block was for some experimental development but no longer works anyway.
//					if (projectSettingsPath.ToLower().Contains("web"))
//					{
//						return factory(c.Resolve<WebLibraryView>());
//					}
//					else
//					{
                        return(factory(c.Resolve <LibraryView>()));
//					}
                    });

                    builder.RegisterType <AccessibilityCheckWindow>();
                });

                /*
                 * this is from spike, which worked, but we aren't using (yet)
                 * var allCommands = from c in commandTypes select _scope.Resolve(c) as ICommand;
                 * _commandAvailabilityPublisher = new CommandAvailabilityPublisher(allCommands);
                 */
            }
            catch (FileNotFoundException error)
            {
                MessageBox.Show("Bloom was not able to find all its bits. This sometimes happens when upgrading to a newer version. To fix it, please run the installer again and choose 'Repair', or uninstall and reinstall. We truly value your time and apologize for wasting it. The error was:" + Environment.NewLine + Environment.NewLine + error.Message, "Bloom Installation Problem", MessageBoxButtons.OK, MessageBoxIcon.Error);
                Application.Exit();
            }

            var server = _scope.Resolve <BloomServer>();
            server.StartListening();
            _scope.Resolve <AudioRecording>().RegisterWithApiHandler(server.ApiHandler);

            _scope.Resolve <BloomWebSocketServer>().Init((BloomServer.portForHttp + 1).ToString(CultureInfo.InvariantCulture));
            HelpLauncher.RegisterWithApiHandler(server.ApiHandler);
            ExternalLinkController.RegisterWithApiHandler(server.ApiHandler);
            ToolboxView.RegisterWithApiHandler(server.ApiHandler);
            _scope.Resolve <PageTemplatesApi>().RegisterWithApiHandler(server.ApiHandler);
            _scope.Resolve <AddOrChangePageApi>().RegisterWithApiHandler(server.ApiHandler);
            _scope.Resolve <PublishToAndroidApi>().RegisterWithApiHandler(server.ApiHandler);
            _scope.Resolve <PublishEpubApi>().RegisterWithApiHandler(server.ApiHandler);
            _scope.Resolve <AccessibilityCheckApi>().RegisterWithApiHandler(server.ApiHandler);
            _scope.Resolve <CollectionSettingsApi>().RegisterWithApiHandler(server.ApiHandler);
            _scope.Resolve <CollectionApi>().RegisterWithApiHandler(server.ApiHandler);
            _scope.Resolve <PageControlsApi>().RegisterWithApiHandler(server.ApiHandler);
            _scope.Resolve <KeyboardingConfigApi>().RegisterWithApiHandler(server.ApiHandler);
            _scope.Resolve <BookSettingsApi>().RegisterWithApiHandler(server.ApiHandler);
            _scope.Resolve <BookMetadataApi>().RegisterWithApiHandler(server.ApiHandler);
            _scope.Resolve <ImageApi>().RegisterWithApiHandler(server.ApiHandler);
            _scope.Resolve <ReadersApi>().RegisterWithApiHandler(server.ApiHandler);
            _scope.Resolve <MusicApi>().RegisterWithApiHandler(server.ApiHandler);
            _scope.Resolve <PageListApi>().RegisterWithApiHandler(server.ApiHandler);
            _scope.Resolve <TalkingBookApi>().RegisterWithApiHandler(server.ApiHandler);
            _scope.Resolve <ToolboxApi>().RegisterWithApiHandler(server.ApiHandler);
            _scope.Resolve <CommonApi>().RegisterWithApiHandler(server.ApiHandler);
            _scope.Resolve <TeamCollectionApi>().RegisterWithApiHandler(server.ApiHandler);
            _scope.Resolve <AppApi>().RegisterWithApiHandler(server.ApiHandler);
            _scope.Resolve <SignLanguageApi>().RegisterWithApiHandler(server.ApiHandler);
            _scope.Resolve <AudioSegmentationApi>().RegisterWithApiHandler(server.ApiHandler);
            _scope.Resolve <BrowserDialogApi>().RegisterWithApiHandler(server.ApiHandler);
            _scope.Resolve <ProblemReportApi>().RegisterWithApiHandler(server.ApiHandler);
            _scope.Resolve <I18NApi>().RegisterWithApiHandler(server.ApiHandler);
            _scope.Resolve <FileIOApi>().RegisterWithApiHandler(server.ApiHandler);
            _scope.Resolve <EditingViewApi>().RegisterWithApiHandler(server.ApiHandler);
            _scope.Resolve <PerformanceMeasurement>().RegisterWithApiHandler(server.ApiHandler);
        }
示例#7
0
        private void _backgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
        {
            //nb: we want exceptions to be uncaught, to be transferred up to the worker completed event
            _folderName = GetRootFolderName();
            if (_folderName == null)
            {
                return;
            }
            // ZipFile internally converts all \ separators to / (at least on Linux). So using
            // ZipFile instead of FastZip fixes https://jira.sil.org/browse/BL-1213.
            ZipFile zip = null;

            try
            {
                zip = new ZipFile(_path);
                byte[] buffer = new byte[4096];                     // 4K is optimum
                foreach (ZipEntry entry in zip)
                {
                    var fullOutputPath = Path.Combine(ProjectContext.GetInstalledCollectionsDirectory(), entry.Name);
                    if (entry.IsDirectory)
                    {
                        Directory.CreateDirectory(fullOutputPath);
                        // In the SharpZipLib code, IsFile and IsDirectory are not defined exactly as inverse: a third
                        // (or fourth) type of entry might be possible.  In practice in BloomPacks, this should not be
                        // an issue.
                        continue;
                    }
                    var directoryName = Path.GetDirectoryName(fullOutputPath);
                    if (!String.IsNullOrEmpty(directoryName))
                    {
                        Directory.CreateDirectory(directoryName);
                    }
                    using (var instream = zip.GetInputStream(entry))
                        using (var writer = RobustFile.Create(fullOutputPath))
                        {
                            ICSharpCode.SharpZipLib.Core.StreamUtils.Copy(instream, writer, buffer);
                        }
                }
            }
            catch (Exception ex)
            {
                // Report a corrupt file instead of crashing.  See http://issues.bloomlibrary.org/youtrack/issue/BL-2485.
                if (InvokeRequired)
                {
                    Invoke(new ReportBadBloomPack(ReportErrorUnzippingBloomPack));
                }
                else
                {
                    ReportErrorUnzippingBloomPack();
                }
                return;
            }
            finally
            {
                if (zip != null)
                {
                    zip.Close();
                }
            }

            var newlyAddedFolderOfThePack = Path.Combine(ProjectContext.GetInstalledCollectionsDirectory(), _folderName);

            CopyXMatterFoldersToWhereTheyBelong(newlyAddedFolderOfThePack);
            ToolboxView.CopyToolSettingsForBloomPack(newlyAddedFolderOfThePack);
        }