Ejemplo n.º 1
0
class TagBrowserWidget(QFrame):  # {{{

    def __init__(self, parent):
        QFrame.__init__(self, parent)
        self.setFrameStyle(QFrame.NoFrame if gprefs['tag_browser_old_look'] else QFrame.StyledPanel)
        self._parent = parent
        self._layout = QVBoxLayout(self)
        self._layout.setContentsMargins(0,0,0,0)

        # Set up the find box & button
        self.tb_bar = tbb = TagBrowserBar(self)
        self.alter_tb, self.item_search, self.search_button = tbb.alter_tb, tbb.item_search, tbb.search_button
        self.toggle_search_button = tbb.toggle_search_button
        self._layout.addWidget(tbb)

        self.current_find_position = None
        self.search_button.clicked.connect(self.find)
        self.item_search.lineEdit().textEdited.connect(self.find_text_changed)
        self.item_search.activated[str].connect(self.do_find)

        # The tags view
        parent.tags_view = TagsView(parent)
        self.tags_view = parent.tags_view
        self._layout.insertWidget(0, parent.tags_view)

        # Now the floating 'not found' box
        l = QLabel(self.tags_view)
        self.not_found_label = l
        l.setFrameStyle(QFrame.StyledPanel)
        l.setAutoFillBackground(True)
        l.setText('<p><b>'+_('No More Matches.</b><p> Click Find again to go to first match'))
        l.setAlignment(Qt.AlignVCenter)
        l.setWordWrap(True)
        l.resize(l.sizeHint())
        l.move(10,20)
        l.setVisible(False)
        self.not_found_label_timer = QTimer()
        self.not_found_label_timer.setSingleShot(True)
        self.not_found_label_timer.timeout.connect(self.not_found_label_timer_event,
                                                   type=Qt.QueuedConnection)
        # The Alter Tag Browser button
        l = self.alter_tb
        self.collapse_all_action = ac = QAction(parent)
        parent.addAction(ac)
        parent.keyboard.register_shortcut('tag browser collapse all',
                _('Collapse all'), default_keys=(),
                action=ac, group=_('Tag browser'))
        connect_lambda(ac.triggered, self, lambda self: self.tags_view.collapseAll())

        ac = QAction(parent)
        parent.addAction(ac)
        parent.keyboard.register_shortcut('tag browser alter',
                _('Configure Tag browser'), default_keys=(),
                action=ac, group=_('Tag browser'))
        ac.triggered.connect(l.showMenu)

        sb = l.m.addAction(_('Sort by'))
        sb.m = l.sort_menu = QMenu(l.m)
        sb.setMenu(sb.m)
        sb.bg = QActionGroup(sb)

        # Must be in the same order as db2.CATEGORY_SORTS
        for i, x in enumerate((_('Name'), _('Number of books'),
                  _('Average rating'))):
            a = sb.m.addAction(x)
            sb.bg.addAction(a)
            a.setCheckable(True)
            if i == 0:
                a.setChecked(True)
        sb.setToolTip(
                _('Set the sort order for entries in the Tag browser'))
        sb.setStatusTip(sb.toolTip())

        ma = l.m.addAction(_('Search type when selecting multiple items'))
        ma.m = l.match_menu = QMenu(l.m)
        ma.setMenu(ma.m)
        ma.ag = QActionGroup(ma)

        # Must be in the same order as db2.MATCH_TYPE
        for i, x in enumerate((_('Match any of the items'), _('Match all of the items'))):
            a = ma.m.addAction(x)
            ma.ag.addAction(a)
            a.setCheckable(True)
            if i == 0:
                a.setChecked(True)
        ma.setToolTip(
                _('When selecting multiple entries in the Tag browser '
                    'match any or all of them'))
        ma.setStatusTip(ma.toolTip())

        mt = l.m.addAction(_('Manage authors, tags, etc.'))
        mt.setToolTip(_('All of these category_managers are available by right-clicking '
                       'on items in the tag browser above'))
        mt.m = l.manage_menu = QMenu(l.m)
        mt.setMenu(mt.m)

        ac = QAction(parent)
        parent.addAction(ac)
        parent.keyboard.register_shortcut('tag browser toggle item',
                _("'Click' found item"), default_keys=(),
                action=ac, group=_('Tag browser'))
        ac.triggered.connect(self.toggle_item)

        # self.leak_test_timer = QTimer(self)
        # self.leak_test_timer.timeout.connect(self.test_for_leak)
        # self.leak_test_timer.start(5000)

    def save_state(self):
        gprefs.set('tag browser search box visible', self.toggle_search_button.isChecked())

    def toggle_item(self):
        self.tags_view.toggle_current_index()

    def set_pane_is_visible(self, to_what):
        self.tags_view.set_pane_is_visible(to_what)

    def find_text_changed(self, str):
        self.current_find_position = None

    def set_focus_to_find_box(self):
        self.tb_bar.set_focus_to_find_box()

    def do_find(self, str=None):
        self.current_find_position = None
        self.find()

    @property
    def find_text(self):
        return unicode_type(self.item_search.currentText()).strip()

    def find(self):
        model = self.tags_view.model()
        model.clear_boxed()
        txt = self.find_text

        if txt.startswith('*'):
            model.set_categories_filter(txt[1:])
            self.tags_view.recount()
            self.current_find_position = None
            return
        if model.get_categories_filter():
            model.set_categories_filter(None)
            self.tags_view.recount()
            self.current_find_position = None

        if not txt:
            return

        self.item_search.lineEdit().blockSignals(True)
        self.search_button.setFocus(True)
        self.item_search.lineEdit().blockSignals(False)

        key = None
        colon = txt.rfind(':') if len(txt) > 2 else 0
        if colon > 0:
            key = self._parent.library_view.model().db.\
                        field_metadata.search_term_to_field_key(txt[:colon])
            txt = txt[colon+1:]

        self.current_find_position = \
            model.find_item_node(key, txt, self.current_find_position)

        if self.current_find_position:
            self.tags_view.show_item_at_path(self.current_find_position, box=True)
        elif self.item_search.text():
            self.not_found_label.setVisible(True)
            if self.tags_view.verticalScrollBar().isVisible():
                sbw = self.tags_view.verticalScrollBar().width()
            else:
                sbw = 0
            width = self.width() - 8 - sbw
            height = self.not_found_label.heightForWidth(width) + 20
            self.not_found_label.resize(width, height)
            self.not_found_label.move(4, 10)
            self.not_found_label_timer.start(2000)

    def not_found_label_timer_event(self):
        self.not_found_label.setVisible(False)

    def keyPressEvent(self, ev):
        if ev.key() in (Qt.Key_Enter, Qt.Key_Return) and self.find_text:
            self.find()
            ev.accept()
            return
        return QFrame.keyPressEvent(self, ev)
Ejemplo n.º 2
0
class TagBrowserWidget(QFrame):  # {{{

    def __init__(self, parent):
        QFrame.__init__(self, parent)
        self.setFrameStyle(QFrame.NoFrame if gprefs['tag_browser_old_look'] else QFrame.StyledPanel)
        self._parent = parent
        self._layout = QVBoxLayout(self)
        self._layout.setContentsMargins(0,0,0,0)

        # Set up the find box & button
        self.tb_bar = tbb = TagBrowserBar(self)
        tbb.clear_find.connect(self.reset_find)
        self.alter_tb, self.item_search, self.search_button = tbb.alter_tb, tbb.item_search, tbb.search_button
        self.toggle_search_button = tbb.toggle_search_button
        self._layout.addWidget(tbb)

        self.current_find_position = None
        self.search_button.clicked.connect(self.find)
        self.item_search.lineEdit().textEdited.connect(self.find_text_changed)
        self.item_search.activated[str].connect(self.do_find)

        # The tags view
        parent.tags_view = TagsView(parent)
        self.tags_view = parent.tags_view
        self._layout.insertWidget(0, parent.tags_view)

        # Now the floating 'not found' box
        l = QLabel(self.tags_view)
        self.not_found_label = l
        l.setFrameStyle(QFrame.StyledPanel)
        l.setAutoFillBackground(True)
        l.setText('<p><b>'+_('No more matches.</b><p> Click Find again to go to first match'))
        l.setAlignment(Qt.AlignVCenter)
        l.setWordWrap(True)
        l.resize(l.sizeHint())
        l.move(10,20)
        l.setVisible(False)
        self.not_found_label_timer = QTimer()
        self.not_found_label_timer.setSingleShot(True)
        self.not_found_label_timer.timeout.connect(self.not_found_label_timer_event,
                                                   type=Qt.QueuedConnection)
        # The Alter Tag Browser button
        l = self.alter_tb
        self.collapse_all_action = ac = QAction(parent)
        parent.addAction(ac)
        parent.keyboard.register_shortcut('tag browser collapse all',
                _('Collapse all'), default_keys=(),
                action=ac, group=_('Tag browser'))
        connect_lambda(ac.triggered, self, lambda self: self.tags_view.collapseAll())

        ac = QAction(parent)
        parent.addAction(ac)
        parent.keyboard.register_shortcut('tag browser alter',
                _('Configure Tag browser'), default_keys=(),
                action=ac, group=_('Tag browser'))
        ac.triggered.connect(l.showMenu)

        sb = l.m.addAction(_('Sort by'))
        sb.m = l.sort_menu = QMenu(l.m)
        sb.setMenu(sb.m)
        sb.bg = QActionGroup(sb)

        # Must be in the same order as db2.CATEGORY_SORTS
        for i, x in enumerate((_('Name'), _('Number of books'),
                  _('Average rating'))):
            a = sb.m.addAction(x)
            sb.bg.addAction(a)
            a.setCheckable(True)
            if i == 0:
                a.setChecked(True)
        sb.setToolTip(
                _('Set the sort order for entries in the Tag browser'))
        sb.setStatusTip(sb.toolTip())

        ma = l.m.addAction(_('Search type when selecting multiple items'))
        ma.m = l.match_menu = QMenu(l.m)
        ma.setMenu(ma.m)
        ma.ag = QActionGroup(ma)

        # Must be in the same order as db2.MATCH_TYPE
        for i, x in enumerate((_('Match any of the items'), _('Match all of the items'))):
            a = ma.m.addAction(x)
            ma.ag.addAction(a)
            a.setCheckable(True)
            if i == 0:
                a.setChecked(True)
        ma.setToolTip(
                _('When selecting multiple entries in the Tag browser '
                    'match any or all of them'))
        ma.setStatusTip(ma.toolTip())

        mt = l.m.addAction(_('Manage authors, tags, etc.'))
        mt.setToolTip(_('All of these category_managers are available by right-clicking '
                       'on items in the Tag browser above'))
        mt.m = l.manage_menu = QMenu(l.m)
        mt.setMenu(mt.m)

        ac = QAction(parent)
        parent.addAction(ac)
        parent.keyboard.register_shortcut('tag browser toggle item',
                _("'Click' found item"), default_keys=(),
                action=ac, group=_('Tag browser'))
        ac.triggered.connect(self.toggle_item)

        # self.leak_test_timer = QTimer(self)
        # self.leak_test_timer.timeout.connect(self.test_for_leak)
        # self.leak_test_timer.start(5000)

    def save_state(self):
        gprefs.set('tag browser search box visible', self.toggle_search_button.isChecked())

    def toggle_item(self):
        self.tags_view.toggle_current_index()

    def set_pane_is_visible(self, to_what):
        self.tags_view.set_pane_is_visible(to_what)

    def find_text_changed(self, str):
        self.current_find_position = None

    def set_focus_to_find_box(self):
        self.tb_bar.set_focus_to_find_box()

    def do_find(self, str=None):
        self.current_find_position = None
        self.find()

    @property
    def find_text(self):
        return unicode_type(self.item_search.currentText()).strip()

    def reset_find(self):
        model = self.tags_view.model()
        if model.get_categories_filter():
            model.set_categories_filter(None)
            self.tags_view.recount()
            self.current_find_position = None

    def find(self):
        model = self.tags_view.model()
        model.clear_boxed()

        # When a key is specified don't use the auto-collapsing search.
        # A colon separates the lookup key from the search string.
        # A leading colon says not to use autocollapsing search but search all keys
        txt = self.find_text
        colon = txt.find(':')
        if colon >= 0:
            key = self._parent.library_view.model().db.\
                        field_metadata.search_term_to_field_key(txt[:colon])
            if key in self._parent.library_view.model().db.field_metadata:
                txt = txt[colon+1:]
            else:
                key = ''
                txt = txt[1:] if colon == 0 else txt
        else:
            key = None

        # key is None indicates that no colon was found.
        # key == '' means either a leading : was found or the key is invalid

        # At this point the txt might have a leading =, in which case do an
        # exact match search

        if (gprefs.get('tag_browser_always_autocollapse', False) and
                key is None and not txt.startswith('*')):
            txt = '*' + txt
        if txt.startswith('*'):
            self.tags_view.collapseAll()
            model.set_categories_filter(txt[1:])
            self.tags_view.recount()
            self.current_find_position = None
            return
        if model.get_categories_filter():
            model.set_categories_filter(None)
            self.tags_view.recount()
            self.current_find_position = None

        if not txt:
            return

        self.item_search.lineEdit().blockSignals(True)
        self.search_button.setFocus(True)
        self.item_search.lineEdit().blockSignals(False)

        if txt.startswith('='):
            equals_match = True
            txt = txt[1:]
        else:
            equals_match = False
        self.current_find_position = \
            model.find_item_node(key, txt, self.current_find_position,
                                 equals_match=equals_match)

        if self.current_find_position:
            self.tags_view.show_item_at_path(self.current_find_position, box=True)
        elif self.item_search.text():
            self.not_found_label.setVisible(True)
            if self.tags_view.verticalScrollBar().isVisible():
                sbw = self.tags_view.verticalScrollBar().width()
            else:
                sbw = 0
            width = self.width() - 8 - sbw
            height = self.not_found_label.heightForWidth(width) + 20
            self.not_found_label.resize(width, height)
            self.not_found_label.move(4, 10)
            self.not_found_label_timer.start(2000)

    def not_found_label_timer_event(self):
        self.not_found_label.setVisible(False)

    def keyPressEvent(self, ev):
        if ev.key() in (Qt.Key_Enter, Qt.Key_Return) and self.find_text:
            self.find()
            ev.accept()
            return
        return QFrame.keyPressEvent(self, ev)
Ejemplo n.º 3
0
class WebTab(QWidget):

    class SavedTab(object):
        def __init__(self, webTab=None):
            self.title = ''
            self.url = QUrl()
            self.icon = QIcon()
            self.history = QByteArray()
            self.isPinned = False
            self.zoomLevel = 1
            self.parentTab = -1
            self.childTabs = []
            self.sessionData = {}
            if webTab:
                self.setWebTab(webTab)

        def __getstate__(self):
            result = dict(self.__dict__)
            result['url'] = result['url'].toEncoded()
            data = QByteArray()
            ds = QDataStream(data, QIODevice.WriteOnly)
            ds.writeQVariant(self.icon)
            result['icon'] = data.data()
            return result

        def __setstate__(self, state):
            for key, val in state.items():
                if key == 'url':
                    self.__dict__[key] = QUrl.fromEncoded(val)
                elif key == 'icon':
                    ds = QDataStream(QByteArray(val))
                    self.__dict__[key] = ds.readQVariant()
                else:
                    self.__dict__[key] = val

        def setWebTab(self, webTab):
            self.title = webTab.title()
            self.url = webTab.url()
            self.icon = webTab.icon()
            self.history = webTab.historyData()
            self.isPinned = webTab.isPinned()
            self.zoomLevel = webTab.zoomLevel()
            if webTab.parentTab():
                self.parentTab = webTab.parentTab().tabIndex()
            else:
                self.parentTab = -1
            self.childTabs = [ tab.tabIndex() for tab in webTab.childTabs() ]
            self.sessionData = webTab.sessionData()

        def isValid(self):
            return not self.url.isEmpty() or not self.history.isEmpty()

        def clear(self):
            self.title = ''
            self.url = QUrl()
            self.icon = QIcon()
            self.history = QByteArray()
            self.isPinned = False
            self.zoomLevel = 1
            self.parentTab = -1
            self.childTabs = []
            self.sessionData = {}

    # type AddChildBehavior
    AppendChild = 0
    PrependChild = 1

    s_addChildBehavior = AppendChild

    def __init__(self, parent=None):
        super(WebTab, self).__init__(parent)
        self.setObjectName('webtab')

        self._tabBar = None
        self._window = None
        self._parentTab = None
        self._childTabs = []
        self._sessionData = {}
        self._savedTab = self.SavedTab()
        self._isPinned = False
        self._isCurrentTab = False

        self._webView = TabbedWebView(self)
        self._webView.setPage(WebPage())
        self._webView.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
        self.setFocusProxy(self._webView)

        self._locationBar = LocationBar(self)
        self._locationBar.setWebView(self._webView)

        self._tabIcon = TabIcon(self)
        self._tabIcon.setWebTab(self)

        self._layout = QVBoxLayout(self)
        self._layout.setContentsMargins(0, 0, 0, 0)
        self._layout.setSpacing(0)
        self._layout.addWidget(self._webView)

        viewWidget = QWidget(self)
        viewWidget.setLayout(self._layout)

        self._splitter = QSplitter(Qt.Vertical, self)
        self._splitter.setChildrenCollapsible(False)
        self._splitter.addWidget(viewWidget)

        layout = QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.addWidget(self._splitter)
        self.setLayout(layout)

        self._notificationWidget = QWidget(self)
        self._notificationWidget.setAutoFillBackground(True)
        pal = self._notificationWidget.palette()
        pal.setColor(QPalette.Window, pal.window().color().darker(110))
        self._notificationWidget.setPalette(pal)

        nlayout = QVBoxLayout(self._notificationWidget)
        nlayout.setSizeConstraint(QLayout.SetMinAndMaxSize)
        nlayout.setContentsMargins(0, 0, 0, 0)
        nlayout.setSpacing(1)

        self._webView.showNotification.connect(self.showNotification)
        self._webView.loadFinished.connect(self.loadFinished)
        self._webView.titleChanged.connect(self.titleWasChanged)
        self._webView.titleChanged.connect(self.titleChanged)
        self._webView.iconChanged.connect(self.iconChanged)

        self._webView.backgroundActivityChanged.connect(self.backgroundActivityChanged)
        self._webView.loadStarted.connect(lambda: self.loadingChanged.emit(True))
        self._webView.loadFinished.connect(lambda: self.loadingChanged.emit(False))

        def pageChanged(page):
            page.audioMutedChanged.connect(self.playingChanged)
            page.recentlyAudibleChanged.connect(self.mutedChanged)

        pageChanged(self._webView.page())
        self._webView.pageChanged.connect(pageChanged)

        def tabIconResized():
            if self._tabBar:
                self._tabBar.update()
        self._tabIcon.resized.connect(tabIconResized)

    def browserWindow(self):
        '''
        @return BrowserWindow
        '''
        return self._window

    def webView(self):
        '''
        @return TabbedWebView
        '''
        return self._webView

    def locationBar(self):
        '''
        @return LocationBar
        '''
        return self._locationBar

    def tabIcon(self):
        '''
        @return TabIcon
        '''
        return self._tabIcon

    def parentTab(self):
        '''
        @return WebTab
        '''
        return self._parentTab

    def setParentTab(self, tab):
        if self._isPinned or self._parentTab == tab:
            return
        if tab and tab.isPinned():
            return

        if self._parentTab:
            index = self._parentTab._childTabs.index(self)
            if index >= 0:
                self._parentTab._childTabs.pop(index)
                self._parentTab.childTabRemoved.emit(self, index)
        self._parentTab = tab

        if tab:
            self._parentTab = None
            tab.addChildTab(self)
        else:
            self.parentTabChanged.emit(self._parentTab)

    def addChildTab(self, tab, index=-1):
        if self._isPinned or not tab or tab.isPinned():
            return

        oldParent = tab._parentTab
        tab._parentTab = self
        if oldParent:
            index = oldParent._childTabs.index(tab)
            if index >= 0:
                oldParent._childTabs.pop(index)
                oldParent.childTabRemoved.emit(tab, index)

        if index < 0 or index > len(self._childTabs):
            index = 0
            if self.addChildBehavior() == self.AppendChild:
                index = len(self._childTabs)
            else:  # PrependChild
                index = 0

        self._childTabs.insert(index, tab)
        self.childTabAdded.emit(tab, index)
        tab.parentTabChanged.emit(self)

    def childTabs(self):
        '''
        @return QVector<WebTab*>
        '''
        return self._childTabs

    def sessionData(self):
        '''
        @return {}
        '''
        return self._sessionData

    def setSessionData(self, key, value):
        self._sessionData[key] = value

    def url(self):
        if self.isRestored():
            if self._webView.url().isEmpty() and self._webView.isLoading():
                return self._webView.page().requestedUrl()
            return self._webView.url()
        else:
            return self._savedTab.url

    def title(self, allowEmpty=False):
        if self.isRestored():
            return self._webView.title(allowEmpty)
        else:
            return self._savedTab.title

    def icon(self, allowNull=False):
        if self.isRestored():
            return self._webView.icon(allowNull)
        if allowNull or not self._savedTab.icon.isNull():
            return self._savedTab.icon
        return IconProvider.emptyWebIcon()

    def history(self):
        '''
        @return QWebEngineHistory
        '''
        return self._webView.history()

    def zoomLevel(self):
        return self._webView.zoomLevel()

    def setZoomLevel(self, level):
        self._webView.setZoomLevel(level)

    def detach(self):
        assert(self._window)
        assert(self._tabBar)

        # Remove from tab tree
        self.removeFromTabTree()

        # Remove icon from tab
        self._tabBar.setTabButton(self.tabIndex(), self._tabBar.iconButtonPosition(), None)
        self._tabIcon.setParent(self)

        # Remove the tab from tabbar
        self._window.tabWidget().removeTab(self.tabIndex())
        self.setParent(None)
        # Remove the locationbar from window
        self._locationBar.setParent(self)
        # Detach TabbedWindow
        self._webView.setBrowserWindow(None)

        if self._isCurrentTab:
            self._isCurrentTab = False
            self.currentTabChanged.emit(self._isCurrentTab)

        self._tabBar.currentChanged.disconnect(self.onCurrentChanged)

        self._window = None
        self._tabBar = None

    def onCurrentChanged(self, index):
        wasCurrent = self._isCurrentTab
        self._isCurrentTab = index == self.tabIndex()
        if wasCurrent != self._isCurrentTab:
            self.currentTabChanged.emit(self._isCurrentTab)

    def attach(self, window):
        self._window = window
        self._tabBar = self._window.tabWidget().tabBar()

        self._webView.setBrowserWindow(self._window)
        self._locationBar.setBrowserWindow(self._window)
        self._tabBar.setTabText(self.tabIndex(), self.title())
        self._tabBar.setTabButton(self.tabIndex(), self._tabBar.iconButtonPosition(), self._tabIcon)
        QTimer.singleShot(0, self._tabIcon.updateIcon)

        self.onCurrentChanged(self._tabBar.currentIndex())
        self._tabBar.currentChanged.connect(self.onCurrentChanged)

    def historyData(self):
        '''
        @return QByteArray
        '''
        if self.isRestored():
            historyArray = QByteArray()
            stream = QDataStream(historyArray, QIODevice.WriteOnly)
            history = self._webView.history()
            stream << history
            return historyArray
        else:
            return self._savedTab.history

    def stop(self):
        self._webView.stop()

    def reload(self):
        self._webView.reload()

    def load(self, request):
        '''
        @param: requset LoadRequest
        '''
        if self.isRestored():
            self.tabActivated()
            QTimer.singleShot(0, lambda: self.load(request))
        else:
            self._webView.load(request)

    def unload(self):
        self._savedTab = self.SavedTab(self)
        self.restoredChanged.emit(self.isRestored())
        self._webView.setPage(WebPage())
        self._webView.setFocus()

    def isLoading(self):
        return self._webView.isloading()

    def isPinned(self):
        return self._isPinned

    def setPinned(self, state):
        if self._isPinned == state:
            return
        if state:
            self.removeFromTabTree()
        self._isPinned = state
        self.pinnedChanged.emit(self._isPinned)

    def togglePinned(self):
        assert(self._tabBar)
        assert(self._window)
        self.setPinned(not self.isPinned())
        self._window.tabWidget().pinUnPinTab(self.tabIndex(), self.title())

    def isMuted(self):
        return self._webView.page().isAudioMuted()

    def isPlaying(self):
        return self._webView.page().recentlyAudible()

    def setMuted(self, muted):
        self._webView.page().setAudioMuted(muted)

    def toggleMuted(self):
        self.setMuted(not self.isMuted())

    def backgroundActivity(self):
        return self._webView.backgroundActivity()

    def tabIndex(self):
        index = -1
        if self._tabBar:
            index = self._tabBar.tabWidget().indexOf(self)
        return index

    def isCurrentTab(self):
        return self._isCurrentTab

    def makeCurrentTab(self):
        if self._tabBar:
            self._tabBar.tabWidget().setCurrentIndex(self.tabIndex())

    def closeTab(self):
        if self._tabBar:
            self._tabBar.tabWidget().closeTab(self.tabIndex())

    def moveTab(self, to):
        if self._tabBar:
            self._tabBar.tabWidget().moveTab(self.tabIndex(), to)

    def haveInspector(self):
        return self._splitter.count() > 1 and self._splitter.widget(1).inherits('WebInspector')

    def showWebInspector(self, inspectElement=False):
        if not WebInspector.isEnabled() or self.haveInspector():
            return

        inspector = WebInspector(self)
        inspector.setView(self._webView)
        if inspectElement:
            inspector.inspectElement()

        height = inspector.sizeHint().height()
        self._splitter.addWidget(inspector)
        self._splitter.setSizes((self._splitter.height() - height, height))

    def toggleWebInspector(self):
        if not self.haveInspector():
            self.showWebInspector()
        else:
            self._splitter.widget(1).destroy()  # TODO: del?

    def showSearchToolBar(self, searchText=''):
        index = 1
        toolBar = None
        if self._layout.count() == 1:
            toolBar = SearchToolBar(self._webView, self)
            self._layout.insertWidget(index, toolBar)
        if self._layout.count() == 2:
            assert(isinstance(self._layout.itemAt(index).widget(), SearchToolBar))
            toolBar = self._layout.itemAt(index).widget()
        assert(toolBar)
        if not searchText:
            toolBar.setText(searchText)
        toolBar.focusSearchLine()

    def isRestored(self):
        return not self._savedTab.isValid()

    def restoreTab(self, tab):
        '''
        @param: tab SavedTab
        '''
        assert(self._tabBar)
        self.setPinned(tab.isPinned)
        self._sessionData = tab.sessionData

        if not self.isPinned() and gVar.appSettings.loadTabsOnActivation:
            self._savedTab = tab
            self.restoredChanged.emit(self.isRestored())
            index = self.tabIndex()

            self._tabBar.setTabText(index, tab.title)
            self._locationBar.showUrl(tab.url)
            self._tabIcon.updateIcon()
        else:
            # This is called only on restore session and restoring tabs
            # immediately crashes QtWebEngine, waiting after initialization is
            # complete fixes it
            QTimer.singleShot(1000, lambda: self.p_restoreTab(tab))

    def p_restoreTab(self, tab):
        '''
        @param: tab SavedTab
        '''
        self.p_restoreTabByUrl(tab.url, tab.history, tab.zoomLevel)

    def p_restoreTabByUrl(self, url, history, zoomLevel):
        self._webView.load(url)

        # Restoring history of internal pages crashes QtWebEngine 5.8
        blacklistedSchemes = ['view-source', 'chrome']

        if (url.scheme() not in blacklistedSchemes):
            stream = QDataStream(history)
            stream >> self._webView.history()

        self._webView.setZoomLevel(zoomLevel)
        self._webView.setFocus()

    def tabActivated(self):
        if self.isRestored():
            return

        def _onTabActivated():
            if self.isRestored():
                return
            self.p_restoreTab(self._savedTab)
            self._savedTab.clear()
            self.restoredChanged.emit(self.isRestored())

        QTimer.singleShot(0, _onTabActivated)

    def addChildBehavior(self):
        '''
        @return AddChildBehavior
        '''
        return self.s_addChildBehavior

    def setAddChildBehavior(self, behavior):
        self.s_addChildBehavior = behavior

    # Q_SLOTS
    @pyqtSlot(QWidget)
    def showNotification(self, notif):
        self._notificationWidget.setParent(self)
        self._notificationWidget.raise_()
        self._notificationWidget.setFixedWidth(self.width())
        self._notificationWidget.layout().addWidget(notif)
        self._notificationWidget.show()
        notif.show()

    @pyqtSlot()
    def loadFinished(self):
        self.titleWasChanged(self._webView.title())

    # Q_SIGNALS
    titleChanged = pyqtSignal(str) # title
    iconChanged = pyqtSignal(QIcon) # icon
    pinnedChanged = pyqtSignal(bool) # pinned
    restoredChanged = pyqtSignal(bool) # restored
    currentTabChanged = pyqtSignal(bool) # current
    loadingChanged = pyqtSignal(bool) # loading
    mutedChanged = pyqtSignal(bool) # muted
    playingChanged = pyqtSignal(bool) # playing
    backgroundActivityChanged = pyqtSignal(bool) # activity
    parentTabChanged = pyqtSignal('PyQt_PyObject') # WebTab*
    childTabAdded = pyqtSignal('PyQt_PyObject', int) # WebTab*, index
    childTabRemoved = pyqtSignal('PyQt_PyObject', int) # WebTab*, index

    def titleWasChanged(self, title):
        if not self._tabBar or not self._window or not title:
            return
        if self._isCurrentTab:
            self._window.setWindowTitle('%s - Demo' % title)
        self._tabBar.setTabText(self.tabIndex(), title)

    # override
    def resizeEvent(self, event):
        QWidget.resizeEvent(self, event)
        self._notificationWidget.setFixedWidth(self.width())

    def removeFromTabTree(self):
        parentTab = self._parentTab
        parentIndex = -1
        if parentTab:
            parentIndex = parentTab._childTabs.index(self)
        self.setParentTab(None)

        idx = 0
        while self._childTabs:
            child = self._childTabs[0]
            child.setParentTab(None)
            if parentTab:
                parentTab.addChildTab(child, parentIndex + idx)
                idx += 1