Пример #1
0
    def get(self, qurl, html=None, num_retries=1, delay=10, timeout=10):
        t1 = time()

        loop = QEventLoop()
        timer = QTimer()
        timer.setSingleShot(True)
        timer.timeout.connect(loop.quit)
        self.loadFinished.connect(loop.quit)
        if qurl:
            if html:
                self.setHtml(html, qurl)
            else:
                self.mainFrame().load(QUrl(qurl))
        timer.start(timeout * 1000)
        loop.exec_()  # delay here until download finished or timeout

        if timer.isActive():
            # downloaded successfully
            timer.stop()
            self._wait(delay - (time() - t1))
            parsed_html = self.mainFrame().toHtml()
        else:
            # did not download in time
            if num_retries > 0:
                logging.debug('Timeout - retrying')
                parsed_html = self.get(qurl,
                                       num_retries=num_retries - 1,
                                       timerout=timeout,
                                       delay=delay)
            else:
                logging.debug('Timed out')
                parsed_html = ''
        self.mainFrame().setHtml(None)
        return parsed_html
Пример #2
0
 def get(self, qurl, html=None, num_retries=1, delay = 10, timeout = 10):
     t1 = time()
     
     loop = QEventLoop()
     timer = QTimer()
     timer.setSingleShot(True)
     timer.timeout.connect(loop.quit)
     self.loadFinished.connect(loop.quit)
     if qurl:
         if html:
             self.setHtml(html, qurl)
         else: 
             self.mainFrame().load(QUrl(qurl))
     timer.start(timeout * 1000)
     loop.exec_() # delay here until download finished or timeout
 
     if timer.isActive():
         # downloaded successfully
         timer.stop()
         self._wait(delay - (time() - t1))
         parsed_html = self.mainFrame().toHtml()
     else:
         # did not download in time
         if num_retries > 0:
             logging.debug('Timeout - retrying')
             parsed_html = self.get(qurl, num_retries=num_retries-1, timerout=timeout, delay=delay)
         else:
             logging.debug('Timed out')
             parsed_html = ''
     self.mainFrame().setHtml(None)
     return parsed_html
Пример #3
0
class CoverDelegate(QStyledItemDelegate):  # {{{

    ICON_SIZE = 150, 200

    needs_redraw = pyqtSignal()

    def __init__(self, parent):
        QStyledItemDelegate.__init__(self, parent)

        self.angle = 0
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.frame_changed)
        self.dark_color = parent.palette().color(QPalette.ColorRole.WindowText)
        self.light_color = parent.palette().color(QPalette.ColorRole.Window)
        self.spinner_width = 64

    def frame_changed(self, *args):
        self.angle = (self.angle - 2) % 360
        self.needs_redraw.emit()

    def start_animation(self):
        self.angle = 0
        self.timer.start(10)

    def stop_animation(self):
        self.timer.stop()

    def paint(self, painter, option, index):
        QStyledItemDelegate.paint(self, painter, option, index)
        style = QApplication.style()
        waiting = self.timer.isActive() and bool(
            index.data(Qt.ItemDataRole.UserRole))
        if waiting:
            rect = QRect(0, 0, self.spinner_width, self.spinner_width)
            rect.moveCenter(option.rect.center())
            draw_snake_spinner(painter, rect, self.angle, self.light_color,
                               self.dark_color)
        else:
            # Ensure the cover is rendered over any selection rect
            style.drawItemPixmap(
                painter, option.rect,
                Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignHCenter,
                QPixmap(index.data(Qt.ItemDataRole.DecorationRole)))
Пример #4
0
    def reconnect_to_backend(self, app_backend, error):  # pragma: no cover
        """
        Set AlignakApp in reconnect mode and try to login to Backend

        :param app_backend: AppBackend object
        :type app_backend: AppBackend
        :param error: string error to display in banner
        :type error: str
        """

        self.reconnect_mode = True
        logger.warning('Application reconnecting MODE: %s', self.reconnecting)
        send_banner('ERROR', 'Alignak Backend seems unreachable ! %s' % error, duration=5000)
        timer = QTimer(self)

        def connect_to_backend():
            """Try to log in to Backend"""
            try:
                connect = app_backend.login()
                assert connect
                # If connect, reconnecting is disable
                timer.stop()
                logger.info('Connection restored : %s', connect)
                send_banner(
                    'OK',
                    'Connection with the Backend has been restored ! You are logged in again',
                    duration=5000
                )
                self.reconnect_mode = False
            except AssertionError:
                send_banner(
                    'ERROR',
                    'Backend is still unreachable... Alignak-app try to reconnect',
                    duration=5000
                )
                logger.error('Backend is still unreachable...')

        if timer.isActive():
            pass
        else:
            timer.start(10000)
            timer.timeout.connect(connect_to_backend)
Пример #5
0
class CoverDelegate(QStyledItemDelegate):  # {{{

    ICON_SIZE = 150, 200

    needs_redraw = pyqtSignal()

    def __init__(self, parent):
        QStyledItemDelegate.__init__(self, parent)

        self.angle = 0
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.frame_changed)
        self.dark_color = parent.palette().color(QPalette.WindowText)
        self.light_color = parent.palette().color(QPalette.Window)
        self.spinner_width = 64

    def frame_changed(self, *args):
        self.angle = (self.angle-2)%360
        self.needs_redraw.emit()

    def start_animation(self):
        self.angle = 0
        self.timer.start(10)

    def stop_animation(self):
        self.timer.stop()

    def paint(self, painter, option, index):
        QStyledItemDelegate.paint(self, painter, option, index)
        style = QApplication.style()
        waiting = self.timer.isActive() and bool(index.data(Qt.UserRole))
        if waiting:
            rect = QRect(0, 0, self.spinner_width, self.spinner_width)
            rect.moveCenter(option.rect.center())
            draw_snake_spinner(painter, rect, self.angle, self.light_color, self.dark_color)
        else:
            # Ensure the cover is rendered over any selection rect
            style.drawItemPixmap(painter, option.rect, Qt.AlignTop|Qt.AlignHCenter,
                QPixmap(index.data(Qt.DecorationRole)))
Пример #6
0
class External():
    def __init__(self):
        self.watchTimer = QTimer()
        self.watchTimer.setInterval(800)
        self.watchTimer.timerEvent = self.onWatch
        self.files_changed = queue.Queue()
        pass

    def onWatch(self,e):
        try:
            file_type, filename, action = self.files_changed.get_nowait()
            print(file_type, filename, action)
        except queue.Empty:
            pass
        pass

    def onFileChanged(self, path=None):
        print('file changed', path)
        pass

    def load(self, path):
        print('load', path)
        Watcher(path, self.files_changed)
        if not self.watchTimer.isActive():
            self.watchTimer.start()
            pass
        pass

    def update(self, path):
        pass

    def create(self, tmppath=None):
        if not tmppath:
            tmppath = 'tmp'
        if not os.path.exists(tmppath):
            os.mkdir(tmppath)
            pass
        pass
Пример #7
0
class LiveCSS(QWidget):

    goto_declaration = pyqtSignal(object)

    def __init__(self, preview, parent=None):
        QWidget.__init__(self, parent)
        self.preview = preview
        self.preview_is_refreshing = False
        self.refresh_needed = False
        preview.refresh_starting.connect(self.preview_refresh_starting)
        preview.refreshed.connect(self.preview_refreshed)
        self.apply_theme()
        self.setAutoFillBackground(True)
        self.update_timer = QTimer(self)
        self.update_timer.timeout.connect(self.update_data)
        self.update_timer.setSingleShot(True)
        self.update_timer.setInterval(500)
        self.now_showing = (None, None, None)

        self.stack = s = QStackedLayout(self)
        self.setLayout(s)

        self.clear_label = la = QLabel('<h3>' + _(
            'No style information found') + '</h3><p>' + _(
                'Move the cursor inside a HTML tag to see what styles'
                ' apply to that tag.'))
        la.setWordWrap(True)
        la.setAlignment(Qt.AlignTop | Qt.AlignLeft)
        s.addWidget(la)

        self.box = box = Box(self)
        box.hyperlink_activated.connect(self.goto_declaration, type=Qt.QueuedConnection)
        self.scroll = sc = QScrollArea(self)
        sc.setWidget(box)
        sc.setWidgetResizable(True)
        s.addWidget(sc)

    def preview_refresh_starting(self):
        self.preview_is_refreshing = True

    def preview_refreshed(self):
        self.preview_is_refreshing = False
        # We must let the event loop run otherwise the webview will return
        # stale data in read_data()
        self.refresh_needed = True
        self.start_update_timer()

    def apply_theme(self):
        f = self.font()
        f.setFamily(tprefs['editor_font_family'] or default_font_family())
        f.setPointSize(tprefs['editor_font_size'])
        self.setFont(f)
        theme = get_theme(tprefs['editor_theme'])
        pal = self.palette()
        pal.setColor(pal.Window, theme_color(theme, 'Normal', 'bg'))
        pal.setColor(pal.WindowText, theme_color(theme, 'Normal', 'fg'))
        pal.setColor(pal.AlternateBase, theme_color(theme, 'HighlightRegion', 'bg'))
        pal.setColor(pal.Link, theme_color(theme, 'Link', 'fg'))
        pal.setColor(pal.LinkVisited, theme_color(theme, 'Keyword', 'fg'))
        self.setPalette(pal)
        if hasattr(self, 'box'):
            self.box.relayout()
        self.update()

    def clear(self):
        self.stack.setCurrentIndex(0)

    def show_data(self, editor_name, sourceline, tags):
        if self.preview_is_refreshing:
            return
        if sourceline is None:
            self.clear()
        else:
            data = self.read_data(sourceline, tags)
            if data is None or len(data['computed_css']) < 1:
                if editor_name == self.current_name and (editor_name, sourceline, tags) == self.now_showing:
                    # Try again in a little while in case there was a transient
                    # error in the web view
                    self.start_update_timer()
                    return
                if self.now_showing == (None, None, None) or self.now_showing[0] != self.current_name:
                    self.clear()
                    return
                # Try to refresh the data for the currently shown tag instead
                # of clearing
                editor_name, sourceline, tags = self.now_showing
                data = self.read_data(sourceline, tags)
                if data is None or len(data['computed_css']) < 1:
                    self.clear()
                    return
            self.now_showing = (editor_name, sourceline, tags)
            data['html_name'] = editor_name
            self.box.show_data(data)
            self.refresh_needed = False
            self.stack.setCurrentIndex(1)

    def read_data(self, sourceline, tags):
        mf = self.preview.view.page().mainFrame()
        tags = [x.lower() for x in tags]
        result = unicode_type(mf.evaluateJavaScript(
            'window.calibre_preview_integration.live_css(%s, %s)' % (
                json.dumps(sourceline), json.dumps(tags))) or '')
        try:
            result = json.loads(result)
        except ValueError:
            result = None
        if result is not None:
            maximum_specificities = {}
            for node in result['nodes']:
                is_ancestor = node['is_ancestor']
                for rule in node['css']:
                    self.process_rule(rule, is_ancestor, maximum_specificities)
            for node in result['nodes']:
                for rule in node['css']:
                    for prop in rule['properties']:
                        if prop.specificity < maximum_specificities[prop.name]:
                            prop.is_overriden = True

        return result

    def process_rule(self, rule, is_ancestor, maximum_specificities):
        selector = rule['selector']
        sheet_index = rule['sheet_index']
        rule_address = rule['rule_address'] or ()
        if selector is not None:
            try:
                specificity = [0] + list(parse(selector)[0].specificity())
            except (AttributeError, TypeError, SelectorError):
                specificity = [0, 0, 0, 0]
        else:  # style attribute
            specificity = [1, 0, 0, 0]
        specificity.extend((sheet_index, tuple(rule_address)))
        ancestor_specificity = 0 if is_ancestor else 1
        properties = []
        for prop in rule['properties']:
            important = 1 if prop[-1] == 'important' else 0
            p = Property(prop, [ancestor_specificity] + [important] + specificity)
            properties.append(p)
            if p.specificity > maximum_specificities.get(p.name, (0,0,0,0,0,0)):
                maximum_specificities[p.name] = p.specificity
        rule['properties'] = properties

        href = rule['href']
        if hasattr(href, 'startswith') and href.startswith('%s://%s' % (FAKE_PROTOCOL, FAKE_HOST)):
            qurl = QUrl(href)
            name = qurl.path()[1:]
            if name:
                rule['href'] = name

    @property
    def current_name(self):
        return self.preview.current_name

    @property
    def is_visible(self):
        return self.isVisible()

    def showEvent(self, ev):
        self.update_timer.start()
        actions['auto-reload-preview'].setEnabled(True)
        return QWidget.showEvent(self, ev)

    def sync_to_editor(self):
        self.update_data()

    def update_data(self):
        if not self.is_visible or self.preview_is_refreshing:
            return
        editor_name = self.current_name
        ed = editors.get(editor_name, None)
        if self.update_timer.isActive() or (ed is None and editor_name is not None):
            return QTimer.singleShot(100, self.update_data)
        if ed is not None:
            sourceline, tags = ed.current_tag(for_position_sync=False)
            if self.refresh_needed or self.now_showing != (editor_name, sourceline, tags):
                self.show_data(editor_name, sourceline, tags)

    def start_update_timer(self):
        if self.is_visible:
            self.update_timer.start()

    def stop_update_timer(self):
        self.update_timer.stop()

    def navigate_to_declaration(self, data, editor):
        if data['type'] == 'inline':
            sourceline, tags = data['sourceline_address']
            editor.goto_sourceline(sourceline, tags, attribute='style')
        elif data['type'] == 'sheet':
            editor.goto_css_rule(data['rule_address'])
        elif data['type'] == 'elem':
            editor.goto_css_rule(data['rule_address'], sourceline_address=data['sourceline_address'])
Пример #8
0
class Preview(QWidget):

    sync_requested = pyqtSignal(object, object)
    split_requested = pyqtSignal(object, object, object)
    split_start_requested = pyqtSignal()
    link_clicked = pyqtSignal(object, object)
    refresh_starting = pyqtSignal()
    refreshed = pyqtSignal()
    live_css_data = pyqtSignal(object)
    render_process_restarted = pyqtSignal()
    open_file_with = pyqtSignal(object, object, object)
    edit_file = pyqtSignal(object)

    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.l = l = QVBoxLayout()
        self.setLayout(l)
        l.setContentsMargins(0, 0, 0, 0)
        self.view = WebView(self)
        self.view._page.bridge.request_sync.connect(self.request_sync)
        self.view._page.bridge.request_split.connect(self.request_split)
        self.view._page.bridge.live_css_data.connect(self.live_css_data)
        self.view._page.loadFinished.connect(self.load_finished)
        self.view.render_process_restarted.connect(
            self.render_process_restarted)
        self.pending_go_to_anchor = None
        self.inspector = self.view.inspector
        l.addWidget(self.view)
        self.bar = QToolBar(self)
        l.addWidget(self.bar)

        ac = actions['auto-reload-preview']
        ac.setCheckable(True)
        ac.setChecked(True)
        ac.toggled.connect(self.auto_reload_toggled)
        self.auto_reload_toggled(ac.isChecked())
        self.bar.addAction(ac)

        ac = actions['sync-preview-to-editor']
        ac.setCheckable(True)
        ac.setChecked(True)
        ac.toggled.connect(self.sync_toggled)
        self.sync_toggled(ac.isChecked())
        self.bar.addAction(ac)

        self.bar.addSeparator()

        ac = actions['split-in-preview']
        ac.setCheckable(True)
        ac.setChecked(False)
        ac.toggled.connect(self.split_toggled)
        self.split_toggled(ac.isChecked())
        self.bar.addAction(ac)

        ac = actions['reload-preview']
        ac.triggered.connect(self.refresh)
        self.bar.addAction(ac)

        actions['preview-dock'].toggled.connect(self.visibility_changed)

        self.current_name = None
        self.last_sync_request = None
        self.refresh_timer = QTimer(self)
        self.refresh_timer.timeout.connect(self.refresh)
        parse_worker.start()
        self.current_sync_request = None

        self.search = HistoryLineEdit2(self)
        self.search.initialize('tweak_book_preview_search')
        self.search.setPlaceholderText(_('Search in preview'))
        self.search.returnPressed.connect(self.find_next)
        self.bar.addSeparator()
        self.bar.addWidget(self.search)
        for d in ('next', 'prev'):
            ac = actions['find-%s-preview' % d]
            ac.triggered.connect(getattr(self, 'find_' + d))
            self.bar.addAction(ac)

    def find(self, direction):
        text = unicode_type(self.search.text())
        self.view._page.findText(text,
                                 (QWebEnginePage.FindBackward if direction
                                  == 'prev' else QWebEnginePage.FindFlags(0)))

    def find_next(self):
        self.find('next')

    def find_prev(self):
        self.find('prev')

    def go_to_anchor(self, anchor):
        self.view._page.go_to_anchor(anchor)

    def request_sync(self, tagname, href, lnum):
        if self.current_name:
            c = current_container()
            if tagname == 'a' and href:
                if href and href.startswith('#'):
                    name = self.current_name
                else:
                    name = c.href_to_name(href,
                                          self.current_name) if href else None
                if name == self.current_name:
                    return self.go_to_anchor(urlparse(href).fragment)
                if name and c.exists(name) and c.mime_map[name] in OEB_DOCS:
                    return self.link_clicked.emit(
                        name,
                        urlparse(href).fragment or TOP)
            self.sync_requested.emit(self.current_name, lnum)

    def request_split(self, loc, totals):
        actions['split-in-preview'].setChecked(False)
        if not loc or not totals:
            return error_dialog(self,
                                _('Invalid location'),
                                _('Cannot split on the body tag'),
                                show=True)
        if self.current_name:
            self.split_requested.emit(self.current_name, loc, totals)

    def sync_to_editor(self, name, sourceline_address):
        self.current_sync_request = (name, sourceline_address)
        QTimer.singleShot(100, self._sync_to_editor)

    def _sync_to_editor(self):
        if not actions['sync-preview-to-editor'].isChecked():
            return
        try:
            if self.refresh_timer.isActive(
            ) or self.current_sync_request[0] != self.current_name:
                return QTimer.singleShot(100, self._sync_to_editor)
        except TypeError:
            return  # Happens if current_sync_request is None
        sourceline_address = self.current_sync_request[1]
        self.current_sync_request = None
        self.view._page.go_to_sourceline_address(sourceline_address)

    def report_worker_launch_error(self):
        if parse_worker.launch_error is not None:
            tb, parse_worker.launch_error = parse_worker.launch_error, None
            error_dialog(
                self,
                _('Failed to launch worker'),
                _('Failed to launch the worker process used for rendering the preview'
                  ),
                det_msg=tb,
                show=True)

    def name_to_qurl(self, name=None):
        name = name or self.current_name
        qurl = QUrl()
        qurl.setScheme(FAKE_PROTOCOL), qurl.setAuthority(
            FAKE_HOST), qurl.setPath('/' + name)
        return qurl

    def show(self, name):
        if name != self.current_name:
            self.refresh_timer.stop()
            self.current_name = name
            self.report_worker_launch_error()
            parse_worker.add_request(name)
            self.view.setUrl(self.name_to_qurl())
            return True

    def refresh(self):
        if self.current_name:
            self.refresh_timer.stop()
            # This will check if the current html has changed in its editor,
            # and re-parse it if so
            self.report_worker_launch_error()
            parse_worker.add_request(self.current_name)
            # Tell webkit to reload all html and associated resources
            current_url = self.name_to_qurl()
            self.refresh_starting.emit()
            if current_url != self.view.url():
                # The container was changed
                self.view.setUrl(current_url)
            else:
                self.view.refresh()
            self.refreshed.emit()

    def clear(self):
        self.view.clear()
        self.current_name = None

    @property
    def is_visible(self):
        return actions['preview-dock'].isChecked()

    @property
    def live_css_is_visible(self):
        try:
            return actions['live-css-dock'].isChecked()
        except KeyError:
            return False

    def start_refresh_timer(self):
        if self.live_css_is_visible or (
                self.is_visible
                and actions['auto-reload-preview'].isChecked()):
            self.refresh_timer.start(tprefs['preview_refresh_time'] * 1000)

    def stop_refresh_timer(self):
        self.refresh_timer.stop()

    def auto_reload_toggled(self, checked):
        if self.live_css_is_visible and not actions[
                'auto-reload-preview'].isChecked():
            actions['auto-reload-preview'].setChecked(True)
            error_dialog(
                self,
                _('Cannot disable'),
                _('Auto reloading of the preview panel cannot be disabled while the'
                  ' Live CSS panel is open.'),
                show=True)
        actions['auto-reload-preview'].setToolTip(
            _('Auto reload preview when text changes in editor'
              ) if not checked else _('Disable auto reload of preview'))

    def sync_toggled(self, checked):
        actions['sync-preview-to-editor'].setToolTip(
            _('Disable syncing of preview position to editor position'
              ) if checked else _(
                  'Enable syncing of preview position to editor position'))

    def visibility_changed(self, is_visible):
        if is_visible:
            self.refresh()

    def split_toggled(self, checked):
        actions['split-in-preview'].setToolTip(
            textwrap.fill(
                _('Abort file split') if checked else
                _('Split this file at a specified location.\n\nAfter clicking this button, click'
                  ' inside the preview panel above at the location you want the file to be split.'
                  )))
        if checked:
            self.split_start_requested.emit()
        else:
            self.view._page.split_mode(False)

    def do_start_split(self):
        self.view._page.split_mode(True)

    def stop_split(self):
        actions['split-in-preview'].setChecked(False)

    def load_finished(self, ok):
        if self.pending_go_to_anchor:
            self.view._page.go_to_anchor(self.pending_go_to_anchor)
            self.pending_go_to_anchor = None
        if actions['split-in-preview'].isChecked():
            if ok:
                self.do_start_split()
            else:
                self.stop_split()

    def request_live_css_data(self, editor_name, sourceline, tags):
        if self.view._page.bridge.ready:
            self.view._page.bridge.live_css(editor_name, sourceline, tags)

    def apply_settings(self):
        s = self.view.settings()
        s.setFontSize(s.DefaultFontSize, tprefs['preview_base_font_size'])
        s.setFontSize(s.DefaultFixedFontSize, tprefs['preview_mono_font_size'])
        s.setFontSize(s.MinimumLogicalFontSize,
                      tprefs['preview_minimum_font_size'])
        s.setFontSize(s.MinimumFontSize, tprefs['preview_minimum_font_size'])
        sf, ssf, mf = tprefs['engine_preview_serif_family'], tprefs[
            'engine_preview_sans_family'], tprefs['engine_preview_mono_family']
        if sf:
            s.setFontFamily(s.SerifFont, sf)
        if ssf:
            s.setFontFamily(s.SansSerifFont, ssf)
        if mf:
            s.setFontFamily(s.FixedFont, mf)
        stdfnt = tprefs['preview_standard_font_family'] or 'serif'
        stdfnt = getattr(s, {
            'serif': 'SerifFont',
            'sans': 'SansSerifFont',
            'mono': 'FixedFont'
        }[stdfnt])
        s.setFontFamily(s.StandardFont, s.fontFamily(stdfnt))
Пример #9
0
class ScudCloud(QtWidgets.QMainWindow):

    forceClose = False
    messages = 0
    speller = Speller()
    title = 'ScudCloud'

    def __init__(self,
                 debug=False,
                 minimized=None,
                 urgent_hint=None,
                 settings_path='',
                 cache_path=''):
        super(ScudCloud, self).__init__(None)
        self.debug = debug
        self.minimized = minimized
        self.urgent_hint = urgent_hint
        self.setWindowTitle(self.title)
        self.settings_path = settings_path
        self.cache_path = cache_path
        self.notifier = Notifier(Resources.APP_NAME,
                                 Resources.get_path('scudcloud.png'))
        self.settings = QSettings(self.settings_path + '/scudcloud_qt5.cfg',
                                  QSettings.IniFormat)
        self.notifier.enabled = self.settings.value('Notifications',
                                                    defaultValue=True,
                                                    type=bool)
        self.identifier = self.settings.value("Domain")
        if Unity is not None:
            self.launcher = Unity.LauncherEntry.get_for_desktop_id(
                "scudcloud.desktop")
        else:
            self.launcher = DummyLauncher(self)
        self.webSettings()
        self.snippetsSettings()
        self.leftPane = LeftPane(self)
        self.stackedWidget = QtWidgets.QStackedWidget()
        centralWidget = QtWidgets.QWidget(self)
        layout = QtWidgets.QHBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.addWidget(self.leftPane)
        layout.addWidget(self.stackedWidget)
        centralWidget.setLayout(layout)
        self.setCentralWidget(centralWidget)
        self.startURL = Resources.SIGNIN_URL
        if self.identifier is not None:
            if isinstance(self.identifier, str):
                self.domains = self.identifier.split(",")
            else:
                self.domains = self.identifier
            self.startURL = self.normalize(self.domains[0])
        else:
            self.domains = []
        self.addWrapper(self.startURL)
        self.addMenu()
        self.tray = Systray(self)
        self.systray(self.minimized)
        self.installEventFilter(self)
        self.statusBar().showMessage('Loading Slack...')
        self.tickler = QTimer(self)
        self.tickler.setInterval(1800000)
        # Watch for ScreenLock events
        if DBusQtMainLoop is not None:
            DBusQtMainLoop(set_as_default=True)
            sessionBus = dbus.SessionBus()
            # Ubuntu 12.04 and other distros
            sessionBus.add_match_string(
                "type='signal',interface='org.gnome.ScreenSaver'")
            # Ubuntu 14.04
            sessionBus.add_match_string(
                "type='signal',interface='com.ubuntu.Upstart0_6'")
            # Ubuntu 16.04 and KDE
            sessionBus.add_match_string(
                "type='signal',interface='org.freedesktop.ScreenSaver'")
            # Cinnamon
            sessionBus.add_match_string(
                "type='signal',interface='org.cinnamon.ScreenSaver'")
            sessionBus.add_message_filter(self.screenListener)
            self.tickler.timeout.connect(self.sendTickle)
        # If dbus is not present, tickler timer will act like a blocker to not send tickle too often
        else:
            self.tickler.setSingleShot(True)
        self.tickler.start()

    def screenListener(self, bus, message):
        event = message.get_member()
        # "ActiveChanged" for Ubuntu 12.04 and other distros. "EventEmitted" for Ubuntu 14.04 and above
        if event == "ActiveChanged" or event == "EventEmitted":
            arg = message.get_args_list()[0]
            # True for Ubuntu 12.04 and other distros. "desktop-lock" for Ubuntu 14.04 and above
            if (arg == True
                    or arg == "desktop-lock") and self.tickler.isActive():
                self.tickler.stop()
            elif (arg == False
                  or arg == "desktop-unlock") and not self.tickler.isActive():
                self.sendTickle()
                self.tickler.start()

    def sendTickle(self):
        for i in range(0, self.stackedWidget.count()):
            self.stackedWidget.widget(i).sendTickle()

    def addWrapper(self, url):
        webView = Wrapper(self)
        webView.load(QtCore.QUrl(url))
        webView.show()
        webView.setZoomFactor(self.zoom)
        self.stackedWidget.addWidget(webView)
        self.stackedWidget.setCurrentWidget(webView)
        self.clearMemory()

    def webSettings(self):
        self.cookiesjar = PersistentCookieJar(self)
        self.zoom = self.readZoom()
        # We don't want Flash (it causes a lot of trouble in some distros)
        QWebSettings.globalSettings().setAttribute(QWebSettings.PluginsEnabled,
                                                   False)
        # We don't need Java
        QWebSettings.globalSettings().setAttribute(QWebSettings.JavaEnabled,
                                                   False)
        # Enabling Local Storage (now required by Slack)
        QWebSettings.globalSettings().setAttribute(
            QWebSettings.LocalStorageEnabled, True)
        # We need browsing history (required to not limit LocalStorage)
        QWebSettings.globalSettings().setAttribute(
            QWebSettings.PrivateBrowsingEnabled, False)
        # Enabling Cache
        self.diskCache = QNetworkDiskCache(self)
        self.diskCache.setCacheDirectory(self.cache_path)
        # Required for copy and paste clipboard integration
        QWebSettings.globalSettings().setAttribute(
            QWebSettings.JavascriptCanAccessClipboard, True)
        # Enabling Inspeclet only when --debug=True (requires more CPU usage)
        QWebSettings.globalSettings().setAttribute(
            QWebSettings.DeveloperExtrasEnabled, self.debug)
        # Sharing the same networkAccessManager
        self.networkAccessManager = QNetworkAccessManager(self)
        self.networkAccessManager.setCookieJar(self.cookiesjar)
        self.networkAccessManager.setCache(self.diskCache)

    def snippetsSettings(self):
        self.disable_snippets = self.settings.value("Snippets")
        if self.disable_snippets is not None:
            self.disable_snippets = self.disable_snippets == "False"
        else:
            self.disable_snippets = False
        if self.disable_snippets:
            disable_snippets_css = ''
            with open(Resources.get_path('disable_snippets.css'), 'r') as f:
                disable_snippets_css = f.read()
            with open(os.path.join(self.cache_path, 'resources.css'),
                      'a') as f:
                f.write(disable_snippets_css)

    def toggleFullScreen(self):
        if self.isFullScreen():
            self.showMaximized()
        else:
            self.showFullScreen()

    def toggleMenuBar(self):
        menu = self.menuBar()
        state = menu.isHidden()
        menu.setVisible(state)
        if state:
            self.settings.setValue("Menu", "True")
        else:
            self.settings.setValue("Menu", "False")

    def restore(self):
        geometry = self.settings.value("geometry")
        if geometry is not None:
            self.restoreGeometry(geometry)
        windowState = self.settings.value("windowState")
        if windowState is not None:
            self.restoreState(windowState)
        else:
            self.setWindowState(QtCore.Qt.WindowMaximized)

    def systray(self, show=None):
        if show is None:
            show = self.settings.value("Systray") == "True"
        if show:
            self.tray.show()
            self.menus["file"]["close"].setEnabled(True)
            self.settings.setValue("Systray", "True")
        else:
            self.tray.setVisible(False)
            self.menus["file"]["close"].setEnabled(False)
            self.settings.setValue("Systray", "False")

    def readZoom(self):
        default = 1
        if self.settings.value("Zoom") is not None:
            default = float(self.settings.value("Zoom"))
        return default

    def setZoom(self, factor=1):
        if factor > 0:
            for i in range(0, self.stackedWidget.count()):
                widget = self.stackedWidget.widget(i)
                widget.setZoomFactor(factor)
            self.settings.setValue("Zoom", factor)

    def zoomIn(self):
        self.setZoom(self.current().zoomFactor() + 0.1)

    def zoomOut(self):
        self.setZoom(self.current().zoomFactor() - 0.1)

    def zoomReset(self):
        self.setZoom()

    def addTeam(self):
        self.switchTo(Resources.SIGNIN_URL)

    def addMenu(self):
        # We'll register the webpage shorcuts with the window too (Fixes #338)
        undo = self.current().pageAction(QWebPage.Undo)
        redo = self.current().pageAction(QWebPage.Redo)
        cut = self.current().pageAction(QWebPage.Cut)
        copy = self.current().pageAction(QWebPage.Copy)
        paste = self.current().pageAction(QWebPage.Paste)
        back = self.current().pageAction(QWebPage.Back)
        forward = self.current().pageAction(QWebPage.Forward)
        reload = self.current().pageAction(QWebPage.Reload)
        self.menus = {
            "file": {
                "preferences":
                self.createAction("Preferences",
                                  lambda: self.current().preferences()),
                "systray":
                self.createAction("Close to Tray", self.systray, None, True),
                "addTeam":
                self.createAction("Sign in to Another Team",
                                  lambda: self.addTeam()),
                "signout":
                self.createAction("Signout", lambda: self.current().logout()),
                "close":
                self.createAction("Close", self.close, QKeySequence.Close),
                "exit":
                self.createAction("Quit", self.exit, QKeySequence.Quit)
            },
            "edit": {
                "undo":
                self.createAction(
                    undo.text(),
                    lambda: self.current().page().triggerAction(QWebPage.Undo),
                    undo.shortcut()),
                "redo":
                self.createAction(
                    redo.text(),
                    lambda: self.current().page().triggerAction(QWebPage.Redo),
                    redo.shortcut()),
                "cut":
                self.createAction(
                    cut.text(),
                    lambda: self.current().page().triggerAction(QWebPage.Cut),
                    cut.shortcut()),
                "copy":
                self.createAction(
                    copy.text(),
                    lambda: self.current().page().triggerAction(QWebPage.Copy),
                    copy.shortcut()),
                "paste":
                self.createAction(
                    paste.text(), lambda: self.current().page().triggerAction(
                        QWebPage.Paste), paste.shortcut()),
                "back":
                self.createAction(
                    back.text(),
                    lambda: self.current().page().triggerAction(QWebPage.Back),
                    back.shortcut()),
                "forward":
                self.createAction(
                    forward.text(), lambda: self.current().page().
                    triggerAction(QWebPage.Forward), forward.shortcut()),
                "reload":
                self.createAction(
                    reload.text(), lambda: self.current().page().triggerAction(
                        QWebPage.Reload), reload.shortcut()),
            },
            "view": {
                "zoomin":
                self.createAction("Zoom In", self.zoomIn, QKeySequence.ZoomIn),
                "zoomout":
                self.createAction("Zoom Out", self.zoomOut,
                                  QKeySequence.ZoomOut),
                "reset":
                self.createAction("Reset", self.zoomReset,
                                  QtCore.Qt.CTRL + QtCore.Qt.Key_0),
                "fullscreen":
                self.createAction("Toggle Full Screen", self.toggleFullScreen,
                                  QtCore.Qt.Key_F11),
                "hidemenu":
                self.createAction("Toggle Menubar", self.toggleMenuBar,
                                  QtCore.Qt.Key_F12)
            },
            "help": {
                "help":
                self.createAction("Help and Feedback",
                                  lambda: self.current().help(),
                                  QKeySequence.HelpContents),
                "center":
                self.createAction("Slack Help Center",
                                  lambda: self.current().helpCenter()),
                "about":
                self.createAction("About", lambda: self.current().about())
            }
        }
        menu = self.menuBar()
        fileMenu = menu.addMenu("&File")
        fileMenu.addAction(self.menus["file"]["preferences"])
        fileMenu.addAction(self.menus["file"]["systray"])
        fileMenu.addSeparator()
        fileMenu.addAction(self.menus["file"]["addTeam"])
        fileMenu.addAction(self.menus["file"]["signout"])
        fileMenu.addSeparator()
        fileMenu.addAction(self.menus["file"]["close"])
        fileMenu.addAction(self.menus["file"]["exit"])
        editMenu = menu.addMenu("&Edit")
        editMenu.addAction(self.menus["edit"]["undo"])
        editMenu.addAction(self.menus["edit"]["redo"])
        editMenu.addSeparator()
        editMenu.addAction(self.menus["edit"]["cut"])
        editMenu.addAction(self.menus["edit"]["copy"])
        editMenu.addAction(self.menus["edit"]["paste"])
        editMenu.addSeparator()
        editMenu.addAction(self.menus["edit"]["back"])
        editMenu.addAction(self.menus["edit"]["forward"])
        editMenu.addAction(self.menus["edit"]["reload"])
        viewMenu = menu.addMenu("&View")
        viewMenu.addAction(self.menus["view"]["zoomin"])
        viewMenu.addAction(self.menus["view"]["zoomout"])
        viewMenu.addAction(self.menus["view"]["reset"])
        viewMenu.addSeparator()
        viewMenu.addAction(self.menus["view"]["fullscreen"])
        viewMenu.addAction(self.menus["view"]["hidemenu"])
        helpMenu = menu.addMenu("&Help")
        helpMenu.addAction(self.menus["help"]["help"])
        helpMenu.addAction(self.menus["help"]["center"])
        helpMenu.addSeparator()
        helpMenu.addAction(self.menus["help"]["about"])
        self.enableMenus(False)
        showSystray = self.settings.value("Systray") == "True"
        self.menus["file"]["systray"].setChecked(showSystray)
        self.menus["file"]["close"].setEnabled(showSystray)
        # Restore menu visibility
        visible = self.settings.value("Menu")
        if visible is not None and visible == "False":
            menu.setVisible(False)

    def enableMenus(self, enabled):
        self.menus["file"]["preferences"].setEnabled(enabled == True)
        self.menus["file"]["addTeam"].setEnabled(enabled == True)
        self.menus["file"]["signout"].setEnabled(enabled == True)
        self.menus["help"]["help"].setEnabled(enabled == True)

    def createAction(self, text, slot, shortcut=None, checkable=False):
        action = QtWidgets.QAction(text, self)
        action.triggered.connect(slot)
        if shortcut is not None:
            action.setShortcut(shortcut)
            self.addAction(action)
        if checkable:
            action.setCheckable(True)
        return action

    def normalize(self, url):
        if url.endswith(".slack.com"):
            url += "/"
        elif not url.endswith(".slack.com/"):
            url = "https://" + url + ".slack.com/"
        return url

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

    def teams(self, teams):
        if len(self.domains) == 0:
            self.domains.append(teams[0]['team_url'])
        team_list = [t['team_url'] for t in teams]
        for t in teams:
            for i in range(0, len(self.domains)):
                self.domains[i] = self.normalize(self.domains[i])
                # When team_icon is missing, the team already exists (Fixes #381, #391)
                if 'team_icon' in t:
                    if self.domains[i] in team_list:
                        add = next(item for item in teams
                                   if item['team_url'] == self.domains[i])
                        if 'team_icon' in add:
                            self.leftPane.addTeam(add['id'], add['team_name'],
                                                  add['team_url'],
                                                  add['team_icon']['image_44'],
                                                  add == teams[0])
                            # Adding new teams and saving loading positions
                            if t['team_url'] not in self.domains:
                                self.leftPane.addTeam(
                                    t['id'], t['team_name'], t['team_url'],
                                    t['team_icon']['image_44'], t == teams[0])
                                self.domains.append(t['team_url'])
                                self.settings.setValue("Domain", self.domains)
        if len(teams) > 1:
            self.leftPane.show()

    def switchTo(self, url):
        exists = False
        for i in range(0, self.stackedWidget.count()):
            if self.stackedWidget.widget(i).url().toString().startswith(url):
                self.stackedWidget.setCurrentIndex(i)
                self.quicklist(self.current().listChannels())
                self.current().setFocus()
                self.leftPane.click(i)
                self.clearMemory()
                exists = True
                break
        if not exists:
            self.addWrapper(url)

    def eventFilter(self, obj, event):
        if event.type(
        ) == QtCore.QEvent.ActivationChange and self.isActiveWindow():
            self.focusInEvent(event)
        if event.type() == QtCore.QEvent.KeyPress:
            # Ctrl + <n>
            modifiers = QtWidgets.QApplication.keyboardModifiers()
            if modifiers == QtCore.Qt.ControlModifier:
                if event.key() == QtCore.Qt.Key_1: self.leftPane.click(0)
                elif event.key() == QtCore.Qt.Key_2: self.leftPane.click(1)
                elif event.key() == QtCore.Qt.Key_3: self.leftPane.click(2)
                elif event.key() == QtCore.Qt.Key_4: self.leftPane.click(3)
                elif event.key() == QtCore.Qt.Key_5: self.leftPane.click(4)
                elif event.key() == QtCore.Qt.Key_6: self.leftPane.click(5)
                elif event.key() == QtCore.Qt.Key_7: self.leftPane.click(6)
                elif event.key() == QtCore.Qt.Key_8: self.leftPane.click(7)
                elif event.key() == QtCore.Qt.Key_9:
                    self.leftPane.click(8)
                    # Ctrl + Tab
                elif event.key() == QtCore.Qt.Key_Tab:
                    self.leftPane.clickNext(1)
            # Ctrl + BackTab
            if (modifiers & QtCore.Qt.ControlModifier) and (
                    modifiers & QtCore.Qt.ShiftModifier):
                if event.key() == QtCore.Qt.Key_Backtab:
                    self.leftPane.clickNext(-1)
            # Ctrl + Shift + <key>
            if (modifiers & QtCore.Qt.ShiftModifier) and (
                    modifiers & QtCore.Qt.ShiftModifier):
                if event.key() == QtCore.Qt.Key_V:
                    self.current().createSnippet()
        return QtWidgets.QMainWindow.eventFilter(self, obj, event)

    def focusInEvent(self, event):
        self.launcher.set_property("urgent", False)
        self.tray.stopAlert()
        # Let's tickle all teams on window focus, but only if tickle was not fired in last 30 minutes
        if DBusQtMainLoop is None and not self.tickler.isActive():
            self.sendTickle()
            self.tickler.start()

    def titleChanged(self):
        self.setWindowTitle(self.current().title())

    def setForceClose(self):
        self.forceClose = True

    def closeEvent(self, event):
        if not self.forceClose and self.settings.value("Systray") == "True":
            self.hide()
            event.ignore()
        else:
            self.cookiesjar.save()
            self.settings.setValue("Domain", self.domains)
            self.settings.setValue("geometry", self.saveGeometry())
            self.settings.setValue("windowState", self.saveState())
            self.settings.setValue("Domain", self.domains)
        self.forceClose = False

    def show(self):
        self.setWindowState(self.windowState() & ~QtCore.Qt.WindowMinimized
                            | QtCore.Qt.WindowActive)
        self.activateWindow()
        self.setVisible(True)

    def exit(self):
        # Make sure tray is not visible (Fixes #513)
        self.tray.setVisible(False)
        self.setForceClose()
        self.close()

    def quicklist(self, channels):
        if Dbusmenu is not None:
            if channels is not None:
                ql = Dbusmenu.Menuitem.new()
                self.launcher.set_property("quicklist", ql)
                for c in channels:
                    if type(c) is dict and hasattr(
                            c, '__getitem__') and c['is_member']:
                        item = Dbusmenu.Menuitem.new()
                        item.property_set(Dbusmenu.MENUITEM_PROP_LABEL,
                                          "#" + c['name'])
                        item.property_set("id", c['name'])
                        item.property_set_bool(Dbusmenu.MENUITEM_PROP_VISIBLE,
                                               True)
                        item.connect(Dbusmenu.MENUITEM_SIGNAL_ITEM_ACTIVATED,
                                     self.current().openChannel)
                        ql.child_append(item)
                self.launcher.set_property("quicklist", ql)

    def notify(self, title, message, icon):
        if self.debug:
            print("Notification: title [{}] message [{}] icon [{}]".format(
                title, message, icon))
        self.notifier.notify(title, message, icon)
        self.alert()

    def alert(self):
        if not self.isActiveWindow():
            self.launcher.set_property("urgent", True)
            self.tray.alert()
        if self.urgent_hint is True:
            QApplication.alert(self)

    def count(self):
        total = 0
        unreads = 0
        for i in range(0, self.stackedWidget.count()):
            widget = self.stackedWidget.widget(i)
            highlights = widget.highlights
            unreads += widget.unreads
            total += highlights
        if total > self.messages:
            self.alert()
        if 0 == total:
            self.launcher.set_property("count_visible", False)
            self.tray.setCounter(0)
            if unreads > 0:
                self.setWindowTitle("*{}".format(self.title))
            else:
                self.setWindowTitle(self.title)
        else:
            self.tray.setCounter(total)
            self.launcher.set_property("count", total)
            self.launcher.set_property("count_visible", True)
            self.setWindowTitle("[{}]{}".format(str(total), self.title))
        self.messages = total

    def clearMemory(self):
        QWebSettings.globalSettings().clearMemoryCaches()
        QWebSettings.globalSettings().clearIconDatabase()
Пример #10
0
class LiveCSS(QWidget):

    goto_declaration = pyqtSignal(object)

    def __init__(self, preview, parent=None):
        QWidget.__init__(self, parent)
        self.preview = preview
        self.preview_is_refreshing = False
        self.refresh_needed = False
        preview.refresh_starting.connect(self.preview_refresh_starting)
        preview.refreshed.connect(self.preview_refreshed)
        self.apply_theme()
        self.setAutoFillBackground(True)
        self.update_timer = QTimer(self)
        self.update_timer.timeout.connect(self.update_data)
        self.update_timer.setSingleShot(True)
        self.update_timer.setInterval(500)
        self.now_showing = (None, None, None)

        self.stack = s = QStackedLayout(self)
        self.setLayout(s)

        self.clear_label = la = QLabel(
            '<h3>' + _('No style information found') + '</h3><p>' +
            _('Move the cursor inside a HTML tag to see what styles'
              ' apply to that tag.'))
        la.setWordWrap(True)
        la.setAlignment(Qt.AlignTop | Qt.AlignLeft)
        s.addWidget(la)

        self.box = box = Box(self)
        box.hyperlink_activated.connect(self.goto_declaration,
                                        type=Qt.QueuedConnection)
        self.scroll = sc = QScrollArea(self)
        sc.setWidget(box)
        sc.setWidgetResizable(True)
        s.addWidget(sc)

    def preview_refresh_starting(self):
        self.preview_is_refreshing = True

    def preview_refreshed(self):
        self.preview_is_refreshing = False
        # We must let the event loop run otherwise the webview will return
        # stale data in read_data()
        self.refresh_needed = True
        self.start_update_timer()

    def apply_theme(self):
        f = self.font()
        f.setFamily(tprefs['editor_font_family'] or default_font_family())
        f.setPointSize(tprefs['editor_font_size'])
        self.setFont(f)
        theme = get_theme(tprefs['editor_theme'])
        pal = self.palette()
        pal.setColor(pal.Window, theme_color(theme, 'Normal', 'bg'))
        pal.setColor(pal.WindowText, theme_color(theme, 'Normal', 'fg'))
        pal.setColor(pal.AlternateBase,
                     theme_color(theme, 'HighlightRegion', 'bg'))
        pal.setColor(pal.Link, theme_color(theme, 'Link', 'fg'))
        pal.setColor(pal.LinkVisited, theme_color(theme, 'Keyword', 'fg'))
        self.setPalette(pal)
        if hasattr(self, 'box'):
            self.box.relayout()
        self.update()

    def clear(self):
        self.stack.setCurrentIndex(0)

    def show_data(self, editor_name, sourceline, tags):
        if self.preview_is_refreshing:
            return
        if sourceline is None:
            self.clear()
        else:
            data = self.read_data(sourceline, tags)
            if data is None or len(data['computed_css']) < 1:
                if editor_name == self.current_name and (
                        editor_name, sourceline, tags) == self.now_showing:
                    # Try again in a little while in case there was a transient
                    # error in the web view
                    self.start_update_timer()
                    return
                if self.now_showing == (
                        None, None,
                        None) or self.now_showing[0] != self.current_name:
                    self.clear()
                    return
                # Try to refresh the data for the currently shown tag instead
                # of clearing
                editor_name, sourceline, tags = self.now_showing
                data = self.read_data(sourceline, tags)
                if data is None or len(data['computed_css']) < 1:
                    self.clear()
                    return
            self.now_showing = (editor_name, sourceline, tags)
            data['html_name'] = editor_name
            self.box.show_data(data)
            self.refresh_needed = False
            self.stack.setCurrentIndex(1)

    def read_data(self, sourceline, tags):
        mf = self.preview.view.page().mainFrame()
        tags = [x.lower() for x in tags]
        result = str(
            mf.evaluateJavaScript(
                'window.calibre_preview_integration.live_css(%s, %s)' %
                (json.dumps(sourceline), json.dumps(tags))) or '')
        try:
            result = json.loads(result)
        except ValueError:
            result = None
        if result is not None:
            maximum_specificities = {}
            for node in result['nodes']:
                is_ancestor = node['is_ancestor']
                for rule in node['css']:
                    self.process_rule(rule, is_ancestor, maximum_specificities)
            for node in result['nodes']:
                for rule in node['css']:
                    for prop in rule['properties']:
                        if prop.specificity < maximum_specificities[prop.name]:
                            prop.is_overriden = True

        return result

    def process_rule(self, rule, is_ancestor, maximum_specificities):
        selector = rule['selector']
        sheet_index = rule['sheet_index']
        rule_address = rule['rule_address'] or ()
        if selector is not None:
            try:
                specificity = [0] + list(parse(selector)[0].specificity())
            except (AttributeError, TypeError, SelectorError):
                specificity = [0, 0, 0, 0]
        else:  # style attribute
            specificity = [1, 0, 0, 0]
        specificity.extend((sheet_index, tuple(rule_address)))
        ancestor_specificity = 0 if is_ancestor else 1
        properties = []
        for prop in rule['properties']:
            important = 1 if prop[-1] == 'important' else 0
            p = Property(prop,
                         [ancestor_specificity] + [important] + specificity)
            properties.append(p)
            if p.specificity > maximum_specificities.get(
                    p.name, (0, 0, 0, 0, 0, 0)):
                maximum_specificities[p.name] = p.specificity
        rule['properties'] = properties

        href = rule['href']
        if hasattr(href, 'startswith') and href.startswith(
                '%s://%s' % (FAKE_PROTOCOL, FAKE_HOST)):
            qurl = QUrl(href)
            name = qurl.path()[1:]
            if name:
                rule['href'] = name

    @property
    def current_name(self):
        return self.preview.current_name

    @property
    def is_visible(self):
        return self.isVisible()

    def showEvent(self, ev):
        self.update_timer.start()
        actions['auto-reload-preview'].setEnabled(True)
        return QWidget.showEvent(self, ev)

    def sync_to_editor(self):
        self.update_data()

    def update_data(self):
        if not self.is_visible or self.preview_is_refreshing:
            return
        editor_name = self.current_name
        ed = editors.get(editor_name, None)
        if self.update_timer.isActive() or (ed is None
                                            and editor_name is not None):
            return QTimer.singleShot(100, self.update_data)
        if ed is not None:
            sourceline, tags = ed.current_tag(for_position_sync=False)
            if self.refresh_needed or self.now_showing != (editor_name,
                                                           sourceline, tags):
                self.show_data(editor_name, sourceline, tags)

    def start_update_timer(self):
        if self.is_visible:
            self.update_timer.start()

    def stop_update_timer(self):
        self.update_timer.stop()

    def navigate_to_declaration(self, data, editor):
        if data['type'] == 'inline':
            sourceline, tags = data['sourceline_address']
            editor.goto_sourceline(sourceline, tags, attribute='style')
        elif data['type'] == 'sheet':
            editor.goto_css_rule(data['rule_address'])
        elif data['type'] == 'elem':
            editor.goto_css_rule(data['rule_address'],
                                 sourceline_address=data['sourceline_address'])
Пример #11
0
class Splitter(QSplitter):

    state_changed = pyqtSignal(object)

    def __init__(self,
                 name,
                 label,
                 icon,
                 initial_show=True,
                 initial_side_size=120,
                 connect_button=True,
                 orientation=Qt.Horizontal,
                 side_index=0,
                 parent=None,
                 shortcut=None,
                 hide_handle_on_single_panel=True):
        QSplitter.__init__(self, parent)
        if hide_handle_on_single_panel:
            self.state_changed.connect(self.update_handle_width)
        self.original_handle_width = self.handleWidth()
        self.resize_timer = QTimer(self)
        self.resize_timer.setSingleShot(True)
        self.desired_side_size = initial_side_size
        self.desired_show = initial_show
        self.resize_timer.setInterval(5)
        self.resize_timer.timeout.connect(self.do_resize)
        self.setOrientation(orientation)
        self.side_index = side_index
        self._name = name
        self.label = label
        self.initial_side_size = initial_side_size
        self.initial_show = initial_show
        self.splitterMoved.connect(self.splitter_moved,
                                   type=Qt.QueuedConnection)
        self.button = LayoutButton(icon, label, self, shortcut=shortcut)
        if connect_button:
            self.button.clicked.connect(self.double_clicked)

        if shortcut is not None:
            self.action_toggle = QAction(QIcon(icon),
                                         _('Toggle') + ' ' + label, self)
            self.action_toggle.changed.connect(self.update_shortcut)
            self.action_toggle.triggered.connect(self.toggle_triggered)
            if parent is not None:
                parent.addAction(self.action_toggle)
                if hasattr(parent, 'keyboard'):
                    parent.keyboard.register_shortcut(
                        'splitter %s %s' % (name, label),
                        unicode_type(self.action_toggle.text()),
                        default_keys=(shortcut, ),
                        action=self.action_toggle)
                else:
                    self.action_toggle.setShortcut(shortcut)
            else:
                self.action_toggle.setShortcut(shortcut)

    def update_shortcut(self):
        self.button.update_shortcut(self.action_toggle)

    def toggle_triggered(self, *args):
        self.toggle_side_pane()

    def createHandle(self):
        return SplitterHandle(self.orientation(), self)

    def initialize(self):
        for i in range(self.count()):
            h = self.handle(i)
            if h is not None:
                h.splitter_moved()
        self.state_changed.emit(not self.is_side_index_hidden)

    def splitter_moved(self, *args):
        self.desired_side_size = self.side_index_size
        self.state_changed.emit(not self.is_side_index_hidden)

    def update_handle_width(self, not_one_panel):
        self.setHandleWidth(self.original_handle_width if not_one_panel else 0)

    @property
    def is_side_index_hidden(self):
        sizes = list(self.sizes())
        try:
            return sizes[self.side_index] == 0
        except IndexError:
            return True

    @property
    def save_name(self):
        ori = 'horizontal' if self.orientation() == Qt.Horizontal \
                else 'vertical'
        return self._name + '_' + ori

    def print_sizes(self):
        if self.count() > 1:
            print(self.save_name,
                  'side:',
                  self.side_index_size,
                  'other:',
                  end=' ')
            print(list(self.sizes())[self.other_index])

    @property
    def side_index_size(self):
        if self.count() < 2:
            return 0
        return self.sizes()[self.side_index]

    @side_index_size.setter
    def side_index_size(self, val):
        if self.count() < 2:
            return
        if val == 0 and not self.is_side_index_hidden:
            self.save_state()
        sizes = list(self.sizes())
        for i in range(len(sizes)):
            sizes[i] = val if i == self.side_index else 10
        self.setSizes(sizes)
        total = sum(self.sizes())
        sizes = list(self.sizes())
        for i in range(len(sizes)):
            sizes[i] = val if i == self.side_index else total - val
        self.setSizes(sizes)
        self.initialize()

    def do_resize(self, *args):
        orig = self.desired_side_size
        QSplitter.resizeEvent(self, self._resize_ev)
        if orig > 20 and self.desired_show:
            c = 0
            while abs(self.side_index_size - orig) > 10 and c < 5:
                self.apply_state(self.get_state(), save_desired=False)
                c += 1

    def resizeEvent(self, ev):
        if self.resize_timer.isActive():
            self.resize_timer.stop()
        self._resize_ev = ev
        self.resize_timer.start()

    def get_state(self):
        if self.count() < 2:
            return (False, 200)
        return (self.desired_show, self.desired_side_size)

    def apply_state(self, state, save_desired=True):
        if state[0]:
            self.side_index_size = state[1]
            if save_desired:
                self.desired_side_size = self.side_index_size
        else:
            self.side_index_size = 0
        self.desired_show = state[0]

    def default_state(self):
        return (self.initial_show, self.initial_side_size)

    # Public API {{{

    def update_desired_state(self):
        self.desired_show = not self.is_side_index_hidden

    def save_state(self):
        if self.count() > 1:
            gprefs[self.save_name + '_state'] = self.get_state()

    @property
    def other_index(self):
        return (self.side_index + 1) % 2

    def restore_state(self):
        if self.count() > 1:
            state = gprefs.get(self.save_name + '_state', self.default_state())
            self.apply_state(state, save_desired=False)
            self.desired_side_size = state[1]

    def toggle_side_pane(self, hide=None):
        if hide is None:
            action = 'show' if self.is_side_index_hidden else 'hide'
        else:
            action = 'hide' if hide else 'show'
        getattr(self, action + '_side_pane')()

    def show_side_pane(self):
        if self.count() < 2 or not self.is_side_index_hidden:
            return
        if self.desired_side_size == 0:
            self.desired_side_size = self.initial_side_size
        self.apply_state((True, self.desired_side_size))

    def hide_side_pane(self):
        if self.count() < 2 or self.is_side_index_hidden:
            return
        self.apply_state((False, self.desired_side_size))

    def double_clicked(self, *args):
        self.toggle_side_pane()
Пример #12
0
class Preview(QWidget):

    sync_requested = pyqtSignal(object, object)
    split_requested = pyqtSignal(object, object, object)
    split_start_requested = pyqtSignal()
    link_clicked = pyqtSignal(object, object)
    refresh_starting = pyqtSignal()
    refreshed = pyqtSignal()

    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.l = l = QVBoxLayout()
        self.setLayout(l)
        l.setContentsMargins(0, 0, 0, 0)
        self.view = WebView(self)
        self.view.page().sync_requested.connect(self.request_sync)
        self.view.page().split_requested.connect(self.request_split)
        self.view.page().loadFinished.connect(self.load_finished)
        self.inspector = self.view.inspector
        self.inspector.setPage(self.view.page())
        l.addWidget(self.view)
        self.bar = QToolBar(self)
        l.addWidget(self.bar)

        ac = actions['auto-reload-preview']
        ac.setCheckable(True)
        ac.setChecked(True)
        ac.toggled.connect(self.auto_reload_toggled)
        self.auto_reload_toggled(ac.isChecked())
        self.bar.addAction(ac)

        ac = actions['sync-preview-to-editor']
        ac.setCheckable(True)
        ac.setChecked(True)
        ac.toggled.connect(self.sync_toggled)
        self.sync_toggled(ac.isChecked())
        self.bar.addAction(ac)

        self.bar.addSeparator()

        ac = actions['split-in-preview']
        ac.setCheckable(True)
        ac.setChecked(False)
        ac.toggled.connect(self.split_toggled)
        self.split_toggled(ac.isChecked())
        self.bar.addAction(ac)

        ac = actions['reload-preview']
        ac.triggered.connect(self.refresh)
        self.bar.addAction(ac)

        actions['preview-dock'].toggled.connect(self.visibility_changed)

        self.current_name = None
        self.last_sync_request = None
        self.refresh_timer = QTimer(self)
        self.refresh_timer.timeout.connect(self.refresh)
        parse_worker.start()
        self.current_sync_request = None

        self.search = HistoryLineEdit2(self)
        self.search.initialize('tweak_book_preview_search')
        self.search.setPlaceholderText(_('Search in preview'))
        self.search.returnPressed.connect(partial(self.find, 'next'))
        self.bar.addSeparator()
        self.bar.addWidget(self.search)
        for d in ('next', 'prev'):
            ac = actions['find-%s-preview' % d]
            ac.triggered.connect(partial(self.find, d))
            self.bar.addAction(ac)

    def find(self, direction):
        text = unicode(self.search.text())
        self.view.findText(text, QWebPage.FindWrapsAroundDocument | (
            QWebPage.FindBackward if direction == 'prev' else QWebPage.FindFlags(0)))

    def request_sync(self, tagname, href, lnum):
        if self.current_name:
            c = current_container()
            if tagname == 'a' and href:
                if href and href.startswith('#'):
                    name = self.current_name
                else:
                    name = c.href_to_name(href, self.current_name) if href else None
                if name == self.current_name:
                    return self.view.page().go_to_anchor(urlparse(href).fragment, lnum)
                if name and c.exists(name) and c.mime_map[name] in OEB_DOCS:
                    return self.link_clicked.emit(name, urlparse(href).fragment or TOP)
            self.sync_requested.emit(self.current_name, lnum)

    def request_split(self, loc, totals):
        if self.current_name:
            self.split_requested.emit(self.current_name, loc, totals)

    def sync_to_editor(self, name, sourceline_address):
        self.current_sync_request = (name, sourceline_address)
        QTimer.singleShot(100, self._sync_to_editor)

    def _sync_to_editor(self):
        if not actions['sync-preview-to-editor'].isChecked():
            return
        try:
            if self.refresh_timer.isActive() or self.current_sync_request[0] != self.current_name:
                return QTimer.singleShot(100, self._sync_to_editor)
        except TypeError:
            return  # Happens if current_sync_request is None
        sourceline_address = self.current_sync_request[1]
        self.current_sync_request = None
        self.view.page().go_to_sourceline_address(sourceline_address)

    def report_worker_launch_error(self):
        if parse_worker.launch_error is not None:
            tb, parse_worker.launch_error = parse_worker.launch_error, None
            error_dialog(self, _('Failed to launch worker'), _(
                'Failed to launch the worker process used for rendering the preview'), det_msg=tb, show=True)

    def show(self, name):
        if name != self.current_name:
            self.refresh_timer.stop()
            self.current_name = name
            self.report_worker_launch_error()
            parse_worker.add_request(name)
            self.view.setUrl(QUrl.fromLocalFile(current_container().name_to_abspath(name)))
            return True

    def refresh(self):
        if self.current_name:
            self.refresh_timer.stop()
            # This will check if the current html has changed in its editor,
            # and re-parse it if so
            self.report_worker_launch_error()
            parse_worker.add_request(self.current_name)
            # Tell webkit to reload all html and associated resources
            current_url = QUrl.fromLocalFile(current_container().name_to_abspath(self.current_name))
            self.refresh_starting.emit()
            if current_url != self.view.url():
                # The container was changed
                self.view.setUrl(current_url)
            else:
                self.view.refresh()
            self.refreshed.emit()

    def clear(self):
        self.view.clear()
        self.current_name = None

    @property
    def current_root(self):
        return self.view.page().current_root

    @property
    def is_visible(self):
        return actions['preview-dock'].isChecked()

    @property
    def live_css_is_visible(self):
        try:
            return actions['live-css-dock'].isChecked()
        except KeyError:
            return False

    def start_refresh_timer(self):
        if self.live_css_is_visible or (self.is_visible and actions['auto-reload-preview'].isChecked()):
            self.refresh_timer.start(tprefs['preview_refresh_time'] * 1000)

    def stop_refresh_timer(self):
        self.refresh_timer.stop()

    def auto_reload_toggled(self, checked):
        if self.live_css_is_visible and not actions['auto-reload-preview'].isChecked():
            actions['auto-reload-preview'].setChecked(True)
            error_dialog(self, _('Cannot disable'), _(
                'Auto reloading of the preview panel cannot be disabled while the'
                ' Live CSS panel is open.'), show=True)
        actions['auto-reload-preview'].setToolTip(_(
            'Auto reload preview when text changes in editor') if not checked else _(
                'Disable auto reload of preview'))

    def sync_toggled(self, checked):
        actions['sync-preview-to-editor'].setToolTip(_(
            'Disable syncing of preview position to editor position') if checked else _(
                'Enable syncing of preview position to editor position'))

    def visibility_changed(self, is_visible):
        if is_visible:
            self.refresh()

    def split_toggled(self, checked):
        actions['split-in-preview'].setToolTip(textwrap.fill(_(
            'Abort file split') if checked else _(
                'Split this file at a specified location.\n\nAfter clicking this button, click'
                ' inside the preview panel above at the location you want the file to be split.')))
        if checked:
            self.split_start_requested.emit()
        else:
            self.view.page().split_mode(False)

    def do_start_split(self):
        self.view.page().split_mode(True)

    def stop_split(self):
        actions['split-in-preview'].setChecked(False)

    def load_finished(self, ok):
        if actions['split-in-preview'].isChecked():
            if ok:
                self.do_start_split()
            else:
                self.stop_split()

    def apply_settings(self):
        s = self.view.page().settings()
        s.setFontSize(s.DefaultFontSize, tprefs['preview_base_font_size'])
        s.setFontSize(s.DefaultFixedFontSize, tprefs['preview_mono_font_size'])
        s.setFontSize(s.MinimumLogicalFontSize, tprefs['preview_minimum_font_size'])
        s.setFontSize(s.MinimumFontSize, tprefs['preview_minimum_font_size'])
        sf, ssf, mf = tprefs['preview_serif_family'], tprefs['preview_sans_family'], tprefs['preview_mono_family']
        s.setFontFamily(s.StandardFont, {'serif':sf, 'sans':ssf, 'mono':mf, None:sf}[tprefs['preview_standard_font_family']])
        s.setFontFamily(s.SerifFont, sf)
        s.setFontFamily(s.SansSerifFont, ssf)
        s.setFontFamily(s.FixedFont, mf)
Пример #13
0
class BrowserWindow(QMainWindow):
    class SavedWindow(object):
        def __init__(self, window=None):
            '''
            @param: window BrowserWindow
            '''
            self.windowState = QByteArray()
            self.windowGeometry = QByteArray()
            self.windowUiState = {}  # QString -> QVariant
            self.virtualDesktop = -1
            self.currentTab = -1
            self.tabs = []  # WebTab.SavedTab
            if window:
                self.init(window)

        def init(self, window):
            if window.isFullScreen():
                self.windowState = QByteArray()
            else:
                self.windowState = window.saveState()
            self.windowGeometry = window.saveGeometry()
            self.windowUiState = window._saveUiState()

            tabsCount = window.tabCount()
            for idx in range(tabsCount):
                # TabbedWebView
                webView = window.weView(idx)
                if not webView:
                    continue
                webTab = webView.webTab()
                if not webTab:
                    continue
                tab = WebTab.SavedTab(webTab)
                if not tab.isValid():
                    continue
                if webTab.isCurrentTab():
                    self.currentTab = len(self.tabs)
                self.tabs.append(tab)

        def isValid(self):
            for tab in self.tabs:
                if not tab.isValid():
                    return False
            return self.currentTab > -1

        def clear(self):
            self.windowState.clear()
            self.windowGeometry.clear()
            self.virtualDesktop = -1
            self.currentTab = -1
            self.tabs.clear()

    def __init__(self, type_, startUrl=QUrl()):
        super().__init__(None)
        self._startUrl = startUrl
        self._homepage = QUrl()
        self._windowType = type_
        self._startTab = None  # WebTab
        self._startPage = None  # WebPage
        self._mainLayout = None  # QVBoxLayout
        self._mainSplitter = None  # QSplitter
        self._tabWidget = None  # TabWidget
        self._sideBar = None  # QPointer<SideBar>
        self._sideBarManager = SideBarManager(self)
        self._statusBar = None

        self._navigationContainer = None  # NavigationContainer
        self._navigationToolbar = None  # NavigationBar
        self._bookmarksToolbar = None  # BookmarksToolbar

        self._progressBar = None  # ProgressBar
        self._ipLabel = None  # QLabel
        self._superMenu = None  # QMenu
        self._mainMenu = None  # MainMenu

        self._tabModel = None  # TabModel
        self._tabMruModel = None  # TabMruModel

        self._sideBarWidth = 0
        self._webViewWidth = 0

        # Shortcuts
        self._useTabNumberShortcuts = False
        self._useSpeedDialNumberShortcuts = False
        self._useSingleKeyShortcuts = False

        # Remember visibility of menubar and statusbar after entering Fullscreen
        self._menuBarVisible = False
        self._statusBarVisible = False
        self._htmlFullScreenView = None  # TabbedWebView
        self._hideNavigationTimer = None  # QTimer

        self._deleteOnCloseWidgets = []  # QList<QPointer<QWidget>>

        self.setAttribute(Qt.WA_DeleteOnClose)
        self.setAttribute(Qt.WA_DontCreateNativeAncestors)

        self.setObjectName('mainwindow')
        self.setWindowTitle(const.APPNAME)
        self.setProperty('private', gVar.app.isPrivate())

        self._setupUi()
        self._setupMenu()

        self._hideNavigationTimer = QTimer(self)
        self._hideNavigationTimer.setInterval(1000)
        self._hideNavigationTimer.setSingleShot(True)
        self._hideNavigationTimer.timeout.connect(self._hideNavigationSlot)

        gVar.app.settingsReloaded.connect(self._loadSettings)

        QTimer.singleShot(0, self._postLaunch)

        if gVar.app.isPrivate():
            gVar.appTools.setWmClass(
                '%s Browser (Private Window)' % const.APPNAME, self)
        else:
            gVar.appTools.setWmClass('%s Browser' % const.APPNAME, self)

    def __del__(self):
        gVar.app.plugins().emitMainWindowDeleted(self)
        for widget in self._deleteOnCloseWidgets:
            widget.deleteLater()

    def setStartTab(self, tab):
        '''
        @param: tab WebTab
        '''
        self._startTab = tab

    def setStartPage(self, page):
        '''
        @param: page WebPage
        '''
        self._startPage = page

    def restoreWindow(self, window):
        '''
        @param: window SavedWindow
        '''
        self.restoreState(window.windowState)
        self.restoreGeometry(window.windowGeometry)
        self._restoreUiState(window.windowUiState)
        if not gVar.app.isTestModeEnabled():
            self.show(
            )  # Window has to be visible before adding QWebEngineView's
        self._tabWidget.restoreState(window.tabs, window.currentTab)
        self._updateStartupFocus()

    def fullScreenNavigationVisible(self):
        return self._navigationContainer.isVisible()

    def showNavigationWithFullScreen(self):
        if self._htmlFullScreenView:
            return
        if self._hideNavigationTimer.isActive():
            self._hideNavigationTimer.stop()
        self._navigationContainer.show()

    def hideNavigationWithFullScreen(self):
        if self._tabWidget.isCurrentTabFresh():
            return
        if not self._hideNavigationTimer.isActive():
            self._hideNavigationTimer.start()

    def currentTabChanged(self):
        view = self.weView()
        self._navigationToolbar.setCurrentView(view)
        if not view:
            return

        title = view.webTab().title(True)  # allowEmpty
        if not title:
            self.setWindowTitle(const.APPNAME)
        else:
            self.setWindowTitle('%s - %s' % (title, const.APPNAME))
        self._ipLabel.setText(view.getIp())
        view.setFocus()

        self.updateLoadingActions()

        # Settings correct tab order (LocationBar -> WebSearchBar -> WebView)
        self.setTabOrder(self.locationBar(),
                         self._navigationToolbar.webSearchBar())
        self.setTabOrder(self._navigationToolbar.webSearchBar(), view)

    def updateLoadingActions(self):
        view = self.weView()
        if not view:
            return

        isLoading = view.isLoading()
        self._ipLabel.setVisible(not isLoading)
        self._progressBar.setVisible(isLoading)
        self.action('View/Stop').setEnabled(isLoading)
        self.action('View/Reload').setEnabled(not isLoading)

        if isLoading:
            self._progressBar.setValue(view.loadingProgress())
            self._navigationToolbar.showStopButton()
        else:
            self._navigationToolbar.showReloadButton()

    def addBookmark(self, url, title):
        BookmarksTools.addBookmarkDilaog(self, url, title)

    def addDeleteOnCloseWidget(self, widget):
        '''
        @param: widget QWidget
        '''
        if widget not in self._deleteOnCloseWidgets:
            self._deleteOnCloseWidgets.append(widget)

    def createToolbarsMenu(self, menu):
        self.removeActions(menu.actions())
        menu.clear()

        action = menu.addAction('&Menu Bar', self.toggleShowMenubar)
        action.setCheckable(True)
        action.setChecked(self.menuBar().isVisible())

        action = menu.addAction('&Navigation Toolbar',
                                self.toggleShowNavigationToolbar)
        action.setCheckable(True)
        action.setChecked(self._navigationToolbar.isVisible())

        action = menu.addAction('&Bookmarks Toolbar',
                                self.toggleShowBookmarksToolbar)
        action.setCheckable(True)
        action.setChecked(Settings().value(
            'Browser-View-Settings/showBookmarksToolbar', True))

        menu.addSeparator()

        action = menu.addAction('&Tabs on Top', self.toggleTabsOnTop)
        action.setCheckable(True)
        action.setChecked(gVar.appSettings.tabsOnTop)

        self.addActions(menu.actions())

    def createSidebarsMenu(self, menu):
        self._sideBarManager.createMenu(menu)

    def createEncodingMenu(self, menu):
        isoCodecs = []
        utfCodecs = []
        windowsCodecs = []
        isciiCodecs = []
        ibmCodecs = []
        otherCodecs = []
        allCodecs = []

        for mib in QTextCodec.availableMibs():
            codecName = QTextCodec.codecForMib(mib).name()
            codecName = codecName.data().decode('utf8')
            if codecName in allCodecs:
                continue
            allCodecs.append(codecName)
            if codecName.startswith('ISO'):
                isoCodecs.append(codecName)
            elif codecName.startswith('UTF'):
                utfCodecs.append(codecName)
            elif codecName.startswith('windows'):
                windowsCodecs.append(codecName)
            elif codecName.startswith('Iscii'):
                isciiCodecs.append(codecName)
            elif codecName.startswith('IBM'):
                ibmCodecs.append(codecName)
            else:
                otherCodecs.append(codecName)

        if not menu.isEmpty():
            menu.addSeperator()

        self._createEncodingSubMenu('ISO', isoCodecs, menu)
        self._createEncodingSubMenu('UTF', utfCodecs, menu)
        self._createEncodingSubMenu('Windows', windowsCodecs, menu)
        self._createEncodingSubMenu('Iscii', isciiCodecs, menu)
        self._createEncodingSubMenu('IBM', ibmCodecs, menu)
        self._createEncodingSubMenu('Other', otherCodecs, menu)

    def removeActions(self, actions):
        '''
        @param: actions QList<QAction*>
        '''
        for action in actions:
            self.removeAction(action)

    def addSideBar(self):
        '''
        @return SideBar*
        '''
        if self._sideBar:
            return self._sideBar
        self._sideBar = SideBar(self._sideBarManager, self)
        self._mainSplitter.insertWidget(0, self._sideBar)
        self._mainSplitter.setCollapsible(0, False)
        self._mainSplitter.setSizes([self._sideBarWidth, self._webViewWidth])
        return self._sideBar

    def setSideBarNone(self):
        '''
        @note: when sideBar is notified destroyed, we should clean python ref in
        window
        '''
        self._sideBar = None

    def saveSideBarSettings(self):
        if self._sideBar:
            # That +1 is important here, without it, the sidebar width would
            # decrease by 1 pixel every close
            self._sideBarWidth = self._mainSplitter.sizes()[0] + 1
            self._webViewWidth = self.width() - self._sideBarWidth
        Settings().setValue('Browser-View-Settings/SideBar',
                            self._sideBarManager.activeSideBar())

    def tabCount(self):
        return self._tabWidget.count()

    def weView(self, index=None):
        '''
        @return TabbedWebView
        '''

        if index is None:
            index = self._tabWidget.currentIndex()
        webTab = self._tabWidget.widget(index)
        if not webTab:
            return None
        return webTab.webView()

    def windowType(self):
        '''
        @return const.BrowserWindowType
        '''
        return self._windowType

    def locationBar(self):
        '''
        @return LocationBar
        '''
        return self._tabWidget.locationBars().currentWidget()

    def tabWidget(self):
        return self._tabWidget

    def bookmarksToolbar(self):
        '''
        @return BookmarksToolbar
        '''
        return self._bookmarksToolbar

    def statusBar(self):
        '''
        @return StatusBar
        '''
        return self._statusBar

    def navigationBar(self):
        '''
        @return NavigationBar
        '''
        return self._navigationToolbar

    def sideBarManager(self):
        '''
        @return SideBarManager
        '''
        return self._sideBarManager

    def ipLabel(self):
        '''
        @return QLabel
        '''
        return self._ipLabel

    def superMenu(self):
        '''
        @return QMenu
        '''
        return self._superMenu

    def homepageUrl(self):
        return self._homepage

    def action(self, name):
        '''
        @return QAction
        '''
        return self._mainMenu.action(name)

    def tabModel(self):
        '''
        @return TabModel
        '''
        return self._tabModel

    def tabMruModel(self):
        '''
        @return TabMruModel
        '''
        return self._tabMruModel

    # public Q_SIGNALS:
    startingCompleted = pyqtSignal()
    aboutToClose = pyqtSignal()

    # public Q_SLOTS:
    def addTab(self):
        self._tabWidget.addViewByUrl(QUrl(), const.NT_SelectedNewEmptyTab,
                                     True)
        self._tabWidget.setCurrentTabFresh(True)

        if self.isFullScreen():
            self.showNavigationWithFullScreen()

    def goHome(self):
        self.loadAddress(self._homepage)

    def goHomeInNewTab(self):
        self._tabWidget.addViewByUrl(self._homepage, const.NT_SelectedTab)

    def goBack(self):
        self.weView().back()

    def goForward(self):
        self.weView().forward()

    def reload(self):
        self.weView().reload()

    def reloadBypassCache(self):
        self.weView().reloadBypassCache()

    def setWindowTitle(self, title):
        if gVar.app.isPrivate():
            title = '%s (Private Browsing)' % title
        super().setWindowTitle(title)

    def showWebInspector(self):
        webView = self.weView()
        if webView and webView.webTab():
            webView.webTab().showWebInspector()

    def toggleWebInspector(self):
        webView = self.weView()
        if webView and webView.webTab():
            webView.webTab().toggleWebInspector()

    def showHistoryManager(self):
        gVar.app.browsingLibrary().showHistory(self)

    def toggleShowMenubar(self):
        self.setUpdatesEnabled(False)
        menuBar = self.menuBar()
        menuBar.setVisible(not menuBar.isVisible())
        self._navigationToolbar.setSuperMenuVisible(not menuBar.isVisible())
        self.setUpdatesEnabled(True)

        Settings().setValue('Browser-View-Settings/showMenuBar',
                            menuBar.isVisible())

        # Make sure we show Navigation Toolbar when Menu Bar is hidden
        if not self._navigationToolbar.isVisible() and not menuBar.isVisible():
            self.toggleShowNavigationToolbar()

    def toggleShowStatusBar(self):
        self.setUpdatesEnabled(False)
        self._statusBar.setVisible(not self._statusBar.isVisible())
        self.setUpdatesEnabled(True)
        Settings().setValue('Browser-View-Settings/showStatusBar',
                            self._statusBar.isVisible())

    def toggleShowBookmarksToolbar(self):
        self.setUpdatesEnabled(False)
        self._bookmarksToolbar.setVisible(
            not self._bookmarksToolbar.isVisible())
        self.setUpdatesEnabled(True)
        Settings().setValue('Browser-View-Settings/showBookmarksToolbar',
                            self._bookmarksToolbar.isVisible())
        Settings().setValue('Browser-View-Settings/instantBookmarksToolbar',
                            False)

    def toggleShowNavigationToolbar(self):
        self.setUpdatesEnabled(False)
        self._navigationToolbar.setVisible(
            not self._navigationToolbar.isVisible())
        self.setUpdatesEnabled(True)
        Settings().setValue('Browser-View-Settings/showNavigationToolbar',
                            self._navigationToolbar.isVisible())

        # Make sure we show Navigation Toolbar when Menu Bar is hidden
        if not self._navigationToolbar.isVisible() and not self.menuBar(
        ).isVisible():
            self.toggleShowMenubar()

    def toggleTabsOnTop(self, enable):
        gVar.appSettings.tabsOnTop = enable
        self._navigationContainer.toggleTabsOnTop(enable)

    def toggleFullScreen(self):
        if self._htmlFullScreenView:
            self.weView().triggerPageAction(QWebEnginePage.ExitFullScreen)
            return
        if self.isFullScreen():
            self.setWindowState(self.windowState() & ~Qt.WindowFullScreen)
        else:
            self.setWindowState(self.windowState() | Qt.WindowFullScreen)

    def requestHtmlFullScreen(self, view, enable):
        '''
        @param: view TabbedWebView
        @param: enable bool
        '''
        if enable:
            self.setWindowState(self.windowState() | Qt.WindowFullScreen)
        else:
            self.setWindowState(self.windowState() & ~Qt.WindowFullScreen)
        if self._sideBar:
            self._sideBar.setHidden(enable)
        self._htmlFullScreenView = enable and view or None

    def loadActionUrl(self, obj=None):
        if not obj:
            obj = self.sender()
        action = obj
        if isinstance(action, QAction):
            self.loadAddress(action.data().toUrl())

    def loadActionUrlInNewTab(self, obj=None):
        if not obj:
            obj = self.sender()
        action = obj
        if isinstance(action, QAction):
            self.loadAddress(action.data().toUrl(),
                             const.NT_SelectedTabAtTheEnd)

    def bookmarkPage(self):
        view = self.weView()
        BookmarksTools.addBookmarkDialog(self, view.url(), view.title())

    def bookmarkAllTabs(self):
        BookmarksTools.bookmarkAllTabsDialog(self, self._tabWidget)

    def loadAddress(self, url):
        webView = self.weView()
        if webView.webTab().isPinned():
            index = self._tabWidget.addViewByUrl(
                url, gVar.appSettings.newTabPosition)
            self.weView(index).setFocus()
        else:
            webView.setFocus()
            webView.loadByReq(LoadRequest(url))

    def showSource(self, view=None):
        if not view:
            view = self.weView()
        if not view:
            return
        view.showSource()

    # private Q_SLOTS:
    def _openLocation(self):
        if self.isFullScreen():
            self.showNavigationWithFullScreen()
        self.locationBar().setFocus()
        self.locationBar().selectAll()

    def _openFile(self):
        fileTypes = ("%s(*.html *.htm *.shtml *.shtm *.xhtml);;"
                     "%s(*.png *.jpg *.jpeg *.bmp *.gif *.svg *.tiff);;"
                     "%s(*.txt);;"
                     "%s(*.*)")
        fileTypes %= ("HTML files", "Image files", "Text files", "All files")
        filePath = gVar.appTools.getOpenFileName("MainWindow-openFile",
                                                 self, "Open file...",
                                                 QDir.homePath(), fileTypes)

        if filePath:
            self.loadAddress(QUrl.fromLocalFile(filePath))

    def _closeWindow(self):
        if const.OS_MAC:
            self.close()
            return
        if gVar.app.windowCount() > 1:
            self.close()

    def _closeTab(self):
        # Don't close pinned tabs with keyboard shortcuts (Ctrl+w, Ctrl+F4)
        webView = self.weView()
        if webView and not webView.webTab().isPinned():
            self._tabWidget.requestCloseTab()

    def _loadSettings(self):
        settings = Settings()
        # Url settings
        settings.beginGroup('Web-URL-Settings')
        self._homepage = settings.value('homepage', 'app:start', type=QUrl)
        settings.endGroup()
        # Browser Window settings
        settings.beginGroup('Browser-View-Settings')
        showStatusBar = settings.value('showStatusBar', False)
        showBookmarksToolbar = settings.value('showBookmarksToolbar', False)
        showNavigationToolbar = settings.value('showNavigationToolbar', True)
        showMenuBar = settings.value('showMenuBar', False)
        # Make sure both menubar and navigationbar are not hidden
        if not showNavigationToolbar:
            showMenuBar = True
            settings.setValue('showMenubar', True)
        settings.endGroup()

        settings.beginGroup('Shortcuts')
        self._useTabNumberShortcuts = settings.value('useTabNumberShortcuts',
                                                     True)
        self._useSpeedDialNumberShortcuts = settings.value(
            'useSpeedDialNumberShortcuts', True)
        self._useSingleKeyShortcuts = settings.value('useSingleKeyShortcuts',
                                                     False)
        settings.endGroup()

        settings.beginGroup('Web-Browser-Settings')
        quitAction = self._mainMenu.action('Standard/Quit')
        if settings.value('closeAppWithCtrlQ', True):
            quitAction.setShortcut(
                gVar.appTools.actionShortcut(QKeySequence.Quit,
                                             QKeySequence('Ctrl+Q')))
        else:
            quitAction.setShortcut(QKeySequence())
        settings.endGroup()

        self._statusBarVisible = showStatusBar
        self.statusBar().setVisible(not self.isFullScreen() and showStatusBar)
        self._bookmarksToolbar.setVisible(showBookmarksToolbar)
        self._navigationToolbar.setVisible(showNavigationToolbar)
        if not const.OS_MACOS:
            self._menuBarVisible = showMenuBar
            self.menuBar().setVisible(not self.isFullScreen() and showMenuBar)
        showSuperMenu = self.isFullScreen() or not showMenuBar
        # TODO: debug code
        showSuperMenu = True
        self._navigationToolbar.setSuperMenuVisible(showSuperMenu)

    def _postLaunch(self):  # noqa C901
        self._loadSettings()
        addTab = True
        startUrl = QUrl()

        from .MainApplication import MainApplication
        afterLaunch = gVar.app.afterLaunch()
        if afterLaunch == MainApplication.OpenBlankPage:
            startUrl = QUrl()
        elif afterLaunch == MainApplication.OpenSpeedDial:
            startUrl = QUrl('app:speeddial')
        elif afterLaunch in (MainApplication.OpenHomePage,
                             MainApplication.RestoreSession,
                             MainApplication.SelectSession):
            startUrl = QUrl(self._homepage)

        if not gVar.app.isTestModeEnabled():
            self.show()

        if self._windowType == const.BW_FirstAppWindow:
            if gVar.app.isStartingAfterCrash():
                addTab = False
                startUrl.clear()
                self._tabWidget.addViewByUrl(QUrl('app:restore'),
                                             const.NT_CleanSelectedTabAtTheEnd)
            elif afterLaunch in (MainApplication.SelectSession,
                                 MainApplication.RestoreSession):
                addTab = self._tabWidget.count() <= 0
        elif self._windowType in (const.BW_NewWindow, const.BW_MacFirstWindow):
            addTab = True
        elif self._windowType == const.BW_OtherRestoredWindow:
            addTab = False

        if not self._startUrl.isEmpty():
            startUrl = QUrl(self._startUrl)
            addTab = True
        if self._startTab:
            addTab = False
            self._tabWidget.addViewByTab(self._startTab, const.NT_SelectedTab)
        if self._startPage:
            addTab = False
            self._tabWidget.addViewByUrl(QUrl())
            self.weView().setPage(self._startPage)
        if addTab:
            self._tabWidget.addViewByUrl(startUrl,
                                         const.NT_CleanSelectedTabAtTheEnd)
            if not startUrl or startUrl == 'app:speeddial':
                self.locationBar().setFocus()
        # Someting went really wrong .. add one tab
        if self._tabWidget.count() <= 0:
            self._tabWidget.addViewByUrl(self._homepage,
                                         const.NT_SelectedTabAtTheEnd)

        gVar.app.plugins().emitMainWindowCreated(self)
        self.startingCompleted.emit()

        self.raise_()
        self.activateWindow()
        self._updateStartupFocus()

    def _webSearch(self):
        self._navigationToolbar.webSearchBar().setFocus()
        self._navigationToolbar.webSearchBar().selectAll()

    def _searchOnPage(self):
        webView = self.weView()
        if webView and webView.webTab():
            searchText = webView.page().selectedText()
            if '\n' not in searchText:
                webView.webTab().showSearchToolBar(searchText)
            else:
                webView.webTab().showSearchToolBar()

    def _changeEncoding(self):
        action = self.sender()
        if action:
            encoding = action.data()
            gVar.app.webSettings().setDefaultTextEncoding(encoding)
            Settings().setValue('Web-Browser-Settings/DefaultEncoding',
                                encoding)
            self.weView().reload()

    def _printPage(self):
        self.weView().printPage()

    def _saveSettings(self):
        if gVar.app.isPrivate():
            return
        settings = Settings()
        settings.beginGroup('Browser-View-Settings')
        settings.setValue('WindowGeometry', self.saveGeometry())

        state = self._saveUiState()
        for key, val in state.items():
            settings.setValue(key, val)

        settings.endGroup()

    def _hideNavigationSlot(self):
        view = self.weView()
        mouseInView = view and view.underMouse()
        if self.isFullScreen() and mouseInView:
            self._navigationContainer.hide()

    # private
    # override
    def event(self, event):
        '''
        @param: event QEvent
        '''
        if event.type() == QEvent.WindowStateChange:
            e = event
            assert (isinstance(e, QWindowStateChangeEvent))
            if not (e.oldState() & Qt.WindowFullScreen) and (
                    self.windowState() & Qt.WindowFullScreen):
                # Enter fullscreen
                self._statusBarVisible = self._statusBar.isVisible()
                if not const.OS_MACOS:
                    self._menuBarVisible = self.menuBar().isVisible()
                    self.menuBar().hide()
                self._statusBar.hide()

                self._navigationContainer.hide()
                self._navigationToolbar.enterFullScreen()

                # Show main menu button since menubar is hidden
                self._navigationToolbar.setSuperMenuVisible(True)
            elif (e.oldState() & Qt.WindowFullScreen
                  ) and not (self.windowState() & Qt.WindowFullScreen):
                # Leave fullscreen
                self._statusBar.setVisible(self._statusBarVisible)
                if not const.OS_MACOS:
                    self.menuBar().setVisible(self._menuBarVisible)

                self._navigationContainer.show()
                self._navigationToolbar.setSuperMenuVisible(
                    not self._menuBarVisible)
                self._navigationToolbar.leaveFullScreen()
                self._htmlFullScreenView = None

            if self._hideNavigationTimer:
                self._hideNavigationTimer.stop()

        return super().event(event)

    # override
    def resizeEvent(self, event):
        '''
        @param: event QResizeEvent
        '''
        self._bookmarksToolbar.setMaximumWidth(self.width())
        super().resizeEvent(event)

    # override
    def keyPressEvent(self, event):  # noqa C901
        '''
        @param: event QKeyEvent
        '''
        if gVar.app.plugins().processKeyPress(const.ON_BrowserWindow, self,
                                              event):
            return

        number = -1
        # TabbedWebView
        view = self.weView()
        evtKey = event.key()

        if evtKey == Qt.Key_Back:
            if view:
                view.back()
                event.accept()

        elif evtKey == Qt.Key_Forward:
            if view:
                view.forward()
                event.accept()

        elif evtKey == Qt.Key_Stop:
            if view:
                view.stop()
                event.accept()

        elif evtKey in (Qt.Key_Reload, Qt.Key_Refresh):
            if view:
                view.reload()
                event.accept()

        elif evtKey == Qt.Key_HomePage:
            self.goHome()
            event.accept()

        elif evtKey == Qt.Key_Favorites:
            gVar.app.browsingLibrary().showBookmarks(self)
            event.accept()

        elif evtKey == Qt.Key_Search:
            self.searchOnPage()
            event.accept()

        elif evtKey in (Qt.Key_F6, Qt.Key_OpenUrl):
            self.openLocation()
            event.accept()

        elif evtKey == Qt.Key_History:
            self.showHistoryManager()
            event.accept()

        elif evtKey == Qt.Key_AddFavorite:
            self.bookmarkPage()
            event.accept()

        elif evtKey == Qt.Key_News:
            self.action("Tools/RssReader").trigger()
            event.accept()

        elif evtKey == Qt.Key_Tools:
            self.action("Standard/Preferences").trigger()
            event.accept()

        elif evtKey == Qt.Key_Tab:
            if event.modifiers() == Qt.ControlModifier:
                self._tabWidget.event(event)

        elif evtKey == Qt.Key_Backtab:
            if event.modifiers() == Qt.ControlModifier + Qt.ShiftModifier:
                self._tabWidget.event(event)

        elif evtKey == Qt.Key_PageDown:
            if event.modifiers() == Qt.ControlModifier:
                self._tabWidget.nextTab()
                event.accept()

        elif evtKey == Qt.Key_PageUp:
            if event.modifiers() == Qt.ControlModifier:
                self._tabWidget.previousTab()
                event.accept()

        elif evtKey == Qt.Key_Equal:
            if view and event.modifiers() == Qt.ControlModifier:
                view.zoomIn()
                event.accept()

        elif evtKey == Qt.Key_I:
            if event.modifiers() == Qt.ControlModifier:
                self.action("Tools/SiteInfo").trigger()
                event.accept()

        elif evtKey == Qt.Key_U:
            if event.modifiers() == Qt.ControlModifier:
                self.action("View/PageSource").trigger()
                event.accept()

        elif evtKey == Qt.Key_F:
            if event.modifiers() == Qt.ControlModifier:
                self.action("Edit/Find").trigger()
                event.accept()

        elif evtKey == Qt.Key_Slash:
            if self._useSingleKeyShortcuts:
                self.action("Edit/Find").trigger()
                event.accept()

        elif evtKey == Qt.Key_1:
            number = 1
        elif evtKey == Qt.Key_2:
            number = 2
        elif evtKey == Qt.Key_3:
            number = 3
        elif evtKey == Qt.Key_4:
            number = 4
        elif evtKey == Qt.Key_5:
            number = 5
        elif evtKey == Qt.Key_6:
            number = 6
        elif evtKey == Qt.Key_7:
            number = 7
        elif evtKey == Qt.Key_8:
            number = 8
        elif evtKey == Qt.Key_9:
            number = 9

        if number != -1:
            modifiers = event.modifiers()
            if modifiers & Qt.AltModifier and self._useTabNumberShortcuts:
                if number == 9:
                    number = self._tabWidget.count()
                self._tabWidget.setCurrentIndex(number - 1)
                event.accept()
                return
            if modifiers & Qt.ControlModifier and self._useSpeedDialNumberShortcuts:
                # QUrl
                url = gVar.app.plugins().speedDial().urlForShortcut(number - 1)
                if url.isValid():
                    self.loadAddress(url)
                    event.accept()
                    return
            if modifiers == Qt.NoModifier and self._useSingleKeyShortcuts:
                if number == 1:
                    self._tabWidget.previousTab()
                if number == 2:
                    self._tabWidget.nextTab()

        super().keyPressEvent(event)

    # override
    def keyReleaseEvent(self, event):
        '''
        @param: event QKeyEvent
        '''
        if gVar.app.plugins().processKeyRelease(const.ON_BrowserWindow, self,
                                                event):
            return

        evtKey = event.key()
        if evtKey == Qt.Key_F:
            if event.modifiers() == Qt.ControlModifier:
                self.action('Edit/Find').trigger()
                event.accept()

        super().keyReleaseEvent(event)

    # override
    def closeEvent(self, event):
        '''
        @param: event QCloseEvent
        '''
        if gVar.app.isClosing():
            self._saveSettings()
            return
        settings = Settings()
        askOnClose = settings.value('Browser-Tabs-Settings/AskOnClosing', True)

        from .MainApplication import MainApplication
        if gVar.app.afterLaunch() in (MainApplication.SelectSession,
                                      MainApplication.RestoreSession
                                      ) and gVar.app.windowCount() == 1:
            askOnClose = False

        if askOnClose and self._tabWidget.normalTabsCount() > 1:
            dialog = CheckBoxDialog(QMessageBox.Yes | QMessageBox.No, self)
            dialog.setDefaultButton(QMessageBox.No)
            dialog.setText(
                "There are still %s open tabs and your session won't be stored.\n"
                % self._tabWidget.count() +
                "Are you sure you want to close this window?")
            dialog.setCheckBoxText("Don't ask again")
            dialog.setWindowTitle('There are still open tabs')
            dialog.setIcon(QMessageBox.Warning)
            if dialog.exec_() != QMessageBox.Yes:
                event.ignore()
                return
            if dialog.isChecked():
                settings.setValue('Browser-Tabs-Settings/AskOnClosing', False)

        self.aboutToClose.emit()

        self._saveSettings()
        gVar.app.closedWindowsManager().saveWindow(self)
        if gVar.app.windowCount() == 1:
            gVar.app.quitApplication()

        event.accept()

    # == private ==
    def _setupUi(self):
        settings = Settings()
        settings.beginGroup('Browser-View-Settings')
        windowGeometry = settings.value('WindowGeometry', b'')

        keys = [
            ('LocationBarWidth', int),
            ('WebSearchBarWidth', int),
            ('SideBarWidth', int),
            ('WebViewWidth', int),
            ('SideBar', str),
        ]

        uiState = {}
        for key, typ in keys:
            if settings.contains(key):
                uiState[key] = typ(settings.value(key))

        settings.endGroup()

        widget = QWidget(self)
        widget.setCursor(Qt.ArrowCursor)
        self.setCentralWidget(widget)

        self._mainLayout = QVBoxLayout(widget)
        self._mainLayout.setContentsMargins(0, 0, 0, 0)
        self._mainLayout.setSpacing(0)
        self._mainSplitter = QSplitter(self)
        self._mainSplitter.setObjectName('sidebar-splitter')
        self._tabWidget = TabWidget(self)
        self._superMenu = QMenu(self)
        self._navigationToolbar = NavigationBar(self)
        self._bookmarksToolbar = BookmarksToolbar(self)

        self._tabModel = TabModel(self, self)
        self._tabMruModel = TabMruModel(self, self)
        self._tabMruModel.setSourceModel(self._tabModel)

        self._navigationContainer = NavigationContainer(self)
        self._navigationContainer.addWidget(self._navigationToolbar)
        self._navigationContainer.addWidget(self._bookmarksToolbar)
        self._navigationContainer.setTabBar(self._tabWidget.tabBar())

        self._mainSplitter.addWidget(self._tabWidget)
        self._mainSplitter.setCollapsible(0, False)

        self._mainLayout.addWidget(self._navigationContainer)
        self._mainLayout.addWidget(self._mainSplitter)

        self._statusBar = StatusBar(self)
        self._statusBar.setObjectName('mainwindow-statusbar')
        self._statusBar.setCursor(Qt.ArrowCursor)
        self.setStatusBar(self._statusBar)
        self._progressBar = ProgressBar(self._statusBar)
        self._ipLabel = QLabel(self)
        self._ipLabel.setObjectName('statusbar-ip-label')
        self._ipLabel.setToolTip('IP Address of current page')

        self._statusBar.addPermanentWidget(self._progressBar)
        self._statusBar.addPermanentWidget(self.ipLabel())

        downloadsButton = DownloadsButton(self)
        self._statusBar.addButton(downloadsButton)
        self._navigationToolbar.addToolButton(downloadsButton)

        desktop = gVar.app.desktop()
        windowWidth = desktop.availableGeometry().width() / 1.3
        windowHeight = desktop.availableGeometry().height() / 1.3

        # Let the WM decides where to put new browser window
        if self._windowType not in (const.BW_FirstAppWindow, const.BW_MacFirstWindow) and \
                gVar.app.getWindow():
            if const.OS_WIN:
                # Windows WM places every new window in the middle of screen .. for some reason
                p = gVar.app.getWindow().geometry().topLeft()
                p.setX(p.x() + 30)
                p.setY(p.y() + 30)
                if not desktop.availableGeometry(
                        gVar.app.getWindow()).contains(p):
                    p.setX(
                        desktop.availableGeometry(gVar.app.getWindow()).x() +
                        30)
                    p.setY(
                        desktop.availableGeometry(gVar.app.getWindow()).y() +
                        30)
                self.setGeometry(QRect(p, gVar.app.getWindow().size()))
            else:
                self.resize(gVar.app.getWindow().size())
        elif not self.restoreGeometry(windowGeometry):
            if const.OS_WIN:
                self.setGeometry(
                    QRect(
                        desktop.availableGeometry(gVar.app.getWindow()).x() +
                        30,
                        desktop.availableGeometry(gVar.app.getWindow()).y() +
                        30, windowWidth, windowHeight))
            else:
                self.resize(windowWidth, windowHeight)

        self._restoreUiState(uiState)
        # Set some sane minimum width
        self.setMinimumWidth(300)

    def _setupMenu(self):
        if const.OS_MACOS:
            macMainMenu = None
            if not macMainMenu:
                macMainMenu = MainMenu(self, 0)
                macMainMenu.initMenuBar(QMenuBar(0))
                gVar.app.activeWindowChanged.connect(macMainMenu.setWindow)
            else:
                macMainMenu.setWindow(self)
        else:
            self.setMenuBar(MenuBar(self))
            self._mainMenu = MainMenu(self, self)
            self._mainMenu.initMenuBar(self.menuBar())

        self._mainMenu.initSuperMenu(self._superMenu)

        # Setup other shortcuts
        reloadBypassCacheAction = QShortcut(QKeySequence('Ctrl+F5'), self)
        reloadBypassCacheAction2 = QShortcut(QKeySequence('Ctrl+Shift+R'),
                                             self)
        reloadBypassCacheAction.activated.connect(self.reloadBypassCache)
        reloadBypassCacheAction2.activated.connect(self.reloadBypassCache)

        closeTabAction = QShortcut(QKeySequence('Ctrl+W'), self)
        closeTabAction2 = QShortcut(QKeySequence('Ctrl+F4'), self)

        closeTabAction.activated.connect(self._closeTab)
        closeTabAction2.activated.connect(self._closeTab)

        reloadAction = QShortcut(QKeySequence('Ctrl+R'), self)
        reloadAction.activated.connect(self.reload)

        openLocationAction = QShortcut(QKeySequence('Alt+D'), self)
        openLocationAction.activated.connect(self._openLocation)

        inspectorAction = QShortcut(QKeySequence('F12'), self)
        inspectorAction.activated.connect(self.toggleWebInspector)

        restoreClosedWindow = QShortcut(QKeySequence('Ctrl+Shift+N'), self)
        restoreClosedWindow.activated.connect(
            gVar.app.closedWindowsManager().restoreClosedWindow)

    def _updateStartupFocus(self):
        def _updateStartupFocusCb():
            # Scroll to current tab
            self.tabWidget().tabBar().ensureVisible()
            # Update focus
            page = self.weView().page()
            url = page.requestedUrl()
            if not self._startPage and not LocationBar.convertUrlToText(url):
                self.locationBar().setFocus()
            else:
                self.weView().setFocus()

        QTimer.singleShot(500, _updateStartupFocusCb)

    def _createEncodingAction(self, codecName, activeCodecName, menu):
        '''
        @param: codecName QString
        @param: activeCodecName QString
        @param: menu QMenu
        '''
        action = QAction(codecName, menu)
        action.setData(codecName)
        action.setCheckable(True)
        action.triggered.connect(self._changeEncoding)
        if activeCodecName.lower() == codecName.lower():
            action.setChecked(True)
        return action

    def _createEncodingSubMenu(self, name, codecNames, menu):
        '''
        @param: name QString
        @param: codecName QStringList
        @param: menu QMenu
        '''
        if not codecNames:
            return

        codecNames.sort()
        subMenu = QMenu(name, menu)
        activeCodecName = gVar.app.webSettings().defaultTextEncoding()

        group = QActionGroup(subMenu)

        for codecName in codecNames:
            act = self._createEncodingAction(codecName, activeCodecName,
                                             subMenu)
            group.addAction(act)
            subMenu.addAction(act)

        menu.addMenu(subMenu)

    def _saveUiState(self):
        '''
        @return: QHash<QStirng, QVariant>
        '''
        self.saveSideBarSettings()
        state = {}
        state['LocationBarWidth'] = self._navigationToolbar.splitter().sizes(
        )[0]
        state['WebSearchBarWidth'] = self._navigationToolbar.splitter().sizes(
        )[1]
        state['SideBarWidth'] = self._sideBarWidth
        state['WebViewWidth'] = self._webViewWidth
        state['SideBar'] = self._sideBarManager.activeSideBar()
        return state

    def _restoreUiState(self, state):
        '''
        @param: state QHash<QString, QVariant>
        '''
        locationBarWidth = state.get('LocationBarWidth', 480)
        webSearchBarWidth = state.get('WebSearchBarWidth', 140)
        self._navigationToolbar.setSplitterSizes(locationBarWidth,
                                                 webSearchBarWidth)

        self._sideBarWidth = state.get('SideBarWidth', 250)
        self._webViewWidth = state.get('WebViewWidth', 2000)
        if self._sideBar:
            self._mainSplitter.setSizes(
                [self._sideBarWidth, self._webViewWidth])
        activeSideBar = state.get('SideBar')
        if not activeSideBar and self._sideBar:
            self._sideBar.close()
        else:
            self._sideBarManager.showSideBar(activeSideBar, False)
Пример #14
0
class Splitter(QSplitter):

    state_changed = pyqtSignal(object)

    def __init__(self, name, label, icon, initial_show=True,
            initial_side_size=120, connect_button=True,
            orientation=Qt.Horizontal, side_index=0, parent=None,
            shortcut=None, hide_handle_on_single_panel=True):
        QSplitter.__init__(self, parent)
        if hide_handle_on_single_panel:
            self.state_changed.connect(self.update_handle_width)
        self.original_handle_width = self.handleWidth()
        self.resize_timer = QTimer(self)
        self.resize_timer.setSingleShot(True)
        self.desired_side_size = initial_side_size
        self.desired_show = initial_show
        self.resize_timer.setInterval(5)
        self.resize_timer.timeout.connect(self.do_resize)
        self.setOrientation(orientation)
        self.side_index = side_index
        self._name = name
        self.label = label
        self.initial_side_size = initial_side_size
        self.initial_show = initial_show
        self.splitterMoved.connect(self.splitter_moved, type=Qt.QueuedConnection)
        self.button = LayoutButton(icon, label, self, shortcut=shortcut)
        if connect_button:
            self.button.clicked.connect(self.double_clicked)

        if shortcut is not None:
            self.action_toggle = QAction(QIcon(icon), _('Toggle') + ' ' + label,
                    self)
            self.action_toggle.changed.connect(self.update_shortcut)
            self.action_toggle.triggered.connect(self.toggle_triggered)
            if parent is not None:
                parent.addAction(self.action_toggle)
                if hasattr(parent, 'keyboard'):
                    parent.keyboard.register_shortcut('splitter %s %s'%(name,
                        label), unicode_type(self.action_toggle.text()),
                        default_keys=(shortcut,), action=self.action_toggle)
                else:
                    self.action_toggle.setShortcut(shortcut)
            else:
                self.action_toggle.setShortcut(shortcut)

    def update_shortcut(self):
        self.button.update_shortcut(self.action_toggle)

    def toggle_triggered(self, *args):
        self.toggle_side_pane()

    def createHandle(self):
        return SplitterHandle(self.orientation(), self)

    def initialize(self):
        for i in range(self.count()):
            h = self.handle(i)
            if h is not None:
                h.splitter_moved()
        self.state_changed.emit(not self.is_side_index_hidden)

    def splitter_moved(self, *args):
        self.desired_side_size = self.side_index_size
        self.state_changed.emit(not self.is_side_index_hidden)

    def update_handle_width(self, not_one_panel):
        self.setHandleWidth(self.original_handle_width if not_one_panel else 0)

    @property
    def is_side_index_hidden(self):
        sizes = list(self.sizes())
        try:
            return sizes[self.side_index] == 0
        except IndexError:
            return True

    @property
    def save_name(self):
        ori = 'horizontal' if self.orientation() == Qt.Horizontal \
                else 'vertical'
        return self._name + '_' + ori

    def print_sizes(self):
        if self.count() > 1:
            print(self.save_name, 'side:', self.side_index_size, 'other:', end=' ')
            print(list(self.sizes())[self.other_index])

    @dynamic_property
    def side_index_size(self):
        def fget(self):
            if self.count() < 2:
                return 0
            return self.sizes()[self.side_index]

        def fset(self, val):
            if self.count() < 2:
                return
            if val == 0 and not self.is_side_index_hidden:
                self.save_state()
            sizes = list(self.sizes())
            for i in range(len(sizes)):
                sizes[i] = val if i == self.side_index else 10
            self.setSizes(sizes)
            total = sum(self.sizes())
            sizes = list(self.sizes())
            for i in range(len(sizes)):
                sizes[i] = val if i == self.side_index else total-val
            self.setSizes(sizes)
            self.initialize()

        return property(fget=fget, fset=fset)

    def do_resize(self, *args):
        orig = self.desired_side_size
        QSplitter.resizeEvent(self, self._resize_ev)
        if orig > 20 and self.desired_show:
            c = 0
            while abs(self.side_index_size - orig) > 10 and c < 5:
                self.apply_state(self.get_state(), save_desired=False)
                c += 1

    def resizeEvent(self, ev):
        if self.resize_timer.isActive():
            self.resize_timer.stop()
        self._resize_ev = ev
        self.resize_timer.start()

    def get_state(self):
        if self.count() < 2:
            return (False, 200)
        return (self.desired_show, self.desired_side_size)

    def apply_state(self, state, save_desired=True):
        if state[0]:
            self.side_index_size = state[1]
            if save_desired:
                self.desired_side_size = self.side_index_size
        else:
            self.side_index_size = 0
        self.desired_show = state[0]

    def default_state(self):
        return (self.initial_show, self.initial_side_size)

    # Public API {{{

    def update_desired_state(self):
        self.desired_show = not self.is_side_index_hidden

    def save_state(self):
        if self.count() > 1:
            gprefs[self.save_name+'_state'] = self.get_state()

    @property
    def other_index(self):
        return (self.side_index+1)%2

    def restore_state(self):
        if self.count() > 1:
            state = gprefs.get(self.save_name+'_state',
                    self.default_state())
            self.apply_state(state, save_desired=False)
            self.desired_side_size = state[1]

    def toggle_side_pane(self, hide=None):
        if hide is None:
            action = 'show' if self.is_side_index_hidden else 'hide'
        else:
            action = 'hide' if hide else 'show'
        getattr(self, action+'_side_pane')()

    def show_side_pane(self):
        if self.count() < 2 or not self.is_side_index_hidden:
            return
        if self.desired_side_size == 0:
            self.desired_side_size = self.initial_side_size
        self.apply_state((True, self.desired_side_size))

    def hide_side_pane(self):
        if self.count() < 2 or self.is_side_index_hidden:
            return
        self.apply_state((False, self.desired_side_size))

    def double_clicked(self, *args):
        self.toggle_side_pane()
Пример #15
0
class CoverDelegate(QStyledItemDelegate):  # {{{

    needs_redraw = pyqtSignal()

    def __init__(self, parent):
        QStyledItemDelegate.__init__(self, parent)

        self.angle = 0
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.frame_changed)
        self.color = parent.palette().color(QPalette.WindowText)
        self.spinner_width = 64

    def frame_changed(self, *args):
        self.angle = (self.angle + 30) % 360
        self.needs_redraw.emit()

    def start_animation(self):
        self.angle = 0
        self.timer.start(200)

    def stop_animation(self):
        self.timer.stop()

    def draw_spinner(self, painter, rect):
        width = rect.width()

        outer_radius = (width - 1) * 0.5
        inner_radius = (width - 1) * 0.5 * 0.38

        capsule_height = outer_radius - inner_radius
        capsule_width = int(capsule_height * (0.23 if width > 32 else 0.35))
        capsule_radius = capsule_width // 2

        painter.save()
        painter.setRenderHint(painter.Antialiasing)

        for i in xrange(12):
            color = QColor(self.color)
            color.setAlphaF(1.0 - (i / 12.0))
            painter.setPen(Qt.NoPen)
            painter.setBrush(color)
            painter.save()
            painter.translate(rect.center())
            painter.rotate(self.angle - i * 30.0)
            painter.drawRoundedRect(
                -capsule_width * 0.5,
                -(inner_radius + capsule_height),
                capsule_width,
                capsule_height,
                capsule_radius,
                capsule_radius,
            )
            painter.restore()
        painter.restore()

    def paint(self, painter, option, index):
        QStyledItemDelegate.paint(self, painter, option, index)
        style = QApplication.style()
        waiting = self.timer.isActive() and bool(index.data(Qt.UserRole))
        if waiting:
            rect = QRect(0, 0, self.spinner_width, self.spinner_width)
            rect.moveCenter(option.rect.center())
            self.draw_spinner(painter, rect)
        else:
            # Ensure the cover is rendered over any selection rect
            style.drawItemPixmap(
                painter, option.rect, Qt.AlignTop | Qt.AlignHCenter, QPixmap(index.data(Qt.DecorationRole))
            )
Пример #16
0
class LiveCSS(QWidget):

    goto_declaration = pyqtSignal(object)

    def __init__(self, preview, parent=None):
        QWidget.__init__(self, parent)
        self.preview = preview
        self.preview_is_refreshing = False
        self.refresh_needed = False
        preview.refresh_starting.connect(self.preview_refresh_starting)
        preview.refreshed.connect(self.preview_refreshed)
        self.apply_theme()
        self.setAutoFillBackground(True)
        self.update_timer = QTimer(self)
        self.update_timer.timeout.connect(self.update_data)
        self.update_timer.setSingleShot(True)
        self.update_timer.setInterval(500)
        self.now_showing = (None, None, None)

        self.stack = s = QStackedLayout(self)
        self.setLayout(s)

        self.clear_label = la = QLabel(
            "<h3>"
            + _("No style information found")
            + "</h3><p>"
            + _("Move the cursor inside a HTML tag to see what styles" " apply to that tag.")
        )
        la.setWordWrap(True)
        la.setAlignment(Qt.AlignTop | Qt.AlignLeft)
        s.addWidget(la)

        self.box = box = Box(self)
        box.hyperlink_activated.connect(self.goto_declaration, type=Qt.QueuedConnection)
        self.scroll = sc = QScrollArea(self)
        sc.setWidget(box)
        sc.setWidgetResizable(True)
        s.addWidget(sc)

    def preview_refresh_starting(self):
        self.preview_is_refreshing = True

    def preview_refreshed(self):
        self.preview_is_refreshing = False
        # We must let the event loop run otherwise the webview will return
        # stale data in read_data()
        self.refresh_needed = True
        self.start_update_timer()

    def apply_theme(self):
        f = self.font()
        f.setFamily(tprefs["editor_font_family"] or default_font_family())
        f.setPointSize(tprefs["editor_font_size"])
        self.setFont(f)
        theme = get_theme(tprefs["editor_theme"])
        pal = self.palette()
        pal.setColor(pal.Window, theme_color(theme, "Normal", "bg"))
        pal.setColor(pal.WindowText, theme_color(theme, "Normal", "fg"))
        pal.setColor(pal.AlternateBase, theme_color(theme, "HighlightRegion", "bg"))
        pal.setColor(pal.Link, theme_color(theme, "Link", "fg"))
        pal.setColor(pal.LinkVisited, theme_color(theme, "Keyword", "fg"))
        self.setPalette(pal)
        if hasattr(self, "box"):
            self.box.relayout()
        self.update()

    def clear(self):
        self.stack.setCurrentIndex(0)

    def show_data(self, editor_name, sourceline, tags):
        if self.preview_is_refreshing:
            return
        if sourceline is None:
            self.clear()
        else:
            data = self.read_data(sourceline, tags)
            if data is None or len(data["computed_css"]) < 1:
                if editor_name == self.current_name and (editor_name, sourceline, tags) == self.now_showing:
                    # Try again in a little while in case there was a transient
                    # error in the web view
                    self.start_update_timer()
                    return
                if self.now_showing == (None, None, None) or self.now_showing[0] != self.current_name:
                    self.clear()
                    return
                # Try to refresh the data for the currently shown tag instead
                # of clearing
                editor_name, sourceline, tags = self.now_showing
                data = self.read_data(sourceline, tags)
                if data is None or len(data["computed_css"]) < 1:
                    self.clear()
                    return
            self.now_showing = (editor_name, sourceline, tags)
            data["html_name"] = editor_name
            self.box.show_data(data)
            self.refresh_needed = False
            self.stack.setCurrentIndex(1)

    def read_data(self, sourceline, tags):
        mf = self.preview.view.page().mainFrame()
        tags = [x.lower() for x in tags]
        result = unicode(
            mf.evaluateJavaScript(
                "window.calibre_preview_integration.live_css(%s, %s)" % (json.dumps(sourceline), json.dumps(tags))
            )
            or ""
        )
        try:
            result = json.loads(result)
        except ValueError:
            result = None
        if result is not None:
            maximum_specificities = {}
            for node in result["nodes"]:
                is_ancestor = node["is_ancestor"]
                for rule in node["css"]:
                    self.process_rule(rule, is_ancestor, maximum_specificities)
            for node in result["nodes"]:
                for rule in node["css"]:
                    for prop in rule["properties"]:
                        if prop.specificity < maximum_specificities[prop.name]:
                            prop.is_overriden = True

        return result

    def process_rule(self, rule, is_ancestor, maximum_specificities):
        selector = rule["selector"]
        sheet_index = rule["sheet_index"]
        rule_address = rule["rule_address"] or ()
        if selector is not None:
            try:
                specificity = [0] + list(parse(selector)[0].specificity())
            except (AttributeError, TypeError):
                specificity = [0, 0, 0, 0]
        else:  # style attribute
            specificity = [1, 0, 0, 0]
        specificity.extend((sheet_index, tuple(rule_address)))
        ancestor_specificity = 0 if is_ancestor else 1
        properties = []
        for prop in rule["properties"]:
            important = 1 if prop[-1] == "important" else 0
            p = Property(prop, [ancestor_specificity] + [important] + specificity)
            properties.append(p)
            if p.specificity > maximum_specificities.get(p.name, (0, 0, 0, 0, 0, 0)):
                maximum_specificities[p.name] = p.specificity
        rule["properties"] = properties

        href = rule["href"]
        if hasattr(href, "startswith") and href.startswith("file://"):
            href = href[len("file://") :]
            if iswindows and href.startswith("/"):
                href = href[1:]
            if href:
                rule["href"] = current_container().abspath_to_name(href, root=self.preview.current_root)

    @property
    def current_name(self):
        return self.preview.current_name

    @property
    def is_visible(self):
        return self.isVisible()

    def showEvent(self, ev):
        self.update_timer.start()
        actions["auto-reload-preview"].setEnabled(True)
        return QWidget.showEvent(self, ev)

    def sync_to_editor(self):
        self.update_data()

    def update_data(self):
        if not self.is_visible or self.preview_is_refreshing:
            return
        editor_name = self.current_name
        ed = editors.get(editor_name, None)
        if self.update_timer.isActive() or (ed is None and editor_name is not None):
            return QTimer.singleShot(100, self.update_data)
        if ed is not None:
            sourceline, tags = ed.current_tag(for_position_sync=False)
            if self.refresh_needed or self.now_showing != (editor_name, sourceline, tags):
                self.show_data(editor_name, sourceline, tags)

    def start_update_timer(self):
        if self.is_visible:
            self.update_timer.start()

    def stop_update_timer(self):
        self.update_timer.stop()

    def navigate_to_declaration(self, data, editor):
        if data["type"] == "inline":
            sourceline, tags = data["sourceline_address"]
            editor.goto_sourceline(sourceline, tags, attribute="style")
        elif data["type"] == "sheet":
            editor.goto_css_rule(data["rule_address"])
        elif data["type"] == "elem":
            editor.goto_css_rule(data["rule_address"], sourceline_address=data["sourceline_address"])
Пример #17
0
class SequencePlayback():
    def __init__(self):
        self.imageSequence = []
        self.framerate = 0
        self.currentFrame = -1
        self.currentFrameIdx = -1
        self.endFrameIdx = 1
        self.state = ''

        self.timer = QTimer()
        self.timer.timerEvent = self.onTick
        Event.add(AudioPlaybackEvent.TICK, self.onTick)
        Event.add(PlaybackEvent.STATE, self.onState)
        self.setFramerate(24)
        pass

    def onState(self, state):
        self.state = state
        if state == PlayStateType.PLAY:
            self.play()
        elif state == PlayStateType.PAUSE:
            self.pause()
            pass
        pass

    def onTick(self, time):
        self.render()
        pass

    # def load(self, imagesPath=None):
    #     if imagesPath:
    #         for root, dirs, files in os.walk(imagesPath):
    #             for filespath in files:
    #                 filename = os.path.join(root, filespath).replace('\\', '/')
    #                 # todo support image ext
    #                 if filename.find('.png') < 0:
    #                     continue
    #                 simage = SImage(filename)
    #                 self.imageSequence.append(simage)
    #                 simage.frameIdx = len(self.imageSequence)
    #                 self.endFrameIdx = simage.frameIdx
    #                 print('[load img]: ', filename)
    #         Event.dis(ActionEvent.LOAD_SEQ, self.imageSequence)
    #         pass

    def play(self):
        if not self.timer.isActive():
            self.timer.start()
            pass
        pass

    def pause(self):
        if self.timer.isActive():
            self.timer.stop()
            pass
        pass

    def render(self):
        self.currentFrameIdx = (self.currentFrameIdx + 1) % self.endFrameIdx
        event = SequencePlaybackEvent()
        event.type = SequencePlaybackEvent.RENDER_FRAME
        event.frameIdx = self.currentFrameIdx
        Event.dis(SequencePlaybackEvent.RENDER_FRAME, event)

    def setFramerate(self, framerate):
        self.framerate = framerate
        self.timer.setInterval(1000 / self.framerate)
        pass
Пример #18
0
class Widget(QWidget, ScreenWidget):
    name = "timeSetup"

    def __init__(self):
        QWidget.__init__(self)
        self.ui = Ui_DateTimeWidget()
        self.ui.setupUi(self)
        self.timer = QTimer(self)
        self.from_time_updater = True
        self.is_date_changed = False

        self.current_zone = ""

        self.tz_dict = {}
        self.continents = []
        self.countries = []

        for country, data in yali.localedata.locales.items():
            if country == ctx.consts.lang:
                if data.has_key("timezone"):
                    ctx.installData.timezone = data["timezone"]

        # Append continents and countries the time zone dictionary
        self.createTZDictionary()

        # Sort continent list
        self.sortContinents()

        # Append sorted continents to combobox
        self.loadContinents()

        # Load current continents country list
        self.getCountries(self.current_zone["continent"])

        # Highlight the current zone
        self.index = self.ui.continentList.findText(
            self.current_zone["continent"])
        self.ui.continentList.setCurrentIndex(self.index)

        self.index = self.ui.countryList.findText(self.current_zone["country"])
        self.ui.countryList.setCurrentIndex(self.index)

        # Initialize widget signal and slots
        self.__initSignals__()

        self.ui.calendarWidget.setDate(QDate.currentDate())

        self.pthread = None
        self.pds_messagebox = PMessageBox(self)
        self.pds_messagebox.enableOverlay()

        self.timer.start(1000)

    def __initSignals__(self):
        self.ui.timeEdit.timeChanged[QTime].connect(self.timerStop)
        self.ui.calendarWidget.dateChanged.connect(self.dateChanged)
        self.timer.timeout.connect(self.updateClock)
        self.ui.continentList.activated[str].connect(self.getCountries)

    def createTZDictionary(self):
        tz = TimeZoneList()
        zones = [x.timeZone for x in tz.getEntries()]
        zones.sort()

        for zone in zones:
            split = zone.split("/")

            # Human readable continent names
            continent_pretty_name = split[0].replace("_", " ")
            continent_pretty_name = continent_pretty_name

            # Some country names can be like Argentina/Catamarca so this fixes the splitting problem
            # caused by zone.split("/")
            #
            # Remove continent info and take the rest as the country name
            split.pop(0)
            country_pretty_name = " / ".join(split)

            # Human readable country names
            country_pretty_name = country_pretty_name.replace("_", " ")

            # Get current zone
            if zone == ctx.installData.timezone:
                self.current_zone = {
                    "continent": continent_pretty_name,
                    "country": country_pretty_name
                }

            # Append to dictionary
            if self.tz_dict.has_key(continent_pretty_name):
                self.tz_dict[continent_pretty_name].append(
                    [country_pretty_name, zone])
            else:
                self.tz_dict[continent_pretty_name] = [[
                    country_pretty_name, zone
                ]]

    def sortContinents(self):
        for continent in self.tz_dict.keys():
            self.continents.append(continent)
        self.continents.sort()

    def loadContinents(self):
        for continent in self.continents:
            self.ui.continentList.addItem(continent)

    def getCountries(self, continent):
        # Countries of the selected continent
        countries = self.tz_dict[str(continent)]

        self.ui.countryList.clear()

        for country, zone in countries:
            self.ui.countryList.addItem(country, zone)
            self.countries.append(country)

    def dateChanged(self):
        self.is_date_changed = True

    def timerStop(self):
        if self.from_time_updater:
            return
        # Human action detected; stop the timer.
        self.timer.stop()

    def updateClock(self):

        # What time is it ?
        cur = QTime.currentTime()

        self.from_time_updater = True
        self.ui.timeEdit.setTime(cur)
        self.from_time_updater = False

    def shown(self):
        self.timer.start(1000)

        if ctx.flags.install_type == ctx.STEP_BASE:
            self.pthread = PThread(self, self.startInit, self.dummy)

    def dummy(self):
        pass

    def setTime(self):
        ctx.interface.informationWindow.update(_("Adjusting time settings"))
        date = self.ui.calendarWidget.date()
        time = self.ui.timeEdit.time()
        args = "%02d%02d%02d%02d%04d.%02d" % (date.month(), date.day(),
                                              time.hour(), time.minute(),
                                              date.year(), time.second())

        # Set current date and time
        ctx.logger.debug("Date/Time setting to %s" % args)
        yali.util.run_batch("date", [args])

        # Sync date time with hardware
        ctx.logger.debug("YALI's time is syncing with the system.")
        yali.util.run_batch("hwclock", ["--systohc"])
        ctx.interface.informationWindow.hide()

    def execute(self):
        if not self.timer.isActive() or self.is_date_changed:
            QTimer.singleShot(500, self.setTime)
            self.timer.stop()

        index = self.ui.countryList.currentIndex()
        ctx.installData.timezone = self.ui.countryList.itemData(index)
        ctx.logger.debug("Time zone selected as %s " %
                         ctx.installData.timezone)

        if ctx.flags.install_type == ctx.STEP_BASE:
            #FIXME:Refactor hacky code
            ctx.installData.rootPassword = ctx.consts.default_password
            ctx.installData.hostName = yali.util.product_release()
            if ctx.storageInitialized:
                disks = filter(lambda d: not d.format.hidden,
                               ctx.storage.disks)
                if len(disks) == 1:
                    ctx.storage.clearPartDisks = [disks[0].name]
                    ctx.mainScreen.step_increment = 2
                else:
                    ctx.mainScreen.step_increment = 1
                return True
            else:
                self.pds_messagebox.setMessage(
                    _("Storage Devices initialising..."))
                self.pds_messagebox.animate(start=MIDCENTER, stop=MIDCENTER)
                ctx.mainScreen.step_increment = 0
                self.pthread.start()
                QTimer.singleShot(2, self.startStorageInitialize)
                return False

        return True

    def startInit(self):
        self.pds_messagebox.animate(start=MIDCENTER, stop=MIDCENTER)

    def startStorageInitialize(self):
        ctx.storageInitialized = yali.storage.initialize(
            ctx.storage, ctx.interface)
        self.initFinished()

    def initFinished(self):
        self.pds_messagebox.animate(start=CURRENT, stop=CURRENT, direction=OUT)
        disks = filter(lambda d: not d.format.hidden, ctx.storage.disks)
        if ctx.storageInitialized:
            if len(disks) == 1:
                ctx.storage.clearPartDisks = [disks[0].name]
                ctx.mainScreen.step_increment = 2
            else:
                ctx.mainScreen.step_increment = 1
            ctx.mainScreen.slotNext(dry_run=True)
        else:
            ctx.mainScreen.enableBack()
Пример #19
0
class CoverDelegate(QStyledItemDelegate):  # {{{

    needs_redraw = pyqtSignal()

    def __init__(self, parent):
        QStyledItemDelegate.__init__(self, parent)

        self.angle = 0
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.frame_changed)
        self.color = parent.palette().color(QPalette.WindowText)
        self.spinner_width = 64

    def frame_changed(self, *args):
        self.angle = (self.angle + 30) % 360
        self.needs_redraw.emit()

    def start_animation(self):
        self.angle = 0
        self.timer.start(200)

    def stop_animation(self):
        self.timer.stop()

    def draw_spinner(self, painter, rect):
        width = rect.width()

        outer_radius = (width - 1) * 0.5
        inner_radius = (width - 1) * 0.5 * 0.38

        capsule_height = outer_radius - inner_radius
        capsule_width = int(capsule_height * (0.23 if width > 32 else 0.35))
        capsule_radius = capsule_width // 2

        painter.save()
        painter.setRenderHint(painter.Antialiasing)

        for i in xrange(12):
            color = QColor(self.color)
            color.setAlphaF(1.0 - (i / 12.0))
            painter.setPen(Qt.NoPen)
            painter.setBrush(color)
            painter.save()
            painter.translate(rect.center())
            painter.rotate(self.angle - i * 30.0)
            painter.drawRoundedRect(-capsule_width * 0.5,
                                    -(inner_radius + capsule_height),
                                    capsule_width, capsule_height,
                                    capsule_radius, capsule_radius)
            painter.restore()
        painter.restore()

    def paint(self, painter, option, index):
        QStyledItemDelegate.paint(self, painter, option, index)
        style = QApplication.style()
        waiting = self.timer.isActive() and bool(index.data(Qt.UserRole))
        if waiting:
            rect = QRect(0, 0, self.spinner_width, self.spinner_width)
            rect.moveCenter(option.rect.center())
            self.draw_spinner(painter, rect)
        else:
            # Ensure the cover is rendered over any selection rect
            style.drawItemPixmap(painter, option.rect,
                                 Qt.AlignTop | Qt.AlignHCenter,
                                 QPixmap(index.data(Qt.DecorationRole)))
Пример #20
0
class Widget(QWidget, ScreenWidget):
    name = "timeSetup"

    def __init__(self):
        QWidget.__init__(self)
        self.ui = Ui_DateTimeWidget()
        self.ui.setupUi(self)
        self.timer = QTimer(self)
        self.from_time_updater = True
        self.is_date_changed = False

        self.current_zone = ""

        self.tz_dict = {}
        self.continents = []
        self.countries = []

        for country, data in yali.localedata.locales.items():
            if country == ctx.consts.lang:
                if data.has_key("timezone"):
                    ctx.installData.timezone = data["timezone"]

        # Append continents and countries the time zone dictionary
        self.createTZDictionary()

        # Sort continent list
        self.sortContinents()

        # Append sorted continents to combobox
        self.loadContinents()

        # Load current continents country list
        self.getCountries(self.current_zone["continent"])

        # Highlight the current zone
        self.index = self.ui.continentList.findText(self.current_zone["continent"])
        self.ui.continentList.setCurrentIndex(self.index)

        self.index = self.ui.countryList.findText(self.current_zone["country"])
        self.ui.countryList.setCurrentIndex(self.index)

        # Initialize widget signal and slots
        self.__initSignals__()

        self.ui.calendarWidget.setDate(QDate.currentDate())

        self.pthread = None
        self.pds_messagebox = PMessageBox(self)
        self.pds_messagebox.enableOverlay()

        self.timer.start(1000)

    def __initSignals__(self):
        self.ui.timeEdit.timeChanged[QTime].connect(self.timerStop)
        self.ui.calendarWidget.dateChanged.connect(self.dateChanged)
        self.timer.timeout.connect(self.updateClock)
        self.ui.continentList.activated[str].connect(self.getCountries)

    def createTZDictionary(self):
        tz = TimeZoneList()
        zones = [ x.timeZone for x in tz.getEntries() ]
        zones.sort()

        for zone in zones:
            split = zone.split("/")

            # Human readable continent names
            continent_pretty_name = split[0].replace("_", " ")
            continent_pretty_name = continent_pretty_name

            # Some country names can be like Argentina/Catamarca so this fixes the splitting problem
            # caused by zone.split("/")
            #
            # Remove continent info and take the rest as the country name
            split.pop(0)
            country_pretty_name = " / ".join(split)

            # Human readable country names
            country_pretty_name = country_pretty_name.replace("_", " ")

            # Get current zone
            if zone == ctx.installData.timezone:
                self.current_zone = { "continent":continent_pretty_name, "country":country_pretty_name}

            # Append to dictionary
            if self.tz_dict.has_key(continent_pretty_name):
                self.tz_dict[continent_pretty_name].append([country_pretty_name, zone])
            else:
                self.tz_dict[continent_pretty_name] = [[country_pretty_name, zone]]


    def sortContinents(self):
        for continent in self.tz_dict.keys():
            self.continents.append(continent)
        self.continents.sort()

    def loadContinents(self):
        for continent in self.continents:
            self.ui.continentList.addItem(continent)

    def getCountries(self, continent):
        # Countries of the selected continent
        countries = self.tz_dict[str(continent)]

        self.ui.countryList.clear()

        for country, zone in countries:
            self.ui.countryList.addItem(country, zone)
            self.countries.append(country)



    def dateChanged(self):
        self.is_date_changed = True

    def timerStop(self):
        if self.from_time_updater:
            return
        # Human action detected; stop the timer.
        self.timer.stop()

    def updateClock(self):

        # What time is it ?
        cur = QTime.currentTime()

        self.from_time_updater = True
        self.ui.timeEdit.setTime(cur)
        self.from_time_updater = False

    def shown(self):
        self.timer.start(1000)

        if ctx.flags.install_type == ctx.STEP_BASE:
            self.pthread = PThread(self, self.startInit, self.dummy)

    def dummy(self):
        pass

    def setTime(self):
        ctx.interface.informationWindow.update(_("Adjusting time settings"))
        date = self.ui.calendarWidget.date()
        time = self.ui.timeEdit.time()
        args = "%02d%02d%02d%02d%04d.%02d" % (date.month(), date.day(),
                                              time.hour(), time.minute(),
                                              date.year(), time.second())


        # Set current date and time
        ctx.logger.debug("Date/Time setting to %s" % args)
        yali.util.run_batch("date", [args])

        # Sync date time with hardware
        ctx.logger.debug("YALI's time is syncing with the system.")
        yali.util.run_batch("hwclock", ["--systohc"])
        ctx.interface.informationWindow.hide()

    def execute(self):
        if not self.timer.isActive() or self.is_date_changed:
            QTimer.singleShot(500, self.setTime)
            self.timer.stop()

        index = self.ui.countryList.currentIndex()
        ctx.installData.timezone = self.ui.countryList.itemData(index)
        ctx.logger.debug("Time zone selected as %s " % ctx.installData.timezone)

        if ctx.flags.install_type == ctx.STEP_BASE:
            #FIXME:Refactor hacky code
            ctx.installData.rootPassword = ctx.consts.default_password
            ctx.installData.hostName = yali.util.product_release()
            if ctx.storageInitialized:
                disks = filter(lambda d: not d.format.hidden, ctx.storage.disks)
                if len(disks) == 1:
                    ctx.storage.clearPartDisks = [disks[0].name]
                    ctx.mainScreen.step_increment = 2
                else:
                    ctx.mainScreen.step_increment = 1
                return True
            else:
                self.pds_messagebox.setMessage(_("Storage Devices initialising..."))
                self.pds_messagebox.animate(start=MIDCENTER, stop=MIDCENTER)
                ctx.mainScreen.step_increment = 0
                self.pthread.start()
                QTimer.singleShot(2, self.startStorageInitialize)
                return False

        return True

    def startInit(self):
        self.pds_messagebox.animate(start=MIDCENTER, stop=MIDCENTER)

    def startStorageInitialize(self):
        ctx.storageInitialized = yali.storage.initialize(ctx.storage, ctx.interface)
        self.initFinished()

    def initFinished(self):
        self.pds_messagebox.animate(start=CURRENT, stop=CURRENT, direction=OUT)
        disks = filter(lambda d: not d.format.hidden, ctx.storage.disks)
        if ctx.storageInitialized:
            if len(disks) == 1:
                ctx.storage.clearPartDisks = [disks[0].name]
                ctx.mainScreen.step_increment = 2
            else:
                ctx.mainScreen.step_increment = 1
            ctx.mainScreen.slotNext(dry_run=True)
        else:
            ctx.mainScreen.enableBack()