# http://pyrocko.org - GPLv3
#
# The Pyrocko Developers, 21st Century
# ---|P------/S----------~Lg----------
from __future__ import absolute_import

import sys

from .qt_compat import qc, qg, qw, QSortFilterProxyModel, \
    QItemSelectionModel, QItemSelection, QPixmapCache, use_pyqt5

from .util import EventMarker, PhaseMarker, make_QPolygonF
from pyrocko.plot.beachball import mt2beachball, BeachballError
from pyrocko.moment_tensor import kagan_angle
from pyrocko.plot import tango_colors
from pyrocko import orthodrome

import numpy as num
import logging


def noop(x=None):
    return x


if sys.version_info[0] >= 3 or use_pyqt5:
    qc.QString = str
    qc.QVariant = noop

    def toDateTime(val):
        return val

    def isDateTime(val):
        return isinstance(val, qc.QDateTime)

    def toFloat(val):
        try:
            return float(val), True
        except ValueError:
            return 9e99, False

    def toString(val):
        return str(val).encode('utf-8').decode()

else:
    def toDateTime(val):
        return val.toDateTime()

    def isDateTime(val):
        return val.type() == qc.QVariant.DateTime

    def toFloat(val):
        return val.toFloat()

    def toString(val):
        return str(val).encode('utf-8')


logger = logging.getLogger('pyrocko.gui.marker_editor')

_header_data = [
    'T', 'Time', 'M', 'Label', 'Depth [km]', 'Lat', 'Lon', 'Kind', 'Dist [km]',
    'NSLCs', 'Polarity', 'Kagan Angle [deg]', 'Event Hash', 'MT']

_column_mapping = dict(zip(_header_data, range(len(_header_data))))

_string_header = (_column_mapping['Time'], _column_mapping['Label'])

_header_sizes = [70] * len(_header_data)
_header_sizes[0] = 40
_header_sizes[1] = 190
_header_sizes[-1] = 20


class BeachballWidget(qw.QWidget):

    def __init__(self, moment_tensor, color, *args, **kwargs):
        qw.QWidget.__init__(self, *args, **kwargs)
        self.color = color
        self.moment_tensor = moment_tensor
        self.setGeometry(0, 0, 100, 100)
        self.setAttribute(qc.Qt.WA_TranslucentBackground)

        self.flipy = qg.QTransform()
        self.flipy.translate(0, self.height())
        self.flipy.scale(1, -1)

    def paintEvent(self, e):
        center = e.rect().center()
        painter = qg.QPainter(self)
        painter.save()
        painter.setWorldTransform(self.flipy)
        try:
            data = mt2beachball(
                self.moment_tensor, size=self.height()/2.2,
                position=(center.x(), center.y()),
                color_t=self.color, color_p=qc.Qt.white, edgecolor=qc.Qt.black)
            for pdata in data:
                paths, fill, edges, thickness = pdata

                pen = qg.QPen(edges)
                pen.setWidthF(3)
                if fill != 'none':
                    brush = qg.QBrush(fill)
                    painter.setBrush(brush)

                polygon = qg.QPolygonF()
                polygon = make_QPolygonF(*paths.T)
                painter.setRenderHint(qg.QPainter.Antialiasing)
                painter.setPen(pen)
                painter.drawPolygon(polygon)
        except BeachballError as e:
            logger.exception(e)
        finally:
            painter.restore()

    def to_qpixmap(self):
        try:
            return self.grab(self.rect())
        except AttributeError:
            return qg.QPixmap().grabWidget(self, self.rect())


class MarkerItemDelegate(qw.QStyledItemDelegate):
    '''Takes care of the table's style.'''

    def __init__(self, *args, **kwargs):
        qw.QStyledItemDelegate.__init__(self, *args, **kwargs)
        self.c_alignment = qc.Qt.AlignHCenter
        self.bbcache = QPixmapCache()

    def paint(self, painter, option, index):
        iactive = self.parent().active_event_index
        mcolor = self.color_from_index(index)

        if index.column() == _column_mapping['MT']:
            mt = self.bb_data_from_index(index)
            if mt:
                key = ''.join([str(round(x, 1)) for x in mt.m6()])
                pixmap = self.bbcache.cached(key)
                if pixmap:
                    pixmap = pixmap.scaledToHeight(option.rect.height())
                else:
                    pixmap = BeachballWidget(
                        moment_tensor=mt,
                        color=qg.QColor(*tango_colors['scarletred3'])
                    ).to_qpixmap()
                    self.bbcache.insert(key, pixmap)
                a, b, c, d = option.rect.getRect()
                painter.save()
                painter.setRenderHint(qg.QPainter.Antialiasing)
                painter.drawPixmap(a+d/2., b, d, d, pixmap)
                painter.restore()

        else:
            qw.QStyledItemDelegate.paint(self, painter, option, index)

        if iactive is not None and \
                self.parent().model().mapToSource(index).row() == iactive:

            painter.save()

            rect = option.rect
            x1, y1, x2, y2 = rect.getCoords()
            pen = painter.pen()
            pen.setWidth(2)
            pen.setColor(mcolor)
            painter.setPen(pen)
            painter.drawLine(qc.QLineF(x1, y1, x2, y1))
            painter.drawLine(qc.QLineF(x1, y2, x2, y2))
            painter.restore()

    def displayText(self, value, locale):
        if isDateTime(value):
            return toDateTime(value).toUTC().toString(
                'yyyy-MM-dd HH:mm:ss.zzz')
        else:
            return toString(value)

    def marker_from_index(self, index):
        tv = self.parent()
        pv = tv.pile_viewer
        return pv.markers[tv.model().mapToSource(index).row()]

    def bb_data_from_index(self, index):
        marker = self.marker_from_index(index)
        if isinstance(marker, EventMarker):
            return marker.get_event().moment_tensor
        else:
            return None

    def color_from_index(self, index):
        marker = self.marker_from_index(index)
        return qg.QColor(*marker.select_color(marker.color_b))


class MarkerSortFilterProxyModel(QSortFilterProxyModel):
    '''Sorts the table's columns.'''

    def __init__(self, *args, **kwargs):
        QSortFilterProxyModel.__init__(self, *args, **kwargs)
        self.sort(1, qc.Qt.DescendingOrder)

    def lessThan(self, left, right):
        if left.column() in [0, 3, 9, 10, 12]:
            return toString(left.data()) < toString(right.data())
        elif left.column() == 1:
            return toDateTime(left.data()) < toDateTime(right.data())
        else:
            return toFloat(left.data())[0] < toFloat(right.data())[0]

    def headerData(self, col, orientation, role):
        '''Set and format header data.'''
        if orientation == qc.Qt.Horizontal:
            if role == qc.Qt.DisplayRole:
                return qc.QVariant(_header_data[col])
            elif role == qc.Qt.SizeHintRole:
                return qc.QSize(10, 20)

        elif orientation == qc.Qt.Vertical:
            if role == qc.Qt.DisplayRole:
                return qc.QVariant(str(col))

        else:
            return qc.QVariant()

    def sort(self, column, order):
        if column != _column_mapping['MT']:
            super(MarkerSortFilterProxyModel, self).sort(column, order)


class MarkerTableView(qw.QTableView):
    def __init__(self, *args, **kwargs):
        qw.QTableView.__init__(self, *args, **kwargs)
        self.setSelectionBehavior(qw.QAbstractItemView.SelectRows)
        self.setHorizontalScrollMode(qw.QAbstractItemView.ScrollPerPixel)
        self.setEditTriggers(qw.QAbstractItemView.DoubleClicked)
        self.setSortingEnabled(True)
        self.setStyleSheet(
            'QTableView{selection-background-color: \
            rgba(130, 130, 130, 100% );}')

        self.sortByColumn(1, qc.Qt.DescendingOrder)
        self.setAlternatingRowColors(True)

        self.setShowGrid(False)
        self.verticalHeader().hide()
        self.pile_viewer = None

        self.clicked.connect(self.table_clicked)
        self.doubleClicked.connect(self.table_double_clicked)

        self.header_menu = qw.QMenu(self)

        show_initially = ['Type', 'Time', 'Magnitude']
        self.menu_labels = ['Type', 'Time', 'Magnitude', 'Label', 'Depth [km]',
                            'Latitude/Longitude', 'Kind', 'Distance [km]',
                            'NSLCs', 'Polarity', 'Kagan Angle [deg]',
                            'Event Hash', 'MT']

        self.menu_items = dict(zip(
            self.menu_labels, [0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13]))

        self.editable_columns = [2, 3, 4, 5, 6, 7]

        self.column_actions = {}
        for hd in self.menu_labels:
            a = qw.QAction(hd, self.header_menu)
            a.triggered.connect(
                self.toggle_columns)
            a.setCheckable(True)
            a.setChecked(hd in show_initially)
            self.header_menu.addAction(a)
            self.column_actions[hd] = a

        a = qw.QAction('Numbering', self.header_menu)
        a.setCheckable(True)
        a.setChecked(False)
        a.triggered.connect(
            self.toggle_numbering)
        self.header_menu.addAction(a)

        header = self.horizontalHeader()
        header.setContextMenuPolicy(qc.Qt.CustomContextMenu)
        header.customContextMenuRequested.connect(
            self.show_context_menu)

        self.active_event_index = None

        self.right_click_menu = qw.QMenu(self)
        print_action = qw.QAction('Print Table', self.right_click_menu)
        print_action.triggered.connect(self.print_menu)
        self.right_click_menu.addAction(print_action)

    def wheelEvent(self, wheel_event):
        if wheel_event.modifiers() & qc.Qt.ControlModifier:
            height = self.rowAt(self.height())
            ci = self.indexAt(
                qc.QPoint(self.viewport().rect().x(), height))
            v = self.verticalHeader()

            if use_pyqt5:
                wheel_delta = wheel_event.angleDelta().y()
            else:
                wheel_delta = wheel_event.delta()

            v.setDefaultSectionSize(
                max(12, v.defaultSectionSize()+wheel_delta//60))
            self.scrollTo(ci)
            if v.isVisible():
                self.toggle_numbering(False)
                self.toggle_numbering(True)

        else:
            super(MarkerTableView, self).wheelEvent(wheel_event)

    def set_viewer(self, viewer):
        '''Set a pile_viewer and connect to signals.'''

        self.pile_viewer = viewer

    def keyPressEvent(self, key_event):
        '''Propagate ``key_event`` to pile_viewer, unless up/down pressed.'''
        if key_event.key() in [qc.Qt.Key_Up, qc.Qt.Key_Down]:
            qw.QTableView.keyPressEvent(self, key_event)
            self.pile_viewer.go_to_selection()
        else:
            self.pile_viewer.keyPressEvent(key_event)

    def table_clicked(self, model_index):
        '''Ignore mouse clicks.'''
        pass

    def contextMenuEvent(self, event):
        self.right_click_menu.popup(qg.QCursor.pos())

    def toggle_numbering(self, want):
        if want:
            self.verticalHeader().show()
        else:
            self.verticalHeader().hide()

    def print_menu(self):
        from .qt_compat import qprint
        printer = qprint.QPrinter(qprint.QPrinter.ScreenResolution)
        printer.setOutputFormat(qprint.QPrinter.NativeFormat)
        printer_dialog = qprint.QPrintDialog(printer, self)
        if printer_dialog.exec_() == qw.QDialog.Accepted:

            scrollbarpolicy = self.verticalScrollBarPolicy()
            self.setVerticalScrollBarPolicy(qc.Qt.ScrollBarAlwaysOff)
            rect = printer.pageRect()
            painter = qg.QPainter()
            painter.begin(printer)
            xscale = rect.width() / (self.width()*1.1)
            yscale = rect.height() / (self.height() * 1.1)
            scale = min(xscale, yscale)
            painter.translate(rect.x() + rect.width()/2.,
                              rect.y() + rect.height()/2.)
            painter.scale(scale, scale)
            painter.translate(-self.width()/2., -self.height()/2.)
            painter.setRenderHints(qg.QPainter.HighQualityAntialiasing |
                                   qg.QPainter.TextAntialiasing)
            self.render(painter)
            painter.end()
            self.setVerticalScrollBarPolicy(scrollbarpolicy)

    def table_double_clicked(self, model_index):
        if model_index.column() in self.editable_columns:
            return
        else:
            self.pile_viewer.go_to_selection()

    def show_context_menu(self, point):
        '''Pop-up menu to toggle columns in the :py:class:`MarkerTableView`.'''

        self.header_menu.popup(self.mapToGlobal(point))

    def toggle_columns(self):
        '''Toggle columns depending in checked state. '''
        width = 0
        want_distances = False
        want_angles = False
        for header, ca in self.column_actions.items():
            hide = not ca.isChecked()
            self.setColumnHidden(self.menu_items[header], hide)
            if header == 'Latitude/Longitude':
                self.setColumnHidden(self.menu_items[header]+1, hide)
            if not hide:
                width += _header_sizes[self.menu_labels.index(header)]
            if header == 'Distance [km]':
                want_distances = True
            elif header == 'Kagan Angle [deg]':
                want_angles = True

        if self.active_event_index:
            self.model().sourceModel().update_distances_and_angles(
                [[self.active_event_index]],
                want_distances=want_distances, want_angles=want_angles)
        self.parent().setMinimumWidth(width)

    def set_active_event_index(self, i):
        if i == -1:
            i = None
        self.active_event_index = i
        self.viewport().update()


class MarkerTableModel(qc.QAbstractTableModel):

    def __init__(self, *args, **kwargs):
        qc.QAbstractTableModel.__init__(self, *args, **kwargs)
        self.pile_viewer = None
        self.distances = {}
        self.kagan_angles = {}
        self.last_active_event = None
        self.row_count = 0
        self.proxy_filter = None

    def set_viewer(self, viewer):
        '''Set a pile_viewer and connect to signals.'''

        self.pile_viewer = viewer
        self.pile_viewer.markers_added.connect(
                     self.markers_added)

        self.pile_viewer.markers_removed.connect(
                     self.markers_removed)

        self.pile_viewer.changed_marker_selection.connect(
                     self.update_distances_and_angles)

    def rowCount(self, parent):
        if not self.pile_viewer:
            return 0
        return len(self.pile_viewer.get_markers())

    def columnCount(self, parent):
        return len(_column_mapping)

    def markers_added(self, istart, istop):
        '''Insert rows into table.'''

        self.beginInsertRows(qc.QModelIndex(), istart, istop)
        self.endInsertRows()

    def markers_removed(self, i_from, i_to):
        '''Remove rows from table.'''

        self.beginRemoveRows(qc.QModelIndex(), i_from, i_to)
        self.endRemoveRows()
        self.marker_table_view.updateGeometries()

    def data(self, index, role):
        '''Set data in each of the table's cell.'''

        if not self.pile_viewer:
            return qc.QVariant()

        marker = self.pile_viewer.markers[index.row()]

        if role == qc.Qt.DisplayRole:
            s = ''
            column = index.column()
            if column == _column_mapping['Time']:
                return qc.QVariant(
                    qc.QDateTime.fromMSecsSinceEpoch(marker.tmin*1000))

            elif column == _column_mapping['T']:
                if isinstance(marker, EventMarker):
                    s = 'E'
                elif isinstance(marker, PhaseMarker):
                    s = 'P'

            elif column == _column_mapping['M']:
                if isinstance(marker, EventMarker):
                    e = marker.get_event()
                    if e.moment_tensor is not None:
                        s = round(e.moment_tensor.magnitude, 1)
                    elif e.magnitude is not None:
                        s = round(e.magnitude, 1)

            elif column == _column_mapping['Label']:
                if isinstance(marker, EventMarker):
                    s = marker.label()
                elif isinstance(marker, PhaseMarker):
                    s = marker.get_label()

            elif column == _column_mapping['Depth [km]']:
                if isinstance(marker, EventMarker):
                    d = marker.get_event().depth
                    if d is not None:
                        s = round(marker.get_event().depth/1000., 1)

            elif column == _column_mapping['Lat']:
                if isinstance(marker, EventMarker):
                    s = round(marker.get_event().lat, 2)

            elif column == _column_mapping['Lon']:
                if isinstance(marker, EventMarker):
                    s = round(marker.get_event().lon, 2)

            elif column == _column_mapping['Kind']:
                s = marker.kind

            elif column == _column_mapping['Dist [km]']:
                if marker in self.distances:
                    s = self.distances[marker]

            elif column == _column_mapping['NSLCs']:
                strs = []
                for nslc_id in marker.get_nslc_ids():
                    strs.append('.'.join(nslc_id))
                s = '|'.join(strs)

            elif column == _column_mapping['Kagan Angle [deg]']:
                if marker in self.kagan_angles:
                    s = round(self.kagan_angles[marker], 1)

            elif column == _column_mapping['MT']:
                return qc.QVariant()

            elif column == _column_mapping['Event Hash']:
                if isinstance(marker, (EventMarker, PhaseMarker)):
                    s = marker.get_event_hash()
                else:
                    return qc.QVariant()

            elif column == _column_mapping['Polarity']:
                if isinstance(marker, (PhaseMarker)):
                    s = marker.get_polarity_symbol()
                else:
                    return qc.QVariant()

            return qc.QVariant(s)

        return qc.QVariant()

    def update_distances_and_angles(self, indices=None, want_angles=False,
                                    want_distances=False):
        '''Calculate and update distances and kagan angles between events.

        :param indices: list of lists of indices (optional)

        Ideally, indices are consecutive for best performance.'''
        want_angles = want_angles or \
            not self.marker_table_view.isColumnHidden(
                _column_mapping['Kagan Angle [deg]'])
        want_distances = want_distances or \
            not self.marker_table_view.isColumnHidden(
                _column_mapping['Dist [km]'])

        if not (want_distances or want_angles):
            return

        indices = indices or [[]]
        indices = [i for ii in indices for i in ii]

        if len(indices) != 1:
            return

        if self.last_active_event == self.pile_viewer.get_active_event():
            return
        else:
            self.last_active_event = self.pile_viewer.get_active_event()

        markers = self.pile_viewer.markers
        nmarkers = len(markers)
        omarker = markers[indices[0]]
        if not isinstance(omarker, EventMarker):
            return
        else:
            oevent = omarker.get_event()

        emarkers = [m for m in markers if isinstance(m, EventMarker)]
        if len(emarkers) < 2:
            return
        else:
            events = [em.get_event() for em in emarkers]
            nevents = len(events)

        if want_distances:
            lats = num.zeros(nevents)
            lons = num.zeros(nevents)
            for i in range(nevents):
                lats[i] = events[i].effective_lat
                lons[i] = events[i].effective_lon

            olats = num.zeros(nevents)
            olons = num.zeros(nevents)
            olats[:] = oevent.effective_lat
            olons[:] = oevent.effective_lon
            dists = orthodrome.distance_accurate50m_numpy(
                lats, lons, olats, olons)
            dists /= 1000.

            dists = [round(x, 1) for x in dists]
            self.distances = dict(zip(emarkers, dists))

        if want_angles:
            if oevent.moment_tensor:
                for em in emarkers:
                    e = em.get_event()
                    if e.moment_tensor:
                        a = kagan_angle(oevent.moment_tensor, e.moment_tensor)
                        self.kagan_angles[em] = a
            else:
                self.kagan_angles = {}

        istart = self.index(0, _column_mapping['Dist [km]'])
        istop = self.index(nmarkers-1, _column_mapping['Kagan Angle [deg]'])

        self.dataChanged.emit(
                  istart,
                  istop)

    def done(self, index):
        self.dataChanged.emit(index, index)
        return True

    def setData(self, index, value, role):
        '''Manipulate :py:class:`EventMarker` instances.'''
        if role == qc.Qt.EditRole:
            imarker = index.row()
            marker = self.pile_viewer.markers[imarker]
            if index.column() in [_column_mapping[c] for c in [
                    'M', 'Lat', 'Lon', 'Depth [km]']]:

                if not isinstance(marker, EventMarker):
                    return False
                else:
                    if index.column() == _column_mapping['M']:
                        valuef, valid = toFloat(value)
                        if valid:
                            e = marker.get_event()
                            if e.moment_tensor is None:
                                e.magnitude = valuef
                            else:
                                e.moment_tensor.magnitude = valuef
                            return self.done(index)

                if index.column() in [_column_mapping['Lon'],
                                      _column_mapping['Lat'],
                                      _column_mapping['Depth [km]']]:
                    if isinstance(marker, EventMarker):
                        valuef, valid = toFloat(value)
                        if valid:
                            if index.column() == _column_mapping['Lat']:
                                marker.get_event().lat = valuef
                            elif index.column() == _column_mapping['Lon']:
                                marker.get_event().lon = valuef
                            elif index.column() == _column_mapping[
                                    'Depth [km]']:
                                marker.get_event().depth = valuef*1000.
                            return self.done(index)

            if index.column() == _column_mapping['Label']:
                values = str(toString(value))
                if values != '':
                    if isinstance(marker, EventMarker):
                        marker.get_event().set_name(values)
                        return self.done(index)

                    if isinstance(marker, PhaseMarker):
                        marker.set_phasename(values)
                        return self.done(index)

        return False

    def flags(self, index):
        '''Set flags for cells which the user can edit.'''

        if index.column() not in self.marker_table_view.editable_columns:
            return qc.Qt.ItemFlags(33)
        else:
            if isinstance(self.pile_viewer.markers[index.row()], EventMarker):
                if index.column() in self.marker_table_view.editable_columns:
                    return qc.Qt.ItemFlags(35)
            if index.column() == _column_mapping['Label']:
                return qc.Qt.ItemFlags(35)
        return qc.Qt.ItemFlags(33)


class MarkerEditor(qw.QFrame):

    def __init__(self, *args, **kwargs):
        qw.QFrame.__init__(self, *args, **kwargs)
        layout = qw.QGridLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(layout)
        self.marker_table_view = MarkerTableView(self)
        self.delegate = MarkerItemDelegate(self.marker_table_view)
        self.marker_table_view.setItemDelegate(self.delegate)

        self.marker_model = MarkerTableModel()
        self.marker_model.marker_table_view = self.marker_table_view

        self.proxy_filter = MarkerSortFilterProxyModel()
        self.proxy_filter.setDynamicSortFilter(True)
        self.proxy_filter.setSourceModel(self.marker_model)
        self.marker_model.proxy_filter = self.proxy_filter

        self.marker_table_view.setModel(self.proxy_filter)

        header = self.marker_table_view.horizontalHeader()
        for i_s, s in enumerate(_header_sizes):
            if use_pyqt5:
                header.setSectionResizeMode(i_s, qw.QHeaderView.Interactive)
            else:
                header.setResizeMode(i_s, qw.QHeaderView.Interactive)

            header.resizeSection(i_s, s)

        header.setStretchLastSection(True)

        self.selection_model = QItemSelectionModel(self.proxy_filter)
        self.marker_table_view.setSelectionModel(self.selection_model)
        self.selection_model.selectionChanged.connect(
            self.set_selected_markers)

        layout.addWidget(self.marker_table_view, 0, 0)

        self.pile_viewer = None
        self._size_hint = qc.QSize(1, 1)

    def set_viewer(self, viewer):
        '''Set a pile_viewer and connect to signals.'''

        self.pile_viewer = viewer
        self.marker_model.set_viewer(viewer)
        self.marker_table_view.set_viewer(viewer)
        self.pile_viewer.changed_marker_selection.connect(
            self.update_selection_model)

        self.pile_viewer.active_event_marker_changed.connect(
            self.marker_table_view.set_active_event_index)

        self.marker_table_view.toggle_columns()

    def set_selected_markers(self, selected, deselected):
        ''' set markers selected in viewer at selection in table.'''

        selected_markers = []
        for i in self.selection_model.selectedRows():
            selected_markers.append(
                self.pile_viewer.markers[
                    self.proxy_filter.mapToSource(i).row()])

        self.pile_viewer.set_selected_markers(selected_markers)

    def get_marker_model(self):
        '''Return :py:class:`MarkerTableModel` instance'''

        return self.marker_model

    def update_selection_model(self, indices):
        '''Adopt marker selections done in the pile_viewer in the tableview.

        :param indices: list of indices of selected markers.'''
        self.selection_model.clearSelection()
        selections = QItemSelection()
        selection_flags = QItemSelectionModel.SelectionFlags(
            (QItemSelectionModel.Select |
             QItemSelectionModel.Rows |
             QItemSelectionModel.Current))

        for chunk in indices:
            mi_start = self.marker_model.index(min(chunk), 0)
            mi_stop = self.marker_model.index(max(chunk), 0)
            row_selection = self.proxy_filter.mapSelectionFromSource(
                QItemSelection(mi_start, mi_stop))
            selections.merge(row_selection, selection_flags)

        if len(indices) != 0:
            self.marker_table_view.scrollTo(self.proxy_filter.mapFromSource(
                mi_start))
            self.marker_table_view.setCurrentIndex(mi_start)
            self.selection_model.setCurrentIndex(
                mi_start, selection_flags)

        self.selection_model.select(selections, selection_flags)
