Example #1
0
    def __init__(self, **kwargs):
        super(SupportManager, self).__init__(**kwargs)

        self.bundleModel = BundleTreeModel(self)
        self.themeStylesTableModel = ThemeStylesTableModel(self)
        self.processTableModel = ExternalProcessTableModel(self)

        #STYLE PROXY
        self.themeStyleProxyModel = ThemeStyleProxyTableModel(self)
        self.themeStyleProxyModel.setSourceModel(self.themeStylesTableModel)

        #TREE PROXY
        self.bundleProxyTreeModel = BundleItemProxyTreeModel(self)
        self.bundleProxyTreeModel.setSourceModel(self.bundleModel)

        #BUNDLES
        self.bundleProxyModel = BundleListModel(self)
        self.bundleProxyModel.setSourceModel(self.bundleModel)
        
        #TEMPLATES
        self.templateProxyModel = TemplateListModel(self)
        self.templateProxyModel.setSourceModel(self.bundleModel)
        
        #PROJECTS
        self.projectProxyModel = ProjectListModel(self)
        self.projectProxyModel.setSourceModel(self.bundleModel)

        #SYNTAX
        self.syntaxProxyModel = SyntaxListModel(self)
        self.syntaxProxyModel.setSourceModel(self.bundleModel)
        
        #INTERACTIVEITEMS
        self.actionItemsProxyModel = BundleItemTypeListModel(("command", "snippet", "macro"), self)
        self.actionItemsProxyModel.setSourceModel(self.bundleModel)
        
        #PREFERENCES
        self.preferenceProxyModel = BundleItemTypeListModel(("preference", ), self)
        self.preferenceProxyModel.setSourceModel(self.bundleModel)
        
        #DRAGCOMMANDS
        self.dragcommandProxyModel = BundleItemTypeListModel(("dragcommand", ), self)
        self.dragcommandProxyModel.setSourceModel(self.bundleModel)
        
        #THEMES
        self.themeProxyModel = BundleItemTypeListModel(("theme", ), self)
        self.themeProxyModel.setSourceModel(self.bundleModel)
        
        #BUNDLEMENUGROUP
        self.bundleMenuGroup = BundleItemMenuGroup(self)
        
        # File System Watcher
        self.fileSystemWatcher = QtCore.QFileSystemWatcher()
        self.fileSystemWatcher.directoryChanged.connect(
            self.on_fileSystemWatcher_pathChanged
        )
        self.fileSystemWatcher.fileChanged.connect(
            self.on_fileSystemWatcher_pathChanged
        )
Example #2
0
class SupportManager(PrymatexComponent, SupportBaseManager, QtCore.QObject):
    # Signals for bundle
    bundleAdded = QtCore.Signal(object)
    bundleRemoved = QtCore.Signal(object)
    bundleChanged = QtCore.Signal(object)
    bundlePopulated = QtCore.Signal(object)

    # Signals for bundle items
    bundleItemAdded = QtCore.Signal(object)
    bundleItemRemoved = QtCore.Signal(object)
    bundleItemChanged = QtCore.Signal(object)
    bundleItemTriggered = QtCore.Signal(object)
    
    # Signals for themes
    themeAdded = QtCore.Signal(object)
    themeRemoved = QtCore.Signal(object)
    themeChanged = QtCore.Signal(object)
    
    # Signals for properties
    propertiesChanged = QtCore.Signal(str)
    
    # ------------- Settings
    shell_variables = ConfigurableItem(default=[], tm_name='OakShelVariables')
    
    # TODO: Mejores nombres por aca
    @ConfigurableItem(default=[], tm_name='OakBundleManagerDeletedBundles')
    def deleted(self, deleted):
        self.deletedObjects = [uuidmodule.UUID(uuid) for uuid in deleted]
        
    @ConfigurableItem(default=[], tm_name='OakBundleManagerDeletedBundles')
    def disabled(self, disabled):
        self.disabledObjects = [uuidmodule.UUID(uuid) for uuid in disabled]
    
    #http://manual.macromates.com/en/expert_preferences.html
    #When you create a new item in the bundle editor without having selected a bundle first, then the bundle with the UUID held by this defaults key is used as the target
    default_bundle_for_new_bundle_items = ConfigurableItem(default = 'B7BC3FFD-6E4B-11D9-91AF-000D93589AF6', tm_name = 'OakDefaultBundleForNewBundleItems')
    
    def __init__(self, **kwargs):
        super(SupportManager, self).__init__(**kwargs)

        self.bundleModel = BundleTreeModel(self)
        self.themeStylesTableModel = ThemeStylesTableModel(self)
        self.processTableModel = ExternalProcessTableModel(self)

        #STYLE PROXY
        self.themeStyleProxyModel = ThemeStyleProxyTableModel(self)
        self.themeStyleProxyModel.setSourceModel(self.themeStylesTableModel)

        #TREE PROXY
        self.bundleProxyTreeModel = BundleItemProxyTreeModel(self)
        self.bundleProxyTreeModel.setSourceModel(self.bundleModel)

        #BUNDLES
        self.bundleProxyModel = BundleListModel(self)
        self.bundleProxyModel.setSourceModel(self.bundleModel)
        
        #TEMPLATES
        self.templateProxyModel = TemplateListModel(self)
        self.templateProxyModel.setSourceModel(self.bundleModel)
        
        #PROJECTS
        self.projectProxyModel = ProjectListModel(self)
        self.projectProxyModel.setSourceModel(self.bundleModel)

        #SYNTAX
        self.syntaxProxyModel = SyntaxListModel(self)
        self.syntaxProxyModel.setSourceModel(self.bundleModel)
        
        #INTERACTIVEITEMS
        self.actionItemsProxyModel = BundleItemTypeListModel(("command", "snippet", "macro"), self)
        self.actionItemsProxyModel.setSourceModel(self.bundleModel)
        
        #PREFERENCES
        self.preferenceProxyModel = BundleItemTypeListModel(("preference", ), self)
        self.preferenceProxyModel.setSourceModel(self.bundleModel)
        
        #DRAGCOMMANDS
        self.dragcommandProxyModel = BundleItemTypeListModel(("dragcommand", ), self)
        self.dragcommandProxyModel.setSourceModel(self.bundleModel)
        
        #THEMES
        self.themeProxyModel = BundleItemTypeListModel(("theme", ), self)
        self.themeProxyModel.setSourceModel(self.bundleModel)
        
        #BUNDLEMENUGROUP
        self.bundleMenuGroup = BundleItemMenuGroup(self)
        
        # File System Watcher
        self.fileSystemWatcher = QtCore.QFileSystemWatcher()
        self.fileSystemWatcher.directoryChanged.connect(
            self.on_fileSystemWatcher_pathChanged
        )
        self.fileSystemWatcher.fileChanged.connect(
            self.on_fileSystemWatcher_pathChanged
        )

    @classmethod
    def contributeToSettings(cls):
        from prymatex.gui.settings.environment import VariablesSettingsWidget
        return [ VariablesSettingsWidget ]

    def setEditorAvailable(self, available):
        self.editorAvailable = available

    def appendMenuToBundleMenuGroup(self, menu, offset = None):
        self.bundleMenuGroup.appendMenu(menu, offset)

    def menuForBundle(self, bundle):
        return self.bundleMenuGroup.menuForBundle(bundle)

    # OVERRIDE: SupportManager.namespace
    def namespace(self, name):
        return self.application().namespace(name)

    # OVERRIDE: SupportManager.namespaces
    def namespaces(self):
        return self.application().namespaces()

    # OVERRIDE: SupportManager.protectedNamespace()
    def protectedNamespace(self):
        return self.application().protectedNamespace()
    
    # ------------------- Signals
    def on_fileSystemWatcher_pathChanged(self, path):
        directory = path if os.path.isdir(path) else os.path.dirname(path)
        if self.propertiesHasChanged(directory):
            self.logger().debug("Properties in %s has changed" % directory)
            remove = self.updateProperties(directory)
            self.fileSystemWatcher.removePath(remove)
            self.propertiesChanged.emit(directory)

    #---------------------------------------------------
    # Environment
    #---------------------------------------------------
    def environmentVariables(self):
        environment = SupportBaseManager.environmentVariables(self)
        #Extend wiht the user shell variables
        for var in self.shell_variables:
            if var['enabled']:
                environment[var['variable']] = var['value']
        return environment

    def loadSupport(self, message_handler):
        SupportBaseManager.loadSupport(self, message_handler)
        self.bundleProxyTreeModel.sort(0, QtCore.Qt.AscendingOrder)

    def runSystemCommand(self, **attrs):
        if attrs.get("asynchronous", False):
            return self.runQtProcessCommand(**attrs)
        else:
            return SupportBaseManager.runSystemCommand(self, **attrs)

    #Interface
    def runQtProcessCommand(self, **attrs):
        context = RunningContext(**attrs)
        
        context.process = QtCore.QProcess(self)
        if context.workingDirectory is not None:
            context.process.setWorkingDirectory(context.workingDirectory)
            
        self.processTableModel.appendProcess(context.process, description = context.description())

        environment = QtCore.QProcessEnvironment()
        for key, value in context.scriptFileEnvironment.items():
            environment.insert(key, value)

        context.process.setProcessEnvironment(environment)

        def onQProcessFinished(context):
            def _finished(exitCode, exitStatus):
                self.processTableModel.removeProcess(context.process)
                errorValue = context.process.readAllStandardError().data()
                context.errorValue = encoding.from_fs(
                    context.process.readAllStandardError().data()
                )
                context.outputValue = encoding.from_fs(
                    context.process.readAllStandardOutput().data()
                )
                context.outputType = exitCode
                context.callback(context)
            return _finished

        context.process.finished[int, QtCore.QProcess.ExitStatus].connect(
            onQProcessFinished(context)
        )
        
        def onQProcessStarted(context):
            def _started():
                if context.inputValue is not None:
                    context.process.write(encoding.to_fs(context.inputValue))
                context.process.closeWriteChannel()
            return _started
        
        context.process.started.connect(onQProcessStarted(context))
        
        context.process.start(context.scriptFilePath)
        
        return context

    #--------------- MANAGED OBJECTS OVERRIDE INTERFACE
    def setDeleted(self, uuid):
        """Marcar un managed object como eliminado"""
        if isinstance(uuid, uuidmodule.UUID):
            uuid = self.uuidtotext(uuid)
        self.deletedObjects.append(uuid)
        deleted = [uuid for uuid in self.deletedObjects]
        self.settings().setValue('deleted', deleted)

    def isDeleted(self, uuid):
        if isinstance(uuid, uuidmodule.UUID):
            uuid = self.uuidtotext(uuid)
        return uuid in self.deletedObjects

    def isEnabled(self, uuid):
        if isinstance(uuid, uuidmodule.UUID):
            uuid = self.uuidtotext(uuid)
        return uuid not in self.disabledObjects
    
    def setDisabled(self, uuid):
        if isinstance(uuid, uuidmodule.UUID):
            uuid = self.uuidtotext(uuid)
        self.disabledObjects.append(uuid)
        disabled = [uuid for uuid in self.disabledObjects]
        self.settings().setValue('disabled', disabled)
        
    def setEnabled(self, uuid):
        if isinstance(uuid, uuidmodule.UUID):
            uuid = self.uuidtotext(uuid)
        self.disabledObjects.remove(uuid)
        disabled = [uuid for uuid in self.disabledObjects]
        self.settings().setValue('disabled', disabled)
    
    #--------------- MANAGED OBJECTS NODE INTERFACE
    def getManagedObjectNode(self, uuid):
        if isinstance(uuid, uuidmodule.UUID):
            uuid = self.uuidtotext(uuid)
        if not self.isDeleted(uuid):
            return self.bundleModel.findNode(QtCore.Qt.UUIDRole, uuid)

    # -------------------- BUNDLE INTERFACE 
    def onBundleAdded(self, bundle):
        bundle_node = BundleItemTreeNode(bundle)
        icon = self.resources().get_icon("bundle-item-%s" % bundle.type())
        bundle_node.setIcon(icon)
        self.bundleModel.appendBundle(bundle_node)
        self.bundleAdded.emit(bundle_node)
    
    def modifyBundle(self, bundle):
        self.bundleChanged.emit(bundle)
    
    def removeBundle(self, bundle):
        self.bundleModel.removeBundle(bundle)
        self.bundleRemoved.emit(bundle)
    
    def getDefaultBundle(self):
        return self.getManagedObjectNode(self.defaultBundleForNewBundleItems)
    
    def onBundlePopulated(self, bundle):
        bundle_node = self.getManagedObjectNode(bundle.uuid)
        if bundle_node is not None:
            self.bundlePopulated.emit(bundle_node)
        
    # --------------------------- BUNDLEITEM INTERFACE 
    def onBundleItemAdded(self, bundle_item):
        bundle_item_node = BundleItemTreeNode(bundle_item)
        bundle_node = self.getManagedObjectNode(bundle_item.bundle.uuid)
        icon = self.resources().get_icon("bundle-item-%s" % bundle_item.type())
        bundle_item_node.setIcon(icon)
        self.bundleModel.appendBundleItem(bundle_item_node, bundle_node)
        self.bundleItemAdded.emit(bundle_item_node)

    def onBundleItemModified(self, bundle_item):
        bundle_item_node = self.getManagedObjectNode(bundle_item.uuid)
        self.bundleItemChanged.emit(bundle_item_node)
        
    def onBundleItemRemoved(self, bundle_item):
        bundle_item_node = self.getManagedObjectNode(bundle_item.uuid)
        self.bundleModel.removeBundleItem(bundle_item_node)
        self.bundleItemRemoved.emit(bundle_item_node)
    
    # ----------------- THEME INTERFACE
    def getThemePalette(self, theme, scope=None):
        settings = self.getThemeSettings(theme, scope)
        palette = self.application().palette()
        if 'foreground' in settings:
            #QPalette::Foreground	0	This value is obsolete. Use WindowText instead.
            palette.setColor(QtGui.QPalette.Foreground, rgba2color(settings['background']))
            #QPalette::WindowText	0	A general foreground color.
            palette.setColor(QtGui.QPalette.WindowText, rgba2color(settings['foreground']))
            #QPalette::Text	6	The foreground color used with Base. This is usually the same as the WindowText, in which case it must provide good contrast with Window and Base.
            palette.setColor(QtGui.QPalette.Text, rgba2color(settings['foreground']))
            #QPalette::ToolTipText	19	Used as the foreground color for QToolTip and QWhatsThis. Tool tips use the Inactive color group of QPalette, because tool tips are not active windows.
            palette.setColor(QtGui.QPalette.ToolTipText, rgba2color(settings['foreground']))
            #QPalette::ButtonText	    8	A foreground color used with the Button color.
            palette.setColor(QtGui.QPalette.ButtonText, rgba2color(settings['foreground']))
        if 'background' in settings:
            #QPalette::Background	10	This value is obsolete. Use Window instead.
            palette.setColor(QtGui.QPalette.Background, rgba2color(settings['background']))
            #QPalette::Window	10	A general background color.
            palette.setColor(QtGui.QPalette.Window, rgba2color(settings['background']))
            #QPalette::Base	9	Used mostly as the background color for text entry widgets, but can also be used for other painting - such as the background of combobox drop down lists and toolbar handles. It is usually white or another light color.
            palette.setColor(QtGui.QPalette.Base, rgba2color(settings['background']))
            #QPalette::ToolTipBase	18	Used as the background color for QToolTip and QWhatsThis. Tool tips use the Inactive color group of QPalette, because tool tips are not active windows.
            palette.setColor(QtGui.QPalette.ToolTipBase, rgba2color(settings['background']))
            #QPalette::Button	    1	The general button background color. This background can be different from Window as some styles require a different background color for buttons.
            palette.setColor(QtGui.QPalette.Button, rgba2color(settings['background']))
        if 'selection' in settings:
            #QPalette::Highlight	12	A color to indicate a selected item or the current item. By default, the highlight color is Qt::darkBlue.
            palette.setColor(QtGui.QPalette.Highlight, rgba2color(settings['selection']))
        if 'invisibles' in settings:
            #QPalette::LinkVisited	15	A text color used for already visited hyperlinks. By default, the linkvisited color is Qt::magenta.
            palette.setColor(QtGui.QPalette.LinkVisited, rgba2color(settings['invisibles']))
        if 'lineHighlight' in settings:
            #QPalette::AlternateBase	16	Used as the alternate background color in views with alternating row colors (see QAbstractItemView::setAlternatingRowColors()).
            palette.setColor(QtGui.QPalette.AlternateBase, rgba2color(settings['lineHighlight']))
        if 'caret' in settings:
            #QPalette::BrightText	7	A text color that is very different from WindowText, and contrasts well with e.g. Dark. Typically used for text that needs to be drawn where Text or WindowText would give poor contrast, such as on pressed push buttons. Note that text colors can be used for things other than just words; text colors are usually used for text, but it's quite common to use the text color roles for lines, icons, etc.
            palette.setColor(QtGui.QPalette.BrightText, rgba2color(settings['caret']))
            #QPalette::HighlightedText	13	A text color that contrasts with Highlight. By default, the highlighted text color is Qt::white.
            palette.setColor(QtGui.QPalette.HighlightedText, rgba2color(settings['caret']))
        if 'gutterBackground' in settings and settings['gutterBackground'] != DEFAULT_THEME_SETTINGS['gutterBackground']:
            #QPalette::ToolTipBase	18	Used as the background color for QToolTip and QWhatsThis. Tool tips use the Inactive color group of QPalette, because tool tips are not active windows.
            palette.setColor(QtGui.QPalette.ToolTipBase, rgba2color(settings['gutterBackground']))
        if 'gutterForeground' in settings and settings['gutterForeground'] != DEFAULT_THEME_SETTINGS['gutterForeground']:
            #QPalette::ToolTipText	19	Used as the foreground color for QToolTip and QWhatsThis. Tool tips use the Inactive color group of QPalette, because tool tips are not active windows.
            palette.setColor(QtGui.QPalette.ToolTipText, rgba2color(settings['gutterForeground']))
        #QPalette::Link	14	A text color used for unvisited hyperlinks. By default, the link color is Qt::blue.
        return palette

    @memoize(key_function=lambda m, theme, scope=None: "%s-%s" % (str(theme.uuid), str(scope)))
    def getThemeTextCharFormat(self, theme, scope=None):
        settings = self.getThemeSettings(theme, scope)
        frmt = QtGui.QTextCharFormat()
        if 'foreground' in settings:
            frmt.setForeground(rgba2color(settings['foreground']))
        if 'background' in settings:
            frmt.setBackground(rgba2color(settings['background']))
        if 'fontStyle' in settings:
            if 'bold' in settings['fontStyle']:
                frmt.setFontWeight(QtGui.QFont.Bold)
            if 'underline' in settings['fontStyle']:
                frmt.setFontUnderline(True)
            if 'italic' in settings['fontStyle']:
                frmt.setFontItalic(True)
        return frmt

    # --------------- PROPERTIES OVERRIDE INTERFACE
    def addProperties(self, properties):
        watch = [ cfg.source.exists() and cfg.source.path or cfg.source.name for cfg in properties.configs]
        self.fileSystemWatcher.addPaths(watch)
        return properties
        
    # STATICFILE OVERRIDE INTERFACE
    def onStaticFileAdded(self, static_file):
        static_file_node = BundleItemTreeNode(static_file)
        bundle_item_node = self.getManagedObjectNode(static_file.parentItem.uuid)
        self.bundleModel.appendStaticFile(static_file_node, bundle_item_node)
    
    def removeStaticFile(self, file):
        pass

    @memoize(key_function=lambda m: "all-key-sequences")
    def getAllKeySequences(self):
        return [ keyequivalent_to_keysequence(mnemonic) for mnemonic in self.getAllKeyEquivalentMnemonic() ]

    def getKeySequenceItem(self, sequence, left_scope, right_scope):
        eq = keysequence_to_keyequivalent(sequence)
        print("seq", sequence.count(), "equivalente", type(eq), eq)
        return self.getKeyEquivalentItem(keysequence_to_keyequivalent(sequence), left_scope, right_scope)
        
    # THEME STYLE INTERFACE
    def getThemeStyleNode(self, uuid):
        indexes = self.themeStylesTableModel.match(self.bundleModel.index(0, 0, QtCore.QModelIndex()),
            QtCore.Qt.UUIDRole, uuid, 1, QtCore.Qt.MatchFixedString | QtCore.Qt.MatchRecursive)
        if indexes:
            return self.themeStylesTableModel.style(indexes[0])

    def onThemeStyleAdded(self, style):
        theme_style = ThemeStyleTableRow(style)
        self.themeStylesTableModel.appendStyle(theme_style)
        return style

    def removeThemeStyle(self, style):
        self.themeStylesTableModel.removeStyle(style)

    # ACTION NODES INTERFACE
    def getAllActionItemsNodes(self):
        return self.actionItemsProxyModel.nodes()
    
    # SYNTAXES INTERFACE
    def getAllSyntaxesNodes(self):
        return self.syntaxProxyModel.nodes()

    # CURSOR SCOPE
    def cursorScope(self, cursor):
        left_scope = self.scopeFactory("")
        right_scope = self.scopeFactory("")
        leftCursor = QtGui.QTextCursor(cursor)
        rightCursor = QtGui.QTextCursor(cursor)
        leftCursor.setPosition(cursor.selectionStart())
        rightCursor.setPosition(cursor.selectionEnd())
        if cursor.hasSelection():
            # If there is one or more selections: dyn.selection.
            # TODO If there is a single zero-width selection: dyn.caret.mixed.columnar.
            # TODO If there are multiple carets and/or selections: dyn.caret.mixed.
            left_scope.push_scope("dyn.selection")
            right_scope.push_scope("dyn.selection")
        # When there is only a single caret or a single continuous selection
        # the left scope may contain: dyn.caret.begin.line or dyn.caret.begin.document
        if leftCursor.atBlockStart():
            left_scope.push_scope("dyn.caret.begin.line")
        if leftCursor.atStart():
            left_scope.push_scope("dyn.caret.begin.document")
        # Likewise the right scope may contain: dyn.caret.end.line or dyn.caret.end.document.
        if rightCursor.atBlockEnd():
            right_scope.push_scope("dyn.caret.end.line")
        if rightCursor.atEnd():
            right_scope.push_scope("dyn.caret.end.document")
        return left_scope, right_scope