diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java
index c4fe110c9e94faeb0d0d45b51dd83b6b16cf006e..53822532bb777ad5c04959faae48b373408510af 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java
@@ -74,6 +74,7 @@ public final class FileTypes implements AutopsyVisitableItem {
 
     FileTypes(SleuthkitCase skCase) {
         this.skCase = skCase;
+        updateShowCounts();
     }
 
     @Override
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java
index f8323c89b8b9454da8b8c503254943d626a32a5d..e5bd41b6e8ddbd49046e6b5144b0f188d2b9b1f2 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java
@@ -149,9 +149,10 @@ private void populateHashMap() {
         this.typesRoot = typesRoot;
         this.pcl = (PropertyChangeEvent evt) -> {
             String eventType = evt.getPropertyName();
-            if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
-                    || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
-
+            if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString())
+                    || eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
+                    || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())
+                    || eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) {
                 /**
                  * Checking for a current case is a stop gap measure until a
                  * different way of handling the closing of cases is worked out.
@@ -160,7 +161,7 @@ private void populateHashMap() {
                  */
                 try {
                     Case.getCurrentCase();
-                    typesRoot.updateShowCounts(); 
+                    typesRoot.updateShowCounts();
                     populateHashMap();
                 } catch (IllegalStateException notUsed) {
                     /**
@@ -285,9 +286,9 @@ class MediaTypeNode extends DisplayableItemNode {
         @NbBundle.Messages({"FileTypesByMimeTypeNode.createSheet.mediaType.name=Type",
             "FileTypesByMimeTypeNode.createSheet.mediaType.displayName=Type",
             "FileTypesByMimeTypeNode.createSheet.mediaType.desc=no description"})
-                
+
         MediaTypeNode(String name) {
-            super(Children.create(new MediaTypeNodeChildren(name), true), Lookups.singleton(name)); 
+            super(Children.create(new MediaTypeNodeChildren(name), true), Lookups.singleton(name));
             setName(name);
             setDisplayName(name);
             this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png");
@@ -360,6 +361,7 @@ public void update(Observable o, Object arg) {
      * media subtype is the portion of the MIME type following the /.
      */
     class MediaSubTypeNode extends FileTypes.BGCountUpdatingNode {
+
         @NbBundle.Messages({"FileTypesByMimeTypeNode.createSheet.mediaSubtype.name=Subtype",
             "FileTypesByMimeTypeNode.createSheet.mediaSubtype.displayName=Subtype",
             "FileTypesByMimeTypeNode.createSheet.mediaSubtype.desc=no description"})
@@ -392,7 +394,8 @@ public boolean isLeafTypeNode() {
         public <T> T accept(DisplayableItemNodeVisitor< T> v) {
             return v.visit(this);
         }
-                @Override
+
+        @Override
         protected Sheet createSheet() {
             Sheet s = super.createSheet();
             Sheet.Set ss = s.get(Sheet.PROPERTIES);
diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/NameSearchPanel.form b/Core/src/org/sleuthkit/autopsy/filesearch/NameSearchPanel.form
index 2dfc5af1c30cb73002a693dedf62c257ef868bfb..b06f6d4c17287d32a22b9046f8fecee5f69b6a45 100644
--- a/Core/src/org/sleuthkit/autopsy/filesearch/NameSearchPanel.form
+++ b/Core/src/org/sleuthkit/autopsy/filesearch/NameSearchPanel.form
@@ -109,9 +109,6 @@
           <ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="NameSearchPanel.searchTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
         </Property>
       </Properties>
-      <Events>
-        <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="searchTextFieldMouseClicked"/>
-      </Events>
     </Component>
     <Component class="javax.swing.JLabel" name="noteNameLabel">
       <Properties>
diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/NameSearchPanel.java b/Core/src/org/sleuthkit/autopsy/filesearch/NameSearchPanel.java
index 7e4d902e00d15a4126519d280cb6a87ba5e68c91..cf197c4e00dac97c16c2d6744bc87941f6466f3c 100644
--- a/Core/src/org/sleuthkit/autopsy/filesearch/NameSearchPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/filesearch/NameSearchPanel.java
@@ -1,7 +1,7 @@
 /*
  * Autopsy Forensic Browser
  *
- * Copyright 2011 Basis Technology Corp.
+ * Copyright 2011-2017 Basis Technology Corp.
  * Contact: carrier <at> sleuthkit <dot> org
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,7 +26,6 @@
 
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
-import java.beans.PropertyChangeListener;
 import javax.swing.JCheckBox;
 import javax.swing.JMenuItem;
 import javax.swing.JTextField;
@@ -39,6 +38,8 @@
  */
 class NameSearchPanel extends javax.swing.JPanel {
 
+    private static final long serialVersionUID = 1L;
+
     /**
      * Creates new form NameSearchPanel
      */
@@ -143,11 +144,6 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
 
         searchTextField.setFont(searchTextField.getFont().deriveFont(searchTextField.getFont().getStyle() & ~java.awt.Font.BOLD, 11));
         searchTextField.setText(org.openide.util.NbBundle.getMessage(NameSearchPanel.class, "NameSearchPanel.searchTextField.text")); // NOI18N
-        searchTextField.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseClicked(java.awt.event.MouseEvent evt) {
-                searchTextFieldMouseClicked(evt);
-            }
-        });
 
         noteNameLabel.setFont(noteNameLabel.getFont().deriveFont(noteNameLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 10));
         noteNameLabel.setText(org.openide.util.NbBundle.getMessage(NameSearchPanel.class, "NameSearchPanel.noteNameLabel.text")); // NOI18N
@@ -181,10 +177,6 @@ public void mouseClicked(java.awt.event.MouseEvent evt) {
         );
     }// </editor-fold>//GEN-END:initComponents
 
-    private void searchTextFieldMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_searchTextFieldMouseClicked
-
-        this.nameCheckBox.setSelected(true);     }//GEN-LAST:event_searchTextFieldMouseClicked
-
     private void nameCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nameCheckBoxActionPerformed
         setComponentsEnabled();
         firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/SizeSearchPanel.form b/Core/src/org/sleuthkit/autopsy/filesearch/SizeSearchPanel.form
index 7987a8a031bab5f37d373bc911ff58e09e3c0f18..dcdab6f80249d36cd5cd105d0969b100e908a9cd 100644
--- a/Core/src/org/sleuthkit/autopsy/filesearch/SizeSearchPanel.form
+++ b/Core/src/org/sleuthkit/autopsy/filesearch/SizeSearchPanel.form
@@ -93,9 +93,6 @@
           <Connection code="0" type="code"/>
         </Property>
       </Properties>
-      <Events>
-        <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="sizeTextFieldMouseClicked"/>
-      </Events>
       <AuxValues>
         <AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new JFormattedTextField(NumberFormat.getIntegerInstance())"/>
       </AuxValues>
diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/SizeSearchPanel.java b/Core/src/org/sleuthkit/autopsy/filesearch/SizeSearchPanel.java
index 5e508744c7b9021ce9aca03bb8b3b71d4f55fe1d..3add97924791b4130bcfa84fdd938040e7eb6a01 100644
--- a/Core/src/org/sleuthkit/autopsy/filesearch/SizeSearchPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/filesearch/SizeSearchPanel.java
@@ -1,7 +1,7 @@
 /*
  * Autopsy Forensic Browser
  *
- * Copyright 2011 Basis Technology Corp.
+ * Copyright 2011-2017 Basis Technology Corp.
  * Contact: carrier <at> sleuthkit <dot> org
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -32,6 +32,8 @@
  */
 class SizeSearchPanel extends javax.swing.JPanel {
 
+    private static final long serialVersionUID = 1L;
+
     /**
      * Creates new form SizeSearchPanel
      */
@@ -123,11 +125,6 @@ private void initComponents() {
         sizeUnitComboBox.setModel(new javax.swing.DefaultComboBoxModel<String>(new String[] { "Byte(s)", "KB", "MB", "GB", "TB" }));
 
         sizeTextField.setValue(0);
-        sizeTextField.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseClicked(java.awt.event.MouseEvent evt) {
-                sizeTextFieldMouseClicked(evt);
-            }
-        });
 
         sizeCompareComboBox.setModel(new javax.swing.DefaultComboBoxModel<String>(new String[] { "equal to", "greater than", "less than" }));
 
@@ -161,11 +158,6 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
         );
     }// </editor-fold>//GEN-END:initComponents
 
-    private void sizeTextFieldMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_sizeTextFieldMouseClicked
-        this.sizeCheckBox.setSelected(true);
-        this.sizeTextField.selectAll(); // select all so user can change it easily
-    }//GEN-LAST:event_sizeTextFieldMouseClicked
-
     private void sizeCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sizeCheckBoxActionPerformed
         setComponentsEnabled();
         firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestMonitor.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestMonitor.java
index f1cad5531c25d52248fa6fc67cb1bd88e183f369..771380c415104df58e1bc78660bf0f1fa8ba2180 100644
--- a/Core/src/org/sleuthkit/autopsy/ingest/IngestMonitor.java
+++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestMonitor.java
@@ -36,7 +36,7 @@
 /**
  * Monitors disk space and memory and cancels ingest if disk space runs low.
  * <p>
- * Note: This should be a singleton and currrently is used as such, with the
+ * Note: This should be a singleton and currently is used as such, with the
  * only instance residing in the IngestManager class.
  */
 public final class IngestMonitor {
@@ -191,6 +191,7 @@ public void actionPerformed(ActionEvent e) {
             }
 
             logMemoryUsage();
+            logDiskSpaceUsage();
 
             if (!enoughDiskSpace()) {
                 /*
@@ -212,6 +213,14 @@ public void actionPerformed(ActionEvent e) {
         private void logMemoryUsage() {
             MONITOR_LOGGER.log(Level.INFO, PlatformUtil.getAllMemUsageInfo());
         }
+        
+        /**
+         * Writes current disk space usage of the drive where case dir resides to log.
+         */        
+        private void logDiskSpaceUsage() {
+            final long freeSpace = root.getFreeSpace();
+            logger.log(Level.INFO, "Available disk space on drive where case dir resides is {0} (bytes)", freeSpace);  //NON-NLS
+        }
 
         /**
          * Determines whether there is enough disk space to continue running
@@ -242,18 +251,20 @@ private boolean enoughDiskSpace() {
          * @return free space in bytes
          */
         private long getFreeSpace() throws SecurityException {
+            // always return "UNKNOWN", see note below
+            return DISK_FREE_SPACE_UNKNOWN;
+            
+            /* NOTE: use and accuracy of this code for network drives needs to be investigated and validated
             final long freeSpace = root.getFreeSpace();
             if (0 == freeSpace) {
-                /*
-                 * Check for a network drive, some network filesystems always
-                 * return zero.
-                 */
+                // Check for a network drive, some network filesystems always
+                // return zero.
                 final String monitoredPath = root.getAbsolutePath();
                 if (monitoredPath.startsWith("\\\\") || monitoredPath.startsWith("//")) {
                     return DISK_FREE_SPACE_UNKNOWN;
                 }
             }
-            return freeSpace;
+            return freeSpace;*/
         }
     }
 
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestCaseManager.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestCaseManager.java
index 1b95cd87bebf41c2186b2882038c6a0376b79cb9..0be466fe4b1a6f494db1ee54e42ced39e6cd4b71 100644
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestCaseManager.java
+++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestCaseManager.java
@@ -60,11 +60,6 @@ synchronized static AutoIngestCaseManager getInstance() {
      * auto ingest.
      */
     private AutoIngestCaseManager() {
-        /*
-         * Disable the new case action because review mode is only for looking
-         * at cases created by automated ingest.
-         */
-        CallableSystemAction.get(CaseNewAction.class).setEnabled(false);
 
         /*
          * Permanently delete the "Open Recent Cases" item in the "File" menu.
@@ -108,16 +103,5 @@ synchronized void openCase(Path caseMetadataFilePath) throws CaseActionException
          * Open the case.
          */
         Case.openAsCurrentCase(caseMetadataFilePath.toString());
-
-        /**
-         * Disable the add data source action in auto ingest examiner mode. This
-         * has to be done here because Case.open() calls Case.doCaseChange() and
-         * the latter method enables the action. Since Case.doCaseChange()
-         * enables the menus on EDT by calling SwingUtilities.invokeLater(), we
-         * have to do the same thing here to maintain the order of execution.
-         */
-        SwingUtilities.invokeLater(() -> {
-            CallableSystemAction.get(AddImageAction.class).setEnabled(false);
-        });
     }
 }
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AutoIngestUserPreferences.java b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AutoIngestUserPreferences.java
index e5548f250ecacd351bfd5c51d290f00315f3d73a..f98eb1a9091687ae8fcec42c96c99de0a7c045c7 100644
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AutoIngestUserPreferences.java
+++ b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/AutoIngestUserPreferences.java
@@ -1,7 +1,7 @@
 /*
  * Autopsy Forensic Browser
  *
- * Copyright 2015 Basis Technology Corp.
+ * Copyright 2017 Basis Technology Corp.
  * Contact: carrier <at> sleuthkit <dot> org
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -44,6 +44,7 @@ public final class AutoIngestUserPreferences {
     private static final String SLEEP_BETWEEN_CASES_TIME = "SleepBetweenCasesTime"; // NON-NLS
     private static final String SHOW_TOOLS_WARNING = "ShowToolsWarning"; // NON-NLS
     private static final String MAX_NUM_TIMES_TO_PROCESS_IMAGE = "MaxNumTimesToAttemptToProcessImage"; // NON-NLS
+    private static final int DEFAULT_MAX_TIMES_TO_PROCESS_IMAGE = 0;
     private static final String MAX_CONCURRENT_NODES_FOR_ONE_CASE = "MaxConcurrentNodesForOneCase"; // NON-NLS
     private static final String STATUS_DATABASE_LOGGING_ENABLED = "StatusDatabaseLoggingEnabled"; // NON-NLS
     private static final String LOGGING_DB_HOSTNAME_OR_IP = "LoggingHostnameOrIP"; // NON-NLS
@@ -246,13 +247,13 @@ public static void setSecondsToSleepBetweenCases(int value) {
      * is used to avoid endless attempts to process an image folder with corrupt
      * data that causes a crash.
      *
-     * @return int maximum number of attempts, default is 2.
+     * @return int maximum number of attempts, default is 0.
      */
     public static int getMaxNumTimesToProcessImage() {
         if (ModuleSettings.settingExists(UserPreferences.SETTINGS_PROPERTIES, MAX_NUM_TIMES_TO_PROCESS_IMAGE)) {
             return Integer.parseInt(ModuleSettings.getConfigSetting(UserPreferences.SETTINGS_PROPERTIES, MAX_NUM_TIMES_TO_PROCESS_IMAGE));
         }
-        return 2;
+        return DEFAULT_MAX_TIMES_TO_PROCESS_IMAGE;
     }
 
     /**
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/StartupWindow.java b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/StartupWindow.java
index a9a6a0d269d6e85d9ed0d963cac1f5f95ea52f56..5e038d1ae220d45ae2bf61a714ea9743c0d04251 100644
--- a/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/StartupWindow.java
+++ b/Experimental/src/org/sleuthkit/autopsy/experimental/configuration/StartupWindow.java
@@ -83,7 +83,6 @@ private void init() {
     public void open() {
         
         if (caseManagementPanel != null) {
-            caseManagementPanel.updateView();
             caseManagementPanel.setCursor(Cursor.getDefaultCursor());
         }
         
diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java
index 983433256714560a863adf3720c140a4f1fec68e..45205b0ca725f548849e3a565656307c17c2f712 100644
--- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java
+++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java
@@ -206,8 +206,8 @@ public QueryResults performQuery() throws NoOpenCoreException {
                         for (KeywordHit hit : keywordHits) {
                             hitsMultiMap.put(new Keyword(hit.getHit(), true, true, originalKeyword.getListName(), originalKeyword.getOriginalTerm()), hit);
                         }
-                    } catch (TskException ex) { 
-                        //
+                    } catch (TskCoreException ex) { 
+                        LOGGER.log(Level.SEVERE, "Error creating keyword hits", ex); //NON-NLS
                     }
                 }
 
@@ -228,7 +228,7 @@ public QueryResults performQuery() throws NoOpenCoreException {
         return results;
     }
 
-    private List<KeywordHit> createKeywordHits(SolrDocument solrDoc) throws TskException {
+    private List<KeywordHit> createKeywordHits(SolrDocument solrDoc) throws TskCoreException {
 
         List<KeywordHit> hits = new ArrayList<>();
         final String docId = solrDoc.getFieldValue(Server.Schema.ID.toString()).toString();
@@ -237,83 +237,93 @@ private List<KeywordHit> createKeywordHits(SolrDocument solrDoc) throws TskExcep
         final Collection<Object> content_str = solrDoc.getFieldValues(Server.Schema.CONTENT_STR.toString());
 
         final Pattern pattern = Pattern.compile(keywordString);
-        for (Object content_obj : content_str) {
-            String content = (String) content_obj;
-            Matcher hitMatcher = pattern.matcher(content);
-            int offset = 0;
-
-            while (hitMatcher.find(offset)) {
-                StringBuilder snippet = new StringBuilder();
-
-                // If the location of the hit is beyond this chunk (i.e. it
-                // exists in the overlap region), we skip the hit. It will
-                // show up again as a hit in the chunk following this one.
-                if (chunkSize != null && hitMatcher.start() >= chunkSize) {
-                    break;
-                }
+        try {
+            for (Object content_obj : content_str) {
+                String content = (String) content_obj;
+                Matcher hitMatcher = pattern.matcher(content);
+                int offset = 0;
+
+                while (hitMatcher.find(offset)) {
+                    StringBuilder snippet = new StringBuilder();
+
+                    // If the location of the hit is beyond this chunk (i.e. it
+                    // exists in the overlap region), we skip the hit. It will
+                    // show up again as a hit in the chunk following this one.
+                    if (chunkSize != null && hitMatcher.start() >= chunkSize) {
+                        break;
+                    }
 
-                String hit = hitMatcher.group();
-
-                offset = hitMatcher.end();
-
-                // We attempt to reduce false positives for phone numbers and IP address hits
-                // by querying Solr for hits delimited by a set of known boundary characters.
-                // See KeywordSearchList.PHONE_NUMBER_REGEX for an example.
-                // Because of this the hits may contain an extra character at the beginning or end that
-                // needs to be chopped off, unless the user has supplied their own wildcard suffix
-                // as part of the regex.
-                if (!queryStringContainsWildcardSuffix
-                        && (originalKeyword.getArtifactAttributeType() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER
-                        || originalKeyword.getArtifactAttributeType() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_IP_ADDRESS)) {
-                    if (originalKeyword.getArtifactAttributeType() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER) {
-                        // For phone numbers replace all non numeric characters (except "(") at the start of the hit.
-                        hit = hit.replaceAll("^[^0-9\\(]", "");
-                    } else {
-                        // Replace all non numeric characters at the start of the hit.
-                        hit = hit.replaceAll("^[^0-9]", "");
+                    String hit = hitMatcher.group();
+
+                    offset = hitMatcher.end();
+
+                    // We attempt to reduce false positives for phone numbers and IP address hits
+                    // by querying Solr for hits delimited by a set of known boundary characters.
+                    // See KeywordSearchList.PHONE_NUMBER_REGEX for an example.
+                    // Because of this the hits may contain an extra character at the beginning or end that
+                    // needs to be chopped off, unless the user has supplied their own wildcard suffix
+                    // as part of the regex.
+                    if (!queryStringContainsWildcardSuffix
+                            && (originalKeyword.getArtifactAttributeType() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER
+                            || originalKeyword.getArtifactAttributeType() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_IP_ADDRESS)) {
+                        if (originalKeyword.getArtifactAttributeType() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER) {
+                            // For phone numbers replace all non numeric characters (except "(") at the start of the hit.
+                            hit = hit.replaceAll("^[^0-9\\(]", "");
+                        } else {
+                            // Replace all non numeric characters at the start of the hit.
+                            hit = hit.replaceAll("^[^0-9]", "");
+                        }
+                        // Replace all non numeric at the end of the hit.
+                        hit = hit.replaceAll("[^0-9]$", "");
                     }
-                    // Replace all non numeric at the end of the hit.
-                    hit = hit.replaceAll("[^0-9]$", "");
-                }
 
-                if (originalKeyword.getArtifactAttributeType() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL) {
-                    // Reduce false positives by eliminating email address hits that are either
-                    // too short or are not for valid top level domains.
-                    if (hit.length() < MIN_EMAIL_ADDR_LENGTH
-                            || !DomainValidator.getInstance(true).isValidTld(hit.substring(hit.lastIndexOf('.')))) {
-                        continue;
+                    if (originalKeyword.getArtifactAttributeType() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL) {
+                        // Reduce false positives by eliminating email address hits that are either
+                        // too short or are not for valid top level domains.
+                        if (hit.length() < MIN_EMAIL_ADDR_LENGTH
+                                || !DomainValidator.getInstance(true).isValidTld(hit.substring(hit.lastIndexOf('.')))) {
+                            continue;
+                        }
                     }
-                }
 
-                /*
+                    /*
                  * If searching for credit card account numbers, do a Luhn check
                  * on the term and discard it if it does not pass.
-                 */
-                if (originalKeyword.getArtifactAttributeType() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER) {
-                    Matcher ccnMatcher = CREDIT_CARD_NUM_PATTERN.matcher(hit);
-                    if (ccnMatcher.find()) {
-                        final String ccn = CharMatcher.anyOf(" -").removeFrom(ccnMatcher.group("ccn"));
-                        if (false == TermsComponentQuery.CREDIT_CARD_NUM_LUHN_CHECK.isValid(ccn)) {
+                     */
+                    if (originalKeyword.getArtifactAttributeType() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER) {
+                        Matcher ccnMatcher = CREDIT_CARD_NUM_PATTERN.matcher(hit);
+                        if (ccnMatcher.find()) {
+                            final String ccn = CharMatcher.anyOf(" -").removeFrom(ccnMatcher.group("ccn"));
+                            if (false == TermsComponentQuery.CREDIT_CARD_NUM_LUHN_CHECK.isValid(ccn)) {
+                                continue;
+                            }
+                        } else {
                             continue;
                         }
-                    } else {
-                        continue;
                     }
-                }
 
-                /**
-                 * Get the snippet from the document if keyword search is
-                 * configured to use snippets.
-                 */
-                int maxIndex = content.length() - 1;
-                snippet.append(content.substring(Integer.max(0, hitMatcher.start() - 20), Integer.max(0, hitMatcher.start())));
-                snippet.appendCodePoint(171);
-                snippet.append(hit);
-                snippet.appendCodePoint(171);
-                snippet.append(content.substring(Integer.min(maxIndex, hitMatcher.end()), Integer.min(maxIndex, hitMatcher.end() + 20)));
-
-                hits.add(new KeywordHit(docId, snippet.toString(), hit));
+                    /**
+                     * Get the snippet from the document if keyword search is
+                     * configured to use snippets.
+                     */
+                    int maxIndex = content.length() - 1;
+                    snippet.append(content.substring(Integer.max(0, hitMatcher.start() - 20), Integer.max(0, hitMatcher.start())));
+                    snippet.appendCodePoint(171);
+                    snippet.append(hit);
+                    snippet.appendCodePoint(171);
+                    snippet.append(content.substring(Integer.min(maxIndex, hitMatcher.end()), Integer.min(maxIndex, hitMatcher.end() + 20)));
+
+                    hits.add(new KeywordHit(docId, snippet.toString(), hit));
+                }
             }
+        } catch (TskCoreException ex) {
+            throw ex;
+        } catch (Throwable error) {
+            /* NOTE: Matcher.find() is known to throw StackOverflowError in rare cases (see JIRA-2700). 
+            StackOverflowError is an error, not an exception, and therefore needs to be caught 
+            as a Throwable. When this occurs we should re-throw the error as TskCoreException so that it is 
+            logged by the calling method and move on to the next Solr document. */
+            throw new TskCoreException("Failed to create keyword hits for Solr document id " + docId + " due to " + error.getMessage());
         }
         return hits;
     }
diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java
index 0a765bc04aeb1d620ea873e43ddbda345c68f168..79174ba134aa5a6edeac6a96025d104532db161a 100644
--- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java
+++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java
@@ -30,6 +30,7 @@
 import javax.swing.JOptionPane;
 import javax.swing.SwingUtilities;
 import org.apache.commons.lang.math.NumberUtils;
+import org.apache.commons.io.FileUtils;
 import org.apache.solr.client.solrj.SolrServerException;
 import org.apache.solr.client.solrj.impl.HttpSolrServer;
 import org.openide.util.NbBundle;
@@ -61,6 +62,8 @@ public class SolrSearchService implements KeywordSearchService, AutopsyService {
     private static final String BAD_IP_ADDRESS_FORMAT = "ioexception occurred when talking to server"; //NON-NLS
     private static final String SERVER_REFUSED_CONNECTION = "server refused connection"; //NON-NLS
     private static final int IS_REACHABLE_TIMEOUT_MS = 1000;
+    private static final int LARGE_INDEX_SIZE_GB = 50;
+    private static final int GIANT_INDEX_SIZE_GB = 500;
     private static final Logger logger = Logger.getLogger(SolrSearchService.class.getName());
 
     /**
@@ -198,9 +201,8 @@ public String getServiceName() {
      * Creates/opens the Solr core/text index for a case
      *
      * @param context The case context.
-     *
      * @throws
-     * org.sleuthkit.autopsy.framework.AutopsyService.AutopsyServiceException
+     * org.sleuthkit.autopsy.appservices.AutopsyService.AutopsyServiceException
      */
     @Override
     @NbBundle.Messages({
@@ -211,6 +213,8 @@ public String getServiceName() {
         "SolrSearch.checkingForLatestIndex.msg=Looking for text index with latest Solr and schema version",
         "SolrSearch.indentifyingIndex.msg=Identifying text index to use",
         "SolrSearch.openCore.msg=Opening text index",
+        "SolrSearch.openLargeCore.msg=Opening text index. This may take several minutes.",
+        "SolrSearch.openGiantCore.msg=Opening text index. Text index for this case is very large and may take long time to load.",
         "SolrSearch.complete.msg=Text index successfully opened"})
     public void openCaseResources(CaseContext context) throws AutopsyServiceException {
         if (context.cancelRequested()) {
@@ -322,7 +326,17 @@ public void openCaseResources(CaseContext context) throws AutopsyServiceExceptio
 
         // open core
         try {
-            progress.progress(Bundle.SolrSearch_openCore_msg(), totalNumProgressUnits - 1);
+            // check text index size to gauge estimated time to open/load the index
+            long indexSizeInBytes = FileUtils.sizeOfDirectory(new File(currentVersionIndex.getIndexPath()));
+            long sizeInGb = indexSizeInBytes / 1000000000;
+            if (sizeInGb < LARGE_INDEX_SIZE_GB) {
+                progress.progress(Bundle.SolrSearch_openCore_msg(), totalNumProgressUnits - 1);
+            } else if (sizeInGb >= LARGE_INDEX_SIZE_GB && sizeInGb < GIANT_INDEX_SIZE_GB) {
+                progress.switchToIndeterminate(Bundle.SolrSearch_openLargeCore_msg());
+            } else {
+                progress.switchToIndeterminate(Bundle.SolrSearch_openGiantCore_msg());
+            }
+            
             KeywordSearch.getServer().openCoreForCase(theCase, currentVersionIndex);
         } catch (KeywordSearchModuleException ex) {
             throw new AutopsyServiceException(String.format("Failed to open or create core for %s", caseDirPath), ex);
@@ -332,11 +346,11 @@ public void openCaseResources(CaseContext context) throws AutopsyServiceExceptio
     }
 
     /**
+     * Closes the open core.
      *
      * @param context
-     *
      * @throws
-     * org.sleuthkit.autopsy.framework.AutopsyService.AutopsyServiceException
+     * org.sleuthkit.autopsy.appservices.AutopsyService.AutopsyServiceException
      */
     @Override
     public void closeCaseResources(CaseContext context) throws AutopsyServiceException {
diff --git a/NEWS.txt b/NEWS.txt
index 351a8f2d9ff8e2c65a3ac49affa210581d6109af..2f24eede9e6868cd501aa450bb758ac5f6d4858b 100644
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -1,4 +1,9 @@
 ---------------- VERSION 4.4.1  --------------
+- A new central repository feature has been added to the optional 
+CentralRepository plug-in (NetBeans module); this optional feature includes a 
+database (SQLite or PostgreSQL) and logic for correlating artifacts across 
+cases; results are displayed using an Interesting Artifacts branch of the 
+Interesting Items tree and an Other Data Sources content viewer. 
 - Case deletion is now done using a Case menu item and both single-user and 
 general (not auto ingest) multi-user cases can be deleted.
 - Results viewer (top right area of desktop application) sorts are persistent 
@@ -6,12 +11,8 @@ and can be applied to either the table viewer or the thumbnail viewer.
 - Content viewers (bottom right area of desktop application) now resize 
 correctly.
 - The View Source File in Directory context menu item now works correctly.
-- Tagged image files in the HTML report are now displayed full-size. 
-- A new central repository feature has been added to the optional 
-CentralRepository plug-in (NetBeans module; this optional feature includes a 
-database (SQLite or PostgreSQL) and logic for correlating artifacts across 
-cases; results are displayed using an Interesting Artifacts branch of the 
-Interesting Items tree and an Other Data Sources content viewer. 
+- Tagged image files in the HTML report are now displayed full-size.
+- Some general UI responsiveness issues have been addressed. 
 - Some potential deadlocks during ingest have been eliminated.
 - Assorted small enhancements and bug fixes are included.
 
diff --git a/nbproject/project.properties b/nbproject/project.properties
index 8cceb63bc8e758629c360904b19334573eecfdd9..03d618afe67b9cb72bc2b772e0c9b5dd8338ffd1 100644
--- a/nbproject/project.properties
+++ b/nbproject/project.properties
@@ -6,8 +6,8 @@ app.name=${branding.token}
 ### if left unset, version will default to today's date
 app.version=4.4.1
 ### build.type must be one of: DEVELOPMENT, RELEASE
-build.type=RELEASE
-#build.type=DEVELOPMENT
+#build.type=RELEASE
+build.type=DEVELOPMENT
 
 project.org.netbeans.progress=org-netbeans-api-progress
 project.org.sleuthkit.autopsy.centralrepository=CentralRepository