Beispiel #1
0
class ProjectsDock(PrymatexDock, FileSystemTasks, Ui_ProjectsDock, QtGui.QDockWidget):
    SEQUENCE = resources.get_sequence("Docks", "ProjectsDock", "Alt+X")
    ICON = resources.get_icon("project-development")
    PREFERED_AREA = QtCore.Qt.LeftDockWidgetArea

    #=======================================================================
    # Settings
    #=======================================================================
    SETTINGS_GROUP = 'Projects'
    @ConfigurableItem(default = '')
    def customFilters(self, filters):
        filters = [p.strip() for p in filters.split(",")]
        self.selectableProjectFileModel.setBaseFilters(filters)
        self.projectTreeProxyModel.setFilterRegExp(",".join(filters))
    
    @ConfigurableHook("CodeEditor.defaultTheme")
    def defaultTheme(self, themeUUID):
        theme = self.application.supportManager.getBundleItem(themeUUID)
        self.treeViewProjects.setPalette(theme.palette())
        self.treeViewProjects.viewport().setPalette(theme.palette())
    
    def __init__(self, **kwargs):
        super(ProjectsDock, self).__init__(**kwargs)
        self.setupUi(self)
        self.projectManager = self.application.projectManager
        self.fileManager = self.application.fileManager
        self.projectTreeProxyModel = self.projectManager.projectTreeProxyModel
    
        self.setupTreeViewProjects()
        
        # Model for selector dialog
        self.selectableProjectFileModel = SelectableProjectFileProxyModel(self.projectManager, self.fileManager, parent = self)
        
        # Bundle Filter Dialog
        self.bundleFilterDialog = BundleFilterDialog(self)
        self.bundleFilterDialog.setWindowTitle("Select Related Bundles")
        self.bundleFilterDialog.setModel(self.projectManager.projectMenuProxyModel)
        self.bundleFilterDialog.setHelpVisible(False)
        
    def initialize(self, **kwargs):
        super(ProjectsDock, self).initialize(**kwargs)
        self.projectDialog = self.mainWindow().findChild(QtGui.QDialog, "ProjectDialog")
        self.templateDialog = self.mainWindow().findChild(QtGui.QDialog, "TemplateDialog")
        self.bundleEditorDialog = self.mainWindow().findChild(QtGui.QDialog, "BundleEditorDialog")
        self.propertiesDialog = self.mainWindow().findChild(QtGui.QDialog, "PropertiesDialog")
        self.setupPropertiesWidgets()

    @classmethod
    def contributeToSettings(cls):
        return [ ]
    
    # Contributes to Main Menu
    @classmethod
    def contributeToMainMenu(cls):
        navigation = [
                {'text': 'Go to project file',
                 'triggered': cls.on_actionGoToProjectFile_triggered,
                 'sequence': resources.get_sequence("Projects", "GoToProjectFiles", 'Meta+Ctrl+Shift+F'),
                 },
                {'text': 'Go to project symbol',
                 'triggered': cls.on_actionGoToProjectFile_triggered,
                 'sequence': resources.get_sequence("Projects", "GoToProjectFiles", 'Meta+Ctrl+Shift+F'),
                 }
            ]
        return { "navigation": navigation}
    
    # ------------------ Menu Actions
    def on_actionGoToProjectFile_triggered(self):
        filePath = self.mainWindow().selectorDialog.select(self.selectableProjectFileModel, title=_("Select Project File"))
        if filePath is not None:
            index = self.projectTreeProxyModel.indexForPath(filePath)
            self.treeViewProjects.setCurrentIndex(index)
            self.application.openFile(filePath)

    def addFileSystemNodeFormater(self, formater):
        self.projectTreeProxyModel.addNodeFormater(formater)
    
    def componentState(self):
        expandedIndexes = [index for index in self.projectTreeProxyModel.persistentIndexList() if self.treeViewProjects.isExpanded(index)]
        expandedPaths = [self.projectTreeProxyModel.node(index).path() for index in expandedIndexes]
        return { "expanded": expandedPaths }

    def setComponentState(self, state):
        #Expanded Nodes
        list(map(lambda index: index.isValid() and self.treeViewProjects.setExpanded(index, True), 
            [self.projectTreeProxyModel.indexForPath(path) for path in state["expanded"]]))

    def environmentVariables(self):
        environment = PrymatexDock.environmentVariables(self)
        indexes = self.treeViewProjects.selectedIndexes()
        if indexes:
            node = self.currentNode()
            paths = [self.application.fileManager.normcase(node.path()) for node in [ self.projectTreeProxyModel.node(index) for index in indexes ]]
            environment.update({
                'TM_SELECTED_FILE': node.path(), 
                'TM_SELECTED_FILES': " ".join(["'%s'" % path for path in paths ])
            })
        return environment
        
    def keyPressEvent(self, event):
        if not self.runKeyHelper(event):
            return QtGui.QDockWidget.keyPressEvent(self, event)

    def setupPropertiesWidgets(self):
        from prymatex.gui.properties.project import ProjectPropertiesWidget
        from prymatex.gui.properties.environment import EnvironmentPropertiesWidget
        from prymatex.gui.properties.resource import ResoucePropertiesWidget
        
        for propertyClass in [ProjectPropertiesWidget, EnvironmentPropertiesWidget, ResoucePropertiesWidget]:
            self.application.extendComponent(propertyClass)
            self.application.projectManager.registerPropertyWidget(propertyClass(parent = self.propertiesDialog))

    def setupTreeViewProjects(self):
        self.treeViewProjects.setModel(self.projectTreeProxyModel)
        
        self.treeViewProjects.setHeaderHidden(True)
        self.treeViewProjects.setUniformRowHeights(False)
        
        #Setup Context Menu
        optionsMenu = { 
            "text": "Project Options",
            "items": [
                {   "text": "Order",
                    "items": [
                        (self.actionOrderByName, self.actionOrderBySize, self.actionOrderByDate, self.actionOrderByType),
                        "-", self.actionOrderDescending, self.actionOrderFoldersFirst
                    ]
                }
            ]
        }

        self.actionOrderFoldersFirst.setChecked(True)
        self.actionOrderByName.trigger()
        
        self.projectOptionsMenu = create_menu(self, optionsMenu)
        self.pushButtonOptions.setMenu(self.projectOptionsMenu)

        #Connect context menu
        self.treeViewProjects.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.treeViewProjects.customContextMenuRequested.connect(self.showProjectTreeViewContextMenu)
        
        #=======================================================================
        # Drag and Drop (see the proxy model)
        #=======================================================================
        self.treeViewProjects.setDragEnabled(True)
        self.treeViewProjects.setAcceptDrops(True)
        self.treeViewProjects.setDefaultDropAction(QtCore.Qt.MoveAction)
        self.treeViewProjects.setDropIndicatorShown(True)

        self.treeViewProjects.setAlternatingRowColors(True)
        self.treeViewProjects.setAnimated(True)
        
        # Selection Mode
        self.treeViewProjects.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
    
    #================================================
    # Build Menus
    #================================================
    def buildContextMenu(self, index):    
        contextMenu = { 
            "text": "Context",
            "items": [
                {   "text": "New",
                    "items": [
                        self.actionNewFolder, 
                        self.actionNewFile, 
                        "-", 
                        self.actionNewFromTemplate, 
                        self.actionNewProject,
                    ]
                },
                "--refresh",
                self.actionRefresh,
                "--bundles"
            ]
        }
        if index.isValid():
            node = self.projectTreeProxyModel.node(index)
            self.extendFileSystemItemMenu(contextMenu, node)
            self.extendAddonsItemMenu(contextMenu, node)
            self.extendProjectBundleItemMenu(contextMenu, node)
        # contextMenu = create_menu(contextMenu, self, separatorName = True)
        contextMenu = create_menu(self, contextMenu, separatorName = True)
        
        contextMenu.aboutToShow.connect(self.on_contextMenu_aboutToShow)
        contextMenu.aboutToHide.connect(self.on_contextMenu_aboutToHide)
        contextMenu.triggered.connect(self.on_contextMenu_triggered)
        return contextMenu
        
    def on_contextMenu_aboutToShow(self):      
        # TODO Quiza un metodo que haga esto en el manager  
        self.application.supportManager.setEditorAvailable(False)
        self.application.supportManager.blockSignals(True)
                
    def on_contextMenu_aboutToHide(self):
        self.application.supportManager.setEditorAvailable(True)
        def restore_supportManager_signals():
            self.application.supportManager.blockSignals(False)
        # TODO No estoy muy contento con esto pero que le vamos a hacer
        QtCore.QTimer.singleShot(0, restore_supportManager_signals)

    def on_contextMenu_triggered(self, action):
        if hasattr(action, "bundleTreeNode"):
            node = self.currentNode()
            env =   {   
                'TM_FILEPATH': node.path(),
                'TM_FILENAME': node.nodeName(),
                'TM_DIRECTORY': node.nodeParent().path() 
            } if node.isfile else {
                'TM_DIRECTORY': node.path()
            }
            
            env.update(node.project().environmentVariables())
            self.mainWindow().insertBundleItem(action.bundleTreeNode, environment = env)
    
    def extendFileSystemItemMenu(self, menu, node):
        extend_menu_section(menu, ["--open", self.actionOpenSystemEditor, "--handlepaths", self.actionDelete, self.actionRename])
        #extend_menu_section(menu, ["--interact", self.actionSetInTerminal ], section = -1)
        # TODO Quiza sea mejor ponerle un type y controlar contra una cadena
        if isinstance(node, ProjectTreeNode):
            extend_menu_section(menu, [self.actionPaste, self.actionRemove], section = "handlepaths", position = 0)
            #extend_menu_section(menu, [self.actionBashInit], section = "interact")
            extend_menu_section(menu, [self.actionProjectBundles, self.actionSelectRelatedBundles], section = "bundles")
        else:
            extend_menu_section(menu, [self.actionCut, self.actionCopy, self.actionPaste], section = "handlepaths", position = 0)
        if node.isfile:
            extend_menu_section(menu, self.actionOpen, section = "open", position = 0)
        if node.isdir or isinstance(node, ProjectTreeNode):
            extend_menu_section(menu, [self.actionGoDown], section = "refresh")
            
        #El final
        extend_menu_section(menu, ["--properties", self.actionProperties], section = -1)

    def extendAddonsItemMenu(self, menu, node):
        #Menu de los addons
        addonMenues = [ "-" ]
        for component in self.components():
            addonMenues.extend(component.contributeToContextMenu(node))
        if len(addonMenues) > 1:
            extend_menu_section(menu, addonMenues, section = 'properties')
        
    def extendProjectBundleItemMenu(self, menu, node):
        #Menu de los bundles relacionados al proyecto
        #Try get all bundles for project bundle definition
        bundles = [self.application.supportManager.getManagedObject(uuid) for uuid in node.project().bundleMenu or []]
        #Filter None bundles
        bundles = [bundle for bundle in bundles if bundle is not None]
        #Sort by name
        bundles = sorted(bundles, key=lambda bundle: bundle.name)
        if bundles:
            bundleMenues = [self.application.supportManager.menuForBundle(bundle) for bundle in bundles]
            extend_menu_section(menu, bundleMenues, section = "bundles", position = 0)

    #================================================
    # Tree View Project
    #================================================
    def showProjectTreeViewContextMenu(self, point):
        index = self.treeViewProjects.indexAt(point)
        if not index.isValid():
            index = self.treeViewProjects.currentIndex()
        self.buildContextMenu(index).popup(self.treeViewProjects.mapToGlobal(point))
    
    def on_treeViewProjects_doubleClicked(self, index):
        self.on_actionOpen_triggered()

    def currentPath(self):
        return self.projectTreeProxyModel.filePath(self.treeViewProjects.currentIndex())

    def currentNode(self):
        return self.projectTreeProxyModel.node(self.treeViewProjects.currentIndex())
        
    def currentDirectory(self):
        return self.application.fileManager.directory(self.currentPath())
    
    #================================================
    # Actions Create, Delete, Rename objects
    #================================================      
    @QtCore.Slot()
    def on_actionNewFile_triggered(self):
        currentDirectory = self.currentDirectory()
        filePath = self.createFile(currentDirectory)
        if filePath is not None:
            self.application.openFile(filePath)
            #TODO: si esta en auto update ver como hacer los refresh
            self.projectTreeProxyModel.refreshPath(currentDirectory)
    
    @QtCore.Slot()
    def on_actionNewFromTemplate_triggered(self):
        currentDirectory = self.currentDirectory()
        filePath = self.templateDialog.createFile(fileDirectory = self.currentDirectory())
        if filePath is not None:
            self.application.openFile(filePath)
            #TODO: si esta en auto update ver como hacer los refresh
            self.projectTreeProxyModel.refreshPath(currentDirectory)
    
    @QtCore.Slot()
    def on_actionNewFolder_triggered(self):
        currentDirectory = self.currentDirectory()
        dirPath = self.createDirectory(currentDirectory)
        if dirPath is not None:
            #TODO: si esta en auto update ver como hacer los refresh
            self.projectTreeProxyModel.refreshPath(currentDirectory)

    @QtCore.Slot()
    def on_actionNewProject_triggered(self):
        self.projectDialog.createProject()

    @QtCore.Slot()
    def on_actionDelete_triggered(self):
        indexes = self.treeViewProjects.selectedIndexes()
        projects = []
        paths = []

        for index in indexes:
            node = self.projectTreeProxyModel.node(index)
            if node.isproject:
                #Es proyecto
                question = CheckableMessageBox.questionFactory(self,
                    "Delete project",
                    "Are you sure you want to delete project '%s' from the workspace?" % node.name,
                    "Delete project contents on disk (cannot be undone)",
                    QtGui.QMessageBox.Yes | QtGui.QMessageBox.No | QtGui.QMessageBox.Cancel,
                    QtGui.QMessageBox.Yes
                )
                question.setDetailedText("Project location:\n%s" % node.path())
                ret = question.exec_()
                if ret == QtGui.QMessageBox.Yes:
                    projects.append((node, question.isChecked()))
                elif ret == QtGui.QMessageBox.Cancel:
                    return
            else:
                paths.append(node)
        
        # TODO Que pasa con los proyectos y si un path es subpath de otro?
        for node in paths:
            self.deletePath(node.path())
        for index in indexes:
            self.projectTreeProxyModel.refresh(index.parent())

    @QtCore.Slot()
    def on_actionRemove_triggered(self):
        node = self.currentNode()
        if node.isproject:
            ret = QtGui.QMessageBox.question(self,
                "Remove project",
                "Are you sure you want to remove project '%s' from the workspace?" % node.name,
                QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel,
                QtGui.QMessageBox.Ok
            )
            if ret == QtGui.QMessageBox.Ok:
                self.application.projectManager.removeProject(node)

    @QtCore.Slot()
    def on_actionRename_triggered(self):
        self.renamePath(self.currentPath())
        self.projectTreeProxyModel.refresh(self.treeViewProjects.currentIndex())

    @QtCore.Slot()
    def on_actionCloseProject_triggered(self):
        treeNode = self.currentNode()
        if treeNode.isproject:
            self.application.projectManager.closeProject(treeNode)
    
    @QtCore.Slot()
    def on_actionOpenProject_triggered(self):
        treeNode = self.currentNode()
        if treeNode.isproject:
            self.application.projectManager.openProject(treeNode)
    
    @QtCore.Slot()
    def on_actionProperties_triggered(self):
        self.propertiesDialog.setModel(self.application.projectManager.propertiesProxyModel)
        self.propertiesDialog.exec_(self.currentNode())

    @QtCore.Slot()
    def on_actionRefresh_triggered(self):
        indexes = self.treeViewProjects.selectedIndexes()
        for index in indexes:
            self.projectTreeProxyModel.refresh(index)

    @QtCore.Slot()
    def on_actionOpen_triggered(self):
        node = self.currentNode()
        if node.isfile:
            self.application.openFile(node.path())
        
    @QtCore.Slot()
    def on_actionOpenSystemEditor_triggered(self):
        QtGui.QDesktopServices.openUrl(QtCore.QUrl("file://%s" % self.currentPath(), QtCore.QUrl.TolerantMode))
    
    @QtCore.Slot()
    def on_pushButtonCollapseAll_pressed(self):
        self.treeViewProjects.collapseAll()

    @QtCore.Slot(bool)
    def on_pushButtonSync_toggled(self, checked):
        if checked:
            #Conectar señal
            self.mainWindow().currentEditorChanged.connect(self.on_mainWindow_currentEditorChanged)
            self.on_mainWindow_currentEditorChanged(self.mainWindow().currentEditor())
        else:
            #Desconectar señal
            self.mainWindow().currentEditorChanged.disconnect(self.on_mainWindow_currentEditorChanged)
    
    def on_mainWindow_currentEditorChanged(self, editor):
        if editor is not None and not editor.isNew():
            index = self.projectTreeProxyModel.indexForPath(editor.filePath())
            self.treeViewProjects.setCurrentIndex(index)

    @QtCore.Slot()
    def on_actionProjectBundles_triggered(self):
        self.bundleEditorDialog.execEditor(namespaceFilter = self.currentNode().namespaceName)
    
    @QtCore.Slot()
    def on_actionSelectRelatedBundles_triggered(self):
        project = self.currentNode()
        self.projectManager.projectMenuProxyModel.setCurrentProject(project)
        self.bundleFilterDialog.exec_()
        
    @QtCore.Slot()
    def on_actionCopy_triggered(self):
        mimeData = self.projectTreeProxyModel.mimeData( self.treeViewProjects.selectedIndexes() )
        self.application.clipboard().setMimeData(mimeData)
        
    @QtCore.Slot()
    def on_actionCut_triggered(self):
        mimeData = self.projectTreeProxyModel.mimeData( self.treeViewProjects.selectedIndexes() )
        self.application.clipboard().setMimeData(mimeData)
        
    @QtCore.Slot()
    def on_actionPaste_triggered(self):
        parentPath = self.currentPath()
        mimeData = self.application.clipboard().mimeData()
        if mimeData.hasUrls() and os.path.isdir(parentPath):
            for url in mimeData.urls():
                srcPath = url.toLocalFile()
                basename = self.application.fileManager.basename(srcPath)
                dstPath = os.path.join(parentPath, basename)
                while self.application.fileManager.exists(dstPath):
                    basename, ret = ReplaceRenameInputDialog.getText(self, _("Already exists"), 
                        _("Destiny already exists\nReplace or or replace?"), text = basename, )
                    if ret == ReplaceRenameInputDialog.Cancel: return
                    if ret == ReplaceRenameInputDialog.Replace: break
                    dstPath = os.path.join(parentPath, basename)
                if os.path.isdir(srcPath):
                    self.application.fileManager.copytree(srcPath, dstPath)
                else:
                    self.application.fileManager.copy(srcPath, dstPath)
            self.projectTreeProxyModel.refresh(self.treeViewProjects.currentIndex())

    @QtCore.Slot()
    def on_actionGoDown_triggered(self):
        directory = self.currentNode()
        index = self.projectTreeProxyModel.indexForPath(directory.path())
        self.treeViewProjects.setRootIndex(index)
    
    #================================================
    # Navigation
    #================================================
    @QtCore.Slot()
    def on_pushButtonGoUp_pressed(self):
        index = self.treeViewProjects.rootIndex()
        parentIndex = self.projectTreeProxyModel.parent(index)
        self.treeViewProjects.setRootIndex(parentIndex)
    
    #================================================
    # Custom filters
    #================================================
    @QtCore.Slot()
    def on_pushButtonCustomFilters_pressed(self):
        filters, accepted = QtGui.QInputDialog.getText(self, _("Custom Filter"), 
                                _("Enter the filters (separated by comma)\nOnly * and ? may be used for custom matching"), 
                                text = self.customFilters)
        if accepted:
            #Save and set filters
            self._settings.setValue('customFilters', filters)
            self.projectTreeProxyModel.setFilterRegExp(filters)
            
    #================================================
    # Sort and order Actions
    #================================================      
    @QtCore.Slot()
    def on_actionOrderByName_triggered(self):
        self.projectTreeProxyModel.sortBy("name", self.actionOrderFoldersFirst.isChecked(), self.actionOrderDescending.isChecked())
    
    @QtCore.Slot()
    def on_actionOrderBySize_triggered(self):
        self.projectTreeProxyModel.sortBy("size", self.actionOrderFoldersFirst.isChecked(), self.actionOrderDescending.isChecked())
    
    @QtCore.Slot()
    def on_actionOrderByDate_triggered(self):
        self.projectTreeProxyModel.sortBy("date", self.actionOrderFoldersFirst.isChecked(), self.actionOrderDescending.isChecked())
    
    @QtCore.Slot()
    def on_actionOrderByType_triggered(self):
        self.projectTreeProxyModel.sortBy("type", self.actionOrderFoldersFirst.isChecked(), self.actionOrderDescending.isChecked())
    
    @QtCore.Slot()
    def on_actionOrderDescending_triggered(self):
        self.projectTreeProxyModel.sortBy(self.projectTreeProxyModel.orderBy, self.actionOrderFoldersFirst.isChecked(), self.actionOrderDescending.isChecked())
    
    @QtCore.Slot()
    def on_actionOrderFoldersFirst_triggered(self):
        self.projectTreeProxyModel.sortBy(self.projectTreeProxyModel.orderBy, self.actionOrderFoldersFirst.isChecked(), self.actionOrderDescending.isChecked())

    #================================================
    # Helper actions
    #================================================
    def refresh(self):
        self.on_actionRefresh_triggered()
        
    def copy(self):
        self.on_actionCopy_triggered()
        
    def paste(self):
        self.on_actionPaste_triggered()
        
    def cut(self):
        self.on_actionCut_triggereda()

    def delete(self):
        self.on_actionDelete_triggered()
        
    def rename(self):
        self.on_actionRefresh_triggered()
Beispiel #2
0
class BundleEditorDialog(PrymatexDialog, Ui_BundleEditorDialog, QtWidgets.QDialog):
    BASE_EDITOR = -1 #El ultimo es el editor base, no tiene nada
    
    def __init__(self, **kwargs):
        super(BundleEditorDialog, self).__init__(**kwargs)
        self.setupUi(self)
        self.namespace = None
        self.namespaces = []
        
        self.manager = self.application().supportManager
        self.proxyTreeModel = self.manager.bundleProxyTreeModel
        
        # Connect signals
        self.manager.bundleChanged.connect(self.on_manager_itemChanged)
        self.manager.bundleItemChanged.connect(self.on_manager_itemChanged)
        self.finished.connect(self.on_bundleEditor_finished)
        
        #Cargar los widgets editores
        self.configEditorWidgets()
        
        #Configurar filter, tree, toolbar y activaciones
        self.configSelectTop()
        self.configTreeView()
        self.configToolbar()
        self.configActivation()

    # --------------- signal handlers
    def on_manager_itemChanged(self, item):
        editor = self.currentEditor()
        if editor.bundleItem is not None and editor.bundleItem == item and editor.getName() != item.name:
            editor.setName(item.name)
            self.labelTitle.setText(editor.title())
            self.lineEditName.setText(editor.getName())

    def on_bundleEditor_finished(self, code):
        self.saveChanges()

    # ---------------- custom execs
    def exec_(self):
        #Limiar el editor
        self.setCurrentEditor(self.editors[-1])
        #Quitar seleccion
        firstIndex = self.proxyTreeModel.index(0, 0)
        self.treeView.setSelection(self.treeView.visualRect(firstIndex), QtCore.QItemSelectionModel.Clear)
        
        return super(BundleEditorDialog, self).exec_()

    def execEditor(self, types=None, namespaces=None, title="Bundle Editor"):
        # Title
        self.setWindowTitle(title)
        
        # Set namespace and filters
        self.namespace = namespaces and namespaces[0] or config.USR_NS_NAME
        self.proxyTreeModel.setFilterNamespace(namespaces)
        self.proxyTreeModel.setFilterBundleItemTypes(types)
        index = self.comboBoxItemFilter.findData(types)
        self.comboBoxItemFilter.setCurrentIndex(index)
        
        # Go!
        self.exec_()

    def execCommand(self):
        return self.execEditor(("command",))

    def execLanguage(self):
        return self.execEditor(("syntax",))

    def execSnippet(self):
        return self.execEditor(("snippet",))

    def configEditorWidgets(self):
        self.stackedWidget = QtWidgets.QStackedWidget()
        self.stackedWidget.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.stackedWidget.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.editorsLayout.insertWidget(1, self.stackedWidget)
        self.indexes = {}
        self.editors = [ widgets.SnippetEditorWidget(self),
                         widgets.CommandEditorWidget(self),
                         widgets.DragCommandEditorWidget(self),
                         widgets.BundleEditorWidget(self),
                         widgets.StaticFileEditorWidget(self),
                         widgets.TemplateEditorWidget(self),
                         widgets.PreferenceEditorWidget(self),
                         widgets.LanguageEditorWidget(self),
                         widgets.MacroEditorWidget(self),
                         widgets.ProjectEditorWidget(self),
                         widgets.NoneEditorWidget(self) ]
        for editor in self.editors:
            self.indexes[editor.type()] = self.stackedWidget.addWidget(editor)

    # ----------------- Toolbar create and delete bundle items
    def getBundleForIndex(self, index):
        if not index.isValid():
            return self.manager.getDefaultBundle()
        bundle = self.proxyTreeModel.node(index)
        while bundle.type() != 'bundle':
            bundle = bundle.nodeParent()
        return bundle

    def createBundleItem(self, itemType):
        index = self.treeView.currentIndex()
        bundle = self.getBundleForIndex(index)
        bundleItemNode = self.manager.createBundleItem(itemType, bundle, self.namespace)
        sIndex = self.manager.bundleTreeModel.createIndex(bundleItemNode.row(), 0, bundleItemNode)
        index = self.proxyTreeModel.mapFromSource(sIndex)
        self.treeView.setCurrentIndex(index)
        self.editTreeItem(bundleItemNode)
        self.treeView.edit(index)
        
    @QtCore.Slot()
    def on_actionCommand_triggered(self):
        self.createBundleItem("command")

    @QtCore.Slot()        
    def on_actionDragCommand_triggered(self):
        self.createBundleItem("dragcommand")

    @QtCore.Slot()        
    def on_actionLanguage_triggered(self):
        self.createBundleItem("syntax")

    @QtCore.Slot()        
    def on_actionSnippet_triggered(self):
        self.createBundleItem("snippet")

    @QtCore.Slot()        
    def on_actionTemplate_triggered(self):
        self.createBundleItem("template")

    @QtCore.Slot()        
    def on_actionProject_triggered(self):
        self.createBundleItem("project")

    @QtCore.Slot()        
    def on_actionStaticFile_triggered(self):
        index = self.treeView.currentIndex()
        if index.isValid():
            template = self.proxyTreeModel.node(index)
            if template.type() == 'staticfile':
                template = template.nodeParent()
        self.manager.createStaticFile(template, self.namespace)

    @QtCore.Slot()
    def on_actionPreferences_triggered(self):
        self.createBundleItem("preference")

    @QtCore.Slot()
    def on_actionBundle_triggered(self):
        bundleNode = self.manager.createBundle(self.namespace)
        sIndex = self.manager.bundleTreeModel.createIndex(bundleNode.row(), 0, bundleNode)
        index = self.proxyTreeModel.mapFromSource(sIndex)
        self.treeView.setCurrentIndex(index)
        self.editTreeItem(bundleNode)
        self.treeView.edit(index)

    @QtCore.Slot()
    def on_pushButtonRemove_pressed(self):
        index = self.treeView.currentIndex()
        if index.isValid():
            item = self.proxyTreeModel.node(index)
            if item.type() == 'bundle':
                self.manager.deleteBundle(item)
            elif item.type() == 'staticfile':
                self.manager.deleteStaticFile(item)
            else:
                self.manager.deleteBundleItem(item)

    @QtCore.Slot()
    def on_pushButtonFilter_pressed(self):
        self.bundleFilterDialog.show()

    def configToolbar(self):
        self.toolbarMenu = QtWidgets.QMenu("Menu", self)
        action = QtWidgets.QAction("New Command", self)
        action.triggered.connect(self.on_actionCommand_triggered)
        self.toolbarMenu.addAction(action)
        action = QtWidgets.QAction("New Drag Command", self)
        action.triggered.connect(self.on_actionDragCommand_triggered)
        self.toolbarMenu.addAction(action)
        action = QtWidgets.QAction("New Language", self)
        action.triggered.connect(self.on_actionLanguage_triggered)
        self.toolbarMenu.addAction(action)
        action = QtWidgets.QAction("New Snippet", self)
        action.triggered.connect(self.on_actionSnippet_triggered)
        self.toolbarMenu.addAction(action)
        action = QtWidgets.QAction("New Template", self)
        action.triggered.connect(self.on_actionTemplate_triggered)
        self.toolbarMenu.addAction(action)
        action = QtWidgets.QAction("New Project", self)
        action.triggered.connect(self.on_actionProject_triggered)
        self.toolbarMenu.addAction(action)
        self.staticFileAction = QtWidgets.QAction("New Static File", self)
        self.staticFileAction.triggered.connect(self.on_actionStaticFile_triggered)
        self.toolbarMenu.addAction(self.staticFileAction)
        action = QtWidgets.QAction("New Preferences", self)
        action.triggered.connect(self.on_actionPreferences_triggered)
        self.toolbarMenu.addAction(action)
        self.toolbarMenu.addSeparator()
        action = QtWidgets.QAction("New Bundle", self)
        action.triggered.connect(self.on_actionBundle_triggered)
        self.toolbarMenu.addAction(action)
        
        def conditionalEnabledStaticFile():
            node = self.proxyTreeModel.node(self.treeView.currentIndex())
            self.staticFileAction.setEnabled(not node.isRootNode() and (node.type() in ("template", "staticfile", "project")))
        self.toolbarMenu.aboutToShow.connect(conditionalEnabledStaticFile)
        
        self.pushButtonAdd.setMenu(self.toolbarMenu)
        
        #Bundle global filter dialog
        self.bundleFilterDialog = BundleFilterDialog(self)
        self.bundleFilterDialog.setModel(self.manager.bundleProxyModel)

    # ------------------- Filter bundle items
    def on_comboBoxItemFilter_returnPressed(self):
        self.proxyTreeModel.setFilterBundleItemTypes(None)
        self.proxyTreeModel.setFilterRegExp(".*%s.*" % self.comboBoxItemFilter.currentText())

    @QtCore.Slot(int)
    def on_comboBoxItemFilter_activated(self, index):
        value = self.comboBoxItemFilter.itemData(index)
        self.proxyTreeModel.setFilterBundleItemTypes(value)

    def configSelectTop(self):
        self.comboBoxItemFilter.addItem("Show all", ())
        self.comboBoxItemFilter.addItem(self.resources().get_icon("bundle-item-syntax"), "Languages", ("syntax",))
        self.comboBoxItemFilter.addItem(self.resources().get_icon("bundle-item-snippet"), "Snippets", ("snippet",))
        self.comboBoxItemFilter.addItem(self.resources().get_icon("bundle-item-macro"), "Macros", ("macro",))
        self.comboBoxItemFilter.addItem(self.resources().get_icon("bundle-item-command"), "Commands", ("command",))
        self.comboBoxItemFilter.addItem(self.resources().get_icon("bundle-item-dragcommand"), "DragCommands", ("dragcommand",))
        self.comboBoxItemFilter.addItem(self.resources().get_icon("bundle-item-preference"), "Preferences", ("preference",))
        self.comboBoxItemFilter.addItem(self.resources().get_icon("bundle-item-template"), "Templates", ("template", "staticfile"))
        self.comboBoxItemFilter.addItem(self.resources().get_icon("bundle-item-project"), "Projects", ("project", "staticfile"))
        self.comboBoxItemFilter.setInsertPolicy(QtWidgets.QComboBox.NoInsert)
        self.comboBoxItemFilter.lineEdit().returnPressed.connect(self.on_comboBoxItemFilter_returnPressed)

    # --------------------------- Tree View display the bundle items model
    def getEditorBase(self):
        return self.editors[self.BASE_EDITOR]

    def getEditorForTreeItem(self, node):
        item = node.bundleItem()
        if not node.isRootNode() and item.type() in self.indexes:
            index = self.indexes[item.type()]
            return self.editors[index]

    def on_bundleTreeModel_rowsInserted(self, parent, start, end):
        sIndex = self.manager.bundleTreeModel.index(start, 0, parent)
        index = self.proxyTreeModel.mapFromSource(sIndex)
        node = self.proxyTreeModel.node(index)
        self.treeView.setCurrentIndex(index)
        self.editTreeItem(node)
        self.treeView.edit(index)

    def on_proxyTreeModel_dataChanged(self, sindex, eindex):
        current = self.stackedWidget.currentWidget()
        self.labelTitle.setText(current.title())

    def on_treeView_selectionChanged(self, selected, deselected):
        indexes = selected.indexes()
        if indexes:
            treeItem = self.proxyTreeModel.node(indexes[0])
            self.editTreeItem(treeItem)
        else:
            self.editTreeItem(None)
            
    def configTreeView(self, manager=None):
        self.proxyTreeModel.dataChanged.connect(self.on_proxyTreeModel_dataChanged)
        
        self.treeView.setModel(self.proxyTreeModel)
        self.treeView.setHeaderHidden(True)
        self.treeView.setAnimated(True)
        self.treeView.selectionModel().selectionChanged.connect(self.on_treeView_selectionChanged)

    # -------------------- Activation
    def eventFilter(self, obj, event):
        if event.type() == QtCore.QEvent.KeyPress and obj == self.lineEditKeyEquivalentActivation:
            keyseq = QtWidgets.QKeySequence(int(event.modifiers()) + event.key())
            self.stackedWidget.currentWidget().setKeySequence(keyseq)
            self.lineEditKeyEquivalentActivation.setText(keyseq.toString())
            return True
        return QtWidgets.QDialog.eventFilter(self, obj, event)

    @QtCore.Slot()
    def on_pushButtonCleanKeyEquivalent_pressed(self):
        self.stackedWidget.currentWidget().setKeySequence(None)
        self.lineEditKeyEquivalentActivation.setText("")

    @QtCore.Slot(str)
    def on_lineEditScopeSelector_textEdited(self, text):
        self.stackedWidget.currentWidget().setScope(text)

    @QtCore.Slot(str)
    def on_lineEditTabTriggerActivation_textEdited(self, text):
        self.stackedWidget.currentWidget().setTabTrigger(text)

    @QtCore.Slot(str)
    def on_lineEditName_textEdited(self, text):
        self.stackedWidget.currentWidget().setName(text)

    def configActivation(self):
        self.lineEditKeyEquivalentActivation.installEventFilter(self)

    def saveChanges(self):
        current = self.stackedWidget.currentWidget()
        if current.isChanged():
            if current.type() == "bundle":
                self.manager.updateBundle(current.bundleItem, self.namespace, **current.changes)
            elif current.type() == "staticfile":
                self.manager.updateStaticFile(current.bundleItem, self.namespace, **current.changes)
            else:
                self.manager.updateBundleItem(current.bundleItem, self.namespace, **current.changes)

    def editTreeItem(self, treeItem):
        self.saveChanges()
        editor = self.getEditorForTreeItem(treeItem) if treeItem is not None else self.getEditorBase()
        if editor != None:
            editor.edit(treeItem.bundleItem())
            self.setCurrentEditor(editor)

    def currentEditor(self):
        return self.stackedWidget.currentWidget()

    def setCurrentEditor(self, editor):
        self.stackedWidget.setCurrentWidget(editor)
        self.labelTitle.setText(editor.title())
        self.lineEditName.setText(editor.getName())
        self.lineEditSemanticClass.setText(editor.getSemanticClass())
        scope = editor.getScope()
        tabTrigger = editor.getTabTrigger()
        keySequence = editor.getKeySequence()
        semanticClass = editor.getSemanticClass()
        # Scope
        self.lineEditScopeSelector.setEnabled(scope is not None)
        self.lineEditScopeSelector.setText(scope is not None and scope or "")
        # KeySequence
        self.lineEditKeyEquivalentActivation.setEnabled(keySequence is not None)
        self.lineEditKeyEquivalentActivation.setText(keySequence and\
            keySequence.toString() or "")
        # TabTrigger
        self.lineEditTabTriggerActivation.setEnabled(tabTrigger is not None)
        self.lineEditTabTriggerActivation.setText(tabTrigger or "")
        # SemanticClass
        self.lineEditSemanticClass.setEnabled(semanticClass is not None)
        self.lineEditSemanticClass.setText(semanticClass or "")