# -*- coding: utf-8 -*- """novelWriter GUI Open Project novelWriter – GUI Open Project ================================ New and open project dialog File History: Created: 2020-02-26 [0.4.5] This file is a part of novelWriter Copyright 2020, Veronica Berglyd Olsen 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 . """ import logging import nw from os import path from datetime import datetime from PyQt5.QtCore import Qt from PyQt5.QtGui import QKeySequence from PyQt5.QtWidgets import ( QDialog, QHBoxLayout, QVBoxLayout, QGridLayout, QPushButton, QTreeWidget, QAbstractItemView, QTreeWidgetItem, QDialogButtonBox, QLabel, QShortcut, QFileDialog, QLineEdit ) from nw.common import formatInt from nw.constants import nwFiles logger = logging.getLogger(__name__) class GuiProjectLoad(QDialog): NONE_STATE = 0 NEW_STATE = 1 OPEN_STATE = 2 def __init__(self, theParent): QDialog.__init__(self, theParent) logger.debug("Initialising GuiProjectLoad ...") self.mainConf = nw.CONFIG self.theParent = theParent self.openState = self.NONE_STATE self.openPath = None self.outerBox = QVBoxLayout() self.innerBox = QHBoxLayout() self.outerBox.setSpacing(16) self.innerBox.setSpacing(16) self.setWindowTitle("Open Project") self.setMinimumWidth(650) self.setMinimumHeight(400) self.setModal(True) self.guiDeco = self.theParent.theTheme.loadDecoration("nwicon", (96, 96)) self.innerBox.addWidget(self.guiDeco, 0, Qt.AlignTop) self.projectForm = QGridLayout() self.projectForm.setContentsMargins(0, 0, 0, 0) self.listBox = QTreeWidget() self.listBox.setSelectionMode(QAbstractItemView.SingleSelection) self.listBox.setDragDropMode(QAbstractItemView.NoDragDrop) self.listBox.setColumnCount(4) self.listBox.setHeaderLabels(["Working Title","Words","Last Opened","Path"]) self.listBox.setRootIsDecorated(False) self.listBox.setColumnHidden(3, True) self.listBox.itemSelectionChanged.connect(self._doSelectRecent) self.listBox.itemDoubleClicked.connect(self._doOpenRecent) treeHead = self.listBox.headerItem() treeHead.setTextAlignment(1, Qt.AlignRight) treeHead.setTextAlignment(2, Qt.AlignRight) self.lblRecent = QLabel("Recently Opened Projects") self.lblPath = QLabel("Path") self.selPath = QLineEdit("") self.selPath.setEnabled(False) self.browseButton = QPushButton("...") self.browseButton.setMaximumWidth(30) self.browseButton.clicked.connect(self._doBrowse) self.projectForm.addWidget(self.lblRecent, 0, 0, 1, 3) self.projectForm.addWidget(self.listBox, 1, 0, 1, 3) self.projectForm.addWidget(self.lblPath, 2, 0, 1, 1) self.projectForm.addWidget(self.selPath, 2, 1, 1, 1) self.projectForm.addWidget(self.browseButton, 2, 2, 1, 1) self.projectForm.setColumnStretch(0, 0) self.projectForm.setColumnStretch(1, 1) self.projectForm.setColumnStretch(2, 0) self.projectForm.setVerticalSpacing(4) self.projectForm.setHorizontalSpacing(8) self.innerBox.addLayout(self.projectForm) self.buttonBox = QDialogButtonBox(QDialogButtonBox.Open | QDialogButtonBox.Cancel) self.buttonBox.accepted.connect(self._doOpenRecent) self.buttonBox.rejected.connect(self._doClose) self.newButton = self.buttonBox.addButton("New", QDialogButtonBox.ActionRole) self.newButton.clicked.connect(self._doNewProject) self.outerBox.addLayout(self.innerBox) self.outerBox.addWidget(self.buttonBox) self.setLayout(self.outerBox) self._populateList() self._doSelectRecent() keyDelete = QShortcut(self.listBox) keyDelete.setKey(QKeySequence(Qt.Key_Delete)) keyDelete.activated.connect(self._keyPressDelete) logger.debug("GuiProjectLoad initialisation complete") return ## # Buttons ## def _doOpenRecent(self): """Close the dialog window with a recent project selected. """ logger.verbose("GuiProjectLoad open button clicked") self._saveDialogState() selItems = self.listBox.selectedItems() if selItems: self.openPath = selItems[0].text(3) self.openState = self.OPEN_STATE self.accept() else: self.openPath = None self.openState = self.NONE_STATE return def _doSelectRecent(self): """A recent item has been selected. """ selList = self.listBox.selectedItems() if selList: self.selPath.setText(selList[0].text(3)) return def _doBrowse(self): """Close the dialog window with no selected path, triggering the project browser dialog. """ logger.verbose("GuiProjectLoad browse button clicked") if self.mainConf.showGUI: dlgOpt = QFileDialog.Options() dlgOpt |= QFileDialog.DontUseNativeDialog projFile, _ = QFileDialog.getOpenFileName( self, "Open novelWriter Project", "", "novelWriter Project File (%s);;All Files (*)" % nwFiles.PROJ_FILE, options=dlgOpt ) if projFile: thePath = path.abspath(path.dirname(projFile)) self.selPath.setText(thePath) self.openPath = thePath self.openState = self.OPEN_STATE self.accept() return def _doClose(self): """Close the dialog window without doing anything. """ logger.verbose("GuiProjectLoad close button clicked") self._saveDialogState() self.close() return def _doNewProject(self): """Create a new project. """ logger.verbose("GuiProjectLoad new project button clicked") self._saveDialogState() self.openPath = None self.openState = self.NEW_STATE self.accept() return def _keyPressDelete(self): """Remove an entry from the recent projects list. """ selList = self.listBox.selectedItems() if selList: self.mainConf.removeFromRecentCache(selList[0].text(3)) self._populateList() return ## # Internal Functions ## def _saveDialogState(self): """Save the changes made to the dialog. """ colWidths = [50]*3 for i in range(3): colWidths[i] = self.listBox.columnWidth(i) self.mainConf.setProjColWidths(colWidths) return def _populateList(self): """Populate the list box with recent project data. """ listOrder = [] listData = {} for projPath in self.mainConf.recentProj.keys(): theEntry = self.mainConf.recentProj[projPath] theTitle = "" theTime = 0 theWords = 0 if "title" in theEntry.keys(): theTitle = theEntry["title"] if "time" in theEntry.keys(): theTime = theEntry["time"] if "words" in theEntry.keys(): theWords = theEntry["words"] if theTime > 0: listOrder.append(theTime) listData[theTime] = [theTitle, theWords, projPath] self.listBox.clear() hasSelection = False for timeStamp in sorted(listOrder, reverse=True): newItem = QTreeWidgetItem([""]*4) newItem.setIcon(0, self.theParent.theTheme.getIcon("proj_nwx")) newItem.setText(0, listData[timeStamp][0]) newItem.setText(1, formatInt(listData[timeStamp][1])) newItem.setText(2, datetime.fromtimestamp(timeStamp).strftime("%x %X")) newItem.setText(3, listData[timeStamp][2]) newItem.setTextAlignment(1, Qt.AlignRight) newItem.setTextAlignment(2, Qt.AlignRight) self.listBox.addTopLevelItem(newItem) if not hasSelection: newItem.setSelected(True) hasSelection = True for i in range(3): self.listBox.setColumnWidth(i, self.mainConf.projColWidth[i]) return # END Class GuiProjectLoad