Example #1
0
class ComboEditor(QWidget):
    # Signals
    closeSplit = pyqtSignal('PyQt_PyObject')
    splitEditor = pyqtSignal('PyQt_PyObject', 'PyQt_PyObject', Qt.Orientation)
    allFilesClosed = pyqtSignal()
    about_to_close_combo_editor = pyqtSignal()
    fileClosed = pyqtSignal("PyQt_PyObject")

    def __init__(self, original=False):
        super(ComboEditor, self).__init__(None)
        self.__original = original
        self.__undocked = []
        self._symbols_index = []
        vbox = QVBoxLayout(self)
        vbox.setContentsMargins(0, 0, 0, 0)
        vbox.setSpacing(0)

        self.bar = ActionBar(main_combo=original)
        vbox.addWidget(self.bar)

        # Info bar
        # self.info_bar = InfoBar(self)
        # self.info_bar.setVisible(False)
        # vbox.addWidget(self.info_bar)

        self.stacked = QStackedLayout()
        vbox.addLayout(self.stacked)

        self._main_container = IDE.get_service('main_container')

        if not self.__original:
            self._main_container.fileOpened['QString'].connect(
                self._file_opened_by_main)

        self.bar.combo_files.showComboSelector.connect(
            self._main_container.show_files_handler)
        self.bar.combo_files.hideComboSelector.connect(
            self._main_container.hide_files_handler)
        self.bar.change_current['PyQt_PyObject',
                                int].connect(self._set_current)
        self.bar.splitEditor[bool].connect(self.split_editor)
        self.bar.runFile['QString'].connect(self._run_file)
        self.bar.closeSplit.connect(lambda: self.closeSplit.emit(self))
        self.bar.addToProject['QString'].connect(self._add_to_project)
        self.bar.showFileInExplorer['QString'].connect(
            self._show_file_in_explorer)
        self.bar.goToSymbol[int].connect(self._go_to_symbol)
        self.bar.undockEditor.connect(self.undock_editor)
        self.bar.reopenTab['QString'].connect(
            lambda path: self._main_container.open_file(path))
        self.bar.closeImageViewer.connect(self._close_image)
        self.bar.code_navigator.previousPressed.connect(self._navigate_code)
        self.bar.code_navigator.nextPressed.connect(self._navigate_code)
        # self.connect(self.bar, SIGNAL("recentTabsModified()"),
        #             lambda: self._main_container.recent_files_changed())
        # self.connect(self.bar.code_navigator.btnPrevious,
        #                SIGNAL("clicked()"),
        #             lambda: self._navigate_code(False))
        # self.connect(self.bar.code_navigator.btnNext, SIGNAL("clicked()"),
        #             lambda: self._navigate_code(True))

    def _navigate_code(self, operation, forward=True):
        self._main_container.navigate_code_history(operation, forward)

    #    op = self.bar.code_navigator.operation
    #    self._main_container.navigate_code_history(val, op)

    def current_editor(self):
        return self.stacked.currentWidget()

    def setFocus(self):
        super(ComboEditor, self).setFocus()
        self.current_editor().setFocus()
        self._editor_with_focus()

    def _file_opened_by_main(self, path):
        index = self.stacked.currentIndex()
        ninjaide = IDE.get_service('ide')
        editable = ninjaide.get_or_create_editable(path)
        self.add_editor(editable)
        self.bar.set_current_by_index(index)
        if index == -1:
            self.bar.set_current_by_index(0)

    def add_image_viewer(self, viewer):
        """Add Image Viewer widget to the UI area"""

        self.stacked.addWidget(viewer)
        viewer.scaleFactorChanged.connect(
            self.bar.image_viewer_controls.update_scale_label)
        viewer.imageSizeChanged.connect(
            self.bar.image_viewer_controls.update_size_label)
        self.bar.add_item(viewer.display_name(), None)
        viewer.create_scene()
        if not self.bar.isVisible():
            self.bar.setVisible(True)

    def add_editor(self, neditable, keep_index=False):
        """Add Editor Widget to the UI area."""
        if neditable.editor:
            if self.__original:
                editor = neditable.editor
            else:
                # editor = neditable.editor.clone()
                editor = self._main_container.create_editor_from_editable(
                    neditable)
                neditable.editor.link(editor)

            current_index = self.stacked.currentIndex()
            new_index = self.stacked.addWidget(editor)
            self.stacked.setCurrentIndex(new_index)
            self.bar.add_item(neditable.display_name, neditable)
            # Bar is not visible because all the files have been closed,
            # so if a new file is opened, show the bar
            if not self.bar.isVisible():
                self.bar.setVisible(True)
            if keep_index:
                self.bar.set_current_by_index(current_index)

            # Connections
            neditable.fileClosing.connect(self._close_file)
            neditable.fileSaved.connect(self._update_symbols)
            editor.editorFocusObtained.connect(self._editor_with_focus)
            editor.modificationChanged.connect(self._editor_modified)
            editor.cursor_position_changed[int, int].connect(
                self._update_cursor_position)
            editor.current_line_changed[int].connect(self._set_current_symbol)
            if neditable._swap_file.dirty:
                self._editor_modified(True, sender=editor)
            neditable.checkersUpdated.connect(self._show_notification_icon)
            # Connect file system signals only in the original
            if self.__original:
                neditable.askForSaveFileClosing.connect(self._ask_for_save)
                neditable.fileChanged.connect(self._file_has_been_modified)
            # Load Symbols
            self._load_symbols(neditable)

    def show_combo_file(self):
        self.bar.combo.showPopup()

    def show_combo_symbol(self):
        self.bar.symbols_combo.showPopup()

    def show_combo_set_language(self):
        self.bar.set_language_combo.showPopup()

    def unlink_editors(self):
        for index in range(self.stacked.count()):
            widget = self.stacked.widget(index)
            # widget.setDocument(QsciDocument())

    def clone(self):
        combo = ComboEditor()
        for neditable in self.bar.get_editables():
            combo.add_editor(neditable)
        return combo

    def split_editor(self, orientation):
        new_combo = self.clone()
        self.splitEditor.emit(self, new_combo, orientation)

    def undock_editor(self):
        new_combo = ComboEditor()
        for neditable in self.bar.get_editables():
            new_combo.add_editor(neditable)
        self.__undocked.append(new_combo)
        new_combo.setWindowTitle("NINJA-IDE")
        editor = self.current_editor()
        new_combo.set_current(editor.neditable)
        new_combo.resize(700, 500)
        new_combo.about_to_close_combo_editor.connect(self._remove_undock)
        new_combo.show()

    def _remove_undock(self):
        widget = self.sender()
        self.__undocked.remove(widget)

    def close_current_file(self):
        self.bar.about_to_close_file()

    def _close_image(self, index):
        layout_item = self.stacked.takeAt(index)
        layout_item.widget().deleteLater()
        if self.stacked.isEmpty():
            self.bar.hide()
            self.allFilesClosed.emit()

    def _close_file(self, neditable):
        index = self.bar.close_file(neditable)
        layoutItem = self.stacked.takeAt(index)
        # neditable.editor.completer.cc.unload_module()
        self.fileClosed.emit(neditable.nfile)
        layoutItem.widget().deleteLater()

        if self.stacked.isEmpty():
            self.bar.hide()
            self.allFilesClosed.emit()
            tree_symbols = IDE.get_service("symbols_explorer")
            if tree_symbols is not None:
                tree_symbols.clear()

    def _editor_with_focus(self):
        self._main_container.combo_area = self
        editor = self.current_editor()
        if editor is not None:
            self._main_container.current_editor_changed(
                editor.neditable.file_path)
            self._load_symbols(editor.neditable)
            editor.neditable.update_checkers_display()

    def _ask_for_save(self, neditable):
        val = QMessageBox.No
        fileName = neditable.nfile.file_name
        val = QMessageBox.question(
            self, (self.tr('The file %s was not saved') % fileName),
            self.tr("Do you want to save before closing?"),
            QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
        if val == QMessageBox.No:
            neditable.nfile.close(force_close=True)
        elif val == QMessageBox.Yes:
            neditable.ignore_checkers = True
            self._main_container.save_file(neditable.editor)
            neditable.nfile.close()

    @pyqtSlot("PyQt_PyObject")
    def _recovery(self, neditable):
        print("lalalal")

    def _file_has_been_modified(self, neditable):
        index = self.bar.combo_files.findData(neditable)
        self.stacked.setCurrentIndex(index)
        self.bar.combo_files.setCurrentIndex(index)

        msg_box = QMessageBox(self)
        msg_box.setIcon(QMessageBox.Information)
        msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
        msg_box.setDefaultButton(QMessageBox.Yes)
        msg_box.setWindowTitle(translations.TR_FILE_HAS_BEEN_MODIFIED)
        msg_box.setText(translations.TR_FILE_MODIFIED_OUTSIDE %
                        neditable.display_name)

        result = msg_box.exec_()
        if result == QMessageBox.Yes:
            neditable.reload_file()
        return

    def _run_file(self, path):
        self._main_container.run_file(path)

    def _add_to_project(self, path):
        self._main_container._add_to_project(path)

    def _show_file_in_explorer(self, path):
        '''Connected to ActionBar's showFileInExplorer(QString)
        signal, forwards the file path on to the main container.'''

        self._main_container._show_file_in_explorer(path)

    def set_current(self, neditable):
        if neditable:
            self.bar.set_current_file(neditable)

    def _set_current(self, neditable, index):
        self.stacked.setCurrentIndex(index)
        if neditable:
            self.bar.image_viewer_controls.setVisible(False)
            self.bar.code_navigator.setVisible(True)
            self.bar.symbols_combo.setVisible(True)
            self.bar.lbl_position.setVisible(True)

            editor = self.current_editor()
            self._update_cursor_position(ignore_sender=True)
            editor.setFocus()
            self._main_container.current_editor_changed(neditable.file_path)
            self._load_symbols(neditable)
            # self._show_file_in_explorer(neditable.file_path)
            neditable.update_checkers_display()
        else:
            self.bar.combo_files.setCurrentIndex(index)
            viewer_widget = self.stacked.widget(index)
            self._main_container.current_editor_changed(
                viewer_widget.image_filename)
            self.bar.image_viewer_controls.setVisible(True)
            self.bar.code_navigator.setVisible(False)
            self.bar.symbols_combo.setVisible(False)
            self.bar.lbl_position.setVisible(False)

    def widget(self, index):
        return self.stacked.widget(index)

    def count(self):
        """Return the number of editors opened."""
        return self.stacked.count()

    def _update_cursor_position(self, line=0, col=0, ignore_sender=False):
        obj = self.sender()
        editor = self.current_editor()
        # Check if it's current to avoid signals from other splits.
        if ignore_sender or editor == obj:
            line += 1
            self.bar.update_line_col(line, col)

    def _set_current_symbol(self, line, ignore_sender=False):
        obj = self.sender()
        editor = self.current_editor()
        # Check if it's current to avoid signals from other splits.
        if ignore_sender or editor == obj:
            index = bisect.bisect(self._symbols_index, line)
            if (index >= len(self._symbols_index)
                    or self._symbols_index[index] > (line + 1)):
                index -= 1
            self.bar.set_current_symbol(index)

    def _editor_modified(self, value, sender=None):
        if sender is None:
            sender = self.sender()
        neditable = sender.neditable
        if value:
            text = "\u2022 %s" % neditable.display_name
            self.bar.update_item_text(neditable, text)
        else:
            self.bar.update_item_text(neditable, neditable.display_name)

    def _go_to_symbol(self, index):
        line = self._symbols_index[index]
        editor = self.current_editor()
        editor.go_to_line(line, center=True)
        editor.setFocus()

    def _update_symbols(self, neditable):
        editor = self.current_editor()
        # Check if it's current to avoid signals from other splits.
        if editor.neditable == neditable:
            self._load_symbols(neditable)

    def _update_combo_info(self, neditable):
        self.bar.update_item_text(neditable, neditable.display_name)
        self._main_container.current_editor_changed(neditable.file_path)

    def _load_symbols(self, neditable):
        # Get symbols handler by language
        symbols_handler = handlers.get_symbols_handler(neditable.language())
        if symbols_handler is None:
            return
        source = neditable.editor.text
        source = source.encode(neditable.editor.encoding)
        symbols, symbols_simplified = symbols_handler.obtain_symbols(
            source, simple=True)
        self._symbols_index = sorted(symbols_simplified.keys())
        symbols_simplified = sorted(list(symbols_simplified.items()),
                                    key=lambda x: x[0])
        self.bar.add_symbols(symbols_simplified)
        line, _ = neditable.editor.cursor_position
        self._set_current_symbol(line, True)
        tree_symbols = IDE.get_service('symbols_explorer')
        if tree_symbols is not None:
            tree_symbols.update_symbols_tree(symbols, neditable.file_path)

    def _show_notification_icon(self, neditable):
        checkers = neditable.sorted_checkers
        icon = QIcon()
        for items in checkers:
            checker, color, _ = items
            if checker.checks:
                if isinstance(checker.checker_icon, int):
                    icon = self.style().standardIcon(checker.checker_icon)
                elif isinstance(checker.checker_icon, str):
                    icon = QIcon(checker.checker_icon)
                # FIXME: sucks
                else:
                    icon = QIcon(checker.checker_icon)
                break
        self.bar.update_item_icon(neditable, icon)

    def show_menu_navigation(self):
        self.bar.code_navigator.show_menu_navigation()

    def closeEvent(self, event):
        self.about_to_close_combo_editor.emit()
        # self.emit(SIGNAL("aboutToCloseComboEditor()"))
        super(ComboEditor, self).closeEvent(event)

    def reject(self):
        if not self.__original:
            super(ComboEditor, self).reject()
class ComboEditor(QDialog):

    closeSplit = pyqtSignal(QWidget)
    aboutToCloseComboEditor = pyqtSignal()
    allFilesClosed = pyqtSignal()
    splitEditor = pyqtSignal("QWidget*", "QWidget*", bool)
    recentTabsModified = pyqtSignal()

    class NAVIGATE:
        prev = 0
        next = 1

    Q_ENUMS(NAVIGATE)

    def __init__(self, original=False, Force_Free=False):
        super(ComboEditor, self).__init__(None)  #, Qt.WindowStaysOnTopHint)
        self.__original = original
        self.Force_Free = Force_Free
        self.__undocked = []
        self._single_undocked = []
        self._symbols_index = []
        self.__OFiles = []
        vbox = QVBoxLayout(self)
        vbox.setContentsMargins(0, 0, 0, 0)
        vbox.setSpacing(0)

        self.bar = ActionBar(self, main_combo=original)
        vbox.addWidget(self.bar)

        self.stackedEditor = QStackedLayout()
        vbox.addLayout(self.stackedEditor)

        self._main_container = IDE.get_service('main_container')

        if not self.__original and not self.Force_Free:
            self._main_container.fileOpened.connect(self._file_opened_by_main)

        # QApplication.instance().focusChanged["QWidget*", "QWidget*"].connect(\
        #     lambda w1, w2: QTimer.singleShot(10, lambda w1=w1, w2=w2: print("\n\nQApplication::focusChanged::", w1, w2)))

        self.bar.combo.showComboSelector.connect(
            self._main_container.change_tab)
        self.bar.changeCurrent.connect(self._set_current)
        self.bar.editorSplited.connect(self.split_editor)
        self.bar.runFile[str].connect(self._run_file)
        self.bar.closeFile.connect(lambda: self.closeSplit.emit(self))
        self.bar.addToProject[str].connect(self._add_to_project)
        self.bar.showFileInExplorer.connect(self._show_file_in_explorer)
        self.bar.goToSymbol.connect(self._go_to_symbol)
        self.bar.undockEditor.connect(self.undock_editor)
        self.bar.undockThisEditor.connect(self.single_undock_editor)
        self.bar.reopenTab[str].connect(self._main_container.open_file)
        self.bar.recentTabsModified.connect(
            self._main_container.recent_files_changed)
        self.bar.code_navigator.btnPrevious.clicked['bool'].connect(
            lambda: self._navigate_code(self.NAVIGATE.prev))
        self.bar.code_navigator.btnNext.clicked['bool'].connect(
            lambda: self._navigate_code(self.NAVIGATE.prev))

        # QTimer.singleShot(39999, lambda : print("\n\ncombo:-:", self))

    # def closeEvent(self, event):
    #     for comboeditor in self._single_undocked:
    #         print("has undocked", comboeditor)
    #         comboeditor.reject()
    #         comboeditor.deleteLater()

    #     self.bar._close_all_files()
    #     super(ComboEditor, self).closeEvent(event)

    def _navigate_code(self, val):
        op = self.bar.code_navigator.operation
        self._main_container.navigate_code_history(val, op)

    def setFocus(self):
        super(ComboEditor, self).setFocus()
        w = self.stackedEditor.currentWidget()
        if w:
            w.setFocus()
        self._editor_with_focus()

    def _file_opened_by_main(self, path):
        index = self.stackedEditor.currentIndex()
        ninjaide = IDE.getInstance()
        editable = ninjaide.get_or_create_editable(path)
        print("_file_opened_by_main", editable)
        self.add_editor(editable)
        self.bar.set_current_by_index(index)
        if index == -1:
            self.bar.set_current_by_index(0)

    def add_editor(self, neditable, keep_index=False):
        """Add Editor Widget to the UI area."""
        if neditable.editor:
            if self.__original or self.Force_Free:
                editor = neditable.editor
                print("\n\nadd_editor() ignora por ahora!", editor)

                # disconnect old Signals
                try:
                    editor.cursorPositionChanged[int, int].disconnect()
                except TypeError:
                    pass
                try:
                    editor.editorFocusObtained.disconnect()
                except TypeError:
                    pass
                try:
                    editor.currentLineChanged.disconnect()
                except TypeError:
                    pass
                try:
                    editor.modificationChanged['bool'].disconnect()
                except TypeError:
                    pass
                try:
                    neditable.checkersUpdated.disconnect()
                except TypeError:
                    pass
                try:
                    neditable.fileSaved.disconnect()
                except TypeError:
                    pass

                # Disonnect file system signals only in the original
                try:
                    neditable.fileClosing.disconnect()
                except TypeError:
                    pass
                if self.__original:
                    try:
                        neditable.askForSaveFileClosing.disconnect()
                    except TypeError:
                        pass
                    try:
                        neditable.fileChanged.disconnect()
                    except TypeError:
                        pass

            else:
                editor = self._main_container.create_text_editor_from_editable(
                    neditable)

            index = self.stackedEditor.currentIndex()
            self.stackedEditor.addWidget(editor)
            self.bar.add_item(neditable.display_name, neditable)
            if keep_index:
                self.bar.set_current_by_index(index)

            # Editor Signals
            editor.cursorPositionChanged[int, int].connect(
                self._update_cursor_position)
            editor.editorFocusObtained.connect(self._editor_with_focus)
            editor.currentLineChanged.connect(self._set_current_symbol)
            editor.modificationChanged['bool'].connect(self._editor_modified)
            neditable.checkersUpdated.connect(self._show_notification_icon)
            neditable.fileSaved.connect(self._update_symbols)
            neditable.fileSaved.connect(self._update_combo_info)

            # Connect file system signals only in the original
            neditable.fileClosing.connect(self._close_file)
            if self.__original:
                neditable.askForSaveFileClosing.connect(self._ask_for_save)
                neditable.fileChanged.connect(self._file_has_been_modified)

            # Load Symbols
            self._load_symbols(neditable)

    def show_combo_file(self):
        print("show_combo_file")
        self.bar.combo.showPopup()

    def show_combo_symbol(self):
        self.bar.symbols_combo.showPopup()

    def getOpenedFiles(self):
        return self.__OFiles.copy()

    def addOpenedFiles(self, fil):
        self.__OFiles.append(fil)

    def unlink_editors(self):
        for index in range(self.stackedEditor.count()):
            widget = self.stackedEditor.widget(index)
            widget.setDocument(QsciDocument())

    def split_editor(self, orientationVertical):
        new_widget = ComboEditor()
        for neditable in self.bar.get_editables():
            print("\nsplit_editor", neditable, new_widget)
            new_widget.add_editor(neditable)
        self.splitEditor.emit(self, new_widget, orientationVertical)

    def undock_editor(self):
        new_combo = ComboEditor()
        new_combo.setWindowTitle("NINJA-IDE")
        self.add_Undocked(new_combo)
        for neditable in self.bar.get_editables():
            print("undock_editor", neditable)
            new_combo.add_editor(neditable)
        new_combo.resize(500, 500)
        new_combo.aboutToCloseComboEditor.connect(self._remove_undock)
        new_combo.show()

    def _remove_undock(self):
        print("_remove_undock", self.sender())
        widget = self.sender()
        self.sub_Undocked(widget)

    def add_Undocked(self, combedit):
        self.__undocked.append(combedit)

    def sub_Undocked(self, combedit):
        self.__undocked.remove(combedit)

    def add_SingleUndocked(self, combedit):
        self._single_undocked.append(combedit)

    def sub_SingleUndocked(self, combedit):
        self._single_undocked.remove(combedit)

    # aún no se ha puesto en funcionamiento!.
    def single_split_editor(self, orientationVertical):
        new_widget = ComboEditor()
        for neditable in self.bar.get_editables():
            print("\nsingle_split_editor", neditable, new_widget)
            new_widget.add_editor(neditable)
        self.splitEditor.emit(self, new_widget, orientationVertical)

    def single_undock_editor(self):
        new_combo = ComboEditor(Force_Free=True)
        new_combo.setWindowTitle("NINJA-IDE")
        self.add_SingleUndocked(new_combo)

        nEdit = self.stackedEditor.takeAt(
            self.stackedEditor.currentIndex()).widget()
        ide = IDE.getInstance()
        ide.unload_NEditable(nEdit)
        neditable = self.bar.take_editable()
        print("\n\nsingle_undock_editor:::", neditable, neditable.editor,
              nEdit, neditable.nfile)

        if self.stackedEditor.isEmpty():
            self.allFilesClosed.emit()

        new_combo.add_editor(neditable)
        new_combo.stackedEditor.setCurrentIndex(
            0)  # new_combo.stackedEditor.setCurrentWidget(nEdit)
        new_combo.resize(500, 500)
        new_combo.aboutToCloseComboEditor.connect(self.single__remove_undock)
        new_combo.show()

    def single__remove_undock(self):
        print("single__remove_undock", self.sender())
        widget = self.sender()
        self.sub_SingleUndocked(widget)
        ##widget.deleteLater()

    def bind_Editable(self, editable):
        self.bar.add_item(neditable.display_name, editable)

    def close_current_file(self):
        self.bar.about_to_close_file()

    def _close_file(self, neditable):
        print("\n\n_close_file", self.__original, self.Force_Free)
        index = self.bar.close_file(neditable)
        layoutItem = self.stackedEditor.takeAt(index)
        #neditable.editor.completer.cc.unload_module()
        self._add_to_last_opened(neditable.file_path)
        layoutItem.widget().deleteLater(
        )  # @@4 -> Comentando ésta linea desaparece el mensaje

        if self.stackedEditor.isEmpty():
            self.allFilesClosed.emit()

    def _add_to_last_opened(self, path):
        if path not in settings.LAST_OPENED_FILES:
            settings.LAST_OPENED_FILES.append(path)
            if len(settings.LAST_OPENED_FILES) > settings.MAX_REMEMBER_TABS:
                self.__lastOpened = self.__lastOpened[1:]
            self.recentTabsModified.emit()

    def _editor_with_focus(self):
        if self._main_container.current_comboEditor is not self:
            self._main_container.current_comboEditor = self
            editor = self.stackedEditor.currentWidget()
            self._main_container.current_editor_changed(
                editor.neditable.file_path)
            self._load_symbols(editor.neditable)
            editor.neditable.update_checkers_display()

    def _ask_for_save(self, neditable):
        val = QMessageBox.No
        fileName = neditable.nfile.file_name
        val = QMessageBox.question(
            self, (self.tr('The file %s was not saved') % fileName),
            self.tr("Do you want to save before closing?"),
            QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
        if val == QMessageBox.No:
            neditable.nfile.close(force_close=True)
        elif val == QMessageBox.Yes:
            neditable.ignore_checkers = True
            self._main_container.save_file(neditable.editor)
            neditable.nfile.close()

    def _file_has_been_modified(self, neditable):
        val = QMessageBox.No
        fileName = neditable.file_path
        val = QMessageBox.question(
            self, translations.TR_FILE_HAS_BEEN_MODIFIED,
            "%s%s" % (fileName, translations.TR_FILE_MODIFIED_OUTSIDE),
            QMessageBox.Yes | QMessageBox.No)
        if val == QMessageBox.Yes:
            neditable.reload_file()

    def _run_file(self, path):
        self._main_container.run_file(path)

    def _add_to_project(self, path):
        self._main_container._add_to_project(path)

    def _show_file_in_explorer(self, path):
        '''Connected to ActionBar's showFileInExplorer(QString)
        signal, forwards the file path on to the main container.'''
        self._main_container._show_file_in_explorer(path)

    def set_current(self, neditable):
        if neditable:
            self.bar.set_current_file(neditable)

    def _set_current(self, neditable, index):
        if neditable:
            self.stackedEditor.setCurrentIndex(index)
            editor = self.stackedEditor.currentWidget()
            self._update_cursor_position(ignore_sender=True)
            editor.setFocus()
            self._main_container.current_editor_changed(neditable.file_path)
            self._load_symbols(neditable)
            self._show_file_in_explorer(neditable.file_path)
            neditable.update_checkers_display()

    def widget(self, index):
        return self.stackedEditor.widget(index)

    def countEditors(self):
        """Return the number of editors opened."""
        return self.stackedEditor.count()

    def currentEditor(self):
        """ rtn -> Editor()# local QsciScintilla"""
        return self.stackedEditor.currentWidget()

    def _update_cursor_position(self, line=0, col=0, ignore_sender=False):
        obj = self.sender()
        editor = self.stackedEditor.currentWidget()
        # Check if it's current to avoid signals from other splits.
        if ignore_sender or editor == obj:
            line += 1
            self.bar.update_line_col(line, col)

    def _set_current_symbol(self, line, ignore_sender=False):
        obj = self.sender()
        editor = self.stackedEditor.currentWidget()
        # Check if it's current to avoid signals from other splits.
        if ignore_sender or editor == obj:
            index = bisect.bisect(self._symbols_index, line)
            if (index >= len(self._symbols_index)
                    or self._symbols_index[index] > (line + 1)):
                index -= 1
            self.bar.set_current_symbol(index)

    def _editor_modified(self, value):
        obj = self.sender()
        neditable = obj.neditable
        if value:
            text = "\u2022 %s" % neditable.display_name
            self.bar.update_item_text(neditable, text)
        else:
            self.bar.update_item_text(neditable, neditable.display_name)

    def _go_to_symbol(self, index):
        print("_go_to_symbol in index:", index)
        line = self._symbols_index[index]
        editor = self.stackedEditor.currentWidget()
        editor.go_to_line(line)

    def _update_symbols(self, neditable):
        editor = self.stackedEditor.currentWidget()
        # Check if it's current to avoid signals from other splits.
        if editor == neditable.editor:
            self._load_symbols(neditable)

    def _update_combo_info(self, neditable):
        self.bar.update_item_text(neditable, neditable.display_name)
        self._main_container.current_editor_changed(neditable.file_path)

    def _load_symbols(self, neditable):
        symbols_handler = handlers.get_symbols_handler('py')
        source = neditable.editor.text()
        source = source.encode(neditable.editor.encoding)
        symbols, symbols_simplified = symbols_handler.obtain_symbols(
            source, simple=True)
        self._symbols_index = sorted(symbols_simplified.keys())
        symbols_simplified = sorted(list(symbols_simplified.items()),
                                    key=lambda x: x[0])
        self.bar.add_symbols(symbols_simplified)
        line, _ = neditable.editor.getCursorPosition()
        self._set_current_symbol(line, True)
        tree_symbols = IDE.get_service('symbols_explorer')
        tree_symbols.update_symbols_tree(symbols, neditable.file_path)

    def _show_notification_icon(self, neditable):
        checkers = neditable.sorted_checkers
        icon = QIcon()
        for items in checkers:
            checker, color, _ = items
            if checker.checks:
                if isinstance(checker.checker_icon, int):
                    icon = self.style().standardIcon(checker.checker_icon)
                elif isinstance(checker.checker_icon, str):
                    icon = QIcon(checker.checker_icon)
                break
        self.bar.update_item_icon(neditable, icon)

    def show_menu_navigation(self):
        self.bar.code_navigator.show_menu_navigation()

    def closeEvent(self, event):
        for comboeditor in self._single_undocked:
            print("has undocked", comboeditor)
            comboeditor.reject()
            comboeditor.deleteLater()

        # print("\n\ncloseEvent", self)
        if self.__original:
            self.bar._close_all_files()
        self.aboutToCloseComboEditor.emit()
        super(ComboEditor, self).closeEvent(event)

    def reject(self):
        if not self.__original:
            super(ComboEditor, self).reject()
Example #3
0
class ComboEditor(QWidget):
    # Signals
    closeSplit = pyqtSignal('PyQt_PyObject')
    splitEditor = pyqtSignal(
        'PyQt_PyObject', 'PyQt_PyObject', Qt.Orientation)
    allFilesClosed = pyqtSignal()
    about_to_close_combo_editor = pyqtSignal()
    fileClosed = pyqtSignal("PyQt_PyObject")

    def __init__(self, original=False):
        super(ComboEditor, self).__init__(None)
        self.__original = original
        self.__undocked = []
        self._symbols_index = []
        vbox = QVBoxLayout(self)
        vbox.setContentsMargins(0, 0, 0, 0)
        vbox.setSpacing(0)

        self.bar = ActionBar(main_combo=original)
        vbox.addWidget(self.bar)

        # Info bar
        # self.info_bar = InfoBar(self)
        # self.info_bar.setVisible(False)
        # vbox.addWidget(self.info_bar)

        self.stacked = QStackedLayout()
        vbox.addLayout(self.stacked)

        self._main_container = IDE.get_service('main_container')

        if not self.__original:
            self._main_container.fileOpened['QString'].connect(
                self._file_opened_by_main)

        self.bar.combo_files.showComboSelector.connect(
            self._main_container.show_files_handler)
        self.bar.combo_files.hideComboSelector.connect(
            self._main_container.hide_files_handler)
        self.bar.change_current['PyQt_PyObject',
                                int].connect(self._set_current)
        self.bar.splitEditor[bool].connect(self.split_editor)
        self.bar.runFile['QString'].connect(self._run_file)
        self.bar.closeSplit.connect(lambda: self.closeSplit.emit(self))
        self.bar.addToProject['QString'].connect(self._add_to_project)
        self.bar.showFileInExplorer['QString'].connect(
            self._show_file_in_explorer)
        self.bar.goToSymbol[int].connect(self._go_to_symbol)
        self.bar.undockEditor.connect(self.undock_editor)
        self.bar.reopenTab['QString'].connect(
            lambda path: self._main_container.open_file(path))
        self.bar.closeImageViewer.connect(self._close_image)
        self.bar.code_navigator.previousPressed.connect(self._navigate_code)
        self.bar.code_navigator.nextPressed.connect(self._navigate_code)
        # self.connect(self.bar, SIGNAL("recentTabsModified()"),
        #             lambda: self._main_container.recent_files_changed())
        # self.connect(self.bar.code_navigator.btnPrevious,
        #                SIGNAL("clicked()"),
        #             lambda: self._navigate_code(False))
        # self.connect(self.bar.code_navigator.btnNext, SIGNAL("clicked()"),
        #             lambda: self._navigate_code(True))

    def _navigate_code(self, operation, forward=True):
        self._main_container.navigate_code_history(operation, forward)
    #    op = self.bar.code_navigator.operation
    #    self._main_container.navigate_code_history(val, op)

    def current_editor(self):
        return self.stacked.currentWidget()

    def setFocus(self):
        super(ComboEditor, self).setFocus()
        self.current_editor().setFocus()
        self._editor_with_focus()

    def _file_opened_by_main(self, path):
        index = self.stacked.currentIndex()
        ninjaide = IDE.get_service('ide')
        editable = ninjaide.get_or_create_editable(path)
        self.add_editor(editable)
        self.bar.set_current_by_index(index)
        if index == -1:
            self.bar.set_current_by_index(0)

    def add_image_viewer(self, viewer):
        """Add Image Viewer widget to the UI area"""

        self.stacked.addWidget(viewer)
        viewer.scaleFactorChanged.connect(
            self.bar.image_viewer_controls.update_scale_label)
        viewer.imageSizeChanged.connect(
            self.bar.image_viewer_controls.update_size_label)
        self.bar.add_item(viewer.display_name(), None)
        viewer.create_scene()
        if not self.bar.isVisible():
            self.bar.setVisible(True)

    def add_editor(self, neditable, keep_index=False):
        """Add Editor Widget to the UI area."""
        if neditable.editor:
            if self.__original:
                editor = neditable.editor
            else:
                # editor = neditable.editor.clone()
                editor = self._main_container.create_editor_from_editable(
                   neditable)
                neditable.editor.link(editor)

            current_index = self.stacked.currentIndex()
            new_index = self.stacked.addWidget(editor)
            self.stacked.setCurrentIndex(new_index)
            self.bar.add_item(neditable.display_name, neditable)
            # Bar is not visible because all the files have been closed,
            # so if a new file is opened, show the bar
            if not self.bar.isVisible():
                self.bar.setVisible(True)
            if keep_index:
                self.bar.set_current_by_index(current_index)

            # Connections
            neditable.fileClosing.connect(self._close_file)
            neditable.fileSaved.connect(self._update_symbols)
            editor.editorFocusObtained.connect(self._editor_with_focus)
            editor.modificationChanged.connect(self._editor_modified)
            editor.cursor_position_changed[int, int].connect(
                self._update_cursor_position)
            editor.current_line_changed[int].connect(self._set_current_symbol)
            if neditable._swap_file.dirty:
                self._editor_modified(True, sender=editor)
            neditable.checkersUpdated.connect(self._show_notification_icon)
            # Connect file system signals only in the original
            if self.__original:
                neditable.askForSaveFileClosing.connect(self._ask_for_save)
                neditable.fileChanged.connect(self._file_has_been_modified)
            # Load Symbols
            self._load_symbols(neditable)

    def show_combo_file(self):
        self.bar.combo.showPopup()

    def show_combo_symbol(self):
        self.bar.symbols_combo.showPopup()

    def show_combo_set_language(self):
        self.bar.set_language_combo.showPopup()

    def unlink_editors(self):
        for index in range(self.stacked.count()):
            widget = self.stacked.widget(index)
            # widget.setDocument(QsciDocument())

    def clone(self):
        combo = ComboEditor()
        for neditable in self.bar.get_editables():
            combo.add_editor(neditable)
        return combo

    def split_editor(self, orientation):
        new_combo = self.clone()
        self.splitEditor.emit(self, new_combo, orientation)

    def undock_editor(self):
        new_combo = ComboEditor()
        for neditable in self.bar.get_editables():
            new_combo.add_editor(neditable)
        self.__undocked.append(new_combo)
        new_combo.setWindowTitle("NINJA-IDE")
        editor = self.current_editor()
        new_combo.set_current(editor.neditable)
        new_combo.resize(700, 500)
        new_combo.about_to_close_combo_editor.connect(self._remove_undock)
        new_combo.show()

    def _remove_undock(self):
        widget = self.sender()
        self.__undocked.remove(widget)

    def close_current_file(self):
        self.bar.about_to_close_file()

    def _close_image(self, index):
        layout_item = self.stacked.takeAt(index)
        layout_item.widget().deleteLater()
        if self.stacked.isEmpty():
            self.bar.hide()
            self.allFilesClosed.emit()

    def _close_file(self, neditable):
        index = self.bar.close_file(neditable)
        layoutItem = self.stacked.takeAt(index)
        # neditable.editor.completer.cc.unload_module()
        self.fileClosed.emit(neditable.nfile)
        layoutItem.widget().deleteLater()

        if self.stacked.isEmpty():
            self.bar.hide()
            self.allFilesClosed.emit()
            tree_symbols = IDE.get_service("symbols_explorer")
            if tree_symbols is not None:
                tree_symbols.clear()

    def _editor_with_focus(self):
        self._main_container.combo_area = self
        editor = self.current_editor()
        if editor is not None:
            self._main_container.current_editor_changed(
                editor.neditable.file_path)
            self._load_symbols(editor.neditable)
            editor.neditable.update_checkers_display()

    def _ask_for_save(self, neditable):
        val = QMessageBox.No
        fileName = neditable.nfile.file_name
        val = QMessageBox.question(
            self, (self.tr('The file %s was not saved') %
                   fileName),
            self.tr("Do you want to save before closing?"),
            QMessageBox.Yes | QMessageBox.No |
            QMessageBox.Cancel)
        if val == QMessageBox.No:
            neditable.nfile.close(force_close=True)
        elif val == QMessageBox.Yes:
            neditable.ignore_checkers = True
            self._main_container.save_file(neditable.editor)
            neditable.nfile.close()

    @pyqtSlot("PyQt_PyObject")
    def _recovery(self, neditable):
        print("lalalal")

    def _file_has_been_modified(self, neditable):
        index = self.bar.combo_files.findData(neditable)
        self.stacked.setCurrentIndex(index)
        self.bar.combo_files.setCurrentIndex(index)

        msg_box = QMessageBox(self)
        msg_box.setIcon(QMessageBox.Information)
        msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
        msg_box.setDefaultButton(QMessageBox.Yes)
        msg_box.setWindowTitle(translations.TR_FILE_HAS_BEEN_MODIFIED)
        msg_box.setText(
            translations.TR_FILE_MODIFIED_OUTSIDE % neditable.display_name)

        result = msg_box.exec_()
        if result == QMessageBox.Yes:
            neditable.reload_file()
        return

    def _run_file(self, path):
        self._main_container.run_file(path)

    def _add_to_project(self, path):
        self._main_container._add_to_project(path)

    def _show_file_in_explorer(self, path):
        '''Connected to ActionBar's showFileInExplorer(QString)
        signal, forwards the file path on to the main container.'''

        self._main_container._show_file_in_explorer(path)

    def set_current(self, neditable):
        if neditable:
            self.bar.set_current_file(neditable)

    def _set_current(self, neditable, index):
        self.stacked.setCurrentIndex(index)
        if neditable:
            self.bar.image_viewer_controls.setVisible(False)
            self.bar.code_navigator.setVisible(True)
            self.bar.symbols_combo.setVisible(True)
            self.bar.lbl_position.setVisible(True)

            editor = self.current_editor()
            self._update_cursor_position(ignore_sender=True)
            editor.setFocus()
            self._main_container.current_editor_changed(
                neditable.file_path)
            self._load_symbols(neditable)
            # self._show_file_in_explorer(neditable.file_path)
            neditable.update_checkers_display()
        else:
            self.bar.combo_files.setCurrentIndex(index)
            viewer_widget = self.stacked.widget(index)
            self._main_container.current_editor_changed(
                viewer_widget.image_filename)
            self.bar.image_viewer_controls.setVisible(True)
            self.bar.code_navigator.setVisible(False)
            self.bar.symbols_combo.setVisible(False)
            self.bar.lbl_position.setVisible(False)

    def widget(self, index):
        return self.stacked.widget(index)

    def count(self):
        """Return the number of editors opened."""
        return self.stacked.count()

    def _update_cursor_position(self, line=0, col=0, ignore_sender=False):
        obj = self.sender()
        editor = self.current_editor()
        # Check if it's current to avoid signals from other splits.
        if ignore_sender or editor == obj:
            line += 1
            self.bar.update_line_col(line, col)

    def _set_current_symbol(self, line, ignore_sender=False):
        obj = self.sender()
        editor = self.current_editor()
        # Check if it's current to avoid signals from other splits.
        if ignore_sender or editor == obj:
            index = bisect.bisect(self._symbols_index, line)
            if (index >= len(self._symbols_index) or
                    self._symbols_index[index] > (line + 1)):
                index -= 1
            self.bar.set_current_symbol(index)

    def _editor_modified(self, value, sender=None):
        if sender is None:
            sender = self.sender()
        neditable = sender.neditable
        if value:
            text = "\u2022 %s" % neditable.display_name
            self.bar.update_item_text(neditable, text)
        else:
            self.bar.update_item_text(neditable, neditable.display_name)

    def _go_to_symbol(self, index):
        line = self._symbols_index[index]
        editor = self.current_editor()
        editor.go_to_line(line, center=True)
        editor.setFocus()

    def _update_symbols(self, neditable):
        editor = self.current_editor()
        # Check if it's current to avoid signals from other splits.
        if editor.neditable == neditable:
            self._load_symbols(neditable)

    def _update_combo_info(self, neditable):
        self.bar.update_item_text(neditable, neditable.display_name)
        self._main_container.current_editor_changed(neditable.file_path)

    def _load_symbols(self, neditable):
        # Get symbols handler by language
        symbols_handler = handlers.get_symbols_handler(neditable.language())
        if symbols_handler is None:
            return
        source = neditable.editor.text
        source = source.encode(neditable.editor.encoding)
        symbols, symbols_simplified = symbols_handler.obtain_symbols(
            source, simple=True)
        self._symbols_index = sorted(symbols_simplified.keys())
        symbols_simplified = sorted(
            list(symbols_simplified.items()), key=lambda x: x[0])
        self.bar.add_symbols(symbols_simplified)
        line, _ = neditable.editor.cursor_position
        self._set_current_symbol(line, True)
        tree_symbols = IDE.get_service('symbols_explorer')
        if tree_symbols is not None:
            tree_symbols.update_symbols_tree(symbols, neditable.file_path)

    def _show_notification_icon(self, neditable):
        checkers = neditable.sorted_checkers
        icon = QIcon()
        for items in checkers:
            checker, color, _ = items
            if checker.checks:
                if isinstance(checker.checker_icon, int):
                    icon = self.style().standardIcon(checker.checker_icon)
                elif isinstance(checker.checker_icon, str):
                    icon = QIcon(checker.checker_icon)
                # FIXME: sucks
                else:
                    icon = QIcon(checker.checker_icon)
                break
        self.bar.update_item_icon(neditable, icon)

    def show_menu_navigation(self):
        self.bar.code_navigator.show_menu_navigation()

    def closeEvent(self, event):
        self.about_to_close_combo_editor.emit()
        # self.emit(SIGNAL("aboutToCloseComboEditor()"))
        super(ComboEditor, self).closeEvent(event)

    def reject(self):
        if not self.__original:
            super(ComboEditor, self).reject()
Example #4
0
class ComboEditor(ui_tools.StyledBar):
    # Signals
    closeSplit = pyqtSignal('PyQt_PyObject')
    splitEditor = pyqtSignal('PyQt_PyObject', 'PyQt_PyObject', bool)
    allFilesClosed = pyqtSignal()
    about_to_close_combo_editor = pyqtSignal()

    def __init__(self, original=False):
        super(ComboEditor, self).__init__(None)
        self.__original = original
        self.__undocked = []
        self._symbols_index = []
        vbox = QVBoxLayout(self)
        vbox.setContentsMargins(0, 0, 0, 0)
        vbox.setSpacing(0)

        self.bar = ActionBar(main_combo=original)
        vbox.addWidget(self.bar)

        # Info bar
        self.info_bar = InfoBar(self)
        self.info_bar.setVisible(False)
        vbox.addWidget(self.info_bar)

        self.stacked = QStackedLayout()
        vbox.addLayout(self.stacked)

        self._main_container = IDE.get_service('main_container')

        if not self.__original:
            self._main_container.fileOpened['QString'].connect(
                self._file_opened_by_main)

        # self.bar.combo.showComboSelector.connect(
        #    lambda: self._main_container.change_tab())
        self.bar.change_current['PyQt_PyObject',
                                int].connect(self._set_current)
        self.bar.splitEditor[bool].connect(self.split_editor)
        self.bar.runFile['QString'].connect(self._run_file)
        self.bar.closeSplit.connect(lambda: self.closeSplit.emit(self))
        self.bar.addToProject['QString'].connect(self._add_to_project)
        self.bar.showFileInExplorer['QString'].connect(
            self._show_file_in_explorer)
        self.bar.goToSymbol[int].connect(self._go_to_symbol)
        self.bar.undockEditor.connect(self.undock_editor)
        self.bar.reopenTab['QString'].connect(
            lambda path: self._main_container.open_file(path))

        # self.connect(self.bar, SIGNAL("recentTabsModified()"),
        #             lambda: self._main_container.recent_files_changed())
        # self.connect(self.bar.code_navigator.btnPrevious, SIGNAL("clicked()"),
        #             lambda: self._navigate_code(False))
        # self.connect(self.bar.code_navigator.btnNext, SIGNAL("clicked()"),
        #             lambda: self._navigate_code(True))

    # def _navigate_code(self, val):
    #    op = self.bar.code_navigator.operation
    #    self._main_container.navigate_code_history(val, op)

    def currentWidget(self):
        return self.stacked.currentWidget()

    def setFocus(self):
        super(ComboEditor, self).setFocus()
        self.stacked.currentWidget().setFocus()
        self._editor_with_focus()

    def _file_opened_by_main(self, path):
        index = self.stacked.currentIndex()
        ninjaide = IDE.get_service('ide')
        editable = ninjaide.get_or_create_editable(path)
        self.add_editor(editable)
        self.bar.set_current_by_index(index)
        if index == -1:
            self.bar.set_current_by_index(0)

    def add_editor(self, neditable, keep_index=False):
        """Add Editor Widget to the UI area."""
        if neditable.editor:
            if self.__original:
                editor = neditable.editor
            else:
                # editor = neditable.editor.clone()
                editor = self._main_container.create_editor_from_editable(
                    neditable)
            current_index = self.stacked.currentIndex()
            new_index = self.stacked.addWidget(editor)
            self.stacked.setCurrentIndex(new_index)
            self.bar.add_item(neditable.display_name, neditable)
            # Bar is not visible because all the files have been closed,
            # so if a new file is opened, show the bar
            if not self.bar.isVisible():
                self.bar.setVisible(True)
            if keep_index:
                self.bar.set_current_by_index(current_index)
            # Connections
            neditable.fileClosing.connect(self._close_file)
            editor.editorFocusObtained.connect(self._editor_with_focus)
            editor.modificationChanged.connect(self._editor_modified)
            neditable.checkersUpdated.connect(self._show_notification_icon)
            # Connect file system signals only in the original
            if self.__original:
                neditable.askForSaveFileClosing.connect(self._ask_for_save)
                neditable.fileChanged.connect(self._file_has_been_modified)
            # Editor Signals
            editor.cursor_position_changed[int, int].connect(
                self._update_cursor_position)
            editor.current_line_changed[int].connect(self._set_current_symbol)
            """
            # self.connect(editor, SIGNAL("editorFocusObtained()"),
            #             self._editor_with_focus)
            editor.editorFocusObtained.connect(self._editor_with_focus)
            neditable.fileSaved['PyQt_PyObject'].connect(
                self._update_combo_info)
            neditable.fileSaved['PyQt_PyObject'].connect(
                self._update_symbols)
            editor.modificationChanged[bool].connect(self._editor_modified)
            neditable.checkersUpdated.connect(self._show_notification_icon)

            # Connect file system signals only in the original
            neditable.fileClosing['PyQt_PyObject'].connect(self._close_file)
            if self.__original:
                neditable.askForSaveFileClosing['PyQt_PyObject'].connect(
                    self._ask_for_save)

                neditable.fileChanged['PyQt_PyObject'].connect(
                    self._file_has_been_modified)
                self.info_bar.reloadClicked.connect(neditable.reload_file)

            # Load Symbols
            self._load_symbols(neditable)
            """

    def show_combo_file(self):
        self.bar.combo.showPopup()

    def show_combo_symbol(self):
        self.bar.symbols_combo.showPopup()

    def unlink_editors(self):
        for index in range(self.stacked.count()):
            widget = self.stacked.widget(index)
            # widget.setDocument(QsciDocument())

    def split_editor(self, orientation_vertical):
        new_widget = ComboEditor()
        for neditable in self.bar.get_editables():
            new_widget.add_editor(neditable)
        self.splitEditor.emit(self, new_widget, orientation_vertical)
        # for neditable in self.bar.get_editables():
        #    new_widget.add_editor(neditable)
        # self.splitEditor.emit(self, new_widget, orientationVertical)
        # self.emit(SIGNAL("splitEditor(PyQt_PyObject, PyQt_PyObject, bool)"),
        #          self, new_widget, orientationVertical)

    def undock_editor(self):
        new_combo = ComboEditor()
        new_combo.setWindowTitle("NINJA-IDE")
        editor = self.currentWidget()
        new_combo.add_editor(editor.neditable)
        new_combo.show()
        """
        for neditable in self.bar.get_editables():
            new_combo.add_editor(neditable)
        neditor = self.currentWidget().clone()
        new_combo.set_current(neditor.neditable)
        new_combo.resize(700, 500)
        new_combo.about_to_close_combo_editor.connect(self._remove_undock)
        new_combo.show()
        """

    def _remove_undock(self):
        widget = self.sender()
        self.__undocked.remove(widget)

    def close_current_file(self):
        self.bar.about_to_close_file()

    def _close_file(self, neditable):
        index = self.bar.close_file(neditable)
        print(index)
        layoutItem = self.stacked.takeAt(index)
        # neditable.editor.completer.cc.unload_module()
        self._add_to_last_opened(neditable.file_path)
        layoutItem.widget().deleteLater()

        if self.stacked.isEmpty():
            self.bar.hide()
            self.allFilesClosed.emit()

    def _add_to_last_opened(self, path):
        if path not in settings.LAST_OPENED_FILES:
            settings.LAST_OPENED_FILES.append(path)
            if len(settings.LAST_OPENED_FILES) > settings.MAX_REMEMBER_TABS:
                self.__lastOpened = self.__lastOpened[1:]
            print("RecentTabsModified")
            # self.emit(SIGNAL("recentTabsModified()"))

    def _editor_with_focus(self):
        if self._main_container.current_widget is not self:
            self._main_container.current_widget = self
            editor = self.stacked.currentWidget()
            self._main_container.current_editor_changed(
                editor.neditable.file_path)
            self._load_symbols(editor.neditable)
            editor.neditable.update_checkers_display()

    def _ask_for_save(self, neditable):
        val = QMessageBox.No
        fileName = neditable.nfile.file_name
        val = QMessageBox.question(
            self, (self.tr('The file %s was not saved') % fileName),
            self.tr("Do you want to save before closing?"),
            QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
        if val == QMessageBox.No:
            neditable.nfile.close(force_close=True)
        elif val == QMessageBox.Yes:
            neditable.ignore_checkers = True
            self._main_container.save_file(neditable.editor)
            neditable.nfile.close()

    def _file_has_been_modified(self, neditable):
        self.info_bar.show_message(translations.TR_FILE_MODIFIED_OUTSIDE)
        # val = QMessageBox.No
        # fileName = neditable.file_path
        # val = QMessageBox.question(
        #    self, translations.TR_FILE_HAS_BEEN_MODIFIED,
        #    "%s%s" % (fileName, translations.TR_FILE_MODIFIED_OUTSIDE),
        #    QMessageBox.Yes | QMessageBox.No)
        # if val == QMessageBox.Yes:
        #    neditable.reload_file()

    def _run_file(self, path):
        self._main_container.run_file(path)

    def _add_to_project(self, path):
        self._main_container._add_to_project(path)

    def _show_file_in_explorer(self, path):
        '''Connected to ActionBar's showFileInExplorer(QString)
        signal, forwards the file path on to the main container.'''

        self._main_container._show_file_in_explorer(path)

    def set_current(self, neditable):
        if neditable:
            self.bar.set_current_file(neditable)

    def _set_current(self, neditable, index):
        if neditable:
            self.stacked.setCurrentIndex(index)
            editor = self.stacked.currentWidget()
            self._update_cursor_position(ignore_sender=True)
            editor.setFocus()
            self._main_container.current_editor_changed(neditable.file_path)
            self._load_symbols(neditable)
            # self._show_file_in_explorer(neditable.file_path)
            neditable.update_checkers_display()

    def widget(self, index):
        return self.stacked.widget(index)

    def count(self):
        """Return the number of editors opened."""
        return self.stacked.count()

    def _update_cursor_position(self, line=0, col=0, ignore_sender=False):
        obj = self.sender()
        editor = self.stacked.currentWidget()
        # Check if it's current to avoid signals from other splits.
        if ignore_sender or editor == obj:
            line += 1
            self.bar.update_line_col(line, col)

    def _set_current_symbol(self, line, ignore_sender=False):
        obj = self.sender()
        editor = self.stacked.currentWidget()
        # Check if it's current to avoid signals from other splits.
        if ignore_sender or editor == obj:
            index = bisect.bisect(self._symbols_index, line)
            if (index >= len(self._symbols_index)
                    or self._symbols_index[index] > (line + 1)):
                index -= 1
            self.bar.set_current_symbol(index)

    def _editor_modified(self, value):
        obj = self.sender()
        neditable = obj.neditable
        if value:
            text = "\u2022 %s" % neditable.display_name
            self.bar.update_item_text(neditable, text)
        else:
            self.bar.update_item_text(neditable, neditable.display_name)

    def _go_to_symbol(self, index):
        # FIXME: index 0 invalid
        line = self._symbols_index[index]
        editor = self.stacked.currentWidget()
        editor.go_to_line(line)

    def _update_symbols(self, neditable):
        editor = self.stacked.currentWidget()
        # Check if it's current to avoid signals from other splits.
        if editor == neditable.editor:
            self._load_symbols(neditable)

    def _update_combo_info(self, neditable):
        self.bar.update_item_text(neditable, neditable.display_name)
        self._main_container.current_editor_changed(neditable.file_path)

    def _load_symbols(self, neditable):
        symbols_handler = handlers.get_symbols_handler('py')
        source = neditable.editor.text
        source = source.encode(neditable.editor.encoding)
        symbols, symbols_simplified = symbols_handler.obtain_symbols(
            source, simple=True)
        self._symbols_index = sorted(symbols_simplified.keys())
        symbols_simplified = sorted(list(symbols_simplified.items()),
                                    key=lambda x: x[0])
        self.bar.add_symbols(symbols_simplified)
        line, _ = neditable.editor.cursor_position
        self._set_current_symbol(line, True)
        tree_symbols = IDE.get_service('symbols_explorer')
        tree_symbols.update_symbols_tree(symbols, neditable.file_path)

    def _show_notification_icon(self, neditable):
        checkers = neditable.sorted_checkers
        icon = QIcon()
        for items in checkers:
            checker, color, _ = items
            if checker.checks:
                if isinstance(checker.checker_icon, int):
                    icon = self.style().standardIcon(checker.checker_icon)
                elif isinstance(checker.checker_icon, str):
                    icon = QIcon(checker.checker_icon)
                # FIXME: sucks
                else:
                    icon = QIcon(checker.checker_icon)
                break
        self.bar.update_item_icon(neditable, icon)

    def show_menu_navigation(self):
        self.bar.code_navigator.show_menu_navigation()

    def closeEvent(self, event):
        self.about_to_close_combo_editor.emit()
        # self.emit(SIGNAL("aboutToCloseComboEditor()"))
        super(ComboEditor, self).closeEvent(event)

    def reject(self):
        if not self.__original:
            super(ComboEditor, self).reject()
class ComboEditor(QDialog):
    
    closeSplit = pyqtSignal(QWidget)#closeSplit
    allFilesClosed = pyqtSignal()
    splitEditor = pyqtSignal("QWidget*", "QWidget*", bool)
    recentTabsModified = pyqtSignal()
    aboutToCloseComboEditor = pyqtSignal()

    class NAVIGATE:
        prev = 0
        next = 1

    Q_ENUMS(NAVIGATE)

    def __init__(self, original=False):
        super(ComboEditor, self).__init__(None, Qt.WindowStaysOnTopHint)
        self.__original = original
        self.__undocked = []
        self._symbols_index = []
        vbox = QVBoxLayout(self)
        vbox.setContentsMargins(0, 0, 0, 0)
        vbox.setSpacing(0)

        self.bar = ActionBar(main_combo=original)
        vbox.addWidget(self.bar)

        self.stacked = QStackedLayout()
        vbox.addLayout(self.stacked)

        self._main_container = IDE.get_service('main_container')

        if not self.__original:
            self._main_container.fileOpened.connect(self._file_opened_by_main)

        self.bar.combo.showComboSelector.connect(self._main_container.change_tab)
        self.bar.changeCurrent.connect(self._set_current)
        self.bar.editorSplited.connect(self.split_editor)
        self.bar.runFile[str].connect(self._run_file)
        self.bar.closeFile.connect(lambda: self.closeSplit.emit(self))
        self.bar.addToProject[str].connect(self._add_to_project)
        self.bar.showFileInExplorer.connect(self._show_file_in_explorer)
        self.bar.goToSymbol.connect(self._go_to_symbol)
        self.bar.undockEditor.connect(self.undock_editor)
        self.bar.reopenTab[str].connect(self._main_container.open_file)
        self.bar.recentTabsModified.connect(self._main_container.recent_files_changed)
        self.bar.code_navigator.btnPrevious.clicked['bool'].connect(lambda: self._navigate_code(self.NAVIGATE.prev))
        self.bar.code_navigator.btnNext.clicked['bool'].connect(lambda: self._navigate_code(self.NAVIGATE.prev))

    def _navigate_code(self, val):
        op = self.bar.code_navigator.operation
        self._main_container.navigate_code_history(val, op)

    def currentWidget(self):
        return self.stacked.currentWidget()

    def setFocus(self):
        super(ComboEditor, self).setFocus()
        w = self.stacked.currentWidget()
        if w:
            w.setFocus()
        self._editor_with_focus()

    def _file_opened_by_main(self, path):
        index = self.stacked.currentIndex()
        ninjaide = IDE.getInstance()
        editable = ninjaide.get_or_create_editable(path)
        print("_file_opened_by_main", editable)
        self.add_editor(editable)
        self.bar.set_current_by_index(index)
        if index == -1:
            self.bar.set_current_by_index(0)

    def add_editor(self, neditable, keep_index=False):
        """Add Editor Widget to the UI area."""
        if neditable.editor:
            if self.__original:
                editor = neditable.editor
            else:
                editor = self._main_container.create_editor_from_editable(
                    neditable)
            index = self.stacked.currentIndex()
            self.stacked.addWidget(editor)
            self.bar.add_item(neditable.display_name, neditable)
            if keep_index:
                self.bar.set_current_by_index(index)

            # Editor Signals
            editor.cursorPositionChanged[int, int].connect(self._update_cursor_position)
            editor.editorFocusObtained.connect(self._editor_with_focus)
            editor.currentLineChanged.connect(self._set_current_symbol)
            editor.modificationChanged['bool'].connect(self._editor_modified)
            neditable.checkersUpdated.connect(self._show_notification_icon)
            neditable.fileSaved.connect(self._update_symbols)
            neditable.fileSaved.connect(self._update_combo_info)

            # Connect file system signals only in the original
            neditable.fileClosing.connect(self._close_file)
            if self.__original:
                neditable.askForSaveFileClosing.connect(self._ask_for_save)
                neditable.fileChanged.connect(self._file_has_been_modified)

            # Load Symbols
            self._load_symbols(neditable)

    def show_combo_file(self):
        print("show_combo_file")
        self.bar.combo.showPopup()

    def show_combo_symbol(self):
        self.bar.symbols_combo.showPopup()

    def unlink_editors(self):
        for index in range(self.stacked.count()):
            widget = self.stacked.widget(index)
            widget.setDocument(QsciDocument())

    def split_editor(self, orientationVertical):
        new_widget = ComboEditor()
        for neditable in self.bar.get_editables():
            print("\nsplit_editor", neditable, new_widget)
            new_widget.add_editor(neditable)
        self.splitEditor.emit(self, new_widget, orientationVertical)

    def undock_editor(self):
        new_combo = ComboEditor()
        new_combo.setWindowTitle("NINJA-IDE")
        self.__undocked.append(new_combo)
        for neditable in self.bar.get_editables():
            print("undock_editor", neditable)
            new_combo.add_editor(neditable)
        new_combo.resize(500, 500)
        new_combo.aboutToCloseComboEditor.connect(self._remove_undock)
        new_combo.show()

    def _remove_undock(self):
        widget = self.sender()
        self.__undocked.remove(widget)

    def close_current_file(self):
        self.bar.about_to_close_file()

    def _close_file(self, neditable):
        index = self.bar.close_file(neditable)
        layoutItem = self.stacked.takeAt(index)
        #neditable.editor.completer.cc.unload_module()
        self._add_to_last_opened(neditable.file_path)
        layoutItem.widget().deleteLater()

        if self.stacked.isEmpty():
            self.allFilesClosed.emit()

    def _add_to_last_opened(self, path):
        if path not in settings.LAST_OPENED_FILES:
            settings.LAST_OPENED_FILES.append(path)
            if len(settings.LAST_OPENED_FILES) > settings.MAX_REMEMBER_TABS:
                self.__lastOpened = self.__lastOpened[1:]
            self.recentTabsModified.emit()

    def _editor_with_focus(self):
        if self._main_container.current_widget is not self:
            self._main_container.current_widget = self
            editor = self.stacked.currentWidget()
            self._main_container.current_editor_changed(
                editor.neditable.file_path)
            self._load_symbols(editor.neditable)
            editor.neditable.update_checkers_display()

    def _ask_for_save(self, neditable):
        val = QMessageBox.No
        fileName = neditable.nfile.file_name
        val = QMessageBox.question(
            self, (self.tr('The file %s was not saved') %
                   fileName),
            self.tr("Do you want to save before closing?"),
            QMessageBox.Yes | QMessageBox.No |
            QMessageBox.Cancel)
        if val == QMessageBox.No:
            neditable.nfile.close(force_close=True)
        elif val == QMessageBox.Yes:
            neditable.ignore_checkers = True
            self._main_container.save_file(neditable.editor)
            neditable.nfile.close()

    def _file_has_been_modified(self, neditable):
        val = QMessageBox.No
        fileName = neditable.file_path
        val = QMessageBox.question(
            self, translations.TR_FILE_HAS_BEEN_MODIFIED,
            "%s%s" % (fileName, translations.TR_FILE_MODIFIED_OUTSIDE),
            QMessageBox.Yes | QMessageBox.No)
        if val == QMessageBox.Yes:
            neditable.reload_file()

    def _run_file(self, path):
        self._main_container.run_file(path)

    def _add_to_project(self, path):
        self._main_container._add_to_project(path)

    def _show_file_in_explorer(self, path):
        '''Connected to ActionBar's showFileInExplorer(QString)
        signal, forwards the file path on to the main container.'''
        self._main_container._show_file_in_explorer(path)

    def set_current(self, neditable):
        if neditable:
            self.bar.set_current_file(neditable)

    def _set_current(self, neditable, index):
        if neditable:
            self.stacked.setCurrentIndex(index)
            editor = self.stacked.currentWidget()
            self._update_cursor_position(ignore_sender=True)
            editor.setFocus()
            self._main_container.current_editor_changed(
                neditable.file_path)
            self._load_symbols(neditable)
            self._show_file_in_explorer(neditable.file_path)
            neditable.update_checkers_display()

    def widget(self, index):
        return self.stacked.widget(index)

    def count(self):
        """Return the number of editors opened."""
        return self.stacked.count()

    def _update_cursor_position(self, line=0, col=0, ignore_sender=False):
        obj = self.sender()
        editor = self.stacked.currentWidget()
        # Check if it's current to avoid signals from other splits.
        if ignore_sender or editor == obj:
            line += 1
            self.bar.update_line_col(line, col)

    def _set_current_symbol(self, line, ignore_sender=False):
        obj = self.sender()
        editor = self.stacked.currentWidget()
        # Check if it's current to avoid signals from other splits.
        if ignore_sender or editor == obj:
            index = bisect.bisect(self._symbols_index, line)
            if (index >= len(self._symbols_index) or
                    self._symbols_index[index] > (line + 1)):
                index -= 1
            self.bar.set_current_symbol(index)

    def _editor_modified(self, value):
        obj = self.sender()
        neditable = obj.neditable
        if value:
            text = "\u2022 %s" % neditable.display_name
            self.bar.update_item_text(neditable, text)
        else:
            self.bar.update_item_text(neditable, neditable.display_name)

    def _go_to_symbol(self, index):
        print("_go_to_symbol in index:", index)
        line = self._symbols_index[index]
        editor = self.stacked.currentWidget()
        editor.go_to_line(line)

    def _update_symbols(self, neditable):
        editor = self.stacked.currentWidget()
        # Check if it's current to avoid signals from other splits.
        if editor == neditable.editor:
            self._load_symbols(neditable)

    def _update_combo_info(self, neditable):
        self.bar.update_item_text(neditable, neditable.display_name)
        self._main_container.current_editor_changed(neditable.file_path)

    def _load_symbols(self, neditable):
        symbols_handler = handlers.get_symbols_handler('py')
        source = neditable.editor.text()
        source = source.encode(neditable.editor.encoding)
        symbols, symbols_simplified = symbols_handler.obtain_symbols(
            source, simple=True)
        self._symbols_index = sorted(symbols_simplified.keys())
        symbols_simplified = sorted(
            list(symbols_simplified.items()), key=lambda x: x[0])
        self.bar.add_symbols(symbols_simplified)
        line, _ = neditable.editor.getCursorPosition()
        self._set_current_symbol(line, True)
        tree_symbols = IDE.get_service('symbols_explorer')
        tree_symbols.update_symbols_tree(symbols, neditable.file_path)

    def _show_notification_icon(self, neditable):
        checkers = neditable.sorted_checkers
        icon = QIcon()
        for items in checkers:
            checker, color, _ = items
            if checker.checks:
                if isinstance(checker.checker_icon, int):
                    icon = self.style().standardIcon(checker.checker_icon)
                elif isinstance(checker.checker_icon, str):
                    icon = QIcon(checker.checker_icon)
                break
        self.bar.update_item_icon(neditable, icon)

    def show_menu_navigation(self):
        self.bar.code_navigator.show_menu_navigation()

    def closeEvent(self, event):
        self.aboutToCloseComboEditor.emit()
        super(ComboEditor, self).closeEvent(event)

    def reject(self):
        if not self.__original:
            super(ComboEditor, self).reject()