     /// open multiple files at once
     func application(_ sender: NSApplication, openFiles filenames: [String]) {
         
-        let isAutomaticTabbing = (DocumentWindow.userTabbingPreference == .inFullScreen) && (filenames.count > 1)
+        assert(Thread.isMainThread)
+        
+        let documentURLs = filenames.map { URL(fileURLWithPath: $0) }
+            .filter {
+                // ask installation if the file is CotEditor theme file
+                DocumentType.theme.extensions.contains($0.pathExtension)
+                    ? !self.askThemeInstallation(fileURL: $0)
+                    : true
+            }
+        
+        guard !documentURLs.isEmpty else { return NSApp.reply(toOpenOrPrint: .success) }
+        
+        let isAutomaticTabbing = (DocumentWindow.userTabbingPreference == .inFullScreen) && (documentURLs.count > 1)
         let dispatchGroup = DispatchGroup()
         var firstWindowOpened = false
         var reply: NSApplication.DelegateReply = .success
         
-        for filename in filenames {
-            guard !self.application(sender, openFile: filename) else {
-                continue
-            }
-            
-            let url = URL(fileURLWithPath: filename)
-            
+        for url in documentURLs {
             dispatchGroup.enter()
             DocumentController.shared.openDocument(withContentsOf: url, display: true) { (document, documentWasAlreadyOpen, error) in
                 defer {

        return true
    }
    
    
    
    // MARK: Action Messages
    
    /// activate self and perform "New" menu action
    @IBAction func newDocumentActivatingApplication(_ sender: Any?) {
        
        NSApp.activate(ignoringOtherApps: true)
        NSDocumentController.shared.newDocument(sender)
    }
    
    
    /// activate self and perform "Open..." menu action
    @IBAction func openDocumentActivatingApplication(_ sender: Any?) {
        
        NSApp.activate(ignoringOtherApps: true)
        NSDocumentController.shared.openDocument(sender)
    }
    
    
    /// show standard about panel
    @IBAction func showAboutPanel(_ sender: Any?) {
        
        let creditsURL = Bundle.main.url(forResource: "Credits", withExtension: "html")!
        var html = try! String(contentsOf: creditsURL)
        
        #if !SPARKLE  // Remove Sparkle from 3rd party code list
        if let range = html.range(of: "Sparkle") {
            html.removeSubrange(html.lineRange(for: range))
        }
        #endif
        
        let attrString = NSAttributedString(html: html.data(using: .utf8)!, baseURL: creditsURL, documentAttributes: nil)!
        NSApplication.shared.orderFrontStandardAboutPanel(options: [.credits: attrString])
    }
    
    
    /// show preferences window
    @IBAction func showPreferences(_ sender: Any?) {
        
        self.preferencesWindowController.showWindow(sender)
    }
    
    
    /// show console panel
    @IBAction func showConsolePanel(_ sender: Any?) {
        
        Console.shared.panelController.showWindow(sender)
    }
    
    
    /// show acknowlegements
    @IBAction func showAcknowledgments(_ sender: Any?) {
        
        self.acknowledgmentsWindowController.showWindow(sender)
    }
    
    
    /// open OSAScript dictionary in Script Editor
    @IBAction func openAppleScriptDictionary(_ sender: Any?) {
        
        guard let scriptEditorURL = NSWorkspace.shared.urlForApplication(withBundleIdentifier: BundleIdentifier.ScriptEditor) else { return }
        
        let appURL = Bundle.main.bundleURL
        let configuration = NSWorkspace.OpenConfiguration()
        
        NSWorkspace.shared.open([appURL], withApplicationAt: scriptEditorURL, configuration: configuration)
    }
    
    
    /// open a specific page in Help contents
    @IBAction func openHelpAnchor(_ sender: AnyObject) {
        
        guard let identifier = (sender as? NSUserInterfaceItemIdentification)?.identifier else { return assertionFailure() }
        
        NSHelpManager.shared.openHelpAnchor(identifier.rawValue, inBook: Bundle.main.helpBookName)
    }
    
    
    /// open web site (coteditor.com) in default web browser
    @IBAction func openWebSite(_ sender: Any?) {
        
        NSWorkspace.shared.open(AppWebURL.website.url)
    }
    
    
    /// open bug report page in default web browser
    @IBAction func reportBug(_ sender: Any?) {
        
        NSWorkspace.shared.open(AppWebURL.issueTracker.url)
    }
    
    
    /// open new bug report window
    @IBAction func createBugReport(_ sender: Any?) {
        
        // load template file
        guard
            let url = Bundle.main.url(forResource: "ReportTemplate", withExtension: "md"),
            let template = try? String(contentsOf: url)
            else { return assertionFailure() }
        
        // fill template with user environment info
        let report = template
            .replacingOccurrences(of: "%BUNDLE_VERSION%", with: Bundle.main.bundleVersion)
            .replacingOccurrences(of: "%SHORT_VERSION%", with: Bundle.main.shortVersion)
            .replacingOccurrences(of: "%SYSTEM_VERSION%", with: ProcessInfo.processInfo.operatingSystemVersionString)
        
        // open as document
        guard let document = try? NSDocumentController.shared.openUntitledDocumentAndDisplay(false) as? Document else { return assertionFailure() }
        document.displayName = "Issue Report".localized(comment: "document title")
        document.textStorage.replaceCharacters(in: NSRange(0..<0), with: report)
        document.setSyntaxStyle(name: BundledStyleName.markdown)
        document.makeWindowControllers()
        document.showWindows()
    }
    
}

    }
    
    
    
    // MARK: Action Messages
    
    /// activate self and perform "New" menu action
    @IBAction func newDocumentActivatingApplication(_ sender: Any?) {
        
        NSApp.activate(ignoringOtherApps: true)
        NSDocumentController.shared.newDocument(sender)
    }
    
    
    /// activate self and perform "Open..." menu action
    @IBAction func openDocumentActivatingApplication(_ sender: Any?) {
        
        NSApp.activate(ignoringOtherApps: true)
        NSDocumentController.shared.openDocument(sender)
    }
    
    
    /// show standard about panel
    @IBAction func showAboutPanel(_ sender: Any?) {
        
        let creditsURL = Bundle.main.url(forResource: "Credits", withExtension: "html")!
        var html = try! String(contentsOf: creditsURL)
        
        #if !SPARKLE  // Remove Sparkle from 3rd party code list
        if let range = html.range(of: "Sparkle") {
            html.removeSubrange(html.lineRange(for: range))
        }
        #endif
        
        let attrString = NSAttributedString(html: html.data(using: .utf8)!, baseURL: creditsURL, documentAttributes: nil)!
        NSApplication.shared.orderFrontStandardAboutPanel(options: [.credits: attrString])
    }
    
    
    /// show preferences window
    @IBAction func showPreferences(_ sender: Any?) {
        
        self.preferencesWindowController.showWindow(sender)
    }
    
    
    /// show console panel
    @IBAction func showConsolePanel(_ sender: Any?) {
        
        Console.shared.panelController.showWindow(sender)
    }
    
    
    /// show acknowlegements
    @IBAction func showAcknowledgments(_ sender: Any?) {
        
        self.acknowledgmentsWindowController.showWindow(sender)
    }
    
    
    /// open OSAScript dictionary in Script Editor
    @IBAction func openAppleScriptDictionary(_ sender: Any?) {
        
        guard let scriptEditorURL = NSWorkspace.shared.urlForApplication(withBundleIdentifier: BundleIdentifier.ScriptEditor) else { return }
        
        let appURL = Bundle.main.bundleURL
        let configuration = NSWorkspace.OpenConfiguration()
        
        NSWorkspace.shared.open([appURL], withApplicationAt: scriptEditorURL, configuration: configuration)
    }
    
    
    /// open a specific page in Help contents
    @IBAction func openHelpAnchor(_ sender: AnyObject) {
        
        guard let identifier = (sender as? NSUserInterfaceItemIdentification)?.identifier else { return assertionFailure() }
        
        NSHelpManager.shared.openHelpAnchor(identifier.rawValue, inBook: Bundle.main.helpBookName)
    }
    
    
    /// open web site (coteditor.com) in default web browser
    @IBAction func openWebSite(_ sender: Any?) {
        
        NSWorkspace.shared.open(AppWebURL.website.url)
    }
    
    
    /// open bug report page in default web browser
    @IBAction func reportBug(_ sender: Any?) {
        
        NSWorkspace.shared.open(AppWebURL.issueTracker.url)
    }
    
    
    /// open new bug report window
    @IBAction func createBugReport(_ sender: Any?) {
        
        // load template file
        guard
            let url = Bundle.main.url(forResource: "ReportTemplate", withExtension: "md"),
            let template = try? String(contentsOf: url)
            else { return assertionFailure() }
        
        // fill template with user environment info
        let report = template
            .replacingOccurrences(of: "%BUNDLE_VERSION%", with: Bundle.main.bundleVersion)
            .replacingOccurrences(of: "%SHORT_VERSION%", with: Bundle.main.shortVersion)
            .replacingOccurrences(of: "%SYSTEM_VERSION%", with: ProcessInfo.processInfo.operatingSystemVersionString)
        
        // open as document
        guard let document = try? NSDocumentController.shared.openUntitledDocumentAndDisplay(false) as? Document else { return assertionFailure() }
        document.displayName = "Issue Report".localized(comment: "document title")
        document.textStorage.replaceCharacters(in: NSRange(0..<0), with: report)
        document.setSyntaxStyle(name: BundledStyleName.markdown)
        document.makeWindowControllers()
        document.showWindows()
    }
    
    
    
    // MARK: Private Methods
    
    /// Ask user whether install the file as a CotEditor theme, or process as a text file.
    ///
    /// - Parameter url: The file URL to a theme file.
    /// - Returns: Whether the given file was handled as a theme file.
    private func askThemeInstallation(fileURL url: URL) -> Bool {
        
        assert(DocumentType.theme.extensions.contains(url.pathExtension))
        
        // ask whether theme file should be opened as a text file
        let alert = NSAlert()
