# This file is a part of Lector, a Qt based ebook reader
# Copyright (C) 2017-2019 BasioMeusPuga

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

# TODO
# Reading modes
# Double page, Continuous etc

import os
import logging

from PyQt5 import QtWidgets, QtGui, QtCore

from lector.sorter import resize_image
from lector.dockwidgets import PliantDockWidget, PliantNavBarWidget
from lector.contentwidgets import PliantQGraphicsView, PliantQTextBrowser

logger = logging.getLogger(__name__)


class Tab(QtWidgets.QWidget):
    def __init__(self, metadata, main_window, parent=None):
        super(Tab, self).__init__(parent)
        self._translate = QtCore.QCoreApplication.translate

        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)

        self.main_window = main_window
        self.metadata = metadata  # Save progress data into this dictionary
        self.are_we_doing_images_only = self.metadata['images_only']
        self.is_fullscreen = False
        self.is_library = False

        self.masterLayout = QtWidgets.QHBoxLayout(self)
        self.masterLayout.setContentsMargins(0, 0, 0, 0)

        self.metadata['last_accessed'] = QtCore.QDateTime().currentDateTime()

        # Create relevant containers
        if not self.metadata['annotations']:
            self.metadata['annotations'] = {}
        if not self.metadata['bookmarks']:
            self.metadata['bookmarks'] = {}

        # Generate toc Model
        self.tocModel = QtGui.QStandardItemModel()
        self.tocModel.setHorizontalHeaderLabels(('Table of Contents',))
        self.generate_toc_model()

        # Get the current position of the book
        if self.metadata['position']:
            # A book might have been marked read without being opened
            if self.metadata['position']['is_read']:
                self.generate_position(True)
            current_chapter = self.metadata['position']['current_chapter']
        else:
            self.generate_position()
            current_chapter = 1

        # The content display widget is, by default a QTextBrowser.
        # In case the incoming data is only images
        # such as in the case of comic book files,
        # we want a QGraphicsView widget doing all the heavy lifting
        # instead of a QTextBrowser

        if self.are_we_doing_images_only:
            self.contentView = PliantQGraphicsView(
                self.metadata['path'], self.main_window, self)
            self.image_rotation = 0

        else:
            self.contentView = PliantQTextBrowser(
                self.main_window, self)
            self.contentView.setReadOnly(True)

            # TODO
            # Change this when HTML navigation works
            self.contentView.setOpenLinks(False)

            # TODO
            # Rename the .css files to something else here and keep
            # a record of them .Currently, I'm just removing them
            # for the sake of simplicity
            relative_path_root = os.path.join(
                self.main_window.temp_dir.path(), self.metadata['hash'])
            relative_paths = []
            for i in os.walk(relative_path_root):
                for j in i[2]:
                    file_extension = os.path.splitext(j)[1]
                    if file_extension == '.css':
                        file_path = os.path.join(i[0], j)
                        os.remove(file_path)
                relative_paths.append(os.path.join(relative_path_root, i[0]))
            self.contentView.setSearchPaths(relative_paths)

            self.hiddenButton = QtWidgets.QToolButton(self)
            self.hiddenButton.setVisible(False)
            self.hiddenButton.clicked.connect(self.set_cursor_position)

        # All content must be set through this function
        self.set_content(current_chapter, True, False)
        if not self.are_we_doing_images_only:
            # Setting this later breaks cursor positioning for search results
            self.hiddenButton.animateClick(50)

        # Load annotations for current content
        self.contentView.common_functions.load_annotations(current_chapter)

        # The following are common to both the text browser and
        # the graphics view
        self.contentView.setFrameShape(QtWidgets.QFrame.NoFrame)
        self.contentView.setObjectName('contentView')
        self.contentView.verticalScrollBar().setSingleStep(
            self.main_window.settings['scroll_speed'])

        if self.main_window.settings['hide_scrollbars']:
            self.contentView.setHorizontalScrollBarPolicy(
                QtCore.Qt.ScrollBarAlwaysOff)
            self.contentView.setVerticalScrollBarPolicy(
                QtCore.Qt.ScrollBarAlwaysOff)
        else:
            self.contentView.setHorizontalScrollBarPolicy(
                QtCore.Qt.ScrollBarAsNeeded)
            self.contentView.setVerticalScrollBarPolicy(
                QtCore.Qt.ScrollBarAsNeeded)

        # Create a NavBar widget
        if self.main_window.settings['nav_bar']:
            self.navBar = PliantNavBarWidget(
                self.main_window, self.contentView, self)
            self.navBar.setFloating(True)
            self.navBar.setTitleBarWidget(QtWidgets.QWidget(self))
            self.navBar.hide()
        else:
            # This keeps from having to set visibility conditions
            # everywhere
            self.navBar = QtWidgets.QWidget()
            self.navBar.setFixedSize(0, 0)

        # Create a common dock for bookmarks, annotations, and search
        self.sideDock = PliantDockWidget(
            self.main_window, False, self.contentView, self)
        self.sideDock.populate()

        # Create the annotation notes dock
        self.annotationNoteDock = PliantDockWidget(
            self.main_window, True, self.contentView, self)
        self.annotationNoteDock.setWindowTitle(self._translate('Tab', 'Note'))
        self.annotationNoteDock.setFeatures(QtWidgets.QDockWidget.DockWidgetClosable)
        self.annotationNoteDock.hide()

        self.annotationNoteEdit = QtWidgets.QTextEdit(self.annotationNoteDock)
        self.annotationNoteEdit.setMaximumSize(QtCore.QSize(250, 250))
        self.annotationNoteEdit.setFocusPolicy(QtCore.Qt.StrongFocus)
        self.annotationNoteDock.setWidget(self.annotationNoteEdit)

        self.generate_keyboard_shortcuts()

        self.masterLayout.addWidget(self.contentView)
        self.masterLayout.addWidget(self.sideDock)
        self.masterLayout.addWidget(self.annotationNoteDock)

        # The following has to be after the docks are added to the layout
        self.sideDock.setFloating(True)
        self.sideDock.setWindowOpacity(.95)
        self.annotationNoteDock.setFloating(True)
        self.annotationNoteDock.setWindowOpacity(.95)
        self.sideDock.hide()

        # Create tab in the central tab widget
        title = self.metadata['title']
        if self.main_window.settings['attenuate_titles'] and len(title) > 30:
            title = title[:30] + '...'
        self.main_window.tabWidget.addTab(self, title)

        this_tab_index = self.main_window.tabWidget.indexOf(self)
        cover_icon = QtGui.QPixmap()
        cover_icon.loadFromData(self.metadata['cover'])
        self.main_window.tabWidget.setTabIcon(
            this_tab_index, QtGui.QIcon(cover_icon))

        # Hide mouse cursor timer
        self.mouseHideTimer = QtCore.QTimer()
        self.mouseHideTimer.setSingleShot(True)
        self.mouseHideTimer.timeout.connect(self.hide_mouse)

        # Hide the tab bar in case distraction free mode is active
        if not self.main_window.settings['show_bars']:
            self.main_window.tabWidget.tabBar().setVisible(False)

        self.contentView.setFocus()

    def toggle_side_dock(self, tab_required, override_hide=False):
        if (self.sideDock.isVisible()
                and self.sideDock.sideDockTabWidget.currentIndex() == tab_required
                and not override_hide):
            self.sideDock.hide()
        elif not self.sideDock.isVisible():
            self.sideDock.show()
            if tab_required == 2:
                self.sideDock.activateWindow()
                self.sideDock.search.searchLineEdit.setFocus()
                self.sideDock.search.searchLineEdit.selectAll()

        self.sideDock.sideDockTabWidget.setCurrentIndex(tab_required)

    def generate_rotation(self, delta_angle):
        self.image_rotation += delta_angle

        # Set bounds for rotation angle
        if self.image_rotation == 360:
            self.image_rotation = 0
        if self.image_rotation == -90:
            self.image_rotation = 270

    def update_last_accessed_time(self):
        self.metadata['last_accessed'] = QtCore.QDateTime().currentDateTime()

        start_index = self.main_window.lib_ref.libraryModel.index(0, 0)
        matching_item = self.main_window.lib_ref.libraryModel.match(
            start_index,
            QtCore.Qt.UserRole + 6,
            self.metadata['hash'],
            1, QtCore.Qt.MatchExactly)

        try:
            self.main_window.lib_ref.libraryModel.setData(
                matching_item[0],
                self.metadata['last_accessed'], QtCore.Qt.UserRole + 12)
        except IndexError:  # The file has been deleted
            pass

    def set_cursor_position(self, cursor_position=None, select_chars=0):
        try:
            required_position = self.metadata['position']['cursor_position']
        except KeyError:
            logging.error('Database: Cursor position error. Recommend retry.')
            return

        if cursor_position:
            required_position = cursor_position

        # This is needed so that the line we want is
        # always at the top of the window
        self.contentView.verticalScrollBar().setValue(
            self.contentView.verticalScrollBar().maximum())

        # textCursor() RETURNS a copy of the textcursor
        cursor = self.contentView.textCursor()
        cursor.setPosition(
            required_position - select_chars,
            QtGui.QTextCursor.MoveAnchor)
        if select_chars > 0:  # Select search results
            cursor.movePosition(
                QtGui.QTextCursor.NextCharacter,
                QtGui.QTextCursor.KeepAnchor,
                select_chars)
        self.contentView.setTextCursor(cursor)
        self.contentView.ensureCursorVisible()

        # Finally, to make sure the cover image isn't
        # scrolled halfway through on first open,
        if self.metadata['cover'] and self.metadata['position']['current_chapter'] == 1:
            self.contentView.verticalScrollBar().setValue(0)

    def generate_position(self, is_read=False):
        total_chapters = len(self.metadata['content'])

        current_chapter = 1
        if is_read:
            current_chapter = total_chapters

        # Generate block count @ time of first read
        # Blocks are indexed from 0 up
        blocks_per_chapter = []
        total_blocks = 0

        if not self.are_we_doing_images_only:
            for i in self.metadata['content']:
                textDocument = QtGui.QTextDocument(None)
                textDocument.setHtml(i)
                block_count = textDocument.blockCount()

                blocks_per_chapter.append(block_count)
                total_blocks += block_count

        self.metadata['position'] = {
            'current_chapter': current_chapter,
            'total_chapters': total_chapters,
            'blocks_per_chapter': blocks_per_chapter,
            'total_blocks': total_blocks,
            'is_read': is_read,
            'current_block': 0,
            'cursor_position': 0}

    def generate_keyboard_shortcuts(self):
        ksNextChapter = QtWidgets.QShortcut(
            QtGui.QKeySequence('Right'), self.contentView)
        ksNextChapter.setObjectName('nextChapter')
        ksNextChapter.activated.connect(self.sneaky_change)

        ksPrevChapter = QtWidgets.QShortcut(
            QtGui.QKeySequence('Left'), self.contentView)
        ksPrevChapter.setObjectName('prevChapter')
        ksPrevChapter.activated.connect(self.sneaky_change)

        ksGoFullscreen = QtWidgets.QShortcut(
            QtGui.QKeySequence('F'), self.contentView)
        ksGoFullscreen.activated.connect(self.go_fullscreen)

        ksExitFullscreen = QtWidgets.QShortcut(
            QtGui.QKeySequence('Escape'), self.contentView)
        ksExitFullscreen.setContext(QtCore.Qt.ApplicationShortcut)
        ksExitFullscreen.activated.connect(self.exit_fullscreen)

        ksToggleBookmarks = QtWidgets.QShortcut(
            QtGui.QKeySequence('Ctrl+B'), self.contentView)
        ksToggleBookmarks.activated.connect(
            lambda: self.toggle_side_dock(0))

        # Shortcuts not required for comic view functionality
        if not self.are_we_doing_images_only:
            ksToggleAnnotations = QtWidgets.QShortcut(
                QtGui.QKeySequence('Ctrl+N'), self.contentView)
            ksToggleAnnotations.activated.connect(
                lambda: self.toggle_side_dock(1))

            ksToggleSearch = QtWidgets.QShortcut(
                QtGui.QKeySequence('Ctrl+F'), self.contentView)
            ksToggleSearch.activated.connect(
                lambda: self.toggle_side_dock(2))

    def generate_toc_model(self):
        # The toc list is:
        # 0: Level
        # 1: Title
        # 2: Chapter content / page number
        # pprint it out to get a better idea of structure

        toc = self.metadata['toc']
        parent_list = []
        for i in toc:
            item = QtGui.QStandardItem()
            item.setText(i[1])
            item.setData(i[2], QtCore.Qt.UserRole)
            item.setData(i[1], QtCore.Qt.UserRole + 1)

            current_level = i[0]
            if current_level == 1:
                self.tocModel.appendRow(item)
                parent_list.clear()
                parent_list.append(item)
            else:
                parent_list[current_level - 2].appendRow(item)
                try:
                    next_level = toc[toc.index(i) + 1][0]
                    if next_level > current_level:
                        parent_list.append(item)

                    if next_level < current_level:
                        level_difference = current_level - next_level
                        parent_list = parent_list[:-level_difference]
                except IndexError:
                    pass

        # This is needed to be able to have the toc Combobox
        # jump to the correct position in the book when it is
        # first opened
        self.main_window.bookToolBar.tocBox.setModel(self.tocModel)
        self.main_window.bookToolBar.tocTreeView.expandAll()

    def go_fullscreen(self):
        # To allow toggles to function
        # properly after the fullscreening

        self.sideDock.hide()
        self.annotationNoteDock.hide()

        if self.contentView.windowState() == QtCore.Qt.WindowFullScreen:
            self.exit_fullscreen()
            return

        if not self.are_we_doing_images_only:
            self.contentView.record_position()

        self.contentView.setWindowFlags(QtCore.Qt.Window)
        self.contentView.setWindowState(QtCore.Qt.WindowFullScreen)
        self.contentView.show()
        self.main_window.hide()

        if not self.are_we_doing_images_only:
            self.hiddenButton.animateClick(50)

        self.mouseHideTimer.start(2000)
        self.is_fullscreen = True

    def exit_fullscreen(self):
        # Intercept escape presses
        for i in (self.annotationNoteDock, self.sideDock):
            if i.isVisible():
                i.setVisible(False)
                return

        # Prevents cursor position change on escape presses
        if self.main_window.isVisible():
            return

        if not self.are_we_doing_images_only:
            self.contentView.record_position()

        self.main_window.show()
        self.contentView.setWindowFlags(QtCore.Qt.Widget)
        self.contentView.setWindowState(QtCore.Qt.WindowNoState)
        self.contentView.show()
        self.is_fullscreen = False

        if not self.are_we_doing_images_only:
            self.hiddenButton.animateClick(100)

        # Hide the view modification buttons in case they're visible
        self.main_window.bookToolBar.customize_view_off()

        # Exit distraction free mode too
        if not self.main_window.settings['show_bars']:
            self.main_window.toggle_distraction_free()

        self.navBar.hide()
        self.mouseHideTimer.start(2000)
        self.contentView.setFocus()

    def set_content(self, required_position, tocBox_readjust=False, record_position=False):
        # All content changes must come through here
        # This function will decide how to relate
        # entries in the toc to the actual content

        # Set the required page to the corresponding index
        # For images this is simply a page number
        # For text based books, this is the entire text of the chapter
        try:
            required_content = self.metadata['content'][required_position - 1]
        except IndexError:
            return  # Do not allow cycling beyond last page

        # Update the metadata dictionary to save position
        self.metadata['position']['current_chapter'] = required_position
        self.metadata['position']['is_read'] = False
        if record_position:
            self.contentView.record_position()

        if self.are_we_doing_images_only:
            self.contentView.loadImage(required_content)
        else:
            self.contentView.clear()
            self.contentView.setHtml(required_content)

        # Set the contentview to look the way God intended
        self.main_window.profile_functions.format_contentView()
        self.contentView.common_functions.load_annotations(required_position)

        # Change the index of the tocBox. This is manual and each function
        # that calls set_position must specify if it needs this adjustment
        if tocBox_readjust:
            self.set_tocBox_index(required_position, None)

            # The NavBar doesn't get declared until later
            try:
                self.set_tocBox_index(
                    required_position, self.navBar.tocComboBox)
            except AttributeError:
                pass

        self.contentView.setFocus()

    def set_tocBox_index(self, current_position=None, tocBox=None):
        # Get current position from the metadata dictionary
        # in case it isn't specified
        if not current_position:
            current_position = self.metadata['position']['current_chapter']

        position_reference = 1
        for i in reversed(self.metadata['toc']):
            if i[2] <= current_position:
                position_reference = i[2]
                break

        # Match the position reference to the corresponding
        # index in the QTreeView / QCombobox
        try:
            matchingIndex = self.tocModel.match(
                self.tocModel.index(0, 0),
                QtCore.Qt.UserRole,
                position_reference,
                2, QtCore.Qt.MatchRecursive)[0]
        except IndexError:
            return

        # A tocBox name is specified for the context menu
        if not tocBox:
            tocBox = self.main_window.bookToolBar.tocBox

        # The following sets the QCombobox index according
        # to the index found above.
        tocBox.blockSignals(True)
        currentRootModelIndex = tocBox.rootModelIndex()
        tocBox.setRootModelIndex(matchingIndex.parent())
        tocBox.setCurrentIndex(matchingIndex.row())
        tocBox.setRootModelIndex(currentRootModelIndex)
        tocBox.blockSignals(False)

    def format_view(
            self, font, font_size, foreground,
            background, padding, line_spacing,
            text_alignment):

        if self.are_we_doing_images_only:
            # Tab color does not need to be set separately in case
            # no padding is set for the viewport of a QGraphicsView
            # and image resizing in done in the pixmap
            my_qbrush = QtGui.QBrush(QtCore.Qt.SolidPattern)
            my_qbrush.setColor(background)
            self.contentView.setBackgroundBrush(my_qbrush)
            self.contentView.resizeEvent()

        else:
            self.contentView.setStyleSheet(
                "QTextEdit {{font-family: {0}; font-size: {1}px; color: {2}; background-color: {3}}}".format(
                    font, font_size, foreground.name(), background.name()))

            # Line spacing
            # Set line spacing per a block format
            # This is proportional line spacing so assume a divisor of 100
            block_format = QtGui.QTextBlockFormat()
            block_format.setLineHeight(
                line_spacing, QtGui.QTextBlockFormat.ProportionalHeight)

            block_format.setTextIndent(50)

            # Give options for alignment
            alignment_dict = {
                'left': QtCore.Qt.AlignLeft,
                'right': QtCore.Qt.AlignRight,
                'center': QtCore.Qt.AlignCenter,
                'justify': QtCore.Qt.AlignJustify}

            # Also for padding
            # Using setViewPortMargins for this disables scrolling in the margins
            block_format.setLeftMargin(padding)
            block_format.setRightMargin(padding)

            this_cursor = self.contentView.textCursor()
            this_cursor.setPosition(QtGui.QTextCursor.Start)

            while True:
                # So this fixes the stupid repetitive iteration
                # I was doing over the entire thing
                # It also allows for all images to be center aligned.
                # Magic. *jazz hands*
                block_text = this_cursor.block().text().strip()
                try:
                    # Object replacement char - Seems to work with images
                    if ord(block_text) == 65532:
                        block_format.setAlignment(
                            QtCore.Qt.AlignVCenter | QtCore.Qt.AlignHCenter)
                    else:
                        raise TypeError
                except TypeError:
                    block_format.setAlignment(alignment_dict[text_alignment])

                # Iterate over the entire document block by block
                # The document ends when the cursor position can no longer be incremented
                old_position = this_cursor.position()
                this_cursor.mergeBlockFormat(block_format)
                this_cursor.movePosition(
                    QtGui.QTextCursor.NextBlock, QtGui.QTextCursor.MoveAnchor)

                new_position = this_cursor.position()
                if old_position == new_position:
                    break

    def hide_mouse(self):
        self.contentView.viewport().setCursor(QtCore.Qt.BlankCursor)

        if self.contentView.hasFocus():
            self.navBar.hide()

    def sneaky_change(self):
        direction = -1
        if self.sender().objectName() == 'nextChapter':
            direction = 1

        self.contentView.common_functions.change_chapter(
            direction, True)

    def sneaky_exit(self):
        self.contentView.hide()
        self.main_window.closeEvent()


class PliantQGraphicsScene(QtWidgets.QGraphicsScene):
    def __init__(self, parent=None):
        super(PliantQGraphicsScene, self).__init__(parent)
        self.parent = parent
        self._translate = QtCore.QCoreApplication.translate

    def mouseReleaseEvent(self, event):
        self.parent.previous_position = self.parent.pos()

        image_files = '*.jpg *.png'
        dialog_prompt = self._translate('PliantQGraphicsScene', 'Select new cover')
        images_string = self._translate('PliantQGraphicsScene', 'Images')
        new_cover = QtWidgets.QFileDialog.getOpenFileName(
            None, dialog_prompt, self.parent.parent.settings['last_open_path'],
            f'{images_string} ({image_files})')[0]

        if not new_cover:
            self.parent.show()
            return

        with open(new_cover, 'rb') as cover_ref:
            cover_bytes = cover_ref.read()
            resized_cover = resize_image(cover_bytes)
            self.parent.cover_for_database = resized_cover

        cover_pixmap = QtGui.QPixmap()
        cover_pixmap.load(new_cover)
        cover_pixmap = cover_pixmap.scaled(
            140, 205, QtCore.Qt.IgnoreAspectRatio)

        self.parent.load_cover(cover_pixmap, True)
        self.parent.show()


class DragDropListView(QtWidgets.QListView):
    # This is the library listview
    def __init__(self, main_window, parent):
        super(DragDropListView, self).__init__(parent)
        self.main_window = main_window
        self.setAcceptDrops(True)
        self.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
        self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
        self.setResizeMode(QtWidgets.QListView.Fixed)
        self.setLayoutMode(QtWidgets.QListView.SinglePass)
        self.setViewMode(QtWidgets.QListView.IconMode)
        self.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
        self.setProperty("showDropIndicator", False)
        self.setProperty("isWrapping", True)
        self.setFrameShape(QtWidgets.QFrame.NoFrame)
        self.setUniformItemSizes(True)
        self.setWordWrap(True)
        self.setObjectName("listView")

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls():
            event.acceptProposedAction()
        else:
            super(DragDropListView, self).dragEnterEvent(event)

    def dragMoveEvent(self, event):
        super(DragDropListView, self).dragMoveEvent(event)

    def dropEvent(self, event):
        if event.mimeData().hasUrls():
            file_list = [url.path() for url in event.mimeData().urls()]
            self.main_window.process_post_hoc_files(file_list, False)
            event.acceptProposedAction()
        else:
            super(DragDropListView, self).dropEvent(event)


class DragDropTableView(QtWidgets.QTableView):
    # This is the library tableview
    def __init__(self, main_window, parent):
        super(DragDropTableView, self).__init__(parent)
        self.main_window = main_window
        self.setAcceptDrops(True)
        self.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
        self.setFrameShape(QtWidgets.QFrame.Box)
        self.setFrameShadow(QtWidgets.QFrame.Plain)
        self.setSizeAdjustPolicy(
            QtWidgets.QAbstractScrollArea.AdjustToContentsOnFirstShow)
        self.setEditTriggers(
            QtWidgets.QAbstractItemView.DoubleClicked |
            QtWidgets.QAbstractItemView.EditKeyPressed |
            QtWidgets.QAbstractItemView.SelectedClicked)
        self.setAlternatingRowColors(True)
        self.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
        self.setGridStyle(QtCore.Qt.NoPen)
        self.setSortingEnabled(True)
        self.setWordWrap(False)
        self.setObjectName("tableView")
        self.horizontalHeader().setVisible(True)
        self.verticalHeader().setVisible(False)

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls():
            event.acceptProposedAction()
        else:
            super(DragDropTableView, self).dragEnterEvent(event)

    def dragMoveEvent(self, event):
        super(DragDropTableView, self).dragMoveEvent(event)

    def dropEvent(self, event):
        if event.mimeData().hasUrls():
            file_list = [url.path() for url in event.mimeData().urls()]
            self.main_window.process_post_hoc_files(file_list, False)
            event.acceptProposedAction()
        else:
            super(DragDropTableView, self).dropEvent(event)


class SaysHelloWhenClicked(QtWidgets.QListView):
    # Signal declarations must be outside the constructor
    # The argument is the type of the data emitted
    newIndexSignal = QtCore.pyqtSignal(QtCore.QModelIndex)

    def __init__(self, parent):
        super(SaysHelloWhenClicked, self).__init__(parent)
        self.parent = parent

    def currentChanged(self, index, previous_index):
        if not index.isValid():
            return

        self.newIndexSignal.emit(index)