# -*- coding: utf-8 -*- from glob import glob import fnmatch import os from PyQt4 import QtCore, QtGui from PyQt4.QtCore import Qt import operator from ggpo.common.allgames import allgames from ggpo.common.settings import Settings from ggpo.common.util import findUnsupportedGamesavesDir from ggpo.gui.ui.savestatesdialog_ui import Ui_SavestatesDialog class SavestatesModel(QtCore.QAbstractTableModel): N_DISPLAY_COLUMNS = 4 NAME, MANUFACTURER, YEAR, DESCRIPTION, FULLPATH = range(5) def __init__(self): super(SavestatesModel, self).__init__() self.lastSort = self.NAME self.lastSortDirection = QtCore.Qt.DescendingOrder self.allGames = [] self.filteredGames = [] self.scanFsFiles() def columnCount(self, parent=None, *args, **kwargs): return SavestatesModel.N_DISPLAY_COLUMNS def data(self, modelIndex, role=None): if not modelIndex.isValid(): return None elif role == Qt.DisplayRole: r = modelIndex.row() c = modelIndex.column() return self.filteredGames[r][c] def flags(self, index): if index.isValid(): return Qt.ItemIsEnabled | Qt.ItemIsSelectable return super(SavestatesModel, self).flags(index) def headerData(self, section, orientation, role=None): if role == Qt.DisplayRole and orientation == Qt.Horizontal: return {0: 'Filename', 1: 'Manufacturer', 2: 'Year', 3: 'Description'}[section] def insertFsFile(self, filename): name = os.path.splitext(os.path.basename(filename))[0] if name in allgames: manufacturerYearDesc = allgames[name] self.allGames.append([name] + manufacturerYearDesc + [filename]) else: self.allGames.append([name, '', '', '', filename]) def rowCount(self, parent=None, *args, **kwargs): return len(self.filteredGames) def scanFsFiles(self): d = findUnsupportedGamesavesDir() if not d: return for f in glob(os.path.join(d, '*.fs')): self.insertFsFile(f) self.filteredGames = self.allGames[:] def setFilter(self, userFilter): if userFilter: userFilter = userFilter.strip().lower() if not userFilter: self.filteredGames = self.allGames[:] else: if not userFilter.startswith('*'): userFilter = '*' + userFilter if not userFilter.endswith('*'): userFilter += '*' self.filteredGames = [] for d in self.allGames: if fnmatch.fnmatch(d[self.NAME].lower(), userFilter) or \ fnmatch.fnmatch(d[self.DESCRIPTION].lower(), userFilter): self.filteredGames.append(d) self.sort(self.lastSort, self.lastSortDirection) rowcount = len(self.filteredGames) if rowcount > 0: idx1 = self.createIndex(0, 0) idx2 = self.createIndex(rowcount - 1, self.N_DISPLAY_COLUMNS - 1) else: idx1 = idx2 = QtCore.QModelIndex() self.dataChanged.emit(idx1, idx2) def sort(self, col, order=None): reverse = True if order == QtCore.Qt.DescendingOrder: reverse = False self.lastSort = col self.lastSortDirection = order getter = operator.itemgetter(col) keyfunc = lambda x: getter(x).lower() self.emit(QtCore.SIGNAL("layoutAboutToBeChanged()")) self.filteredGames.sort(key=keyfunc, reverse=reverse) self.emit(QtCore.SIGNAL("layoutChanged()")) class SavestatesDialog(QtGui.QDialog, Ui_SavestatesDialog): def __init__(self, *args, **kwargs): super(SavestatesDialog, self).__init__(*args, **kwargs) self.setupUi(self) self.fsfile = None self.uiButtonBox.button(QtGui.QDialogButtonBox.Ok).setEnabled(False) self.model = SavestatesModel() self.uiSavestatesTblv.setModel(self.model) self.uiSavestatesTblv.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) self.uiSavestatesTblv.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) self.connectSignals() self.setDefaultColumns() self.uiSavestatesTblv.setSortingEnabled(True) self.restoreStateAndGeometry() def connectSignals(self): self.accepted.connect(self.onAccepted) self.model.dataChanged.connect(self.onDataChanged) self.uiFilterLineEdit.textEdited.connect(self.model.setFilter) self.uiSavestatesTblv.doubleClicked.connect(self.accept) self.uiSavestatesTblv.selectionModel().selectionChanged.connect(self.onSelectionChanged) self.accepted.connect(self.saveGeometrySettings) self.finished.connect(self.saveGeometrySettings) self.rejected.connect(self.saveGeometrySettings) def keyPressEvent(self, e): if e.key() in (Qt.Key_Up, Qt.Key_Down): rc = self.model.rowCount() if rc: idx = None sm = self.uiSavestatesTblv.selectionModel() if sm.hasSelection(): selected = self.uiSavestatesTblv.selectionModel().selectedRows()[0] if e.key() == Qt.Key_Down and selected.row() < rc - 1: idx = self.model.createIndex(selected.row() + 1, 0) elif e.key() == Qt.Key_Up and selected.row() > 0: idx = self.model.createIndex(selected.row() - 1, 0) else: row = (e.key() == Qt.Key_Up) and (rc - 1) or 0 idx = self.model.createIndex(row, 0) if idx: sm.clearSelection() sm.select(idx, QtGui.QItemSelectionModel.Select | QtGui.QItemSelectionModel.Rows) self.uiSavestatesTblv.scrollTo(idx) e.ignore() return super(SavestatesDialog, self).keyPressEvent(e) def onAccepted(self): selected = self.uiSavestatesTblv.selectionModel().selectedRows()[0] self.fsfile = self.model.filteredGames[selected.row()][SavestatesModel.FULLPATH] def onDataChanged(self, startIdx, endIdx): sm = self.uiSavestatesTblv.selectionModel() sm.clearSelection() if endIdx.isValid() and endIdx.row() == 0: sm.select(endIdx, QtGui.QItemSelectionModel.Select | QtGui.QItemSelectionModel.Rows) def onSelectionChanged(self): self.uiButtonBox.button(QtGui.QDialogButtonBox.Ok).setEnabled( self.uiSavestatesTblv.selectionModel().hasSelection()) def restoreStateAndGeometry(self): saved = Settings.value(Settings.SAVESTATES_DIALOG_GEOMETRY) if saved: self.restoreGeometry(saved) saved = Settings.value(Settings.SAVESTATES_DIALOG_TABLE_HEADER_STATE) if saved: self.uiSavestatesTblv.horizontalHeader().restoreState(saved) def saveGeometrySettings(self): Settings.setValue(Settings.SAVESTATES_DIALOG_GEOMETRY, self.saveGeometry()) Settings.setValue(Settings.SAVESTATES_DIALOG_TABLE_HEADER_STATE, self.uiSavestatesTblv.horizontalHeader().saveState()) def setDefaultColumns(self): self.uiSavestatesTblv.verticalHeader().setVisible(False) hh = self.uiSavestatesTblv.horizontalHeader() hh.setStretchLastSection(True) hh.setMinimumSectionSize(25) hh.setHighlightSections(False) hh.resizeSection(SavestatesModel.NAME, 100) hh.resizeSection(SavestatesModel.MANUFACTURER, 100) hh.resizeSection(SavestatesModel.YEAR, 50)