Esempio n. 1
0
class SingleApplication(QApplication):
    messageAvailable = pyqtSignal(type(u''))

    def __init__(self, argv, key):
        QApplication.__init__(self, argv)

        self._key = key
        self._timeout = 1000

        socket = QLocalSocket(self)
        socket.connectToServer(self._key)
        if socket.waitForConnected(self._timeout):
            self._isRunning = True
            socket.abort()
            return
        socket.abort()

        self._isRunning = False
        self._server = QLocalServer(self)
        self._server.newConnection.connect(self.__onNewConnection)
        self._server.listen(self._key)

        self.aboutToQuit.connect(self.__onAboutToQuit)

    def __onAboutToQuit(self):
        if self._server:
            self._server.close()
            self._server = None

    def __onNewConnection(self):
        socket = self._server.nextPendingConnection()
        if socket.waitForReadyRead(self._timeout):
            self.messageAvailable.emit(socket.readAll().data().decode('utf-8'))
            socket.disconnectFromServer()
        else:
            pass

    def isRunning(self):
        return self._isRunning

    def sendMessage(self, message):
        assert(self._isRunning)

        if self.isRunning():
            socket = QLocalSocket(self)
            socket.connectToServer(self._key, QIODevice.WriteOnly)
            if not socket.waitForConnected(self._timeout):
                return False
            socket.write(message.encode('utf-8'))
            if not socket.waitForBytesWritten(self._timeout):
                return False
            socket.disconnectFromServer()
            return True
        return False
Esempio n. 2
0
class SingleApplication(QApplication):
    messageAvailable = pyqtSignal(type(u''))

    def __init__(self, argv, key):
        QApplication.__init__(self, argv)

        self._key = key
        self._timeout = 1000

        socket = QLocalSocket(self)
        socket.connectToServer(self._key)
        if socket.waitForConnected(self._timeout):
            self._isRunning = True
            socket.abort()
            return
        socket.abort()

        self._isRunning = False
        self._server = QLocalServer(self)
        self._server.newConnection.connect(self.__onNewConnection)
        self._server.listen(self._key)

        self.aboutToQuit.connect(self.__onAboutToQuit)

    def __onAboutToQuit(self):
        if self._server:
            self._server.close()
            self._server = None

    def __onNewConnection(self):
        socket = self._server.nextPendingConnection()
        if socket.waitForReadyRead(self._timeout):
            self.messageAvailable.emit(socket.readAll().data().decode('utf-8'))
            socket.disconnectFromServer()
        else:
            pass

    def isRunning(self):
        return self._isRunning

    def sendMessage(self, message):
        assert (self._isRunning)

        if self.isRunning():
            socket = QLocalSocket(self)
            socket.connectToServer(self._key, QIODevice.WriteOnly)
            if not socket.waitForConnected(self._timeout):
                return False
            socket.write(message.encode('utf-8'))
            if not socket.waitForBytesWritten(self._timeout):
                return False
            socket.disconnectFromServer()
            return True
        return False
Esempio n. 3
0
def main():
    app = QtGui.QApplication(sys.argv)
    androidToolsMainWin = AndroidToolsMainWindow()
    uiMainWidget = Ui_MainWidget()
    winOsArgv = WinCommandEnCoding.getOsArgv()
    # single QApplication solution
    # http://blog.csdn.net/softdzf/article/details/6704187
    serverName = 'AndroidToolsServer'
    clientSocket = QLocalSocket()
    clientSocket.connectToServer(serverName)
    QSettingsUtil.init()
    # 如果连接成功, 表明server 已经存在,当前已经有实例在运行, 将参数发送给服务端
    if clientSocket.waitForConnected(500):
        # print u'连接成功 arg = ', winOsArgv
        stream = QtCore.QTextStream(clientSocket)
        # for i in range(0, len(winOsArgv)):
        #     stream << winOsArgv[i]
        # 对于打开终端来说,所携带参数为第1位(打开文件的地址),第0位为本执行程序地址
        if len(winOsArgv) > 1:
            stream << winOsArgv[1]
            stream.setCodec('UTF-8')
            stream.flush()
            clientSocket.waitForBytesWritten()
        # close client socket
        clientSocket.close()
        return app.quit()
    # 如果没有实例执行,创建服务器
    localServer = QLocalServer()
    # 一直监听端口
    localServer.listen(serverName)
    # create db
    createDb()
    try:
        uiMainWidget.setupUi(androidToolsMainWin, localServer, winOsArgv)
        androidToolsMainWin.show()
        sys.exit(app.exec_())
    finally:
        localServer.close()
Esempio n. 4
0
def main():
    app = QtGui.QApplication(sys.argv)
    ffstoreMainWin = FFStoreMainWindow()

    # set skin styleSheet
    # SkinHelper().setStyle(app, ':/qss/white_style.qss')
    SkinHelper().setStyle(app, ':/qss/dark_style.qss')

    # single QApplication solution
    # http://blog.csdn.net/softdzf/article/details/6704187
    serverName = 'FFStoreManagerServer'
    clientSocket = QLocalSocket()
    clientSocket.connectToServer(serverName)
    # 如果连接成功, 表明server 已经存在,当前已经有实例在运行, 将参数发送给服务端
    if clientSocket.waitForConnected(500):
        # print u'连接成功 arg = ', winOsArgv
        stream = QtCore.QTextStream(clientSocket)
        # for i in range(0, len(winOsArgv)):
        #     stream << winOsArgv[i]
        # 对于打开终端来说,所携带参数为第1位(打开文件的地址),第0位为本执行程序地址
        # close client socket
        clientSocket.close()
        return app.quit()
    # 如果没有实例执行,创建服务器
    localServer = QLocalServer()
    # 一直监听端口
    localServer.listen(serverName)
    # 初始化全局变量
    GlobalVar.init()
    try:
        showWindowLogic = ShowWindowLogic(mainWindow=ffstoreMainWin,
                                          localServer=localServer)
        showWindowLogic.show()
        # uiMainWidget.setupUi(mainWindow=ffstoreMainWin, localServer=localServer)
        # ffstoreMainWin.show()
        sys.exit(app.exec_())
    finally:
        localServer.close()
Esempio n. 5
0
class __IDE(QMainWindow):
    ###############################################################################
    # SIGNALS
    #
    # goingDown()
    ###############################################################################

    def __init__(self, start_server=False):
        QMainWindow.__init__(self)
        self.setWindowTitle('NINJA-IDE {Ninja-IDE Is Not Just Another IDE}')
        self.setMinimumSize(700, 500)
        #Load the size and the position of the main window
        self.load_window_geometry()

        #Start server if needed
        self.s_listener = None
        if start_server:
            self.s_listener = QLocalServer()
            self.s_listener.listen("ninja_ide")
            self.connect(self.s_listener, SIGNAL("newConnection()"),
                         self._process_connection)

        #Profile handler
        self.profile = None
        #Opacity
        self.opacity = settings.MAX_OPACITY

        #Define Actions object before the UI
        self.actions = actions.Actions()
        #StatusBar
        self.status = status_bar.StatusBar(self)
        self.status.hide()
        self.setStatusBar(self.status)
        #Main Widget - Create first than everything else
        self.central = central_widget.CentralWidget(self)
        self.load_ui(self.central)
        self.setCentralWidget(self.central)

        #ToolBar
        self.toolbar = QToolBar(self)
        self.toolbar.setToolTip(self.tr("Press and Drag to Move"))
        self.toolbar.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self.addToolBar(settings.TOOLBAR_AREA, self.toolbar)
        if settings.HIDE_TOOLBAR:
            self.toolbar.hide()

        #Install Shortcuts after the UI has been initialized
        self.actions.install_shortcuts(self)
        self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"),
                     self.actions.update_explorer)

        #Menu
        menubar = self.menuBar()
        file_ = menubar.addMenu(self.tr("&File"))
        edit = menubar.addMenu(self.tr("&Edit"))
        view = menubar.addMenu(self.tr("&View"))
        source = menubar.addMenu(self.tr("&Source"))
        project = menubar.addMenu(self.tr("&Project"))
        self.pluginsMenu = menubar.addMenu(self.tr("&Addins"))
        about = menubar.addMenu(self.tr("Abou&t"))

        #The order of the icons in the toolbar is defined by this calls
        self._menuFile = menu_file.MenuFile(file_, self.toolbar, self)
        self._menuView = menu_view.MenuView(view, self.toolbar, self)
        self._menuEdit = menu_edit.MenuEdit(edit, self.toolbar)
        self._menuSource = menu_source.MenuSource(source)
        self._menuProject = menu_project.MenuProject(project, self.toolbar)
        self._menuPlugins = menu_plugins.MenuPlugins(self.pluginsMenu)
        self._menuAbout = menu_about.MenuAbout(about)

        self.load_toolbar()

        #Plugin Manager
        services = {
            'editor': plugin_services.MainService(),
            'toolbar': plugin_services.ToolbarService(self.toolbar),
            'menuApp': plugin_services.MenuAppService(self.pluginsMenu),
            'explorer': plugin_services.ExplorerService(),
            'misc': plugin_services.MiscContainerService(self.misc)
        }
        serviceLocator = plugin_manager.ServiceLocator(services)
        self.plugin_manager = plugin_manager.PluginManager(
            resources.PLUGINS, serviceLocator)
        self.plugin_manager.discover()
        #load all plugins!
        self.plugin_manager.load_all()

        #Tray Icon
        self.trayIcon = updates.TrayIconUpdates(self)
        self.trayIcon.show()

        self.connect(self._menuFile, SIGNAL("openFile(QString)"),
                     self.mainContainer.open_file)
        self.connect(self.mainContainer, SIGNAL("fileSaved(QString)"),
                     self.show_status_message)
        self.connect(self.mainContainer,
                     SIGNAL("recentTabsModified(QStringList)"),
                     self._menuFile.update_recent_files)

    def _process_connection(self):
        connection = self.s_listener.nextPendingConnection()
        connection.waitForReadyRead()
        data = connection.readAll()
        connection.close()
        if data:
            files, projects = data.split(ipc.project_delimiter, 1)
            files = map(lambda x: (x.split(':')[0], int(x.split(':')[1])),
                        files.split(ipc.file_delimiter))
            projects = projects.split(ipc.project_delimiter)
            self.load_session_files_projects(files, [], projects, None)

    def load_toolbar(self):
        self.toolbar.clear()
        toolbar_items = {}
        toolbar_items.update(self._menuFile.toolbar_items)
        toolbar_items.update(self._menuView.toolbar_items)
        toolbar_items.update(self._menuEdit.toolbar_items)
        toolbar_items.update(self._menuSource.toolbar_items)
        toolbar_items.update(self._menuProject.toolbar_items)

        for item in settings.TOOLBAR_ITEMS:
            if item == 'separator':
                self.toolbar.addSeparator()
            else:
                tool_item = toolbar_items.get(item, None)
                if tool_item is not None:
                    self.toolbar.addAction(tool_item)
        #load action added by plugins, This is a special case when reload
        #the toolbar after save the preferences widget
        for toolbar_action in settings.get_toolbar_item_for_plugins():
            self.toolbar.addAction(toolbar_action)

    def load_external_plugins(self, paths):
        for path in paths:
            self.plugin_manager.add_plugin_dir(path)
        #load all plugins!
        self.plugin_manager.discover()
        self.plugin_manager.load_all()

    def show_status_message(self, message):
        self.status.showMessage(message, 2000)

    def load_ui(self, centralWidget):
        #Set Application Font for ToolTips
        QToolTip.setFont(QFont(settings.FONT_FAMILY, 10))
        #Create Main Container to manage Tabs
        self.mainContainer = main_container.MainContainer(self)
        self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"),
                     self.change_window_title)
        self.connect(self.mainContainer,
                     SIGNAL("locateFunction(QString, QString, bool)"),
                     self.actions.locate_function)
        self.connect(self.mainContainer, SIGNAL("navigateCode(bool, int)"),
                     self.actions.navigate_code_history)
        self.connect(self.mainContainer, SIGNAL("addBackItemNavigation()"),
                     self.actions.add_back_item_navigation)
        self.connect(self.mainContainer, SIGNAL("updateFileMetadata()"),
                     self.actions.update_explorer)
        self.connect(self.mainContainer, SIGNAL("updateLocator(QString)"),
                     self.actions.update_explorer)
        self.connect(self.mainContainer, SIGNAL("openPreferences()"),
                     self._show_preferences)
        self.connect(self.mainContainer, SIGNAL("dontOpenStartPage()"),
                     self._dont_show_start_page_again)
        self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"),
                     self.status.handle_tab_changed)
        # Update symbols
        self.connect(self.mainContainer, SIGNAL("updateLocator(QString)"),
                     self.status.explore_file_code)
        #Create Explorer Panel
        self.explorer = explorer_container.ExplorerContainer(self)
        self.connect(self.central, SIGNAL("splitterCentralRotated()"),
                     self.explorer.rotate_tab_position)
        self.connect(self.explorer, SIGNAL("updateLocator()"),
                     self.status.explore_code)
        self.connect(self.explorer, SIGNAL("goToDefinition(int)"),
                     self.actions.editor_go_to_line)
        self.connect(self.explorer, SIGNAL("projectClosed(QString)"),
                     self.actions.close_files_from_project)
        #Create Misc Bottom Container
        self.misc = misc_container.MiscContainer(self)
        self.connect(self.mainContainer, SIGNAL("findOcurrences(QString)"),
                     self.misc.show_find_occurrences)

        centralWidget.insert_central_container(self.mainContainer)
        centralWidget.insert_lateral_container(self.explorer)
        centralWidget.insert_bottom_container(self.misc)
        self.connect(self.mainContainer,
                     SIGNAL("cursorPositionChange(int, int)"),
                     self.central.lateralPanel.update_line_col)
        # TODO: Change current symbol on move
        #self.connect(self.mainContainer,
        #SIGNAL("cursorPositionChange(int, int)"),
        #self.explorer.update_current_symbol)
        self.connect(self.mainContainer, SIGNAL("enabledFollowMode(bool)"),
                     self.central.enable_follow_mode_scrollbar)

        if settings.SHOW_START_PAGE:
            self.mainContainer.show_start_page()

    def _show_preferences(self):
        pref = preferences.PreferencesWidget(self.mainContainer)
        pref.show()

    def _dont_show_start_page_again(self):
        settings.SHOW_START_PAGE = False
        qsettings = QSettings()
        qsettings.beginGroup('preferences')
        qsettings.beginGroup('general')
        qsettings.setValue('showStartPage', settings.SHOW_START_PAGE)
        qsettings.endGroup()
        qsettings.endGroup()
        self.mainContainer.actualTab.close_tab()

    def load_session_files_projects(self,
                                    filesTab1,
                                    filesTab2,
                                    projects,
                                    current_file,
                                    recent_files=None):
        self.mainContainer.open_files(filesTab1, notIDEStart=False)
        self.mainContainer.open_files(filesTab2,
                                      mainTab=False,
                                      notIDEStart=False)
        self.explorer.open_session_projects(projects, notIDEStart=False)
        if current_file:
            self.mainContainer.open_file(current_file, notStart=False)
        if recent_files is not None:
            self._menuFile.update_recent_files(recent_files)

    def open_file(self, filename):
        if filename:
            self.mainContainer.open_file(filename)

    def open_project(self, project):
        if project:
            self.actions.open_project(project)

    def __get_profile(self):
        return self.profile

    def __set_profile(self, profileName):
        self.profile = profileName
        if self.profile is not None:
            self.setWindowTitle('NINJA-IDE (PROFILE: %s)' % self.profile)
        else:
            self.setWindowTitle(
                'NINJA-IDE {Ninja-IDE Is Not Just Another IDE}')

    Profile = property(__get_profile, __set_profile)

    def change_window_title(self, title):
        if self.profile is None:
            self.setWindowTitle('NINJA-IDE - %s' % title)
        else:
            self.setWindowTitle('NINJA-IDE (PROFILE: %s) - %s' %
                                (self.profile, title))
        currentEditor = self.mainContainer.get_actual_editor()
        if currentEditor is not None:
            line = currentEditor.textCursor().blockNumber() + 1
            col = currentEditor.textCursor().columnNumber()
            self.central.lateralPanel.update_line_col(line, col)

    def wheelEvent(self, event):
        if event.modifiers() == Qt.ShiftModifier:
            if event.delta() == 120 and self.opacity < settings.MAX_OPACITY:
                self.opacity += 0.1
            elif event.delta() == -120 and self.opacity > settings.MIN_OPACITY:
                self.opacity -= 0.1
            self.setWindowOpacity(self.opacity)
            event.ignore()
        else:
            QMainWindow.wheelEvent(self, event)

    def save_settings(self):
        """Save the settings before the application is closed with QSettings.

        Info saved: Tabs and projects opened, windows state(size and position).
        """
        qsettings = QSettings()
        editor_widget = self.mainContainer.get_actual_editor()
        current_file = ''
        if editor_widget is not None:
            current_file = editor_widget.ID
        if qsettings.value('preferences/general/loadFiles', 'true') == 'true':
            openedFiles = self.mainContainer.get_opened_documents()
            projects_obj = self.explorer.get_opened_projects()
            projects = [p.path for p in projects_obj]
            qsettings.setValue('openFiles/projects', projects)
            if len(openedFiles) > 0:
                qsettings.setValue('openFiles/mainTab', openedFiles[0])
            if len(openedFiles) == 2:
                qsettings.setValue('openFiles/secondaryTab', openedFiles[1])
            qsettings.setValue('openFiles/currentFile', current_file)
            qsettings.setValue(
                'openFiles/recentFiles',
                self.mainContainer._tabMain.get_recent_files_list())
        qsettings.setValue('preferences/editor/bookmarks', settings.BOOKMARKS)
        qsettings.setValue('preferences/editor/breakpoints',
                           settings.BREAKPOINTS)
        qsettings.setValue('preferences/general/toolbarArea',
                           self.toolBarArea(self.toolbar))
        #Save if the windows state is maximixed
        if (self.isMaximized()):
            qsettings.setValue("window/maximized", True)
        else:
            qsettings.setValue("window/maximized", False)
            #Save the size and position of the mainwindow
            qsettings.setValue("window/size", self.size())
            qsettings.setValue("window/pos", self.pos())
        #Save the size of de splitters
        qsettings.setValue("window/central/areaSize",
                           self.central.get_area_sizes())
        qsettings.setValue("window/central/mainSize",
                           self.central.get_main_sizes())
        #Save the toolbar visibility
        qsettings.setValue("window/hide_toolbar", not self.toolbar.isVisible())
        #Save Profiles
        if self.profile is not None:
            self.actions.save_profile(self.profile)
        else:
            qsettings.setValue('ide/profiles', settings.PROFILES)

    def load_window_geometry(self):
        """Load from QSettings the window size of de Ninja IDE"""
        qsettings = QSettings()
        if qsettings.value("window/maximized", 'true') == 'true':
            self.setWindowState(Qt.WindowMaximized)
        else:
            print dir(QSizeF)

            self.resize(
                qsettings.value("window/size", QSizeF(800, 600)).toSize())
            self.move(
                qsettings.value("window/pos", QPointF(100, 100)).toPoint())

    def closeEvent(self, event):
        if self.s_listener:
            self.s_listener.close()
        if settings.CONFIRM_EXIT and \
        self.mainContainer.check_for_unsaved_tabs():
            unsaved_files = self.mainContainer.get_unsaved_files()
            txt = '\n'.join(unsaved_files)
            val = QMessageBox.question(
                self, self.tr("Some changes were not saved"),
                self.tr("%s\n\nDo you want to exit anyway?" % txt),
                QMessageBox.Yes, QMessageBox.No)
            if val == QMessageBox.No:
                event.ignore()
        QApplication.instance().setCursorFlashTime(cursor_flash_time)
        self.emit(SIGNAL("goingDown()"))
        self.save_settings()
        completion_daemon.shutdown_daemon()
        #close python documentation server (if running)
        self.mainContainer.close_python_doc()
        #Shutdown PluginManager
        self.plugin_manager.shutdown()

    def notify_plugin_errors(self):
        errors = self.plugin_manager.errors
        if errors:
            plugin_error_dialog = traceback_widget.PluginErrorDialog()
            for err_tuple in errors:
                plugin_error_dialog.add_traceback(err_tuple[0], err_tuple[1])
            #show the dialog
            plugin_error_dialog.exec_()
Esempio n. 6
0
class IDE(QMainWindow):
    """This class is like the Sauron's Ring:
    One ring to rule them all, One ring to find them,
    One ring to bring them all and in the darkness bind them.

    This Class knows all the containers, and its know by all the containers,
    but the containers don't need to know between each other, in this way we
    can keep a better api without the need to tie the behaviour between
    the widgets, and let them just consume the 'actions' they need."""

###############################################################################
# SIGNALS
#
# goingDown()
###############################################################################

    __IDESERVICES = {}
    __IDECONNECTIONS = {}
    __IDESHORTCUTS = {}
    __IDEBARCATEGORIES = {}
    __IDEMENUS = {}
    __IDETOOLBAR = {}
    # CONNECTIONS structure:
    # ({'target': service_name, 'signal_name': string, 'slot': function_obj},)
    # On modify add: {connected: True}
    __instance = None
    __created = False

    def __init__(self, start_server=False):
        QMainWindow.__init__(self)
        self.setWindowTitle('NINJA-IDE {Ninja-IDE Is Not Just Another IDE}')
        self.setMinimumSize(750, 500)
        QToolTip.setFont(QFont(settings.FONT.family(), 10))
        #Load the size and the position of the main window
        self.load_window_geometry()
        self.__project_to_open = 0

        #Editables
        self.__neditables = {}
        #Filesystem
        self.filesystem = nfilesystem.NVirtualFileSystem()

        #Start server if needed
        self.s_listener = None
        if start_server:
            self.s_listener = QLocalServer()
            self.s_listener.listen("ninja_ide")
            self.connect(self.s_listener, SIGNAL("newConnection()"),
                         self._process_connection)

        #Sessions handler
        self._session = None
        #Opacity
        self.opacity = settings.MAX_OPACITY

        #ToolBar
        self.toolbar = QToolBar(self)
        if settings.IS_MAC_OS:
            self.toolbar.setIconSize(QSize(36, 36))
        else:
            self.toolbar.setIconSize(QSize(24, 24))
        self.toolbar.setToolTip(translations.TR_IDE_TOOLBAR_TOOLTIP)
        self.toolbar.setToolButtonStyle(Qt.ToolButtonIconOnly)
        # Set toggleViewAction text and tooltip
        self.toggleView = self.toolbar.toggleViewAction()
        self.toggleView.setText(translations.TR_TOOLBAR_VISIBILITY)
        self.toggleView.setToolTip(translations.TR_TOOLBAR_VISIBILITY)
        self.addToolBar(settings.TOOLBAR_AREA, self.toolbar)
        if settings.HIDE_TOOLBAR:
            self.toolbar.hide()
        #Notificator
        self.notification = notification.Notification(self)

        #Plugin Manager
        # CHECK ACTIVATE PLUGINS SETTING
        #services = {
            #'editor': plugin_services.MainService(),
            #'toolbar': plugin_services.ToolbarService(self.toolbar),
            ##'menuApp': plugin_services.MenuAppService(self.pluginsMenu),
            #'menuApp': plugin_services.MenuAppService(None),
            #'explorer': plugin_services.ExplorerService(),
            #'misc': plugin_services.MiscContainerService(self.misc)}
        #serviceLocator = plugin_manager.ServiceLocator(services)
        serviceLocator = plugin_manager.ServiceLocator(None)
        self.plugin_manager = plugin_manager.PluginManager(resources.PLUGINS,
                                                           serviceLocator)
        self.plugin_manager.discover()
        #load all plugins!
        self.plugin_manager.load_all()

        #Tray Icon
        self.trayIcon = updates.TrayIconUpdates(self)
        self.connect(self.trayIcon, SIGNAL("closeTrayIcon()"),
                     self._close_tray_icon)
        self.trayIcon.show()

        key = Qt.Key_1
        for i in range(10):
            if settings.IS_MAC_OS:
                short = ui_tools.TabShortcuts(
                    QKeySequence(Qt.CTRL + Qt.ALT + key), self, i)
            else:
                short = ui_tools.TabShortcuts(
                    QKeySequence(Qt.ALT + key), self, i)
            key += 1
            self.connect(short, SIGNAL("activated()"), self._change_tab_index)
        short = ui_tools.TabShortcuts(QKeySequence(Qt.ALT + Qt.Key_0), self, 10)
        self.connect(short, SIGNAL("activated()"), self._change_tab_index)

        # Register menu categories
        IDE.register_bar_category(translations.TR_MENU_FILE, 100)
        IDE.register_bar_category(translations.TR_MENU_EDIT, 110)
        IDE.register_bar_category(translations.TR_MENU_VIEW, 120)
        IDE.register_bar_category(translations.TR_MENU_SOURCE, 130)
        IDE.register_bar_category(translations.TR_MENU_PROJECT, 140)
        IDE.register_bar_category(translations.TR_MENU_EXTENSIONS, 150)
        IDE.register_bar_category(translations.TR_MENU_ABOUT, 160)
        # Register General Menu Items
        ui_tools.install_shortcuts(self, actions.ACTIONS_GENERAL, self)

        self.register_service('ide', self)
        self.register_service('toolbar', self.toolbar)
        #Register signals connections
        connections = (
            {'target': 'main_container',
             'signal_name': 'fileSaved(QString)',
             'slot': self.show_message},
            {'target': 'main_container',
             'signal_name': 'currentEditorChanged(QString)',
             'slot': self.change_window_title},
            {'target': 'main_container',
             'signal_name': 'openPreferences()',
             'slot': self.show_preferences},
            {'target': 'main_container',
             'signal_name': 'allTabsClosed()',
             'slot': self._last_tab_closed},
            {'target': 'explorer_container',
             'signal_name': 'changeWindowTitle(QString)',
             'slot': self.change_window_title},
            {'target': 'explorer_container',
             'signal_name': 'projectClosed(QString)',
             'slot': self.close_project},
            )
        self.register_signals('ide', connections)
        # Central Widget MUST always exists
        self.central = IDE.get_service('central_container')
        self.setCentralWidget(self.central)
        # Install Services
        for service_name in self.__IDESERVICES:
            self.install_service(service_name)
        IDE.__created = True
        menu_bar = IDE.get_service('menu_bar')
        if menu_bar:
            menu_bar.load_menu(self)
            #These two are the same service, I think that's ok
            menu_bar.load_toolbar(self)
        IDE.__instance = self

    @classmethod
    def get_service(cls, service_name):
        """Return the instance of a registered service."""
        return cls.__IDESERVICES.get(service_name, None)

    def get_menuitems(self):
        """Return a dictionary with the registered menu items."""
        return IDE.__IDEMENUS

    def get_bar_categories(self):
        """Get the registered Categories for the Application menus."""
        return IDE.__IDEBARCATEGORIES

    def get_toolbaritems(self):
        """Return a dictionary with the registered menu items."""
        return IDE.__IDETOOLBAR

    @classmethod
    def register_service(cls, service_name, obj):
        """Register a service providing the service name and the instance."""
        cls.__IDESERVICES[service_name] = obj
        if cls.__created:
            cls.__instance.install_service(service_name)

    def install_service(self, service_name):
        """Activate the registered service."""
        obj = IDE.__IDESERVICES.get(service_name, None)
        func = getattr(obj, 'install', None)
        if isinstance(func, collections.Callable):
            func()
        self._connect_signals()

    def place_me_on(self, name, obj, region="central", top=False):
        """Place a widget in some of the areas in the IDE.
        @name: id to access to that widget later if needed.
        @obj: the instance of the widget to be placed.
        @region: the area where to put the widget [central, lateral]
        @top: place the widget as the first item in the split."""
        self.central.add_to_region(name, obj, region, top)

    @classmethod
    def register_signals(cls, service_name, connections):
        """Register all the signals that a particular service wants to be
        attached of.
        @service_name: id of the service
        @connections: list of dictionaries for the connection with:
            - 'target': 'the_other_service_name',
            - 'signal_name': 'name of the signal in the other service',
            - 'slot': function object in this service"""
        cls.__IDECONNECTIONS[service_name] = connections
        if cls.__created:
            cls.__instance._connect_signals()

    def _connect_signals(self):
        """Connect the signals between the different services."""
        for service_name in IDE.__IDECONNECTIONS:
            connections = IDE.__IDECONNECTIONS[service_name]
            for connection in connections:
                if connection.get('connected', False):
                    continue
                target = IDE.__IDESERVICES.get(
                    connection['target'], None)
                slot = connection['slot']
                signal_name = connection['signal_name']
                if target and isinstance(slot, collections.Callable):
                    self.connect(target, SIGNAL(signal_name), slot)
                    connection['connected'] = True

    @classmethod
    def register_shortcut(cls, shortcut_name, shortcut, action=None):
        """Register a shortcut and action."""
        cls.__IDESHORTCUTS[shortcut_name] = (shortcut, action)

    @classmethod
    def register_menuitem(cls, menu_action, section, weight):
        """Register a QAction or QMenu in the IDE to be loaded later in the
        menubar using the section(string) to define where is going to be
        contained, and the weight define the order where is going to be
        placed.
        @menu_action: QAction or QMenu
        @section: String (name)
        @weight: int"""
        cls.__IDEMENUS[menu_action] = (section, weight)

    @classmethod
    def register_toolbar(cls, action, section, weight):
        """Register a QAction in the IDE to be loaded later in the
        toolbar using the section(string) to define where is going to be
        contained, and the weight define the order where is going to be
        placed.
        @action: QAction
        @section: String (name)
        @weight: int"""
        cls.__IDETOOLBAR[action] = (section, weight)

    @classmethod
    def register_bar_category(cls, category_name, weight):
        """Register a Menu Category to be created with the proper weight.
        @category_name: string
        @weight: int"""
        cls.__IDEBARCATEGORIES[category_name] = weight

    @classmethod
    def update_shortcut(cls, shortcut_name):
        """Update all the shortcuts of the application."""
        short = resources.get_shortcut
        shortcut, action = cls.__IDESHORTCUTS.get(shortcut_name)
        if shortcut:
            shortcut.setKey(short(shortcut_name))
        if action:
            action.setShortcut(short(shortcut_name))

    def get_or_create_nfile(self, filename):
        """For convenience access to files from ide"""
        return self.filesystem.get_file(nfile_path=filename)

    def get_or_create_editable(self, filename="", nfile=None):
        if nfile is None:
            nfile = self.filesystem.get_file(nfile_path=filename)
        editable = self.__neditables.get(nfile)
        if editable is None:
            editable = neditable.NEditable(nfile)
            self.connect(editable, SIGNAL("fileClosing(PyQt_PyObject)"),
                         self._unload_neditable)
            self.__neditables[nfile] = editable
        return editable

    def _unload_neditable(self, editable):
        self.__neditables.pop(editable.nfile)
        editable.nfile.deleteLater()
        editable.editor.deleteLater()
        editable.deleteLater()

    @property
    def opened_files(self):
        return tuple(self.__neditables.keys())

    def get_project_for_file(self, filename):
        project = None
        if filename:
            project = self.filesystem.get_project_for_file(filename)
        return project

    def create_project(self, path):
        nproj = nproject.NProject(path)
        self.filesystem.open_project(nproj)
        return nproj

    def close_project(self, project_path):
        self.filesystem.close_project(project_path)

    def get_projects(self):
        return self.filesystem.get_projects()

    def get_current_project(self):
        current_project = None
        projects = self.filesystem.get_projects()
        for project in projects:
            if projects[project].is_current:
                current_project = projects[project]
                break
        return current_project

    @classmethod
    def select_current(cls, widget):
        """Show the widget with a 4px lightblue border line."""
        widget.setProperty("highlight", True)
        widget.style().unpolish(widget)
        widget.style().polish(widget)

    @classmethod
    def unselect_current(cls, widget):
        """Remove the 4px lightblue border line from the widget."""
        widget.setProperty("highlight", False)
        widget.style().unpolish(widget)
        widget.style().polish(widget)

    def _close_tray_icon(self):
        """Close the System Tray Icon."""
        self.trayIcon.hide()
        self.trayIcon.deleteLater()

    def _change_tab_index(self):
        """Change the tabs of the current TabWidget using alt+numbers."""
        widget = QApplication.focusWidget()
        shortcut_index = getattr(widget, 'shortcut_index', None)
        if shortcut_index:
            obj = self.sender()
            shortcut_index(obj.index)

    def _process_connection(self):
        """Read the ipc input from another instance of ninja."""
        connection = self.s_listener.nextPendingConnection()
        connection.waitForReadyRead()
        data = connection.readAll()
        connection.close()
        if data:
            files, projects = str(data).split(ipc.project_delimiter, 1)
            files = [(x.split(':')[0], int(x.split(':')[1]))
                     for x in files.split(ipc.file_delimiter)]
            projects = projects.split(ipc.project_delimiter)
            self.load_session_files_projects(files, [], projects, None)

    def fullscreen_mode(self):
        """Change to fullscreen mode."""
        if self.isFullScreen():
            self.showMaximized()
        else:
            self.showFullScreen()

    def change_toolbar_visibility(self):
        """Switch the toolbar visibility"""
        if self.toolbar.isVisible():
            self.toolbar.hide()
        else:
            self.toolbar.show()

    def load_external_plugins(self, paths):
        """Load external plugins, the ones added to ninja throw the cmd."""
        for path in paths:
            self.plugin_manager.add_plugin_dir(path)
        #load all plugins!
        self.plugin_manager.discover()
        self.plugin_manager.load_all()

    def _last_tab_closed(self):
        """
        Called when the last tasb is closed
        """
        self.explorer.cleanup_tabs()

    def show_preferences(self):
        """Open the Preferences Dialog."""
        pref = preferences.Preferences(self)
        pref.show()

    def load_session_files_projects(self, files, projects,
                                    current_file, recent_files=None):
        """Load the files and projects from previous session."""
        main_container = IDE.get_service('main_container')
        projects_explorer = IDE.get_service('projects_explorer')
        if main_container and files:
            for fileData in files:
                if file_manager.file_exists(fileData[0]):
                    mtime = os.stat(fileData[0]).st_mtime
                    ignore_checkers = (mtime == fileData[2])
                    main_container.open_file(fileData[0], fileData[1],
                                             ignore_checkers=ignore_checkers)
            if current_file:
                main_container.open_file(current_file)
        if projects_explorer and projects:
            projects_explorer.load_session_projects(projects)
            #if recent_files is not None:
                #menu_file = IDE.get_service('menu_file')
                #menu_file.update_recent_files(recent_files)

    #def _set_editors_project_data(self):
        #self.__project_to_open -= 1
        #if self.__project_to_open == 0:
            #self.disconnect(self.explorer, SIGNAL("projectOpened(QString)"),
                #self._set_editors_project_data)
            #self.mainContainer.update_editor_project()

    #def open_file(self, filename):
        #if filename:
            #self.mainContainer.open_file(filename)

    #def open_project(self, project):
        #if project:
            #self.actions.open_project(project)

    def __get_session(self):
        return self._session

    def __set_session(self, sessionName):
        self._session = sessionName
        if self._session is not None:
            self.setWindowTitle(translations.TR_SESSION_IDE_HEADER %
                                {'session': self._session})
        else:
            self.setWindowTitle(
                'NINJA-IDE {Ninja-IDE Is Not Just Another IDE}')

    Session = property(__get_session, __set_session)

    def change_window_title(self, title):
        """Change the title of the Application."""
        if self._session is None:
            self.setWindowTitle('NINJA-IDE - %s' % title)
        else:
            self.setWindowTitle((translations.TR_SESSION_IDE_HEADER %
                                {'session': self._session}) + ' - %s' % title)

    def wheelEvent(self, event):
        """Change the opacity of the application."""
        if event.modifiers() == Qt.ShiftModifier:
            if event.delta() == 120 and self.opacity < settings.MAX_OPACITY:
                self.opacity += 0.1
            elif event.delta() == -120 and self.opacity > settings.MIN_OPACITY:
                self.opacity -= 0.1
            self.setWindowOpacity(self.opacity)
            event.ignore()
        else:
            QMainWindow.wheelEvent(self, event)

    @classmethod
    def ninja_settings(cls):
        qsettings = nsettings.NSettings(resources.SETTINGS_PATH,
                                        prefix="ns")
        if cls.__created:
            cls.__instance.connect(
                qsettings,
                SIGNAL("valueChanged(QString, PyQt_PyObject)"),
                cls.__instance._settings_value_changed)
        return qsettings

    @classmethod
    def data_settings(cls):
        qsettings = nsettings.NSettings(resources.DATA_SETTINGS_PATH,
                                        prefix="ds")
        if cls.__created:
            cls.__instance.connect(
                qsettings,
                SIGNAL("valueChanged(QString, PyQt_PyObject)"),
                cls.__instance._settings_value_changed)
        return qsettings

    def _settings_value_changed(self, key, value):
        signal_name = "%s(PyQt_PyObject)" % key.replace("/", "_")
        self.emit(SIGNAL(signal_name), value)

    def save_settings(self):
        """Save the settings before the application is closed with QSettings.

        Info saved: Tabs and projects opened, windows state(size and position).
        """
        qsettings = IDE.ninja_settings()
        data_qsettings = IDE.data_settings()
        main_container = self.get_service("main_container")
        editor_widget = None
        if main_container:
            editor_widget = main_container.get_current_editor()
        current_file = ''
        if editor_widget is not None:
            current_file = editor_widget.file_path
        if qsettings.value('preferences/general/loadFiles', True, type=bool):
            openedFiles = self.filesystem.get_files()
            projects_obj = self.filesystem.get_projects()
            projects = [projects_obj[proj].path for proj in projects_obj]
            data_qsettings.setValue('lastSession/projects', projects)
            files_info = []
            for path in openedFiles:
                editable = self.__neditables.get(openedFiles[path])
                if editable is not None and editable.is_dirty:
                    stat_value = 0
                else:
                    stat_value = os.stat(path).st_mtime
                files_info.append([path,
                                  editable.editor.get_cursor_position(),
                                  stat_value])
            data_qsettings.setValue('lastSession/openedFiles', files_info)
            if current_file is not None:
                data_qsettings.setValue('lastSession/currentFile', current_file)
            data_qsettings.setValue('lastSession/recentFiles',
                                    settings.LAST_OPENED_FILES)
        data_qsettings.setValue('preferences/editor/bookmarks',
                                settings.BOOKMARKS)
        data_qsettings.setValue('preferences/editor/breakpoints',
                                settings.BREAKPOINTS)

        # Session
        if self._session is not None:
            val = QMessageBox.question(
                self,
                translations.TR_SESSION_ACTIVE_IDE_CLOSING_TITLE,
                (translations.TR_SESSION_ACTIVE_IDE_CLOSING_BODY %
                    {'session': self.Session}),
                QMessageBox.Yes, QMessageBox.No)
            if val == QMessageBox.Yes:
                session_manager.SessionsManager.save_session_data(
                    self.Session, self)
        #qsettings.setValue('preferences/general/toolbarArea',
            #self.toolBarArea(self.toolbar))
        #Save if the windows state is maximixed
        if(self.isMaximized()):
            qsettings.setValue("window/maximized", True)
        else:
            qsettings.setValue("window/maximized", False)
            #Save the size and position of the mainwindow
            qsettings.setValue("window/size", self.size())
            qsettings.setValue("window/pos", self.pos())
        self.central.save_configuration()
        #Save the toolbar visibility
        #if not self.toolbar.isVisible() and self.menuBar().isVisible():
            #qsettings.setValue("window/hide_toolbar", True)
        #else:
            #qsettings.setValue("window/hide_toolbar", False)
        #Save Misc state
        #qsettings.setValue("window/show_region1", self.misc.isVisible())
        #Save Profiles
        #if self.profile is not None:
            #self.actions.save_profile(self.profile)
        #else:
            #qsettings.setValue('ide/profiles', settings.PROFILES)

    def activate_profile(self):
        """Show the Session Manager dialog."""
        profilesLoader = session_manager.SessionsManager(self)
        profilesLoader.show()

    def deactivate_profile(self):
        """Close the Session Session."""
        self.Session = None

    def load_window_geometry(self):
        """Load from QSettings the window size of de Ninja IDE"""
        qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat)
        if qsettings.value("window/maximized", True, type=bool):
            self.setWindowState(Qt.WindowMaximized)
        else:
            self.resize(qsettings.value(
                "window/size",
                QSizeF(800, 600).toSize(), type='QSize'))
            self.move(qsettings.value(
                "window/pos",
                QPointF(100, 100).toPoint(), type='QPoint'))

    def _get_unsaved_files(self):
        """Return an array with the path of the unsaved files."""
        unsaved = []
        files = self.filesystem.get_files()
        for f in files:
            editable = self.__neditables.get(files[f])
            if editable is not None:
                if editable.editor.is_modified:
                    unsaved.append(f)
        return unsaved

    def _save_unsaved_files(self, files):
        """Save the files from the paths in the array."""
        for f in files:
            editable = self.get_or_create_editable(f)
            editable.ignore_checkers = True
            editable.save_content()

    def closeEvent(self, event):
        """Saves some global settings before closing."""
        if self.s_listener:
            self.s_listener.close()
        main_container = self.get_service("main_container")
        unsaved_files = self._get_unsaved_files()
        if (settings.CONFIRM_EXIT and unsaved_files):
            txt = '\n'.join(unsaved_files)
            val = QMessageBox.question(
                self,
                translations.TR_IDE_CONFIRM_EXIT_TITLE,
                (translations.TR_IDE_CONFIRM_EXIT_BODY % {'files': txt}),
                QMessageBox.Yes, QMessageBox.No, QMessageBox.Cancel)
            if val == QMessageBox.Yes:
                #Saves all open files
                self._save_unsaved_files(unsaved_files)
            if val == QMessageBox.Cancel:
                event.ignore()
                return
        self.save_settings()
        self.emit(SIGNAL("goingDown()"))
        #close python documentation server (if running)
        main_container.close_python_doc()
        #Shutdown PluginManager
        self.plugin_manager.shutdown()
        #completion_daemon.shutdown_daemon()
        super(IDE, self).closeEvent(event)

    def notify_plugin_errors(self):
        #TODO: Check if the Plugin Error dialog can be improved
        errors = self.plugin_manager.errors
        if errors:
            plugin_error_dialog = traceback_widget.PluginErrorDialog()
            for err_tuple in errors:
                plugin_error_dialog.add_traceback(err_tuple[0], err_tuple[1])
            #show the dialog
            plugin_error_dialog.exec_()

    def show_message(self, message, duration=3000):
        """Show status message."""
        self.notification.set_message(message, duration)
        self.notification.show()

    def show_plugins_store(self):
        """Open the Plugins Manager to install/uninstall plugins."""
        store = plugins_store.PluginsStore(self)
        store.show()
        #manager = plugins_manager.PluginsManagerWidget(self)
        #manager.show()
        #if manager._requirements:
            #dependencyDialog = plugins_manager.DependenciesHelpDialog(
                #manager._requirements)
            #dependencyDialog.show()

    def show_languages(self):
        """Open the Language Manager to install/uninstall languages."""
        manager = language_manager.LanguagesManagerWidget(self)
        manager.show()

    def show_schemes(self):
        """Open the Schemes Manager to install/uninstall schemes."""
        manager = schemes_manager.SchemesManagerWidget(self)
        manager.show()

    def show_about_qt(self):
        """Show About Qt Dialog."""
        QMessageBox.aboutQt(self, translations.TR_ABOUT_QT)

    def show_about_ninja(self):
        """Show About NINJA-IDE Dialog."""
        about = about_ninja.AboutNinja(self)
        about.show()

    def show_python_detection(self):
        """Show Python detection dialog for windows."""
        #TODO: Notify the user when no python version could be found
        suggested = settings.detect_python_path()
        if suggested:
            dialog = python_detect_dialog.PythonDetectDialog(suggested, self)
            dialog.show()
Esempio n. 7
0
class IDE(QMainWindow):
    """This class is like the Sauron's Ring:
    One ring to rule them all, One ring to find them,
    One ring to bring them all and in the darkness bind them.

    This Class knows all the containers, and its know by all the containers,
    but the containers don't need to know between each other, in this way we
    can keep a better api without the need to tie the behaviour between
    the widgets, and let them just consume the 'actions' they need."""

    ###############################################################################
    # SIGNALS
    #
    # goingDown()
    ###############################################################################

    __IDESERVICES = {}
    __IDECONNECTIONS = {}
    __IDESHORTCUTS = {}
    __IDEBARCATEGORIES = {}
    __IDEMENUS = {}
    __IDETOOLBAR = {}
    # CONNECTIONS structure:
    # ({'target': service_name, 'signal_name': string, 'slot': function_obj},)
    # On modify add: {connected: True}
    __instance = None
    __created = False

    def __init__(self, start_server=False):
        QMainWindow.__init__(self)
        self.setWindowTitle('NINJA-IDE {Ninja-IDE Is Not Just Another IDE}')
        self.setMinimumSize(750, 500)
        QToolTip.setFont(QFont(settings.FONT.family(), 10))
        #Load the size and the position of the main window
        self.load_window_geometry()
        self.__project_to_open = 0

        #Editables
        self.__neditables = {}
        #Filesystem
        self.filesystem = nfilesystem.NVirtualFileSystem()

        #Sessions handler
        self._session = None
        #Opacity
        self.opacity = settings.MAX_OPACITY

        #ToolBar
        self.toolbar = QToolBar(self)
        if settings.IS_MAC_OS:
            self.toolbar.setIconSize(QSize(36, 36))
        else:
            self.toolbar.setIconSize(QSize(24, 24))
        self.toolbar.setToolTip(translations.TR_IDE_TOOLBAR_TOOLTIP)
        self.toolbar.setToolButtonStyle(Qt.ToolButtonIconOnly)
        # Set toggleViewAction text and tooltip
        self.toggleView = self.toolbar.toggleViewAction()
        self.toggleView.setText(translations.TR_TOOLBAR_VISIBILITY)
        self.toggleView.setToolTip(translations.TR_TOOLBAR_VISIBILITY)
        self.addToolBar(settings.TOOLBAR_AREA, self.toolbar)
        if settings.HIDE_TOOLBAR:
            self.toolbar.hide()
        #Notificator
        self.notification = notification.Notification(self)

        #Plugin Manager
        # CHECK ACTIVATE PLUGINS SETTING
        #services = {
        #'editor': plugin_services.MainService(),
        #'toolbar': plugin_services.ToolbarService(self.toolbar),
        ##'menuApp': plugin_services.MenuAppService(self.pluginsMenu),
        #'menuApp': plugin_services.MenuAppService(None),
        #'explorer': plugin_services.ExplorerService(),
        #'misc': plugin_services.MiscContainerService(self.misc)}
        #serviceLocator = plugin_manager.ServiceLocator(services)
        serviceLocator = plugin_manager.ServiceLocator(None)
        self.plugin_manager = plugin_manager.PluginManager(
            resources.PLUGINS, serviceLocator)
        self.plugin_manager.discover()
        #load all plugins!
        self.plugin_manager.load_all()

        #Tray Icon
        self.trayIcon = updates.TrayIconUpdates(self)
        self.connect(self.trayIcon, SIGNAL("closeTrayIcon()"),
                     self._close_tray_icon)
        self.trayIcon.show()

        key = Qt.Key_1
        for i in range(10):
            if settings.IS_MAC_OS:
                short = ui_tools.TabShortcuts(
                    QKeySequence(Qt.CTRL + Qt.ALT + key), self, i)
            else:
                short = ui_tools.TabShortcuts(QKeySequence(Qt.ALT + key), self,
                                              i)
            key += 1
            self.connect(short, SIGNAL("activated()"), self._change_tab_index)
        short = ui_tools.TabShortcuts(QKeySequence(Qt.ALT + Qt.Key_0), self,
                                      10)
        self.connect(short, SIGNAL("activated()"), self._change_tab_index)

        # Register menu categories
        IDE.register_bar_category(translations.TR_MENU_FILE, 100)
        IDE.register_bar_category(translations.TR_MENU_EDIT, 110)
        IDE.register_bar_category(translations.TR_MENU_VIEW, 120)
        IDE.register_bar_category(translations.TR_MENU_SOURCE, 130)
        IDE.register_bar_category(translations.TR_MENU_PROJECT, 140)
        IDE.register_bar_category(translations.TR_MENU_EXTENSIONS, 150)
        IDE.register_bar_category(translations.TR_MENU_ABOUT, 160)
        # Register General Menu Items
        ui_tools.install_shortcuts(self, actions.ACTIONS_GENERAL, self)

        self.register_service('ide', self)
        self.register_service('toolbar', self.toolbar)
        self.register_service('filesystem', self.filesystem)
        #Register signals connections
        connections = (
            {
                'target': 'main_container',
                'signal_name': 'fileSaved(QString)',
                'slot': self.show_message
            },
            {
                'target': 'main_container',
                'signal_name': 'currentEditorChanged(QString)',
                'slot': self.change_window_title
            },
            {
                'target': 'main_container',
                'signal_name': 'openPreferences()',
                'slot': self.show_preferences
            },
            {
                'target': 'main_container',
                'signal_name': 'allTabsClosed()',
                'slot': self._last_tab_closed
            },
            {
                'target': 'explorer_container',
                'signal_name': 'changeWindowTitle(QString)',
                'slot': self.change_window_title
            },
            {
                'target': 'explorer_container',
                'signal_name': 'projectClosed(QString)',
                'slot': self.close_project
            },
        )
        self.register_signals('ide', connections)
        # Central Widget MUST always exists
        self.central = IDE.get_service('central_container')
        self.setCentralWidget(self.central)
        # Install Services
        for service_name in self.__IDESERVICES:
            self.install_service(service_name)
        IDE.__created = True
        # Place Status Bar
        main_container = IDE.get_service('main_container')
        status_bar = IDE.get_service('status_bar')
        main_container.add_status_bar(status_bar)
        # Load Menu Bar
        menu_bar = IDE.get_service('menu_bar')
        if menu_bar:
            menu_bar.load_menu(self)
            #These two are the same service, I think that's ok
            menu_bar.load_toolbar(self)

        #Start server if needed
        self.s_listener = None
        if start_server:
            self.s_listener = QLocalServer()
            self.s_listener.listen("ninja_ide")
            self.connect(self.s_listener, SIGNAL("newConnection()"),
                         self._process_connection)

        IDE.__instance = self

    @classmethod
    def get_service(cls, service_name):
        """Return the instance of a registered service."""
        return cls.__IDESERVICES.get(service_name, None)

    def get_menuitems(self):
        """Return a dictionary with the registered menu items."""
        return IDE.__IDEMENUS

    def get_bar_categories(self):
        """Get the registered Categories for the Application menus."""
        return IDE.__IDEBARCATEGORIES

    def get_toolbaritems(self):
        """Return a dictionary with the registered menu items."""
        return IDE.__IDETOOLBAR

    @classmethod
    def register_service(cls, service_name, obj):
        """Register a service providing the service name and the instance."""
        cls.__IDESERVICES[service_name] = obj
        if cls.__created:
            cls.__instance.install_service(service_name)

    def install_service(self, service_name):
        """Activate the registered service."""
        obj = IDE.__IDESERVICES.get(service_name, None)
        func = getattr(obj, 'install', None)
        if isinstance(func, collections.Callable):
            func()
        self._connect_signals()

    def place_me_on(self, name, obj, region="central", top=False):
        """Place a widget in some of the areas in the IDE.
        @name: id to access to that widget later if needed.
        @obj: the instance of the widget to be placed.
        @region: the area where to put the widget [central, lateral]
        @top: place the widget as the first item in the split."""
        self.central.add_to_region(name, obj, region, top)

    @classmethod
    def register_signals(cls, service_name, connections):
        """Register all the signals that a particular service wants to be
        attached of.
        @service_name: id of the service
        @connections: list of dictionaries for the connection with:
            - 'target': 'the_other_service_name',
            - 'signal_name': 'name of the signal in the other service',
            - 'slot': function object in this service"""
        cls.__IDECONNECTIONS[service_name] = connections
        if cls.__created:
            cls.__instance._connect_signals()

    def _connect_signals(self):
        """Connect the signals between the different services."""
        for service_name in IDE.__IDECONNECTIONS:
            connections = IDE.__IDECONNECTIONS[service_name]
            for connection in connections:
                if connection.get('connected', False):
                    continue
                target = IDE.__IDESERVICES.get(connection['target'], None)
                slot = connection['slot']
                signal_name = connection['signal_name']
                if target and isinstance(slot, collections.Callable):
                    self.connect(target, SIGNAL(signal_name), slot)
                    connection['connected'] = True

    @classmethod
    def register_shortcut(cls, shortcut_name, shortcut, action=None):
        """Register a shortcut and action."""
        cls.__IDESHORTCUTS[shortcut_name] = (shortcut, action)

    @classmethod
    def register_menuitem(cls, menu_action, section, weight):
        """Register a QAction or QMenu in the IDE to be loaded later in the
        menubar using the section(string) to define where is going to be
        contained, and the weight define the order where is going to be
        placed.
        @menu_action: QAction or QMenu
        @section: String (name)
        @weight: int"""
        cls.__IDEMENUS[menu_action] = (section, weight)

    @classmethod
    def register_toolbar(cls, action, section, weight):
        """Register a QAction in the IDE to be loaded later in the
        toolbar using the section(string) to define where is going to be
        contained, and the weight define the order where is going to be
        placed.
        @action: QAction
        @section: String (name)
        @weight: int"""
        cls.__IDETOOLBAR[action] = (section, weight)

    @classmethod
    def register_bar_category(cls, category_name, weight):
        """Register a Menu Category to be created with the proper weight.
        @category_name: string
        @weight: int"""
        cls.__IDEBARCATEGORIES[category_name] = weight

    @classmethod
    def update_shortcut(cls, shortcut_name):
        """Update all the shortcuts of the application."""
        short = resources.get_shortcut
        shortcut, action = cls.__IDESHORTCUTS.get(shortcut_name)
        if shortcut:
            shortcut.setKey(short(shortcut_name))
        if action:
            action.setShortcut(short(shortcut_name))

    def get_or_create_nfile(self, filename):
        """For convenience access to files from ide"""
        return self.filesystem.get_file(nfile_path=filename)

    def get_or_create_editable(self, filename="", nfile=None):
        if nfile is None:
            nfile = self.filesystem.get_file(nfile_path=filename)
        editable = self.__neditables.get(nfile)
        if editable is None:
            editable = neditable.NEditable(nfile)
            self.connect(editable, SIGNAL("fileClosing(PyQt_PyObject)"),
                         self._unload_neditable)
            self.__neditables[nfile] = editable
        return editable

    def _unload_neditable(self, editable):
        self.__neditables.pop(editable.nfile)
        editable.nfile.deleteLater()
        editable.editor.deleteLater()
        editable.deleteLater()

    @property
    def opened_files(self):
        return tuple(self.__neditables.keys())

    def get_project_for_file(self, filename):
        project = None
        if filename:
            project = self.filesystem.get_project_for_file(filename)
        return project

    def create_project(self, path):
        nproj = nproject.NProject(path)
        self.filesystem.open_project(nproj)
        return nproj

    def close_project(self, project_path):
        self.filesystem.close_project(project_path)

    def get_projects(self):
        return self.filesystem.get_projects()

    def get_current_project(self):
        current_project = None
        projects = self.filesystem.get_projects()
        for project in projects:
            if projects[project].is_current:
                current_project = projects[project]
                break
        return current_project

    @classmethod
    def select_current(cls, widget):
        """Show the widget with a 4px lightblue border line."""
        widget.setProperty("highlight", True)
        widget.style().unpolish(widget)
        widget.style().polish(widget)

    @classmethod
    def unselect_current(cls, widget):
        """Remove the 4px lightblue border line from the widget."""
        widget.setProperty("highlight", False)
        widget.style().unpolish(widget)
        widget.style().polish(widget)

    def _close_tray_icon(self):
        """Close the System Tray Icon."""
        self.trayIcon.hide()
        self.trayIcon.deleteLater()

    def _change_tab_index(self):
        """Change the tabs of the current TabWidget using alt+numbers."""
        widget = QApplication.focusWidget()
        shortcut_index = getattr(widget, 'shortcut_index', None)
        if shortcut_index:
            obj = self.sender()
            shortcut_index(obj.index)

    def _process_connection(self):
        """Read the ipc input from another instance of ninja."""
        connection = self.s_listener.nextPendingConnection()
        connection.waitForReadyRead()
        data = connection.readAll()
        connection.close()
        if data:
            files, projects = str(data).split(ipc.project_delimiter, 1)
            files = [(x.split(':')[0], int(x.split(':')[1]))
                     for x in files.split(ipc.file_delimiter)]
            projects = projects.split(ipc.project_delimiter)
            self.load_session_files_projects(files, [], projects, None)

    def fullscreen_mode(self):
        """Change to fullscreen mode."""
        if self.isFullScreen():
            self.showMaximized()
        else:
            self.showFullScreen()

    def change_toolbar_visibility(self):
        """Switch the toolbar visibility"""
        if self.toolbar.isVisible():
            self.toolbar.hide()
        else:
            self.toolbar.show()

    def load_external_plugins(self, paths):
        """Load external plugins, the ones added to ninja throw the cmd."""
        for path in paths:
            self.plugin_manager.add_plugin_dir(path)
        #load all plugins!
        self.plugin_manager.discover()
        self.plugin_manager.load_all()

    def _last_tab_closed(self):
        """
        Called when the last tasb is closed
        """
        self.explorer.cleanup_tabs()

    def show_preferences(self):
        """Open the Preferences Dialog."""
        pref = preferences.Preferences(self)
        main_container = IDE.get_service("main_container")
        if main_container:
            main_container.show_dialog(pref)
        else:
            pref.show()

    def load_session_files_projects(self,
                                    files,
                                    projects,
                                    current_file,
                                    recent_files=None):
        """Load the files and projects from previous session."""
        main_container = IDE.get_service('main_container')
        projects_explorer = IDE.get_service('projects_explorer')
        if main_container and files:
            for fileData in files:
                if file_manager.file_exists(fileData[0]):
                    mtime = os.stat(fileData[0]).st_mtime
                    ignore_checkers = (mtime == fileData[2])
                    main_container.open_file(fileData[0],
                                             fileData[1],
                                             ignore_checkers=ignore_checkers)
            if current_file:
                main_container.open_file(current_file)
        if projects_explorer and projects:
            projects_explorer.load_session_projects(projects)
            #if recent_files is not None:
            #menu_file = IDE.get_service('menu_file')
            #menu_file.update_recent_files(recent_files)

    #def _set_editors_project_data(self):
    #self.__project_to_open -= 1
    #if self.__project_to_open == 0:
    #self.disconnect(self.explorer, SIGNAL("projectOpened(QString)"),
    #self._set_editors_project_data)
    #self.mainContainer.update_editor_project()

    #def open_file(self, filename):
    #if filename:
    #self.mainContainer.open_file(filename)

    #def open_project(self, project):
    #if project:
    #self.actions.open_project(project)

    def __get_session(self):
        return self._session

    def __set_session(self, sessionName):
        self._session = sessionName
        if self._session is not None:
            self.setWindowTitle(translations.TR_SESSION_IDE_HEADER %
                                {'session': self._session})
        else:
            self.setWindowTitle(
                'NINJA-IDE {Ninja-IDE Is Not Just Another IDE}')

    Session = property(__get_session, __set_session)

    def change_window_title(self, title):
        """Change the title of the Application."""
        if self._session is None:
            self.setWindowTitle('NINJA-IDE - %s' % title)
        else:
            self.setWindowTitle((translations.TR_SESSION_IDE_HEADER % {
                'session': self._session
            }) + ' - %s' % title)

    def wheelEvent(self, event):
        """Change the opacity of the application."""
        if event.modifiers() == Qt.ShiftModifier:
            if event.delta() == 120 and self.opacity < settings.MAX_OPACITY:
                self.opacity += 0.1
            elif event.delta() == -120 and self.opacity > settings.MIN_OPACITY:
                self.opacity -= 0.1
            self.setWindowOpacity(self.opacity)
            event.ignore()
        else:
            QMainWindow.wheelEvent(self, event)

    @classmethod
    def ninja_settings(cls):
        qsettings = nsettings.NSettings(resources.SETTINGS_PATH, prefix="ns")
        if cls.__created:
            cls.__instance.connect(
                qsettings, SIGNAL("valueChanged(QString, PyQt_PyObject)"),
                cls.__instance._settings_value_changed)
        return qsettings

    @classmethod
    def data_settings(cls):
        qsettings = nsettings.NSettings(resources.DATA_SETTINGS_PATH,
                                        prefix="ds")
        if cls.__created:
            cls.__instance.connect(
                qsettings, SIGNAL("valueChanged(QString, PyQt_PyObject)"),
                cls.__instance._settings_value_changed)
        return qsettings

    def _settings_value_changed(self, key, value):
        signal_name = "%s(PyQt_PyObject)" % key.replace("/", "_")
        self.emit(SIGNAL(signal_name), value)

    def save_settings(self):
        """Save the settings before the application is closed with QSettings.

        Info saved: Tabs and projects opened, windows state(size and position).
        """
        qsettings = IDE.ninja_settings()
        data_qsettings = IDE.data_settings()
        main_container = self.get_service("main_container")
        editor_widget = None
        if main_container:
            editor_widget = main_container.get_current_editor()
        current_file = ''
        if editor_widget is not None:
            current_file = editor_widget.file_path
        if qsettings.value('preferences/general/loadFiles', True, type=bool):
            openedFiles = self.filesystem.get_files()
            projects_obj = self.filesystem.get_projects()
            projects = [projects_obj[proj].path for proj in projects_obj]
            data_qsettings.setValue('lastSession/projects', projects)
            files_info = []
            for path in openedFiles:
                editable = self.__neditables.get(openedFiles[path])
                if editable is not None and editable.is_dirty:
                    stat_value = 0
                else:
                    stat_value = os.stat(path).st_mtime
                files_info.append(
                    [path,
                     editable.editor.get_cursor_position(), stat_value])
            data_qsettings.setValue('lastSession/openedFiles', files_info)
            if current_file is not None:
                data_qsettings.setValue('lastSession/currentFile',
                                        current_file)
            data_qsettings.setValue('lastSession/recentFiles',
                                    settings.LAST_OPENED_FILES)
        data_qsettings.setValue('preferences/editor/bookmarks',
                                settings.BOOKMARKS)
        data_qsettings.setValue('preferences/editor/breakpoints',
                                settings.BREAKPOINTS)

        # Session
        if self._session is not None:
            val = QMessageBox.question(
                self, translations.TR_SESSION_ACTIVE_IDE_CLOSING_TITLE,
                (translations.TR_SESSION_ACTIVE_IDE_CLOSING_BODY % {
                    'session': self.Session
                }), QMessageBox.Yes, QMessageBox.No)
            if val == QMessageBox.Yes:
                session_manager.SessionsManager.save_session_data(
                    self.Session, self)
        #qsettings.setValue('preferences/general/toolbarArea',
        #self.toolBarArea(self.toolbar))
        #Save if the windows state is maximixed
        if (self.isMaximized()):
            qsettings.setValue("window/maximized", True)
        else:
            qsettings.setValue("window/maximized", False)
            #Save the size and position of the mainwindow
            qsettings.setValue("window/size", self.size())
            qsettings.setValue("window/pos", self.pos())
        self.central.save_configuration()
        #Save the toolbar visibility
        #if not self.toolbar.isVisible() and self.menuBar().isVisible():
        #qsettings.setValue("window/hide_toolbar", True)
        #else:
        #qsettings.setValue("window/hide_toolbar", False)
        #Save Misc state
        #qsettings.setValue("window/show_region1", self.misc.isVisible())
        #Save Profiles
        #if self.profile is not None:
        #self.actions.save_profile(self.profile)
        #else:
        #qsettings.setValue('ide/profiles', settings.PROFILES)

    def activate_profile(self):
        """Show the Session Manager dialog."""
        profilesLoader = session_manager.SessionsManager(self)
        profilesLoader.show()

    def deactivate_profile(self):
        """Close the Session Session."""
        self.Session = None

    def load_window_geometry(self):
        """Load from QSettings the window size of de Ninja IDE"""
        qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat)
        if qsettings.value("window/maximized", True, type=bool):
            self.setWindowState(Qt.WindowMaximized)
        else:
            self.resize(
                qsettings.value("window/size",
                                QSizeF(800, 600).toSize(),
                                type='QSize'))
            self.move(
                qsettings.value("window/pos",
                                QPointF(100, 100).toPoint(),
                                type='QPoint'))

    def _get_unsaved_files(self):
        """Return an array with the path of the unsaved files."""
        unsaved = []
        files = self.opened_files
        for f in files:
            editable = self.__neditables.get(f)
            if editable is not None and editable.editor.is_modified:
                unsaved.append(f)
        return unsaved

    def _save_unsaved_files(self, files):
        """Save the files from the paths in the array."""
        for f in files:
            editable = self.get_or_create_editable(f)
            editable.ignore_checkers = True
            editable.save_content()

    def closeEvent(self, event):
        """Saves some global settings before closing."""
        if self.s_listener:
            self.s_listener.close()
        main_container = self.get_service("main_container")
        unsaved_files = self._get_unsaved_files()
        if (settings.CONFIRM_EXIT and unsaved_files):
            txt = '\n'.join([nfile.file_name for nfile in unsaved_files])
            val = QMessageBox.question(
                self, translations.TR_IDE_CONFIRM_EXIT_TITLE,
                (translations.TR_IDE_CONFIRM_EXIT_BODY % {
                    'files': txt
                }), QMessageBox.Yes, QMessageBox.No, QMessageBox.Cancel)
            if val == QMessageBox.Yes:
                #Saves all open files
                self._save_unsaved_files(unsaved_files)
            if val == QMessageBox.Cancel:
                event.ignore()
                return
        self.save_settings()
        self.emit(SIGNAL("goingDown()"))
        #close python documentation server (if running)
        main_container.close_python_doc()
        #Shutdown PluginManager
        self.plugin_manager.shutdown()
        #completion_daemon.shutdown_daemon()
        super(IDE, self).closeEvent(event)

    def notify_plugin_errors(self):
        #TODO: Check if the Plugin Error dialog can be improved
        errors = self.plugin_manager.errors
        if errors:
            plugin_error_dialog = traceback_widget.PluginErrorDialog()
            for err_tuple in errors:
                plugin_error_dialog.add_traceback(err_tuple[0], err_tuple[1])
            #show the dialog
            plugin_error_dialog.exec_()

    def show_message(self, message, duration=3000):
        """Show status message."""
        self.notification.set_message(message, duration)
        self.notification.show()

    def show_plugins_store(self):
        """Open the Plugins Manager to install/uninstall plugins."""
        store = plugins_store.PluginsStore(self)
        main_container = IDE.get_service("main_container")
        if main_container:
            main_container.show_dialog(store)
        else:
            store.show()

    def show_languages(self):
        """Open the Language Manager to install/uninstall languages."""
        manager = language_manager.LanguagesManagerWidget(self)
        manager.show()

    def show_schemes(self):
        """Open the Schemes Manager to install/uninstall schemes."""
        manager = schemes_manager.SchemesManagerWidget(self)
        manager.show()

    def show_about_qt(self):
        """Show About Qt Dialog."""
        QMessageBox.aboutQt(self, translations.TR_ABOUT_QT)

    def show_about_ninja(self):
        """Show About NINJA-IDE Dialog."""
        about = about_ninja.AboutNinja(self)
        about.show()

    def show_python_detection(self):
        """Show Python detection dialog for windows."""
        #TODO: Notify the user when no python version could be found
        suggested = settings.detect_python_path()
        if suggested:
            dialog = python_detect_dialog.PythonDetectDialog(suggested, self)
            dialog.show()
Esempio n. 8
0
class __IDE(QMainWindow):
###############################################################################
# SIGNALS
#
# goingDown()
###############################################################################

    def __init__(self, start_server=False):
        QMainWindow.__init__(self)
        self.setWindowTitle('NINJA-IDE {Ninja-IDE Is Not Just Another IDE}')
        self.setMinimumSize(700, 500)
        #Load the size and the position of the main window
        self.load_window_geometry()

        #Start server if needed
        self.s_listener = None
        if start_server:
            self.s_listener = QLocalServer()
            self.s_listener.listen("ninja_ide")
            self.connect(self.s_listener, SIGNAL("newConnection()"),
                self._process_connection)

        #Profile handler
        self.profile = None
        #Opacity
        self.opacity = settings.MAX_OPACITY

        #Define Actions object before the UI
        self.actions = actions.Actions()
        #StatusBar
        self.status = status_bar.StatusBar(self)
        self.status.hide()
        self.setStatusBar(self.status)
        #Main Widget - Create first than everything else
        self.central = central_widget.CentralWidget(self)
        self.load_ui(self.central)
        self.setCentralWidget(self.central)

        #ToolBar
        self.toolbar = QToolBar(self)
        self.toolbar.setToolTip(self.tr("Press and Drag to Move"))
        self.toolbar.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self.addToolBar(settings.TOOLBAR_AREA, self.toolbar)
        if settings.HIDE_TOOLBAR:
            self.toolbar.hide()

        #Install Shortcuts after the UI has been initialized
        self.actions.install_shortcuts(self)
        self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"),
            self.actions.update_explorer)

        #Menu
        menubar = self.menuBar()
        file_ = menubar.addMenu(self.tr("&File"))
        edit = menubar.addMenu(self.tr("&Edit"))
        view = menubar.addMenu(self.tr("&View"))
        source = menubar.addMenu(self.tr("&Source"))
        project = menubar.addMenu(self.tr("&Project"))
        self.pluginsMenu = menubar.addMenu(self.tr("&Addins"))
        about = menubar.addMenu(self.tr("Abou&t"))

        #The order of the icons in the toolbar is defined by this calls
        self._menuFile = menu_file.MenuFile(file_, self.toolbar, self)
        self._menuView = menu_view.MenuView(view, self.toolbar, self)
        self._menuEdit = menu_edit.MenuEdit(edit, self.toolbar)
        self._menuSource = menu_source.MenuSource(source)
        self._menuProject = menu_project.MenuProject(project, self.toolbar)
        self._menuPlugins = menu_plugins.MenuPlugins(self.pluginsMenu)
        self._menuAbout = menu_about.MenuAbout(about)

        self.load_toolbar()

        #Plugin Manager
        services = {
            'editor': plugin_services.MainService(),
            'toolbar': plugin_services.ToolbarService(self.toolbar),
            'menuApp': plugin_services.MenuAppService(self.pluginsMenu),
            'explorer': plugin_services.ExplorerService(),
            'misc': plugin_services.MiscContainerService(self.misc)}
        serviceLocator = plugin_manager.ServiceLocator(services)
        self.plugin_manager = plugin_manager.PluginManager(resources.PLUGINS,
            serviceLocator)
        self.plugin_manager.discover()
        #load all plugins!
        self.plugin_manager.load_all()

        #Tray Icon
        self.trayIcon = updates.TrayIconUpdates(self)
        self.trayIcon.show()

        self.connect(self._menuFile, SIGNAL("openFile(QString)"),
            self.mainContainer.open_file)
        self.connect(self.mainContainer, SIGNAL("fileSaved(QString)"),
            self.show_status_message)
        self.connect(self.mainContainer,
            SIGNAL("recentTabsModified(QStringList)"),
            self._menuFile.update_recent_files)

    def _process_connection(self):
        connection = self.s_listener.nextPendingConnection()
        connection.waitForReadyRead()
        data = connection.readAll()
        connection.close()
        if data:
            files, projects = data.split(ipc.project_delimiter, 1)
            files = map(lambda x: (x.split(':')[0], int(x.split(':')[1])),
                files.split(ipc.file_delimiter))
            projects = projects.split(ipc.project_delimiter)
            self.load_session_files_projects(files, [], projects, None)

    def load_toolbar(self):
        self.toolbar.clear()
        toolbar_items = {}
        toolbar_items.update(self._menuFile.toolbar_items)
        toolbar_items.update(self._menuView.toolbar_items)
        toolbar_items.update(self._menuEdit.toolbar_items)
        toolbar_items.update(self._menuSource.toolbar_items)
        toolbar_items.update(self._menuProject.toolbar_items)

        for item in settings.TOOLBAR_ITEMS:
            if item == 'separator':
                self.toolbar.addSeparator()
            else:
                tool_item = toolbar_items.get(item, None)
                if tool_item is not None:
                    self.toolbar.addAction(tool_item)
        #load action added by plugins, This is a special case when reload
        #the toolbar after save the preferences widget
        for toolbar_action in settings.get_toolbar_item_for_plugins():
            self.toolbar.addAction(toolbar_action)

    def load_external_plugins(self, paths):
        for path in paths:
            self.plugin_manager.add_plugin_dir(path)
        #load all plugins!
        self.plugin_manager.discover()
        self.plugin_manager.load_all()

    def show_status_message(self, message):
        self.status.showMessage(message, 2000)

    def load_ui(self, centralWidget):
        #Set Application Font for ToolTips
        QToolTip.setFont(QFont(settings.FONT_FAMILY, 10))
        #Create Main Container to manage Tabs
        self.mainContainer = main_container.MainContainer(self)
        self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"),
            self.change_window_title)
        self.connect(self.mainContainer,
            SIGNAL("locateFunction(QString, QString, bool)"),
            self.actions.locate_function)
        self.connect(self.mainContainer,
            SIGNAL("navigateCode(bool, int)"),
            self.actions.navigate_code_history)
        self.connect(self.mainContainer,
            SIGNAL("addBackItemNavigation()"),
            self.actions.add_back_item_navigation)
        self.connect(self.mainContainer, SIGNAL("updateFileMetadata()"),
            self.actions.update_explorer)
        self.connect(self.mainContainer, SIGNAL("updateLocator(QString)"),
            self.actions.update_explorer)
        self.connect(self.mainContainer, SIGNAL("openPreferences()"),
            self._show_preferences)
        self.connect(self.mainContainer, SIGNAL("dontOpenStartPage()"),
            self._dont_show_start_page_again)
        self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"),
            self.status.handle_tab_changed)
        # Update symbols
        self.connect(self.mainContainer, SIGNAL("updateLocator(QString)"),
            self.status.explore_file_code)
        #Create Explorer Panel
        self.explorer = explorer_container.ExplorerContainer(self)
        self.connect(self.central, SIGNAL("splitterCentralRotated()"),
            self.explorer.rotate_tab_position)
        self.connect(self.explorer, SIGNAL("updateLocator()"),
            self.status.explore_code)
        self.connect(self.explorer, SIGNAL("goToDefinition(int)"),
            self.actions.editor_go_to_line)
        self.connect(self.explorer, SIGNAL("projectClosed(QString)"),
            self.actions.close_files_from_project)
        #Create Misc Bottom Container
        self.misc = misc_container.MiscContainer(self)
        self.connect(self.mainContainer, SIGNAL("findOcurrences(QString)"),
            self.misc.show_find_occurrences)

        centralWidget.insert_central_container(self.mainContainer)
        centralWidget.insert_lateral_container(self.explorer)
        centralWidget.insert_bottom_container(self.misc)
        self.connect(self.mainContainer,
            SIGNAL("cursorPositionChange(int, int)"),
            self.central.lateralPanel.update_line_col)
        # TODO: Change current symbol on move
        #self.connect(self.mainContainer,
            #SIGNAL("cursorPositionChange(int, int)"),
            #self.explorer.update_current_symbol)
        self.connect(self.mainContainer, SIGNAL("enabledFollowMode(bool)"),
            self.central.enable_follow_mode_scrollbar)

        if settings.SHOW_START_PAGE:
            self.mainContainer.show_start_page()

    def _show_preferences(self):
        pref = preferences.PreferencesWidget(self.mainContainer)
        pref.show()

    def _dont_show_start_page_again(self):
        settings.SHOW_START_PAGE = False
        qsettings = QSettings()
        qsettings.beginGroup('preferences')
        qsettings.beginGroup('general')
        qsettings.setValue('showStartPage', settings.SHOW_START_PAGE)
        qsettings.endGroup()
        qsettings.endGroup()
        self.mainContainer.actualTab.close_tab()

    def load_session_files_projects(self, filesTab1, filesTab2, projects,
        current_file, recent_files=None):
        self.mainContainer.open_files(filesTab1, notIDEStart=False)
        self.mainContainer.open_files(filesTab2, mainTab=False,
            notIDEStart=False)
        self.explorer.open_session_projects(projects, notIDEStart=False)
        if current_file:
            self.mainContainer.open_file(current_file, notStart=False)
        if recent_files is not None:
            self._menuFile.update_recent_files(recent_files)

    def open_file(self, filename):
        if filename:
            self.mainContainer.open_file(filename)

    def open_project(self, project):
        if project:
            self.actions.open_project(project)

    def __get_profile(self):
        return self.profile

    def __set_profile(self, profileName):
        self.profile = profileName
        if self.profile is not None:
            self.setWindowTitle('NINJA-IDE (PROFILE: %s)' % self.profile)
        else:
            self.setWindowTitle(
                'NINJA-IDE {Ninja-IDE Is Not Just Another IDE}')

    Profile = property(__get_profile, __set_profile)

    def change_window_title(self, title):
        if self.profile is None:
            self.setWindowTitle('NINJA-IDE - %s' % title)
        else:
            self.setWindowTitle('NINJA-IDE (PROFILE: %s) - %s' % (
                self.profile, title))
        currentEditor = self.mainContainer.get_actual_editor()
        if currentEditor is not None:
            line = currentEditor.textCursor().blockNumber() + 1
            col = currentEditor.textCursor().columnNumber()
            self.central.lateralPanel.update_line_col(line, col)

    def wheelEvent(self, event):
        if event.modifiers() == Qt.ShiftModifier:
            if event.delta() == 120 and self.opacity < settings.MAX_OPACITY:
                self.opacity += 0.1
            elif event.delta() == -120 and self.opacity > settings.MIN_OPACITY:
                self.opacity -= 0.1
            self.setWindowOpacity(self.opacity)
            event.ignore()
        else:
            QMainWindow.wheelEvent(self, event)

    def save_settings(self):
        """Save the settings before the application is closed with QSettings.

        Info saved: Tabs and projects opened, windows state(size and position).
        """
        qsettings = QSettings()
        editor_widget = self.mainContainer.get_actual_editor()
        current_file = ''
        if editor_widget is not None:
            current_file = editor_widget.ID
        if qsettings.value('preferences/general/loadFiles', 'true') == 'true':
            openedFiles = self.mainContainer.get_opened_documents()
            projects_obj = self.explorer.get_opened_projects()
            projects = [p.path for p in projects_obj]
            qsettings.setValue('openFiles/projects',
                projects)
            if len(openedFiles) > 0:
                qsettings.setValue('openFiles/mainTab', openedFiles[0])
            if len(openedFiles) == 2:
                qsettings.setValue('openFiles/secondaryTab', openedFiles[1])
            qsettings.setValue('openFiles/currentFile', current_file)
            qsettings.setValue('openFiles/recentFiles',
                self.mainContainer._tabMain.get_recent_files_list())
        qsettings.setValue('preferences/editor/bookmarks', settings.BOOKMARKS)
        qsettings.setValue('preferences/editor/breakpoints',
            settings.BREAKPOINTS)
        qsettings.setValue('preferences/general/toolbarArea',
            self.toolBarArea(self.toolbar))
        #Save if the windows state is maximixed
        if(self.isMaximized()):
            qsettings.setValue("window/maximized", True)
        else:
            qsettings.setValue("window/maximized", False)
            #Save the size and position of the mainwindow
            qsettings.setValue("window/size", self.size())
            qsettings.setValue("window/pos", self.pos())
        #Save the size of de splitters
        qsettings.setValue("window/central/areaSize",
            self.central.get_area_sizes())
        qsettings.setValue("window/central/mainSize",
            self.central.get_main_sizes())
        #Save the toolbar visibility
        qsettings.setValue("window/hide_toolbar", not self.toolbar.isVisible())
        #Save Profiles
        if self.profile is not None:
            self.actions.save_profile(self.profile)
        else:
            qsettings.setValue('ide/profiles', settings.PROFILES)

    def load_window_geometry(self):
        """Load from QSettings the window size of de Ninja IDE"""
        qsettings = QSettings()
        if qsettings.value("window/maximized", 'true') == 'true':
            self.setWindowState(Qt.WindowMaximized)
        else: 
            print dir(QSizeF) 


            self.resize(qsettings.value("window/size",
                QSizeF(800, 600)).toSize())
            self.move(qsettings.value("window/pos",
                QPointF(100, 100)).toPoint())

    def closeEvent(self, event):
        if self.s_listener:
            self.s_listener.close()
        if settings.CONFIRM_EXIT and \
        self.mainContainer.check_for_unsaved_tabs():
            unsaved_files = self.mainContainer.get_unsaved_files()
            txt = '\n'.join(unsaved_files)
            val = QMessageBox.question(self,
                self.tr("Some changes were not saved"),
                self.tr("%s\n\nDo you want to exit anyway?" % txt),
                QMessageBox.Yes, QMessageBox.No)
            if val == QMessageBox.No:
                event.ignore()
        QApplication.instance().setCursorFlashTime(cursor_flash_time)
        self.emit(SIGNAL("goingDown()"))
        self.save_settings()
        completion_daemon.shutdown_daemon()
        #close python documentation server (if running)
        self.mainContainer.close_python_doc()
        #Shutdown PluginManager
        self.plugin_manager.shutdown()

    def notify_plugin_errors(self):
        errors = self.plugin_manager.errors
        if errors:
            plugin_error_dialog = traceback_widget.PluginErrorDialog()
            for err_tuple in errors:
                plugin_error_dialog.add_traceback(err_tuple[0], err_tuple[1])
            #show the dialog
            plugin_error_dialog.exec_()
Esempio n. 9
0
class IDE(QMainWindow):
    """This class is like the Sauron's Ring:
    One ring to rule them all, One ring to find them,
    One ring to bring them all and in the darkness bind them.

    This Class knows all the containers, and its know by all the containers,
    but the containers don't need to know between each other, in this way we
    can keep a better api without the need to tie the behaviour between
    the widgets, and let them just consume the 'actions' they need."""

    ###############################################################################
    # SIGNALS
    #
    # goingDown()
    ###############################################################################

    __IDESERVICES = {}
    __IDECONNECTIONS = {}
    __IDESHORTCUTS = {}
    __IDEMENUSCATEGORY = {}
    __IDEMENUS = {}
    # CONNECTIONS structure:
    # ({'target': service_name, 'signal_name': string, 'slot': function_obj},)
    # On modify add: {connected: True}
    __instance = None
    __created = False

    def __init__(self, start_server=False):
        QMainWindow.__init__(self)
        self.setWindowTitle("NINJA-IDE {Ninja-IDE Is Not Just Another IDE}")
        self.setMinimumSize(700, 500)
        QToolTip.setFont(QFont(settings.FONT_FAMILY, 10))
        # Load the size and the position of the main window
        self.load_window_geometry()
        self.__project_to_open = 0

        # Editables
        self.__neditables = {}
        # Filesystem
        self.filesystem = nfilesystem.NVirtualFileSystem()

        # Start server if needed
        self.s_listener = None
        if start_server:
            self.s_listener = QLocalServer()
            self.s_listener.listen("ninja_ide")
            self.connect(self.s_listener, SIGNAL("newConnection()"), self._process_connection)

        # Profile handler
        self.profile = None
        # Opacity
        self.opacity = settings.MAX_OPACITY

        # ToolBar
        self.toolbar = QToolBar(self)
        self.toolbar.setToolTip(self.tr("Press and Drag to Move"))
        self.toolbar.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self.addToolBar(settings.TOOLBAR_AREA, self.toolbar)
        if settings.HIDE_TOOLBAR:
            self.toolbar.hide()
        # Notificator
        self.notification = notification.Notification(self)

        # Plugin Manager
        # services = {
        #'editor': plugin_services.MainService(),
        #'toolbar': plugin_services.ToolbarService(self.toolbar),
        ##'menuApp': plugin_services.MenuAppService(self.pluginsMenu),
        #'menuApp': plugin_services.MenuAppService(None),
        #'explorer': plugin_services.ExplorerService(),
        #'misc': plugin_services.MiscContainerService(self.misc)}
        # serviceLocator = plugin_manager.ServiceLocator(services)
        serviceLocator = plugin_manager.ServiceLocator(None)
        self.plugin_manager = plugin_manager.PluginManager(resources.PLUGINS, serviceLocator)
        self.plugin_manager.discover()
        # load all plugins!
        self.plugin_manager.load_all()

        # Tray Icon
        self.trayIcon = updates.TrayIconUpdates(self)
        self.connect(self.trayIcon, SIGNAL("closeTrayIcon()"), self._close_tray_icon)
        self.trayIcon.show()

        key = Qt.Key_1
        for i in range(10):
            if settings.IS_MAC_OS:
                short = ui_tools.TabShortcuts(QKeySequence(Qt.CTRL + Qt.ALT + key), self, i)
            else:
                short = ui_tools.TabShortcuts(QKeySequence(Qt.ALT + key), self, i)
            key += 1
            self.connect(short, SIGNAL("activated()"), self._change_tab_index)
        short = ui_tools.TabShortcuts(QKeySequence(Qt.ALT + Qt.Key_0), self, 10)
        self.connect(short, SIGNAL("activated()"), self._change_tab_index)

        # Register menu categories
        IDE.register_menu_category(translations.TR_MENU_FILE, 100)
        IDE.register_menu_category(translations.TR_MENU_EDIT, 110)
        IDE.register_menu_category(translations.TR_MENU_VIEW, 120)
        IDE.register_menu_category(translations.TR_MENU_SOURCE, 130)
        IDE.register_menu_category(translations.TR_MENU_PROJECT, 140)
        IDE.register_menu_category(translations.TR_MENU_ADDINS, 150)
        IDE.register_menu_category(translations.TR_MENU_ABOUT, 160)
        # Register General Menu Items
        ui_tools.install_shortcuts(self, actions.ACTIONS_GENERAL, self)

        self.register_service("ide", self)
        self.register_service("toolbar", self.toolbar)
        # Register signals connections
        connections = (
            {"target": "main_container", "signal_name": "fileSaved(QString)", "slot": self.show_status_message},
            {"target": "main_container", "signal_name": "currentTabChanged(QString)", "slot": self.change_window_title},
            {"target": "main_container", "signal_name": "openPreferences()", "slot": self.show_preferences},
            {"target": "main_container", "signal_name": "allTabsClosed()", "slot": self._last_tab_closed},
            {
                "target": "explorer_container",
                "signal_name": "changeWindowTitle(QString)",
                "slot": self.change_window_title,
            },
            {"target": "explorer_container", "signal_name": "projectClosed(QString)", "slot": self.close_project},
        )
        self.register_signals("ide", connections)
        # Central Widget MUST always exists
        self.central = IDE.get_service("central_container")
        self.setCentralWidget(self.central)
        # Install Services
        for service_name in self.__IDESERVICES:
            self.install_service(service_name)
        self.__created = True
        menu_bar = IDE.get_service("menu_bar")
        if menu_bar:
            menu_bar.load_menu(self)
            # These two are the same service, I think that's ok
            menu_bar.load_toolbar(self)

    @classmethod
    def get_service(cls, service_name):
        """Return the instance of a registered service."""
        return cls.__IDESERVICES.get(service_name, None)

    def get_menuitems(self):
        """Return a dictionary with the registered menu items."""
        return self.__IDEMENUS

    def get_menu_categories(self):
        """Get the registered Categories for the Application menus."""
        return self.__IDEMENUSCATEGORY

    @classmethod
    def register_service(cls, service_name, obj):
        """Register a service providing the service name and the instance."""
        cls.__IDESERVICES[service_name] = obj
        if cls.__created:
            cls.__instance.install_service(service_name)

    def install_service(self, service_name):
        """Activate the registered service."""
        obj = self.__IDESERVICES.get(service_name, None)
        func = getattr(obj, "install", None)
        if isinstance(func, collections.Callable):
            func()
        self._connect_signals()

    def place_me_on(self, name, obj, region="central", top=False):
        """Place a widget in some of the areas in the IDE.
        @name: id to access to that widget later if needed.
        @obj: the instance of the widget to be placed.
        @region: the area where to put the widget [central, lateral]
        @top: place the widget as the first item in the split."""
        self.central.add_to_region(name, obj, region, top)

    @classmethod
    def register_signals(cls, service_name, connections):
        """Register all the signals that a particular service wants to be
        attached of.
        @service_name: id of the service
        @connections: list of dictionaries for the connection with:
            - 'target': 'the_other_service_name',
            - 'signal_name': 'name of the signal in the other service',
            - 'slot': function object in this service"""
        cls.__IDECONNECTIONS[service_name] = connections
        if cls.__created:
            cls.__instance._connect_signals()

    def _connect_signals(self):
        """Connect the signals between the different services."""
        for service_name in self.__IDECONNECTIONS:
            connections = self.__IDECONNECTIONS[service_name]
            for connection in connections:
                if connection.get("connected", False):
                    continue
                target = self.__IDESERVICES.get(connection["target"], None)
                slot = connection["slot"]
                signal_name = connection["signal_name"]
                if target and isinstance(slot, collections.Callable):
                    self.connect(target, SIGNAL(signal_name), slot)
                    connection["connected"] = True

    @classmethod
    def register_shortcut(cls, shortcut_name, shortcut, action=None):
        """Register a shortcut and action."""
        cls.__IDESHORTCUTS[shortcut_name] = (shortcut, action)

    @classmethod
    def register_menuitem(cls, menu_action, section, weight):
        """Register a QAction or QMenu in the IDE to be loaded later in the
        menubar using the section(string) to define where is going to be
        contained, and the weight define the order where is going to be
        placed.
        @menu_action: QAction or QMenu
        @section: String (name)
        @weight: int"""
        cls.__IDEMENUS[menu_action] = (section, weight)

    @classmethod
    def register_menu_category(cls, category_name, weight):
        """Register a Menu Category to be created with the proper weight.
        @category_name: string
        @weight: int"""
        cls.__IDEMENUSCATEGORY[category_name] = weight

    @classmethod
    def update_shortcut(cls, shortcut_name):
        """Update all the shortcuts of the application."""
        short = resources.get_shortcut
        shortcut, action = cls.__IDESHORTCUTS.get(shortcut_name)
        if shortcut:
            shortcut.setKey(short(shortcut_name))
        if action:
            action.setShortcut(short(shortcut_name))

    def get_or_create_editable(self, filename):
        nfile = self.filesystem.get_file(nfile_path=filename)
        editable = self.__neditables.get(nfile)
        if editable is None:
            editable = neditable.NEditable(nfile)
            self.__neditables[nfile] = editable
        return editable

    def get_project_for_file(self, filename):
        project = None
        if filename:
            project = self.filesystem.get_project_for_file(filename)
        return project

    def create_project(self, path):
        nproj = nproject.NProject(path)
        self.filesystem.open_project(nproj)
        return nproj

    def close_project(self, project_path):
        self.filesystem.close_project(project_path)

    def get_projects(self):
        return self.filesystem.get_projects()

    def get_current_project(self):
        current_project = None
        projects = self.filesystem.get_projects()
        for project in projects:
            if projects[project].is_current:
                current_project = projects[project]
                break
        return current_project

    def select_current(self, widget):
        """Show the widget with a 4px lightblue border line."""
        self.setProperty("highlight", True)
        self.style().unpolish(self)
        self.style().polish(self)

    def unselect_current(self, widget):
        """Remove the 4px lightblue border line from the widget."""
        self.setProperty("highlight", False)
        self.style().unpolish(widget)
        self.style().polish(widget)

    def get_opened_projects(self):
        return self.__projects

    def _close_tray_icon(self):
        """Close the System Tray Icon."""
        self.trayIcon.hide()
        self.trayIcon.deleteLater()

    def _change_tab_index(self):
        """Change the tabs of the current TabWidget using alt+numbers."""
        widget = QApplication.focusWidget()
        shortcut_index = getattr(widget, "shortcut_index", None)
        if shortcut_index:
            obj = self.sender()
            shortcut_index(obj.index)

    def switch_focus(self):
        """Switch the current keyboard focus to the next widget."""
        widget = QApplication.focusWidget()
        main_container = IDE.get_service("main_container")
        tools_dock = IDE.get_service("tools_dock")
        explorer_container = IDE.get_service("explorer_container")
        if widget and main_container and tools_dock and explorer_container:
            if widget in (main_container.actualTab, main_container.actualTab.currentWidget()):
                explorer_container.currentWidget().setFocus()
            elif widget in (explorer_container, explorer_container.currentWidget()):
                if tools_dock.isVisible():
                    tools_dock.stack.currentWidget().setFocus()
                else:
                    main_container.actualTab.currentWidget().setFocus()
            elif widget.parent() is tools_dock.stack:
                main_container.actualTab.currentWidget().setFocus()

    def _process_connection(self):
        """Read the ipc input from another instance of ninja."""
        connection = self.s_listener.nextPendingConnection()
        connection.waitForReadyRead()
        data = connection.readAll()
        connection.close()
        if data:
            files, projects = str(data).split(ipc.project_delimiter, 1)
            files = [(x.split(":")[0], int(x.split(":")[1])) for x in files.split(ipc.file_delimiter)]
            projects = projects.split(ipc.project_delimiter)
            self.load_session_files_projects(files, [], projects, None)

    def fullscreen_mode(self):
        """Change to fullscreen mode."""
        if self.isFullScreen():
            self.showMaximized()
        else:
            self.showFullScreen()

    def change_toolbar_visibility(self):
        """Switch the toolbar visibility"""
        if self.toolbar.isVisible():
            self.toolbar.hide()
        else:
            self.toolbar.show()

    def load_external_plugins(self, paths):
        """Load external plugins, the ones added to ninja throw the cmd."""
        for path in paths:
            self.plugin_manager.add_plugin_dir(path)
        # load all plugins!
        self.plugin_manager.discover()
        self.plugin_manager.load_all()

    def _last_tab_closed(self):
        """
        Called when the last tasb is closed
        """
        self.explorer.cleanup_tabs()

    def show_preferences(self):
        """Open the Preferences Dialog."""
        pref = preferences.PreferencesWidget(self)
        pref.show()

    def load_session_files_projects(self, filesTab1, filesTab2, projects, current_file, recent_files=None):
        """Load the files and projects from previous session."""
        self.__project_to_open = len(projects)
        explorer = IDE.get_service("explorer_container")
        main_container = IDE.get_service("main_container")
        if explorer and main_container:
            self.connect(explorer, SIGNAL("projectOpened(QString)"), self._set_editors_project_data)
            explorer.open_session_projects(projects, notIDEStart=False)
            main_container.open_files(filesTab1, notIDEStart=False)
            main_container.open_files(filesTab2, mainTab=False, notIDEStart=False)
            if current_file:
                main_container.open_file(current_file, notStart=False)
            # if recent_files is not None:
            # menu_file = IDE.get_service('menu_file')
            # menu_file.update_recent_files(recent_files)

    # def _set_editors_project_data(self):
    # self.__project_to_open -= 1
    # if self.__project_to_open == 0:
    # self.disconnect(self.explorer, SIGNAL("projectOpened(QString)"),
    # self._set_editors_project_data)
    # self.mainContainer.update_editor_project()

    # def open_file(self, filename):
    # if filename:
    # self.mainContainer.open_file(filename)

    # def open_project(self, project):
    # if project:
    # self.actions.open_project(project)

    def __get_profile(self):
        return self.profile

    def __set_profile(self, profileName):
        self.profile = profileName
        if self.profile is not None:
            self.setWindowTitle("NINJA-IDE (PROFILE: %s)" % self.profile)
        else:
            self.setWindowTitle("NINJA-IDE {Ninja-IDE Is Not Just Another IDE}")

    Profile = property(__get_profile, __set_profile)

    def change_window_title(self, title):
        """Change the title of the Application."""
        if self.profile is None:
            self.setWindowTitle("NINJA-IDE - %s" % title)
        else:
            self.setWindowTitle("NINJA-IDE (PROFILE: %s) - %s" % (self.profile, title))

    def wheelEvent(self, event):
        """Change the opacity of the application."""
        if event.modifiers() == Qt.ShiftModifier:
            if event.delta() == 120 and self.opacity < settings.MAX_OPACITY:
                self.opacity += 0.1
            elif event.delta() == -120 and self.opacity > settings.MIN_OPACITY:
                self.opacity -= 0.1
            self.setWindowOpacity(self.opacity)
            event.ignore()
        else:
            QMainWindow.wheelEvent(self, event)

    def save_settings(self):
        """Save the settings before the application is closed with QSettings.

        Info saved: Tabs and projects opened, windows state(size and position).
        """
        qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat)
        editor_widget = self.mainContainer.get_actual_editor()
        current_file = ""
        if editor_widget is not None:
            current_file = editor_widget.ID
        if qsettings.value("preferences/general/loadFiles", True, type=bool):
            openedFiles = self.mainContainer.get_opened_documents()
            projects_obj = self.get_opened_projects()
            projects = [p.path for p in projects_obj]
            qsettings.setValue("openFiles/projects", projects)
            if len(openedFiles) > 0:
                qsettings.setValue("openFiles/mainTab", openedFiles[0])
            if len(openedFiles) == 2:
                qsettings.setValue("openFiles/secondaryTab", openedFiles[1])
            qsettings.setValue("openFiles/currentFile", current_file)
            qsettings.setValue("openFiles/recentFiles", self.mainContainer._tabMain.get_recent_files_list())
        qsettings.setValue("preferences/editor/bookmarks", settings.BOOKMARKS)
        qsettings.setValue("preferences/editor/breakpoints", settings.BREAKPOINTS)
        qsettings.setValue("preferences/general/toolbarArea", self.toolBarArea(self.toolbar))
        # Save if the windows state is maximixed
        if self.isMaximized():
            qsettings.setValue("window/maximized", True)
        else:
            qsettings.setValue("window/maximized", False)
            # Save the size and position of the mainwindow
            qsettings.setValue("window/size", self.size())
            qsettings.setValue("window/pos", self.pos())
        # Save the size of de splitters
        qsettings.setValue("window/central/baseSize", self.central.get_area_sizes())
        qsettings.setValue("window/central/insideSize", self.central.get_main_sizes())
        # Save the toolbar visibility
        if not self.toolbar.isVisible() and self.menuBar().isVisible():
            qsettings.setValue("window/hide_toolbar", True)
        else:
            qsettings.setValue("window/hide_toolbar", False)
        # Save Misc state
        qsettings.setValue("window/show_region1", self.misc.isVisible())
        # Save Profiles
        if self.profile is not None:
            self.actions.save_profile(self.profile)
        else:
            qsettings.setValue("ide/profiles", settings.PROFILES)

    def create_profile(self):
        """Create a profile binding files and projects to a key."""
        profileInfo = QInputDialog.getText(
            None,
            self.tr("Create Profile"),
            self.tr("The Current Files and Projects will " "be associated to this profile.\n" "Profile Name:"),
        )
        if profileInfo[1]:
            profileName = profileInfo[0]
            if not profileName or profileName in settings.PROFILES:
                QMessageBox.information(
                    self, self.tr("Profile Name Invalid"), self.tr("The Profile name is invalid or already exists.")
                )
                return
            self.save_profile(profileName)
            return profileName

    def save_profile(self, profileName):
        """Save the updates from a profile."""
        ide = IDE.get_service("ide")
        main_container = IDE.get_service("main_container")
        if main_container:
            projects_obj = ide.get_opened_projects()
            projects = [p.path for p in projects_obj]
            files = main_container.get_opened_documents()
            files = files[0] + files[1]
            settings.PROFILES[profileName] = [files, projects]
            qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat)
            qsettings.setValue("ide/profiles", settings.PROFILES)

    def activate_profile(self):
        """Show the Profile Manager dialog."""
        profilesLoader = ui_tools.ProfilesLoader(
            self._load_profile_data, self.create_profile, self.save_profile, settings.PROFILES, self.ide
        )
        profilesLoader.show()

    def deactivate_profile(self):
        """Close the Profile Session."""
        self.Profile = None

    def _load_profile_data(self, key):
        """Activate the selected profile, closing the current files/projects"""
        explorer = IDE.get_service("explorer_container")
        main_container = IDE.get_service("main_container")
        if explorer and main_container:
            explorer.close_opened_projects()
            main_container.open_files(settings.PROFILES[key][0])
            explorer.open_session_projects(settings.PROFILES[key][1])

    def load_window_geometry(self):
        """Load from QSettings the window size of de Ninja IDE"""
        qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat)
        if qsettings.value("window/maximized", True, type=bool):
            self.setWindowState(Qt.WindowMaximized)
        else:
            self.resize(qsettings.value("window/size", QSizeF(800, 600).toSize(), type="QSize"))
            self.move(qsettings.value("window/pos", QPointF(100, 100).toPoint(), type="QPoint"))

    def closeEvent(self, event):
        """Saves some global settings before closing."""
        if self.s_listener:
            self.s_listener.close()
        # if (settings.CONFIRM_EXIT and
        # self.mainContainer.check_for_unsaved_tabs()):
        # unsaved_files = self.mainContainer.get_unsaved_files()
        # txt = '\n'.join(unsaved_files)
        # val = QMessageBox.question(self,
        # self.tr("Some changes were not saved"),
        # (self.tr("%s\n\nDo you want to save them?") % txt),
        # QMessageBox.Yes, QMessageBox.No, QMessageBox.Cancel)
        # if val == QMessageBox.Yes:
        ##Saves all open files
        # main_container = IDE.get_service('main_container')
        # if main_container:
        # main_container.save_all()
        # if val == QMessageBox.Cancel:
        # event.ignore()
        # self.emit(SIGNAL("goingDown()"))
        # self.save_settings()
        # completion_daemon.shutdown_daemon()
        ##close python documentation server (if running)
        # self.mainContainer.close_python_doc()
        ##Shutdown PluginManager
        self.plugin_manager.shutdown()
        super(IDE, self).closeEvent(event)

    def notify_plugin_errors(self):
        # TODO: Check if the Plugin Error dialog can be improved
        errors = self.plugin_manager.errors
        if errors:
            plugin_error_dialog = traceback_widget.PluginErrorDialog()
            for err_tuple in errors:
                plugin_error_dialog.add_traceback(err_tuple[0], err_tuple[1])
            # show the dialog
            plugin_error_dialog.exec_()

    def show_status_message(self, message, duration=3000):
        """Show status message."""
        self.notification.set_message(message, duration)
        self.notification.show()

    def show_manager(self):
        """Open the Plugins Manager to install/uninstall plugins."""
        manager = plugins_manager.PluginsManagerWidget(self)
        manager.show()
        if manager._requirements:
            dependencyDialog = plugins_manager.DependenciesHelpDialog(manager._requirements)
            dependencyDialog.show()

    def show_languages(self):
        """Open the Language Manager to install/uninstall languages."""
        manager = language_manager.LanguagesManagerWidget(self)
        manager.show()

    def show_themes(self):
        """Open the Themes Manager to install/uninstall themes."""
        manager = themes_manager.ThemesManagerWidget(self)
        manager.show()

    def show_about_qt(self):
        """Show About Qt Dialog."""
        QMessageBox.aboutQt(self, translations.TR_ABOUT_QT)

    def show_about_ninja(self):
        """Show About NINJA-IDE Dialog."""
        about = about_ninja.AboutNinja(self)
        about.show()

    def show_python_detection(self):
        """Show Python detection dialog for windows."""
        # TODO: Notify the user when no python version could be found
        suggested = settings.detect_python_path()
        if suggested:
            dialog = python_detect_dialog.PythonDetectDialog(suggested, self)
            dialog.show()
Esempio n. 10
0
class IDE(QMainWindow):
    """This class is like the Sauron's Ring:
    One ring to rule them all, One ring to find them,
    One ring to bring them all and in the darkness bind them.

    This Class knows all the containers, and its know by all the containers,
    but the containers don't need to know between each other, in this way we
    can keep a better api without the need to tie the behaviour between
    the widgets, and let them just consume the 'actions' they need."""

    ###############################################################################
    # SIGNALS
    #
    # goingDown()
    ###############################################################################

    __IDESERVICES = {}
    __IDECONNECTIONS = {}
    __IDESHORTCUTS = {}
    __IDEBARCATEGORIES = {}
    __IDEMENUS = {}
    __IDETOOLBAR = {}
    # CONNECTIONS structure:
    # ({'target': service_name, 'signal_name': string, 'slot': function_obj},)
    # On modify add: {connected: True}
    __instance = None
    __created = False

    def __init__(self, start_server=False):
        QMainWindow.__init__(self)
        self.setWindowTitle('NINJA-IDE {Ninja-IDE Is Not Just Another IDE}')
        self.setMinimumSize(750, 500)
        QToolTip.setFont(QFont(settings.FONT_FAMILY, 10))
        #Load the size and the position of the main window
        self.load_window_geometry()
        self.__project_to_open = 0

        #Editables
        self.__neditables = {}
        #Filesystem
        self.filesystem = nfilesystem.NVirtualFileSystem()

        #Start server if needed
        self.s_listener = None
        if start_server:
            self.s_listener = QLocalServer()
            self.s_listener.listen("ninja_ide")
            self.connect(self.s_listener, SIGNAL("newConnection()"),
                         self._process_connection)

        #Profile handler
        self.profile = None
        #Opacity
        self.opacity = settings.MAX_OPACITY

        #ToolBar
        self.toolbar = QToolBar(self)
        self.toolbar.setToolTip(self.tr("Press and Drag to Move"))
        self.toolbar.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self.addToolBar(settings.TOOLBAR_AREA, self.toolbar)
        if settings.HIDE_TOOLBAR:
            self.toolbar.hide()
        #Notificator
        self.notification = notification.Notification(self)

        #Plugin Manager
        #services = {
        #'editor': plugin_services.MainService(),
        #'toolbar': plugin_services.ToolbarService(self.toolbar),
        ##'menuApp': plugin_services.MenuAppService(self.pluginsMenu),
        #'menuApp': plugin_services.MenuAppService(None),
        #'explorer': plugin_services.ExplorerService(),
        #'misc': plugin_services.MiscContainerService(self.misc)}
        #serviceLocator = plugin_manager.ServiceLocator(services)
        serviceLocator = plugin_manager.ServiceLocator(None)
        self.plugin_manager = plugin_manager.PluginManager(
            resources.PLUGINS, serviceLocator)
        self.plugin_manager.discover()
        #load all plugins!
        self.plugin_manager.load_all()

        #Tray Icon
        self.trayIcon = updates.TrayIconUpdates(self)
        self.connect(self.trayIcon, SIGNAL("closeTrayIcon()"),
                     self._close_tray_icon)
        self.trayIcon.show()

        key = Qt.Key_1
        for i in range(10):
            if settings.IS_MAC_OS:
                short = ui_tools.TabShortcuts(
                    QKeySequence(Qt.CTRL + Qt.ALT + key), self, i)
            else:
                short = ui_tools.TabShortcuts(QKeySequence(Qt.ALT + key), self,
                                              i)
            key += 1
            self.connect(short, SIGNAL("activated()"), self._change_tab_index)
        short = ui_tools.TabShortcuts(QKeySequence(Qt.ALT + Qt.Key_0), self,
                                      10)
        self.connect(short, SIGNAL("activated()"), self._change_tab_index)

        # Register menu categories
        IDE.register_bar_category(translations.TR_MENU_FILE, 100)
        IDE.register_bar_category(translations.TR_MENU_EDIT, 110)
        IDE.register_bar_category(translations.TR_MENU_VIEW, 120)
        IDE.register_bar_category(translations.TR_MENU_SOURCE, 130)
        IDE.register_bar_category(translations.TR_MENU_PROJECT, 140)
        IDE.register_bar_category(translations.TR_MENU_ADDINS, 150)
        IDE.register_bar_category(translations.TR_MENU_ABOUT, 160)
        # Register General Menu Items
        ui_tools.install_shortcuts(self, actions.ACTIONS_GENERAL, self)

        self.register_service('ide', self)
        self.register_service('toolbar', self.toolbar)
        #Register signals connections
        connections = (
            {
                'target': 'main_container',
                'signal_name': 'fileSaved(QString)',
                'slot': self.show_status_message
            },
            {
                'target': 'main_container',
                'signal_name': 'currentEditorChanged(QString)',
                'slot': self.change_window_title
            },
            {
                'target': 'main_container',
                'signal_name': 'openPreferences()',
                'slot': self.show_preferences
            },
            {
                'target': 'main_container',
                'signal_name': 'allTabsClosed()',
                'slot': self._last_tab_closed
            },
            {
                'target': 'explorer_container',
                'signal_name': 'changeWindowTitle(QString)',
                'slot': self.change_window_title
            },
            {
                'target': 'explorer_container',
                'signal_name': 'projectClosed(QString)',
                'slot': self.close_project
            },
        )
        self.register_signals('ide', connections)
        # Central Widget MUST always exists
        self.central = IDE.get_service('central_container')
        self.setCentralWidget(self.central)
        # Install Services
        for service_name in self.__IDESERVICES:
            self.install_service(service_name)
        self.__created = True
        menu_bar = IDE.get_service('menu_bar')
        if menu_bar:
            menu_bar.load_menu(self)
            #These two are the same service, I think that's ok
            menu_bar.load_toolbar(self)

    @classmethod
    def get_service(cls, service_name):
        """Return the instance of a registered service."""
        return cls.__IDESERVICES.get(service_name, None)

    def get_menuitems(self):
        """Return a dictionary with the registered menu items."""
        return self.__IDEMENUS

    def get_bar_categories(self):
        """Get the registered Categories for the Application menus."""
        return self.__IDEBARCATEGORIES

    def get_toolbaritems(self):
        """Return a dictionary with the registered menu items."""
        return self.__IDETOOLBAR

    @classmethod
    def register_service(cls, service_name, obj):
        """Register a service providing the service name and the instance."""
        cls.__IDESERVICES[service_name] = obj
        if cls.__created:
            cls.__instance.install_service(service_name)

    def install_service(self, service_name):
        """Activate the registered service."""
        obj = self.__IDESERVICES.get(service_name, None)
        func = getattr(obj, 'install', None)
        if isinstance(func, collections.Callable):
            func()
        self._connect_signals()

    def place_me_on(self, name, obj, region="central", top=False):
        """Place a widget in some of the areas in the IDE.
        @name: id to access to that widget later if needed.
        @obj: the instance of the widget to be placed.
        @region: the area where to put the widget [central, lateral]
        @top: place the widget as the first item in the split."""
        self.central.add_to_region(name, obj, region, top)

    @classmethod
    def register_signals(cls, service_name, connections):
        """Register all the signals that a particular service wants to be
        attached of.
        @service_name: id of the service
        @connections: list of dictionaries for the connection with:
            - 'target': 'the_other_service_name',
            - 'signal_name': 'name of the signal in the other service',
            - 'slot': function object in this service"""
        cls.__IDECONNECTIONS[service_name] = connections
        if cls.__created:
            cls.__instance._connect_signals()

    def _connect_signals(self):
        """Connect the signals between the different services."""
        for service_name in self.__IDECONNECTIONS:
            connections = self.__IDECONNECTIONS[service_name]
            for connection in connections:
                if connection.get('connected', False):
                    continue
                target = self.__IDESERVICES.get(connection['target'], None)
                slot = connection['slot']
                signal_name = connection['signal_name']
                if target and isinstance(slot, collections.Callable):
                    self.connect(target, SIGNAL(signal_name), slot)
                    connection['connected'] = True

    @classmethod
    def register_shortcut(cls, shortcut_name, shortcut, action=None):
        """Register a shortcut and action."""
        cls.__IDESHORTCUTS[shortcut_name] = (shortcut, action)

    @classmethod
    def register_menuitem(cls, menu_action, section, weight):
        """Register a QAction or QMenu in the IDE to be loaded later in the
        menubar using the section(string) to define where is going to be
        contained, and the weight define the order where is going to be
        placed.
        @menu_action: QAction or QMenu
        @section: String (name)
        @weight: int"""
        cls.__IDEMENUS[menu_action] = (section, weight)

    @classmethod
    def register_toolbar(cls, action, section, weight):
        """Register a QAction in the IDE to be loaded later in the
        toolbar using the section(string) to define where is going to be
        contained, and the weight define the order where is going to be
        placed.
        @action: QAction
        @section: String (name)
        @weight: int"""
        cls.__IDETOOLBAR[action] = (section, weight)

    @classmethod
    def register_bar_category(cls, category_name, weight):
        """Register a Menu Category to be created with the proper weight.
        @category_name: string
        @weight: int"""
        cls.__IDEBARCATEGORIES[category_name] = weight

    @classmethod
    def update_shortcut(cls, shortcut_name):
        """Update all the shortcuts of the application."""
        short = resources.get_shortcut
        shortcut, action = cls.__IDESHORTCUTS.get(shortcut_name)
        if shortcut:
            shortcut.setKey(short(shortcut_name))
        if action:
            action.setShortcut(short(shortcut_name))

    def get_or_create_editable(self, filename):
        nfile = self.filesystem.get_file(nfile_path=filename)
        editable = self.__neditables.get(nfile)
        if editable is None:
            editable = neditable.NEditable(nfile)
            self.__neditables[nfile] = editable
        return editable

    def get_project_for_file(self, filename):
        project = None
        if filename:
            project = self.filesystem.get_project_for_file(filename)
        return project

    def create_project(self, path):
        nproj = nproject.NProject(path)
        self.filesystem.open_project(nproj)
        return nproj

    def close_project(self, project_path):
        self.filesystem.close_project(project_path)

    def get_projects(self):
        return self.filesystem.get_projects()

    def get_current_project(self):
        current_project = None
        projects = self.filesystem.get_projects()
        for project in projects:
            if projects[project].is_current:
                current_project = projects[project]
                break
        return current_project

    def select_current(self, widget):
        """Show the widget with a 4px lightblue border line."""
        self.setProperty("highlight", True)
        self.style().unpolish(self)
        self.style().polish(self)

    def unselect_current(self, widget):
        """Remove the 4px lightblue border line from the widget."""
        self.setProperty("highlight", False)
        self.style().unpolish(widget)
        self.style().polish(widget)

    def get_opened_projects(self):
        return self.__projects

    def _close_tray_icon(self):
        """Close the System Tray Icon."""
        self.trayIcon.hide()
        self.trayIcon.deleteLater()

    def _change_tab_index(self):
        """Change the tabs of the current TabWidget using alt+numbers."""
        widget = QApplication.focusWidget()
        shortcut_index = getattr(widget, 'shortcut_index', None)
        if shortcut_index:
            obj = self.sender()
            shortcut_index(obj.index)

    def switch_focus(self):
        """Switch the current keyboard focus to the next widget."""
        widget = QApplication.focusWidget()
        main_container = IDE.get_service('main_container')
        tools_dock = IDE.get_service('tools_dock')
        explorer_container = IDE.get_service('explorer_container')
        if widget and main_container and tools_dock and explorer_container:
            if widget in (main_container.actualTab,
                          main_container.actualTab.currentWidget()):
                explorer_container.currentWidget().setFocus()
            elif widget in (explorer_container,
                            explorer_container.currentWidget()):
                if tools_dock.isVisible():
                    tools_dock.stack.currentWidget().setFocus()
                else:
                    main_container.actualTab.currentWidget().setFocus()
            elif widget.parent() is tools_dock.stack:
                main_container.actualTab.currentWidget().setFocus()

    def _process_connection(self):
        """Read the ipc input from another instance of ninja."""
        connection = self.s_listener.nextPendingConnection()
        connection.waitForReadyRead()
        data = connection.readAll()
        connection.close()
        if data:
            files, projects = str(data).split(ipc.project_delimiter, 1)
            files = [(x.split(':')[0], int(x.split(':')[1]))
                     for x in files.split(ipc.file_delimiter)]
            projects = projects.split(ipc.project_delimiter)
            self.load_session_files_projects(files, [], projects, None)

    def fullscreen_mode(self):
        """Change to fullscreen mode."""
        if self.isFullScreen():
            self.showMaximized()
        else:
            self.showFullScreen()

    def change_toolbar_visibility(self):
        """Switch the toolbar visibility"""
        if self.toolbar.isVisible():
            self.toolbar.hide()
        else:
            self.toolbar.show()

    def load_external_plugins(self, paths):
        """Load external plugins, the ones added to ninja throw the cmd."""
        for path in paths:
            self.plugin_manager.add_plugin_dir(path)
        #load all plugins!
        self.plugin_manager.discover()
        self.plugin_manager.load_all()

    def _last_tab_closed(self):
        """
        Called when the last tasb is closed
        """
        self.explorer.cleanup_tabs()

    def show_preferences(self):
        """Open the Preferences Dialog."""
        pref = preferences.PreferencesWidget(self)
        pref.show()

    def load_session_files_projects(self,
                                    filesTab1,
                                    filesTab2,
                                    projects,
                                    current_file,
                                    recent_files=None):
        """Load the files and projects from previous session."""
        self.__project_to_open = len(projects)
        explorer = IDE.get_service('explorer_container')
        main_container = IDE.get_service('main_container')
        if explorer and main_container:
            self.connect(explorer, SIGNAL("projectOpened(QString)"),
                         self._set_editors_project_data)
            explorer.open_session_projects(projects, notIDEStart=False)
            main_container.open_files(filesTab1, notIDEStart=False)
            main_container.open_files(filesTab2,
                                      mainTab=False,
                                      notIDEStart=False)
            if current_file:
                main_container.open_file(current_file, notStart=False)
            #if recent_files is not None:
            #menu_file = IDE.get_service('menu_file')
            #menu_file.update_recent_files(recent_files)

    #def _set_editors_project_data(self):
    #self.__project_to_open -= 1
    #if self.__project_to_open == 0:
    #self.disconnect(self.explorer, SIGNAL("projectOpened(QString)"),
    #self._set_editors_project_data)
    #self.mainContainer.update_editor_project()

    #def open_file(self, filename):
    #if filename:
    #self.mainContainer.open_file(filename)

    #def open_project(self, project):
    #if project:
    #self.actions.open_project(project)

    def __get_profile(self):
        return self.profile

    def __set_profile(self, profileName):
        self.profile = profileName
        if self.profile is not None:
            self.setWindowTitle('NINJA-IDE (PROFILE: %s)' % self.profile)
        else:
            self.setWindowTitle(
                'NINJA-IDE {Ninja-IDE Is Not Just Another IDE}')

    Profile = property(__get_profile, __set_profile)

    def change_window_title(self, title):
        """Change the title of the Application."""
        if self.profile is None:
            self.setWindowTitle('NINJA-IDE - %s' % title)
        else:
            self.setWindowTitle('NINJA-IDE (PROFILE: %s) - %s' %
                                (self.profile, title))

    def wheelEvent(self, event):
        """Change the opacity of the application."""
        if event.modifiers() == Qt.ShiftModifier:
            if event.delta() == 120 and self.opacity < settings.MAX_OPACITY:
                self.opacity += 0.1
            elif event.delta() == -120 and self.opacity > settings.MIN_OPACITY:
                self.opacity -= 0.1
            self.setWindowOpacity(self.opacity)
            event.ignore()
        else:
            QMainWindow.wheelEvent(self, event)

    def save_settings(self):
        """Save the settings before the application is closed with QSettings.

        Info saved: Tabs and projects opened, windows state(size and position).
        """
        qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat)
        editor_widget = self.mainContainer.get_actual_editor()
        current_file = ''
        if editor_widget is not None:
            current_file = editor_widget.ID
        if qsettings.value('preferences/general/loadFiles', True, type=bool):
            openedFiles = self.mainContainer.get_opened_documents()
            projects_obj = self.get_opened_projects()
            projects = [p.path for p in projects_obj]
            qsettings.setValue('openFiles/projects', projects)
            if len(openedFiles) > 0:
                qsettings.setValue('openFiles/mainTab', openedFiles[0])
            if len(openedFiles) == 2:
                qsettings.setValue('openFiles/secondaryTab', openedFiles[1])
            qsettings.setValue('openFiles/currentFile', current_file)
            qsettings.setValue(
                'openFiles/recentFiles',
                self.mainContainer._tabMain.get_recent_files_list())
        qsettings.setValue('preferences/editor/bookmarks', settings.BOOKMARKS)
        qsettings.setValue('preferences/editor/breakpoints',
                           settings.BREAKPOINTS)
        qsettings.setValue('preferences/general/toolbarArea',
                           self.toolBarArea(self.toolbar))
        #Save if the windows state is maximixed
        if (self.isMaximized()):
            qsettings.setValue("window/maximized", True)
        else:
            qsettings.setValue("window/maximized", False)
            #Save the size and position of the mainwindow
            qsettings.setValue("window/size", self.size())
            qsettings.setValue("window/pos", self.pos())
        #Save the size of de splitters
        qsettings.setValue("window/central/baseSize",
                           self.central.get_area_sizes())
        qsettings.setValue("window/central/insideSize",
                           self.central.get_main_sizes())
        #Save the toolbar visibility
        if not self.toolbar.isVisible() and self.menuBar().isVisible():
            qsettings.setValue("window/hide_toolbar", True)
        else:
            qsettings.setValue("window/hide_toolbar", False)
        #Save Misc state
        qsettings.setValue("window/show_region1", self.misc.isVisible())
        #Save Profiles
        if self.profile is not None:
            self.actions.save_profile(self.profile)
        else:
            qsettings.setValue('ide/profiles', settings.PROFILES)

    def create_profile(self):
        """Create a profile binding files and projects to a key."""
        profileInfo = QInputDialog.getText(
            None, self.tr("Create Profile"),
            self.tr("The Current Files and Projects will "
                    "be associated to this profile.\n"
                    "Profile Name:"))
        if profileInfo[1]:
            profileName = profileInfo[0]
            if not profileName or profileName in settings.PROFILES:
                QMessageBox.information(
                    self, self.tr("Profile Name Invalid"),
                    self.tr("The Profile name is invalid or already exists."))
                return
            self.save_profile(profileName)
            return profileName

    def save_profile(self, profileName):
        """Save the updates from a profile."""
        ide = IDE.get_service('ide')
        main_container = IDE.get_service('main_container')
        if main_container:
            projects_obj = ide.get_opened_projects()
            projects = [p.path for p in projects_obj]
            files = main_container.get_opened_documents()
            files = files[0] + files[1]
            settings.PROFILES[profileName] = [files, projects]
            qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat)
            qsettings.setValue('ide/profiles', settings.PROFILES)

    def activate_profile(self):
        """Show the Profile Manager dialog."""
        profilesLoader = ui_tools.ProfilesLoader(self._load_profile_data,
                                                 self.create_profile,
                                                 self.save_profile,
                                                 settings.PROFILES, self.ide)
        profilesLoader.show()

    def deactivate_profile(self):
        """Close the Profile Session."""
        self.Profile = None

    def _load_profile_data(self, key):
        """Activate the selected profile, closing the current files/projects"""
        explorer = IDE.get_service('explorer_container')
        main_container = IDE.get_service('main_container')
        if explorer and main_container:
            explorer.close_opened_projects()
            main_container.open_files(settings.PROFILES[key][0])
            explorer.open_session_projects(settings.PROFILES[key][1])

    def load_window_geometry(self):
        """Load from QSettings the window size of de Ninja IDE"""
        qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat)
        if qsettings.value("window/maximized", True, type=bool):
            self.setWindowState(Qt.WindowMaximized)
        else:
            self.resize(
                qsettings.value("window/size",
                                QSizeF(800, 600).toSize(),
                                type='QSize'))
            self.move(
                qsettings.value("window/pos",
                                QPointF(100, 100).toPoint(),
                                type='QPoint'))

    def closeEvent(self, event):
        """Saves some global settings before closing."""
        if self.s_listener:
            self.s_listener.close()
        #if (settings.CONFIRM_EXIT and
        #self.mainContainer.check_for_unsaved_tabs()):
        #unsaved_files = self.mainContainer.get_unsaved_files()
        #txt = '\n'.join(unsaved_files)
        #val = QMessageBox.question(self,
        #self.tr("Some changes were not saved"),
        #(self.tr("%s\n\nDo you want to save them?") % txt),
        #QMessageBox.Yes, QMessageBox.No, QMessageBox.Cancel)
        #if val == QMessageBox.Yes:
        ##Saves all open files
        #main_container = IDE.get_service('main_container')
        #if main_container:
        #main_container.save_all()
        #if val == QMessageBox.Cancel:
        #event.ignore()
        #self.emit(SIGNAL("goingDown()"))
        #self.save_settings()
        #completion_daemon.shutdown_daemon()
        ##close python documentation server (if running)
        #self.mainContainer.close_python_doc()
        ##Shutdown PluginManager
        self.plugin_manager.shutdown()
        super(IDE, self).closeEvent(event)

    def notify_plugin_errors(self):
        #TODO: Check if the Plugin Error dialog can be improved
        errors = self.plugin_manager.errors
        if errors:
            plugin_error_dialog = traceback_widget.PluginErrorDialog()
            for err_tuple in errors:
                plugin_error_dialog.add_traceback(err_tuple[0], err_tuple[1])
            #show the dialog
            plugin_error_dialog.exec_()

    def show_status_message(self, message, duration=3000):
        """Show status message."""
        self.notification.set_message(message, duration)
        self.notification.show()

    def show_manager(self):
        """Open the Plugins Manager to install/uninstall plugins."""
        manager = plugins_manager.PluginsManagerWidget(self)
        manager.show()
        if manager._requirements:
            dependencyDialog = plugins_manager.DependenciesHelpDialog(
                manager._requirements)
            dependencyDialog.show()

    def show_languages(self):
        """Open the Language Manager to install/uninstall languages."""
        manager = language_manager.LanguagesManagerWidget(self)
        manager.show()

    def show_themes(self):
        """Open the Themes Manager to install/uninstall themes."""
        manager = themes_manager.ThemesManagerWidget(self)
        manager.show()

    def show_about_qt(self):
        """Show About Qt Dialog."""
        QMessageBox.aboutQt(self, translations.TR_ABOUT_QT)

    def show_about_ninja(self):
        """Show About NINJA-IDE Dialog."""
        about = about_ninja.AboutNinja(self)
        about.show()

    def show_python_detection(self):
        """Show Python detection dialog for windows."""
        #TODO: Notify the user when no python version could be found
        suggested = settings.detect_python_path()
        if suggested:
            dialog = python_detect_dialog.PythonDetectDialog(suggested, self)
            dialog.show()
Esempio n. 11
0
class __IDE(QMainWindow):
    ###############################################################################
    # SIGNALS
    #
    # goingDown()
    ###############################################################################

    def __init__(self, start_server=False):
        QMainWindow.__init__(self)
        self.setWindowTitle(
            'AL\'EXA-IDE {Al\'exa-IDE Is Not Just Another IDE}')
        self.setMinimumSize(700, 500)
        #Load the size and the position of the main window
        self.load_window_geometry()
        self.__project_to_open = 0

        #Start server if needed
        self.s_listener = None
        if start_server:
            self.s_listener = QLocalServer()
            self.s_listener.listen("ninja_ide")
            self.connect(self.s_listener, SIGNAL("newConnection()"),
                         self._process_connection)

        #Profile handler
        self.profile = None
        #Opacity
        self.opacity = settings.MAX_OPACITY

        #Define Actions object before the UI
        self.actions = actions.Actions()
        #StatusBar
        self.status = status_bar.StatusBar(self)
        self.status.hide()
        self.setStatusBar(self.status)
        #Main Widget - Create first than everything else
        self.central = central_widget.CentralWidget(self)
        self.load_ui(self.central)
        self.setCentralWidget(self.central)

        #ToolBar
        self.toolbar = QToolBar(self)
        self.toolbar.setToolTip(self.tr("Press and Drag to Move"))
        self.toolbar.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self.addToolBar(settings.TOOLBAR_AREA, self.toolbar)
        if settings.HIDE_TOOLBAR:
            self.toolbar.hide()

        #Install Shortcuts after the UI has been initialized
        self.actions.install_shortcuts(self)
        self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"),
                     self.actions.update_explorer)

        #Menu
        menubar = self.menuBar()
        file_ = menubar.addMenu(self.tr("&File"))
        edit = menubar.addMenu(self.tr("&Edit"))
        view = menubar.addMenu(self.tr("&View"))
        source = menubar.addMenu(self.tr("&Source"))
        project = menubar.addMenu(self.tr("&Project"))
        self.pluginsMenu = menubar.addMenu(self.tr("&Addins"))
        about = menubar.addMenu(self.tr("Abou&t"))

        #The order of the icons in the toolbar is defined by this calls
        self._menuFile = menu_file.MenuFile(file_, self.toolbar, self)
        self._menuView = menu_view.MenuView(view, self.toolbar, self)
        self._menuEdit = menu_edit.MenuEdit(edit, self.toolbar)
        self._menuSource = menu_source.MenuSource(source)
        self._menuProject = menu_project.MenuProject(project, self.toolbar)
        self._menuPlugins = menu_plugins.MenuPlugins(self.pluginsMenu)
        self._menuAbout = menu_about.MenuAbout(about)

        self.load_toolbar()

        #Plugin Manager
        services = {
            'editor': plugin_services.MainService(),
            'toolbar': plugin_services.ToolbarService(self.toolbar),
            'menuApp': plugin_services.MenuAppService(self.pluginsMenu),
            'explorer': plugin_services.ExplorerService(),
            'misc': plugin_services.MiscContainerService(self.misc)
        }
        serviceLocator = plugin_manager.ServiceLocator(services)
        self.plugin_manager = plugin_manager.PluginManager(
            resources.PLUGINS, serviceLocator)
        self.plugin_manager.discover()
        #load all plugins!
        self.plugin_manager.load_all()

        #Tray Icon
        self.trayIcon = updates.TrayIconUpdates(self)
        self.trayIcon.show()

        self.connect(self._menuFile, SIGNAL("openFile(QString)"),
                     self.mainContainer.open_file)
        self.connect(self.mainContainer, SIGNAL("fileSaved(QString)"),
                     self.show_status_message)
        self.connect(self.mainContainer,
                     SIGNAL("recentTabsModified(QStringList)"),
                     self._menuFile.update_recent_files)
        self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"),
                     self.actions.update_migration_tips)
        self.connect(self.mainContainer, SIGNAL("updateFileMetadata(QString)"),
                     self.actions.update_migration_tips)
        self.connect(self.mainContainer, SIGNAL("migrationAnalyzed()"),
                     self.actions.update_migration_tips)

    def _process_connection(self):
        connection = self.s_listener.nextPendingConnection()
        connection.waitForReadyRead()
        data = connection.readAll()
        connection.close()
        if data:
            files, projects = str(data).split(ipc.project_delimiter, 1)
            files = [(x.split(':')[0], int(x.split(':')[1]))
                     for x in files.split(ipc.file_delimiter)]
            projects = projects.split(ipc.project_delimiter)
            self.load_session_files_projects(files, [], projects, None)

    def load_toolbar(self):
        self.toolbar.clear()
        toolbar_items = {}
        toolbar_items.update(self._menuFile.toolbar_items)
        toolbar_items.update(self._menuView.toolbar_items)
        toolbar_items.update(self._menuEdit.toolbar_items)
        toolbar_items.update(self._menuSource.toolbar_items)
        toolbar_items.update(self._menuProject.toolbar_items)

        for item in settings.TOOLBAR_ITEMS:
            if item == 'separator':
                self.toolbar.addSeparator()
            else:
                tool_item = toolbar_items.get(item, None)
                if tool_item is not None:
                    self.toolbar.addAction(tool_item)
        #load action added by plugins, This is a special case when reload
        #the toolbar after save the preferences widget
        for toolbar_action in settings.get_toolbar_item_for_plugins():
            self.toolbar.addAction(toolbar_action)

    def load_external_plugins(self, paths):
        for path in paths:
            self.plugin_manager.add_plugin_dir(path)
        #load all plugins!
        self.plugin_manager.discover()
        self.plugin_manager.load_all()

    def show_status_message(self, message):
        self.status.showMessage(message, 2000)

    def load_ui(self, centralWidget):
        #Set Application Font for ToolTips
        QToolTip.setFont(QFont(settings.FONT_FAMILY, 10))
        #Create Main Container to manage Tabs
        self.mainContainer = main_container.MainContainer(self)
        self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"),
                     self.change_window_title)
        self.connect(self.mainContainer,
                     SIGNAL("locateFunction(QString, QString, bool)"),
                     self.actions.locate_function)
        self.connect(self.mainContainer, SIGNAL("navigateCode(bool, int)"),
                     self.actions.navigate_code_history)
        self.connect(self.mainContainer, SIGNAL("addBackItemNavigation()"),
                     self.actions.add_back_item_navigation)
        self.connect(self.mainContainer, SIGNAL("updateFileMetadata()"),
                     self.actions.update_explorer)
        self.connect(self.mainContainer, SIGNAL("updateLocator(QString)"),
                     self.actions.update_explorer)
        self.connect(self.mainContainer, SIGNAL("openPreferences()"),
                     self._show_preferences)
        self.connect(self.mainContainer, SIGNAL("dontOpenStartPage()"),
                     self._dont_show_start_page_again)
        self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"),
                     self.status.handle_tab_changed)
        # When close the last tab cleanup
        self.connect(self.mainContainer, SIGNAL("allTabsClosed()"),
                     self._last_tab_closed)
        # Update symbols
        self.connect(self.mainContainer, SIGNAL("updateLocator(QString)"),
                     self.status.explore_file_code)
        #Create Explorer Panel
        self.explorer = explorer_container.ExplorerContainer(self)
        self.connect(self.central, SIGNAL("splitterCentralRotated()"),
                     self.explorer.rotate_tab_position)
        self.connect(self.explorer, SIGNAL("updateLocator()"),
                     self.status.explore_code)
        self.connect(self.explorer, SIGNAL("goToDefinition(int)"),
                     self.actions.editor_go_to_line)
        self.connect(self.explorer, SIGNAL("projectClosed(QString)"),
                     self.actions.close_files_from_project)
        #Create Misc Bottom Container
        self.misc = misc_container.MiscContainer(self)
        self.connect(self.mainContainer, SIGNAL("findOcurrences(QString)"),
                     self.misc.show_find_occurrences)

        centralWidget.insert_central_container(self.mainContainer)
        centralWidget.insert_lateral_container(self.explorer)
        centralWidget.insert_bottom_container(self.misc)
        if self.explorer.count() == 0:
            centralWidget.change_explorer_visibility(force_hide=True)
        self.connect(self.mainContainer,
                     SIGNAL("cursorPositionChange(int, int)"),
                     self.central.lateralPanel.update_line_col)
        # TODO: Change current symbol on move
        #self.connect(self.mainContainer,
        #SIGNAL("cursorPositionChange(int, int)"),
        #self.explorer.update_current_symbol)
        self.connect(self.mainContainer, SIGNAL("enabledFollowMode(bool)"),
                     self.central.enable_follow_mode_scrollbar)

        if settings.SHOW_START_PAGE:
            self.mainContainer.show_start_page()

    def _last_tab_closed(self):
        """
        Called when the last tasb is closed
        """
        self.explorer.cleanup_tabs()

    def _show_preferences(self):
        pref = preferences.PreferencesWidget(self.mainContainer)
        pref.show()

    def _dont_show_start_page_again(self):
        settings.SHOW_START_PAGE = False
        qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat)
        qsettings.beginGroup('preferences')
        qsettings.beginGroup('general')
        qsettings.setValue('showStartPage', settings.SHOW_START_PAGE)
        qsettings.endGroup()
        qsettings.endGroup()
        self.mainContainer.actualTab.close_tab()

    def load_session_files_projects(self,
                                    filesTab1,
                                    filesTab2,
                                    projects,
                                    current_file,
                                    recent_files=None):
        self.__project_to_open = len(projects)
        self.connect(self.explorer, SIGNAL("projectOpened(QString)"),
                     self._set_editors_project_data)
        self.explorer.open_session_projects(projects, notIDEStart=False)
        self.mainContainer.open_files(filesTab1, notIDEStart=False)
        self.mainContainer.open_files(filesTab2,
                                      mainTab=False,
                                      notIDEStart=False)
        if current_file:
            self.mainContainer.open_file(current_file, notStart=False)
        if recent_files is not None:
            self._menuFile.update_recent_files(recent_files)

    def _set_editors_project_data(self):
        self.__project_to_open -= 1
        if self.__project_to_open == 0:
            self.disconnect(self.explorer, SIGNAL("projectOpened(QString)"),
                            self._set_editors_project_data)
            self.mainContainer.update_editor_project()

    def open_file(self, filename):
        if filename:
            self.mainContainer.open_file(filename)

    def open_project(self, project):
        if project:
            self.actions.open_project(project)

    def __get_profile(self):
        return self.profile

    def __set_profile(self, profileName):
        self.profile = profileName
        if self.profile is not None:
            self.setWindowTitle('AL\'EXA-IDE (PROFILE: %s)' % self.profile)
        else:
            self.setWindowTitle(
                'AL\'EXA-IDE {Al\'exa-IDE Is Not Just Another IDE}')

    Profile = property(__get_profile, __set_profile)

    def change_window_title(self, title):
        if self.profile is None:
            self.setWindowTitle('AL\'EXA-IDE - %s' % title)
        else:
            self.setWindowTitle('AL\'EXA-IDE (PROFILE: %s) - %s' %
                                (self.profile, title))
        currentEditor = self.mainContainer.get_actual_editor()
        if currentEditor is not None:
            line = currentEditor.textCursor().blockNumber() + 1
            col = currentEditor.textCursor().columnNumber()
            self.central.lateralPanel.update_line_col(line, col)

    def wheelEvent(self, event):
        if event.modifiers() == Qt.ShiftModifier:
            if event.delta() == 120 and self.opacity < settings.MAX_OPACITY:
                self.opacity += 0.1
            elif event.delta() == -120 and self.opacity > settings.MIN_OPACITY:
                self.opacity -= 0.1
            self.setWindowOpacity(self.opacity)
            event.ignore()
        else:
            QMainWindow.wheelEvent(self, event)

    def save_settings(self):
        """Save the settings before the application is closed with QSettings.

        Info saved: Tabs and projects opened, windows state(size and position).
        """
        qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat)
        editor_widget = self.mainContainer.get_actual_editor()
        current_file = ''
        if editor_widget is not None:
            current_file = editor_widget.ID
        if qsettings.value('preferences/general/loadFiles', True, type=bool):
            openedFiles = self.mainContainer.get_opened_documents()
            projects_obj = self.explorer.get_opened_projects()
            projects = [p.path for p in projects_obj]
            qsettings.setValue('openFiles/projects', projects)
            if len(openedFiles) > 0:
                qsettings.setValue('openFiles/mainTab', openedFiles[0])
            if len(openedFiles) == 2:
                qsettings.setValue('openFiles/secondaryTab', openedFiles[1])
            qsettings.setValue('openFiles/currentFile', current_file)
            qsettings.setValue(
                'openFiles/recentFiles',
                self.mainContainer._tabMain.get_recent_files_list())
        qsettings.setValue('preferences/editor/bookmarks', settings.BOOKMARKS)
        qsettings.setValue('preferences/editor/breakpoints',
                           settings.BREAKPOINTS)
        qsettings.setValue('preferences/general/toolbarArea',
                           self.toolBarArea(self.toolbar))
        #Save if the windows state is maximixed
        if (self.isMaximized()):
            qsettings.setValue("window/maximized", True)
        else:
            qsettings.setValue("window/maximized", False)
            #Save the size and position of the mainwindow
            qsettings.setValue("window/size", self.size())
            qsettings.setValue("window/pos", self.pos())
        #Save the size of de splitters
        qsettings.setValue("window/central/areaSize",
                           self.central.get_area_sizes())
        qsettings.setValue("window/central/mainSize",
                           self.central.get_main_sizes())
        #Save the toolbar visibility
        if not self.toolbar.isVisible() and self.menuBar().isVisible():
            qsettings.setValue("window/hide_toolbar", True)
        else:
            qsettings.setValue("window/hide_toolbar", False)
        #Save Misc state
        qsettings.setValue("window/show_misc", self.misc.isVisible())
        #Save Profiles
        if self.profile is not None:
            self.actions.save_profile(self.profile)
        else:
            qsettings.setValue('ide/profiles', settings.PROFILES)

    def load_window_geometry(self):
        """Load from QSettings the window size of de Ninja IDE"""
        qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat)
        if qsettings.value("window/maximized", True, type=bool):
            self.setWindowState(Qt.WindowMaximized)
        else:
            self.resize(
                qsettings.value("window/size",
                                QSizeF(800, 600).toSize(),
                                type='QSize'))
            self.move(
                qsettings.value("window/pos",
                                QPointF(100, 100).toPoint(),
                                type='QPoint'))

    def CloseHideAllWindow(self, close=True):
        if sys.platform == 'win32':
            import win32gui
            import win32con
            import re

            toplist, winlist = [], []

            def enum_callback(hwnd, results):
                winlist.append((hwnd, win32gui.GetWindowText(hwnd)))

            win32gui.EnumWindows(enum_callback, toplist)

            #^(?!.*details\.cfm).*selector=size.*$
            #window = [(hwnd, curTitle) for hwnd, curTitle in winlist if re.match("al.*exa.*(tool|nagios utilities|mouse and keyboard).*", curTitle, re.DOTALL | re.IGNORECASE) != None and win32gui.IsWindowVisible(hwnd) != 0]
            windows = [(hwnd, curTitle) for hwnd, curTitle in winlist]

            # just grab the hwnd for first window matching title
            #print "toplist", top
            if len(windows) > 0:
                toplist, winlist = [], []
                #print windows

                for window in windows:
                    try:
                        #if openedWindow[0].lower() == 'Al\'exa tools':
                        title = window[1]
                        if 'al\'exa tools' == title.lower(
                        ) or 'al\'exa - nagios utilities' == title.lower(
                        ) or 'al\'exa - mouse and keyboard' == title.lower(
                        ) or 'al\'exa - windows and region' == title.lower(
                        ) or 'al\'exa - process utilities' == title.lower(
                        ) or 'al\'exa - e-mail' == title.lower():
                            if close is True:
                                win32gui.PostMessage(window[0],
                                                     win32con.WM_CLOSE, 0, 0)
                            else:
                                win32gui.ShowWindow(window[0], 0)
                    except:
                        continue

    def closeEvent(self, event):

        if self.s_listener:
            self.s_listener.close()
        if (settings.CONFIRM_EXIT
                and self.mainContainer.check_for_unsaved_tabs()):
            unsaved_files = self.mainContainer.get_unsaved_files()
            txt = '\n'.join(unsaved_files)

            self.CloseHideAllWindow(False)
            val = QMessageBox.question(
                self, self.tr("Some changes were not saved"),
                (self.tr("%s\n\nDo you want to save them?") % txt),
                QMessageBox.Yes, QMessageBox.No, QMessageBox.Cancel)
            '''
            msgBox = QMessageBox()
            msgBox.setWindowTitle("QMessageBox Always On Top")
            msgBox.setText("This is an always-on-top QMessageBox!")
            msgBox.setWindowFlags(Qt.WindowStaysOnTopHint)
            msgBox.show()
            '''

            #val.setWindowFlags(Qt.WindowStaysOnTopHint)

            if val == QMessageBox.Yes:
                #Saves all open files
                self.mainContainer.save_all()
            if val == QMessageBox.Cancel:
                event.ignore()
                return
        self.emit(SIGNAL("goingDown()"))
        self.save_settings()
        completion_daemon.shutdown_daemon()
        #close python documentation server (if running)
        self.mainContainer.close_python_doc()
        #Shutdown PluginManager
        self.plugin_manager.shutdown()
        self.CloseHideAllWindow(True)

    def notify_plugin_errors(self):
        errors = self.plugin_manager.errors
        if errors:
            plugin_error_dialog = traceback_widget.PluginErrorDialog()
            for err_tuple in errors:
                plugin_error_dialog.add_traceback(err_tuple[0], err_tuple[1])
            #show the dialog
            plugin_error_dialog.exec_()

    def show_python_detection(self):
        suggested = settings.detect_python_path()
        if suggested:
            dialog = python_detect_dialog.PythonDetectDialog(suggested, self)
            dialog.show()
Esempio n. 12
0
class __IDE(QMainWindow):
###############################################################################
# SIGNALS
#
# goingDown()
###############################################################################

    def __init__(self, start_server=False):
        QMainWindow.__init__(self)
        self.setWindowTitle('AL\'EXA-IDE {Al\'exa-IDE Is Not Just Another IDE}')
        self.setMinimumSize(700, 500)
        #Load the size and the position of the main window
        self.load_window_geometry()
        self.__project_to_open = 0

        #Start server if needed
        self.s_listener = None
        if start_server:
            self.s_listener = QLocalServer()
            self.s_listener.listen("ninja_ide")
            self.connect(self.s_listener, SIGNAL("newConnection()"),
                self._process_connection)

        #Profile handler
        self.profile = None
        #Opacity
        self.opacity = settings.MAX_OPACITY

        #Define Actions object before the UI
        self.actions = actions.Actions()
        #StatusBar
        self.status = status_bar.StatusBar(self)
        self.status.hide()
        self.setStatusBar(self.status)
        #Main Widget - Create first than everything else
        self.central = central_widget.CentralWidget(self)
        self.load_ui(self.central)
        self.setCentralWidget(self.central)

        #ToolBar
        self.toolbar = QToolBar(self)
        self.toolbar.setToolTip(self.tr("Press and Drag to Move"))
        self.toolbar.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self.addToolBar(settings.TOOLBAR_AREA, self.toolbar)
        if settings.HIDE_TOOLBAR:
            self.toolbar.hide()

        #Install Shortcuts after the UI has been initialized
        self.actions.install_shortcuts(self)
        self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"),
            self.actions.update_explorer)

        #Menu
        menubar = self.menuBar()
        file_ = menubar.addMenu(self.tr("&File"))
        edit = menubar.addMenu(self.tr("&Edit"))
        view = menubar.addMenu(self.tr("&View"))
        source = menubar.addMenu(self.tr("&Source"))
        project = menubar.addMenu(self.tr("&Project"))
        self.pluginsMenu = menubar.addMenu(self.tr("&Addins"))
        about = menubar.addMenu(self.tr("Abou&t"))

        #The order of the icons in the toolbar is defined by this calls
        self._menuFile = menu_file.MenuFile(file_, self.toolbar, self)
        self._menuView = menu_view.MenuView(view, self.toolbar, self)
        self._menuEdit = menu_edit.MenuEdit(edit, self.toolbar)
        self._menuSource = menu_source.MenuSource(source)
        self._menuProject = menu_project.MenuProject(project, self.toolbar)
        self._menuPlugins = menu_plugins.MenuPlugins(self.pluginsMenu)
        self._menuAbout = menu_about.MenuAbout(about)

        self.load_toolbar()

        #Plugin Manager
        services = {
            'editor': plugin_services.MainService(),
            'toolbar': plugin_services.ToolbarService(self.toolbar),
            'menuApp': plugin_services.MenuAppService(self.pluginsMenu),
            'explorer': plugin_services.ExplorerService(),
            'misc': plugin_services.MiscContainerService(self.misc)}
        serviceLocator = plugin_manager.ServiceLocator(services)
        self.plugin_manager = plugin_manager.PluginManager(resources.PLUGINS,
            serviceLocator)
        self.plugin_manager.discover()
        #load all plugins!
        self.plugin_manager.load_all()

        #Tray Icon
        self.trayIcon = updates.TrayIconUpdates(self)
        self.trayIcon.show()

        self.connect(self._menuFile, SIGNAL("openFile(QString)"),
            self.mainContainer.open_file)
        self.connect(self.mainContainer, SIGNAL("fileSaved(QString)"),
            self.show_status_message)
        self.connect(self.mainContainer,
            SIGNAL("recentTabsModified(QStringList)"),
            self._menuFile.update_recent_files)
        self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"),
            self.actions.update_migration_tips)
        self.connect(self.mainContainer, SIGNAL("updateFileMetadata(QString)"),
            self.actions.update_migration_tips)
        self.connect(self.mainContainer, SIGNAL("migrationAnalyzed()"),
            self.actions.update_migration_tips)

    def _process_connection(self):
        connection = self.s_listener.nextPendingConnection()
        connection.waitForReadyRead()
        data = connection.readAll()
        connection.close()
        if data:
            files, projects = str(data).split(ipc.project_delimiter, 1)
            files = [(x.split(':')[0], int(x.split(':')[1]))
                for x in files.split(ipc.file_delimiter)]
            projects = projects.split(ipc.project_delimiter)
            self.load_session_files_projects(files, [], projects, None)

    def load_toolbar(self):
        self.toolbar.clear()
        toolbar_items = {}
        toolbar_items.update(self._menuFile.toolbar_items)
        toolbar_items.update(self._menuView.toolbar_items)
        toolbar_items.update(self._menuEdit.toolbar_items)
        toolbar_items.update(self._menuSource.toolbar_items)
        toolbar_items.update(self._menuProject.toolbar_items)

        for item in settings.TOOLBAR_ITEMS:
            if item == 'separator':
                self.toolbar.addSeparator()
            else:
                tool_item = toolbar_items.get(item, None)
                if tool_item is not None:
                    self.toolbar.addAction(tool_item)
        #load action added by plugins, This is a special case when reload
        #the toolbar after save the preferences widget
        for toolbar_action in settings.get_toolbar_item_for_plugins():
            self.toolbar.addAction(toolbar_action)

    def load_external_plugins(self, paths):
        for path in paths:
            self.plugin_manager.add_plugin_dir(path)
        #load all plugins!
        self.plugin_manager.discover()
        self.plugin_manager.load_all()

    def show_status_message(self, message):
        self.status.showMessage(message, 2000)

    def load_ui(self, centralWidget):
        #Set Application Font for ToolTips
        QToolTip.setFont(QFont(settings.FONT_FAMILY, 10))
        #Create Main Container to manage Tabs
        self.mainContainer = main_container.MainContainer(self)
        self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"),
            self.change_window_title)
        self.connect(self.mainContainer,
            SIGNAL("locateFunction(QString, QString, bool)"),
            self.actions.locate_function)
        self.connect(self.mainContainer,
            SIGNAL("navigateCode(bool, int)"),
            self.actions.navigate_code_history)
        self.connect(self.mainContainer,
            SIGNAL("addBackItemNavigation()"),
            self.actions.add_back_item_navigation)
        self.connect(self.mainContainer, SIGNAL("updateFileMetadata()"),
            self.actions.update_explorer)
        self.connect(self.mainContainer, SIGNAL("updateLocator(QString)"),
            self.actions.update_explorer)
        self.connect(self.mainContainer, SIGNAL("openPreferences()"),
            self._show_preferences)
        self.connect(self.mainContainer, SIGNAL("dontOpenStartPage()"),
            self._dont_show_start_page_again)
        self.connect(self.mainContainer, SIGNAL("currentTabChanged(QString)"),
            self.status.handle_tab_changed)
        # When close the last tab cleanup
        self.connect(self.mainContainer, SIGNAL("allTabsClosed()"),
            self._last_tab_closed)
        # Update symbols
        self.connect(self.mainContainer, SIGNAL("updateLocator(QString)"),
            self.status.explore_file_code)
        #Create Explorer Panel
        self.explorer = explorer_container.ExplorerContainer(self)
        self.connect(self.central, SIGNAL("splitterCentralRotated()"),
            self.explorer.rotate_tab_position)
        self.connect(self.explorer, SIGNAL("updateLocator()"),
            self.status.explore_code)
        self.connect(self.explorer, SIGNAL("goToDefinition(int)"),
            self.actions.editor_go_to_line)
        self.connect(self.explorer, SIGNAL("projectClosed(QString)"),
            self.actions.close_files_from_project)
        #Create Misc Bottom Container
        self.misc = misc_container.MiscContainer(self)
        self.connect(self.mainContainer, SIGNAL("findOcurrences(QString)"),
            self.misc.show_find_occurrences)

        centralWidget.insert_central_container(self.mainContainer)
        centralWidget.insert_lateral_container(self.explorer)
        centralWidget.insert_bottom_container(self.misc)
        if self.explorer.count() == 0:
            centralWidget.change_explorer_visibility(force_hide=True)
        self.connect(self.mainContainer,
            SIGNAL("cursorPositionChange(int, int)"),
            self.central.lateralPanel.update_line_col)
        # TODO: Change current symbol on move
        #self.connect(self.mainContainer,
            #SIGNAL("cursorPositionChange(int, int)"),
            #self.explorer.update_current_symbol)
        self.connect(self.mainContainer, SIGNAL("enabledFollowMode(bool)"),
            self.central.enable_follow_mode_scrollbar)

        if settings.SHOW_START_PAGE:
            self.mainContainer.show_start_page()

    def _last_tab_closed(self):
        """
        Called when the last tasb is closed
        """
        self.explorer.cleanup_tabs()

    def _show_preferences(self):
        pref = preferences.PreferencesWidget(self.mainContainer)
        pref.show()

    def _dont_show_start_page_again(self):
        settings.SHOW_START_PAGE = False
        qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat)
        qsettings.beginGroup('preferences')
        qsettings.beginGroup('general')
        qsettings.setValue('showStartPage', settings.SHOW_START_PAGE)
        qsettings.endGroup()
        qsettings.endGroup()
        self.mainContainer.actualTab.close_tab()

    def load_session_files_projects(self, filesTab1, filesTab2, projects,
        current_file, recent_files=None):
        self.__project_to_open = len(projects)
        self.connect(self.explorer, SIGNAL("projectOpened(QString)"),
            self._set_editors_project_data)
        self.explorer.open_session_projects(projects, notIDEStart=False)
        self.mainContainer.open_files(filesTab1, notIDEStart=False)
        self.mainContainer.open_files(filesTab2, mainTab=False,
            notIDEStart=False)
        if current_file:
            self.mainContainer.open_file(current_file, notStart=False)
        if recent_files is not None:
            self._menuFile.update_recent_files(recent_files)

    def _set_editors_project_data(self):
        self.__project_to_open -= 1
        if self.__project_to_open == 0:
            self.disconnect(self.explorer, SIGNAL("projectOpened(QString)"),
                self._set_editors_project_data)
            self.mainContainer.update_editor_project()

    def open_file(self, filename):
        if filename:
            self.mainContainer.open_file(filename)

    def open_project(self, project):
        if project:
            self.actions.open_project(project)

    def __get_profile(self):
        return self.profile

    def __set_profile(self, profileName):
        self.profile = profileName
        if self.profile is not None:
            self.setWindowTitle('AL\'EXA-IDE (PROFILE: %s)' % self.profile)
        else:
            self.setWindowTitle(
                'AL\'EXA-IDE {Al\'exa-IDE Is Not Just Another IDE}')

    Profile = property(__get_profile, __set_profile)

    def change_window_title(self, title):
        if self.profile is None:
            self.setWindowTitle('AL\'EXA-IDE - %s' % title)
        else:
            self.setWindowTitle('AL\'EXA-IDE (PROFILE: %s) - %s' % (
                self.profile, title))
        currentEditor = self.mainContainer.get_actual_editor()
        if currentEditor is not None:
            line = currentEditor.textCursor().blockNumber() + 1
            col = currentEditor.textCursor().columnNumber()
            self.central.lateralPanel.update_line_col(line, col)

    def wheelEvent(self, event):
        if event.modifiers() == Qt.ShiftModifier:
            if event.delta() == 120 and self.opacity < settings.MAX_OPACITY:
                self.opacity += 0.1
            elif event.delta() == -120 and self.opacity > settings.MIN_OPACITY:
                self.opacity -= 0.1
            self.setWindowOpacity(self.opacity)
            event.ignore()
        else:
            QMainWindow.wheelEvent(self, event)

    def save_settings(self):
        """Save the settings before the application is closed with QSettings.

        Info saved: Tabs and projects opened, windows state(size and position).
        """
        qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat)
        editor_widget = self.mainContainer.get_actual_editor()
        current_file = ''
        if editor_widget is not None:
            current_file = editor_widget.ID
        if qsettings.value('preferences/general/loadFiles', True, type=bool):
            openedFiles = self.mainContainer.get_opened_documents()
            projects_obj = self.explorer.get_opened_projects()
            projects = [p.path for p in projects_obj]
            qsettings.setValue('openFiles/projects',
                projects)
            if len(openedFiles) > 0:
                qsettings.setValue('openFiles/mainTab', openedFiles[0])
            if len(openedFiles) == 2:
                qsettings.setValue('openFiles/secondaryTab', openedFiles[1])
            qsettings.setValue('openFiles/currentFile', current_file)
            qsettings.setValue('openFiles/recentFiles',
                self.mainContainer._tabMain.get_recent_files_list())
        qsettings.setValue('preferences/editor/bookmarks', settings.BOOKMARKS)
        qsettings.setValue('preferences/editor/breakpoints',
            settings.BREAKPOINTS)
        qsettings.setValue('preferences/general/toolbarArea',
            self.toolBarArea(self.toolbar))
        #Save if the windows state is maximixed
        if(self.isMaximized()):
            qsettings.setValue("window/maximized", True)
        else:
            qsettings.setValue("window/maximized", False)
            #Save the size and position of the mainwindow
            qsettings.setValue("window/size", self.size())
            qsettings.setValue("window/pos", self.pos())
        #Save the size of de splitters
        qsettings.setValue("window/central/areaSize",
            self.central.get_area_sizes())
        qsettings.setValue("window/central/mainSize",
            self.central.get_main_sizes())
        #Save the toolbar visibility
        if not self.toolbar.isVisible() and self.menuBar().isVisible():
            qsettings.setValue("window/hide_toolbar", True)
        else:
            qsettings.setValue("window/hide_toolbar", False)
        #Save Misc state
        qsettings.setValue("window/show_misc", self.misc.isVisible())
        #Save Profiles
        if self.profile is not None:
            self.actions.save_profile(self.profile)
        else:
            qsettings.setValue('ide/profiles', settings.PROFILES)

    def load_window_geometry(self):
        """Load from QSettings the window size of de Ninja IDE"""
        qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat)
        if qsettings.value("window/maximized", True, type=bool):
            self.setWindowState(Qt.WindowMaximized)
        else:
            self.resize(qsettings.value("window/size",
                QSizeF(800, 600).toSize(), type='QSize'))
            self.move(qsettings.value("window/pos",
                QPointF(100, 100).toPoint(), type='QPoint'))

    def CloseHideAllWindow(self, close=True):
        if sys.platform == 'win32':
            import win32gui
            import win32con
            import re

            toplist, winlist = [], []

            def enum_callback(hwnd, results):
                winlist.append((hwnd, win32gui.GetWindowText(hwnd)))

            win32gui.EnumWindows(enum_callback, toplist)

            #^(?!.*details\.cfm).*selector=size.*$
            #window = [(hwnd, curTitle) for hwnd, curTitle in winlist if re.match("al.*exa.*(tool|nagios utilities|mouse and keyboard).*", curTitle, re.DOTALL | re.IGNORECASE) != None and win32gui.IsWindowVisible(hwnd) != 0]
            windows = [(hwnd, curTitle) for hwnd, curTitle in winlist]

            # just grab the hwnd for first window matching title
            #print "toplist", top
            if len(windows) > 0:
                toplist, winlist = [], []
                #print windows

                for window in windows:
                    try:
                        #if openedWindow[0].lower() == 'Al\'exa tools':
                        title = window[1]
                        if 'al\'exa tools' == title.lower() or 'al\'exa - nagios utilities' == title.lower() or 'al\'exa - mouse and keyboard' == title.lower() or 'al\'exa - windows and region' == title.lower() or 'al\'exa - process utilities' == title.lower() or 'al\'exa - e-mail' == title.lower():
                            if close is True:
                                win32gui.PostMessage(window[0], win32con.WM_CLOSE, 0, 0)
                            else:
                                win32gui.ShowWindow(window[0], 0)
                    except:
                        continue

    def closeEvent(self, event):

        if self.s_listener:
            self.s_listener.close()
        if (settings.CONFIRM_EXIT and
                self.mainContainer.check_for_unsaved_tabs()):
            unsaved_files = self.mainContainer.get_unsaved_files()
            txt = '\n'.join(unsaved_files)

            self.CloseHideAllWindow(False)
            val = QMessageBox.question(self,
                self.tr("Some changes were not saved"),
                (self.tr("%s\n\nDo you want to save them?") % txt),
                QMessageBox.Yes, QMessageBox.No, QMessageBox.Cancel)

            '''
            msgBox = QMessageBox()
            msgBox.setWindowTitle("QMessageBox Always On Top")
            msgBox.setText("This is an always-on-top QMessageBox!")
            msgBox.setWindowFlags(Qt.WindowStaysOnTopHint)
            msgBox.show()
            '''

            #val.setWindowFlags(Qt.WindowStaysOnTopHint)

            if val == QMessageBox.Yes:
                #Saves all open files
                self.mainContainer.save_all()
            if val == QMessageBox.Cancel:
                event.ignore()
                return
        self.emit(SIGNAL("goingDown()"))
        self.save_settings()
        completion_daemon.shutdown_daemon()
        #close python documentation server (if running)
        self.mainContainer.close_python_doc()
        #Shutdown PluginManager
        self.plugin_manager.shutdown()
        self.CloseHideAllWindow(True)

    def notify_plugin_errors(self):
        errors = self.plugin_manager.errors
        if errors:
            plugin_error_dialog = traceback_widget.PluginErrorDialog()
            for err_tuple in errors:
                plugin_error_dialog.add_traceback(err_tuple[0], err_tuple[1])
            #show the dialog
            plugin_error_dialog.exec_()

    def show_python_detection(self):
        suggested = settings.detect_python_path()
        if suggested:
            dialog = python_detect_dialog.PythonDetectDialog(suggested, self)
            dialog.show()