diff --git a/comictaggerlib/taggerwindow.py b/comictaggerlib/taggerwindow.py
index 80cbf2e..c8b54fe 100644
--- a/comictaggerlib/taggerwindow.py
+++ b/comictaggerlib/taggerwindow.py
@@ -11,7 +11,7 @@ Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
+ http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
@@ -65,1913 +65,1918 @@ import utils
import ctversion
class OnlineMatchResults():
- def __init__(self):
- self.goodMatches = []
- self.noMatches = []
- self.multipleMatches = []
- self.lowConfidenceMatches = []
- self.writeFailures = []
- self.fetchDataFailures = []
-
+ def __init__(self):
+ self.goodMatches = []
+ self.noMatches = []
+ self.multipleMatches = []
+ self.lowConfidenceMatches = []
+ self.writeFailures = []
+ self.fetchDataFailures = []
+
class MultipleMatch():
- def __init__( self, ca, match_list):
- self.ca = ca
- self.matches = match_list
+ def __init__( self, ca, match_list):
+ self.ca = ca
+ self.matches = match_list
class TaggerWindow( QtGui.QMainWindow):
-
- appName = "ComicTagger"
- version = ctversion.version
-
- def __init__(self, file_list, settings, parent = None, opts=None):
- super(TaggerWindow, self).__init__(parent)
- uic.loadUi(ComicTaggerSettings.getUIFile('taggerwindow.ui' ), self)
- self.settings = settings
+ appName = "ComicTagger"
+ version = ctversion.version
- #----------------------------------
- # prevent multiple instances
- socket = QtNetwork.QLocalSocket(self)
- socket.connectToServer(settings.install_id)
- alive = socket.waitForConnected(3000)
- if alive:
- print "Another application with key [{}] is already running".format( settings.install_id)
- # send file list to other instance
- if len(file_list) > 0:
- socket.write(pickle.dumps(file_list))
- if not socket.waitForBytesWritten(3000):
- print socket.errorString().toLatin1()
- socket.disconnectFromServer()
- sys.exit()
- else:
- # listen on a socket to prevent multiple instances
- self.socketServer = QtNetwork.QLocalServer(self)
- self.socketServer.newConnection.connect(self.onIncomingSocketConnection)
- ok = self.socketServer.listen(settings.install_id)
- if not ok:
- if self.socketServer.serverError() == QtNetwork.QAbstractSocket.AddressInUseError:
- #print "Resetting unresponsive socket with key [{}]".format(settings.install_id)
- self.socketServer.removeServer(settings.install_id)
- ok = self.socketServer.listen(settings.install_id)
- if not ok:
- print "Cannot start local socket with key [{}]. Reason: %s ".format(settings.install_id, self.socketServer.errorString())
- sys.exit()
- #print "Registering as single instance with key [{}]".format(settings.install_id)
- #----------------------------------
+ def __init__(self, file_list, settings, parent = None, opts=None):
+ super(TaggerWindow, self).__init__(parent)
+
+ uic.loadUi(ComicTaggerSettings.getUIFile('taggerwindow.ui' ), self)
+ self.settings = settings
+
+ #----------------------------------
+ # prevent multiple instances
+ socket = QtNetwork.QLocalSocket(self)
+ socket.connectToServer(settings.install_id)
+ alive = socket.waitForConnected(3000)
+ if alive:
+ print "Another application with key [{}] is already running".format( settings.install_id)
+ # send file list to other instance
+ if len(file_list) > 0:
+ socket.write(pickle.dumps(file_list))
+ if not socket.waitForBytesWritten(3000):
+ print socket.errorString().toLatin1()
+ socket.disconnectFromServer()
+ sys.exit()
+ else:
+ # listen on a socket to prevent multiple instances
+ self.socketServer = QtNetwork.QLocalServer(self)
+ self.socketServer.newConnection.connect(self.onIncomingSocketConnection)
+ ok = self.socketServer.listen(settings.install_id)
+ if not ok:
+ if self.socketServer.serverError() == QtNetwork.QAbstractSocket.AddressInUseError:
+ #print "Resetting unresponsive socket with key [{}]".format(settings.install_id)
+ self.socketServer.removeServer(settings.install_id)
+ ok = self.socketServer.listen(settings.install_id)
+ if not ok:
+ print "Cannot start local socket with key [{}]. Reason: %s ".format(settings.install_id, self.socketServer.errorString())
+ sys.exit()
+ #print "Registering as single instance with key [{}]".format(settings.install_id)
+ #----------------------------------
- self.archiveCoverWidget = CoverImageWidget( self.coverImageContainer, CoverImageWidget.ArchiveMode )
- gridlayout = QtGui.QGridLayout( self.coverImageContainer )
- gridlayout.addWidget( self.archiveCoverWidget )
- gridlayout.setContentsMargins(0,0,0,0)
-
- self.pageListEditor = PageListEditor( self.tabPages )
- gridlayout = QtGui.QGridLayout( self.tabPages )
- gridlayout.addWidget( self.pageListEditor )
-
- #---------------------------
- self.fileSelectionList = FileSelectionList( self.widgetListHolder, self.settings )
- gridlayout = QtGui.QGridLayout( self.widgetListHolder )
- gridlayout.addWidget( self.fileSelectionList )
-
- self.fileSelectionList.selectionChanged.connect( self.fileListSelectionChanged )
- self.fileSelectionList.listCleared.connect( self.fileListCleared )
- self.fileSelectionList.setSorting(self.settings.last_filelist_sorted_column,
- self.settings.last_filelist_sorted_order)
+ self.archiveCoverWidget = CoverImageWidget( self.coverImageContainer, CoverImageWidget.ArchiveMode )
+ gridlayout = QtGui.QGridLayout( self.coverImageContainer )
+ gridlayout.addWidget( self.archiveCoverWidget )
+ gridlayout.setContentsMargins(0,0,0,0)
- # we can't specify relative font sizes in the UI designer, so
- # walk through all the lablels in the main form, and make them
- # a smidge smaller
- for child in self.scrollAreaWidgetContents.children():
- if ( isinstance(child, QtGui.QLabel) ):
- f = child.font()
- if f.pointSize() > 10:
- f.setPointSize( f.pointSize() - 2 )
- f.setItalic( True )
- child.setFont( f )
+ self.pageListEditor = PageListEditor( self.tabPages )
+ gridlayout = QtGui.QGridLayout( self.tabPages )
+ gridlayout.addWidget( self.pageListEditor )
- self.scrollAreaWidgetContents.adjustSize()
-
- self.setWindowIcon(QtGui.QIcon( ComicTaggerSettings.getGraphic('app.png')))
+ #---------------------------
+ self.fileSelectionList = FileSelectionList( self.widgetListHolder, self.settings )
+ gridlayout = QtGui.QGridLayout( self.widgetListHolder )
+ gridlayout.addWidget( self.fileSelectionList )
- if opts is not None and opts.data_style is not None and opts.data_style != MetaDataStyle.COMET:
- #respect the command line option tag type
- settings.last_selected_save_data_style = opts.data_style
- settings.last_selected_load_data_style = opts.data_style
+ self.fileSelectionList.selectionChanged.connect( self.fileListSelectionChanged )
+ self.fileSelectionList.listCleared.connect( self.fileListCleared )
+ self.fileSelectionList.setSorting(self.settings.last_filelist_sorted_column,
+ self.settings.last_filelist_sorted_order)
- self.save_data_style = settings.last_selected_save_data_style
- self.load_data_style = settings.last_selected_load_data_style
+ # we can't specify relative font sizes in the UI designer, so
+ # walk through all the lablels in the main form, and make them
+ # a smidge smaller
+ for child in self.scrollAreaWidgetContents.children():
+ if ( isinstance(child, QtGui.QLabel) ):
+ f = child.font()
+ if f.pointSize() > 10:
+ f.setPointSize( f.pointSize() - 2 )
+ f.setItalic( True )
+ child.setFont( f )
- self.setAcceptDrops(True)
- self.configMenus()
- self.statusBar()
- self.populateComboBoxes()
+ self.scrollAreaWidgetContents.adjustSize()
- self.page_browser = None
- self.resetApp()
-
- # set up some basic field validators
- validator = QtGui.QIntValidator(1900, 2099, self)
- self.lePubYear.setValidator(validator)
+ self.setWindowIcon(QtGui.QIcon( ComicTaggerSettings.getGraphic('app.png')))
- validator = QtGui.QIntValidator(1, 12, self)
- self.lePubMonth.setValidator(validator)
-
- validator = QtGui.QIntValidator(1, 99999, self)
- self.leIssueCount.setValidator(validator)
- self.leVolumeNum.setValidator(validator)
- self.leVolumeCount.setValidator(validator)
- self.leAltIssueNum.setValidator(validator)
- self.leAltIssueCount.setValidator(validator)
-
- #TODO set up an RE validator for issueNum that allows
- # for all sorts of wacky things
+ if opts is not None and opts.data_style is not None and opts.data_style != MetaDataStyle.COMET:
+ #respect the command line option tag type
+ settings.last_selected_save_data_style = opts.data_style
+ settings.last_selected_load_data_style = opts.data_style
- # tweak some control fonts
- reduceWidgetFontSize( self.lblFilename, 1 )
- reduceWidgetFontSize( self.lblArchiveType )
- reduceWidgetFontSize( self.lblTagList )
- reduceWidgetFontSize( self.lblPageCount )
+ self.save_data_style = settings.last_selected_save_data_style
+ self.load_data_style = settings.last_selected_load_data_style
- #make sure some editable comboboxes don't take drop actions
- self.cbFormat.lineEdit().setAcceptDrops(False)
- self.cbMaturityRating.lineEdit().setAcceptDrops(False)
+ self.setAcceptDrops(True)
+ self.configMenus()
+ self.statusBar()
+ self.populateComboBoxes()
- # hook up the callbacks
- self.cbLoadDataStyle.currentIndexChanged.connect(self.setLoadDataStyle)
- self.cbSaveDataStyle.currentIndexChanged.connect(self.setSaveDataStyle)
- self.btnEditCredit.clicked.connect(self.editCredit)
- self.btnAddCredit.clicked.connect(self.addCredit)
- self.btnRemoveCredit.clicked.connect(self.removeCredit)
- self.twCredits.cellDoubleClicked.connect(self.editCredit)
- self.connectDirtyFlagSignals()
- self.pageListEditor.modified.connect(self.setDirtyFlag)
- self.pageListEditor.firstFrontCoverChanged.connect( self.frontCoverChanged )
- self.pageListEditor.listOrderChanged.connect( self.pageListOrderChanged )
- self.tabWidget.currentChanged.connect( self.tabChanged )
-
- self.updateStyleTweaks()
+ self.page_browser = None
+ self.resetApp()
- self.show()
- self.setAppPosition()
- if self.settings.last_form_side_width != -1:
- self.splitter.setSizes([ self.settings.last_form_side_width , self.settings.last_list_side_width])
- self.raise_()
- QtCore.QCoreApplication.processEvents()
- self.resizeEvent( None )
+ # set up some basic field validators
+ validator = QtGui.QIntValidator(1900, 2099, self)
+ self.lePubYear.setValidator(validator)
- self.splitter.splitterMoved.connect( self.splitterMovedEvent )
+ validator = QtGui.QIntValidator(1, 12, self)
+ self.lePubMonth.setValidator(validator)
- self.fileSelectionList.addAppAction( self.actionAutoIdentify )
- self.fileSelectionList.addAppAction( self.actionAutoTag )
- self.fileSelectionList.addAppAction( self.actionCopyTags )
- self.fileSelectionList.addAppAction( self.actionRename )
- self.fileSelectionList.addAppAction( self.actionRemoveAuto )
- self.fileSelectionList.addAppAction( self.actionRepackage )
-
- if len(file_list) != 0:
- self.fileSelectionList.addPathList( file_list )
-
- if self.settings.show_disclaimer:
- checked = OptionalMessageDialog.msg( self, "Welcome!",
- """
- Thanks for trying ComicTagger!
- Be aware that this is beta-level software, and consider it experimental.
- You should use it very carefully when modifying your data files. As the
- license says, it's "AS IS!"
- Also, be aware that writing tags to comic archives will change their file hashes,
- which has implications with respect to other software packages. It's best to
- use ComicTagger on local copies of your comics.
- Have fun!
- """
- )
- self.settings.show_disclaimer = not checked
+ # TODO: for now keep it simple, ideally we should check the full date
+ validator = QtGui.QIntValidator(1, 31, self)
+ self.lePubDay.setValidator(validator)
- if self.settings.ask_about_usage_stats:
- reply = QtGui.QMessageBox.question(self,
- self.tr("Anonymous Stats"),
- self.tr(
- "Is it okay if ComicTagger occasionally sends some anonymous usage statistics? Nothing nefarious, "
- "just trying to get a better idea of how the app is being used.\n\nThanks for your support!"
- ),
- QtGui.QMessageBox.Yes|QtGui.QMessageBox.Default, QtGui.QMessageBox.No )
-
- if reply == QtGui.QMessageBox.Yes:
- self.settings.send_usage_stats = True
- self.settings.ask_about_usage_stats = False
-
- if self.settings.check_for_new_version:
- self.checkLatestVersionOnline()
-
- def sigint_handler(self, *args):
- # defer the actual close in the app loop thread
- QtCore.QTimer.singleShot(200, self.close)
+ validator = QtGui.QIntValidator(1, 99999, self)
+ self.leIssueCount.setValidator(validator)
+ self.leVolumeNum.setValidator(validator)
+ self.leVolumeCount.setValidator(validator)
+ self.leAltIssueNum.setValidator(validator)
+ self.leAltIssueCount.setValidator(validator)
- def resetApp( self ):
+ #TODO set up an RE validator for issueNum that allows
+ # for all sorts of wacky things
- self.archiveCoverWidget.clear()
- self.comic_archive = None
- self.dirtyFlag = False
- self.clearForm()
- self.pageListEditor.resetPage()
- if self.page_browser is not None:
- self.page_browser.reset()
- self.updateAppTitle()
- self.updateMenus()
- self.updateInfoBox()
-
- self.droppedFile = None
- self.page_loader = None
+ # tweak some control fonts
+ reduceWidgetFontSize( self.lblFilename, 1 )
+ reduceWidgetFontSize( self.lblArchiveType )
+ reduceWidgetFontSize( self.lblTagList )
+ reduceWidgetFontSize( self.lblPageCount )
-
- def updateAppTitle( self ):
-
- if self.comic_archive is None:
- self.setWindowTitle( self.appName )
- else:
- mod_str = ""
- ro_str = ""
-
- if self.dirtyFlag:
- mod_str = " [modified]"
-
- if not self.comic_archive.isWritable():
- ro_str = " [read only]"
-
- self.setWindowTitle( self.appName + " - " + self.comic_archive.path + mod_str + ro_str)
+ #make sure some editable comboboxes don't take drop actions
+ self.cbFormat.lineEdit().setAcceptDrops(False)
+ self.cbMaturityRating.lineEdit().setAcceptDrops(False)
- def configMenus( self):
-
- # File Menu
- self.actionExit.setShortcut( 'Ctrl+Q' )
- self.actionExit.setStatusTip( 'Exit application' )
- self.actionExit.triggered.connect( self.close )
+ # hook up the callbacks
+ self.cbLoadDataStyle.currentIndexChanged.connect(self.setLoadDataStyle)
+ self.cbSaveDataStyle.currentIndexChanged.connect(self.setSaveDataStyle)
+ self.btnEditCredit.clicked.connect(self.editCredit)
+ self.btnAddCredit.clicked.connect(self.addCredit)
+ self.btnRemoveCredit.clicked.connect(self.removeCredit)
+ self.twCredits.cellDoubleClicked.connect(self.editCredit)
+ self.connectDirtyFlagSignals()
+ self.pageListEditor.modified.connect(self.setDirtyFlag)
+ self.pageListEditor.firstFrontCoverChanged.connect( self.frontCoverChanged )
+ self.pageListEditor.listOrderChanged.connect( self.pageListOrderChanged )
+ self.tabWidget.currentChanged.connect( self.tabChanged )
- self.actionLoad.setShortcut( 'Ctrl+O' )
- self.actionLoad.setStatusTip( 'Load comic archive' )
- self.actionLoad.triggered.connect( self.selectFile )
+ self.updateStyleTweaks()
- self.actionLoadFolder.setShortcut( 'Ctrl+Shift+O' )
- self.actionLoadFolder.setStatusTip( 'Load folder with comic archives' )
- self.actionLoadFolder.triggered.connect( self.selectFolder )
+ self.show()
+ self.setAppPosition()
+ if self.settings.last_form_side_width != -1:
+ self.splitter.setSizes([ self.settings.last_form_side_width , self.settings.last_list_side_width])
+ self.raise_()
+ QtCore.QCoreApplication.processEvents()
+ self.resizeEvent( None )
- self.actionWrite_Tags.setShortcut( 'Ctrl+S' )
- self.actionWrite_Tags.setStatusTip( 'Save tags to comic archive' )
- self.actionWrite_Tags.triggered.connect( self.commitMetadata )
+ self.splitter.splitterMoved.connect( self.splitterMovedEvent )
- self.actionAutoTag.setShortcut( 'Ctrl+T' )
- self.actionAutoTag.setStatusTip( 'Auto-tag multiple archives' )
- self.actionAutoTag.triggered.connect( self.autoTag )
-
- self.actionCopyTags.setShortcut( 'Ctrl+C' )
- self.actionCopyTags.setStatusTip( 'Copy one tag style to another' )
- self.actionCopyTags.triggered.connect( self.copyTags )
+ self.fileSelectionList.addAppAction( self.actionAutoIdentify )
+ self.fileSelectionList.addAppAction( self.actionAutoTag )
+ self.fileSelectionList.addAppAction( self.actionCopyTags )
+ self.fileSelectionList.addAppAction( self.actionRename )
+ self.fileSelectionList.addAppAction( self.actionRemoveAuto )
+ self.fileSelectionList.addAppAction( self.actionRepackage )
- self.actionRemoveAuto.setShortcut( 'Ctrl+D' )
- self.actionRemoveAuto.setStatusTip( 'Remove currently selected modify tag style from the archive' )
- self.actionRemoveAuto.triggered.connect( self.removeAuto )
-
- self.actionRemoveCBLTags.setStatusTip( 'Remove ComicBookLover tags from comic archive' )
- self.actionRemoveCBLTags.triggered.connect( self.removeCBLTags )
+ if len(file_list) != 0:
+ self.fileSelectionList.addPathList( file_list )
- self.actionRemoveCRTags.setStatusTip( 'Remove ComicRack tags from comic archive' )
- self.actionRemoveCRTags.triggered.connect( self.removeCRTags )
-
- self.actionViewRawCRTags.setStatusTip( 'View raw ComicRack tag block from file' )
- self.actionViewRawCRTags.triggered.connect( self.viewRawCRTags )
+ if self.settings.show_disclaimer:
+ checked = OptionalMessageDialog.msg( self, "Welcome!",
+ """
+ Thanks for trying ComicTagger!
+ Be aware that this is beta-level software, and consider it experimental.
+ You should use it very carefully when modifying your data files. As the
+ license says, it's "AS IS!"
+ Also, be aware that writing tags to comic archives will change their file hashes,
+ which has implications with respect to other software packages. It's best to
+ use ComicTagger on local copies of your comics.
+ Have fun!
+ """
+ )
+ self.settings.show_disclaimer = not checked
- self.actionViewRawCBLTags.setStatusTip( 'View raw ComicBookLover tag block from file' )
- self.actionViewRawCBLTags.triggered.connect( self.viewRawCBLTags )
+ if self.settings.ask_about_usage_stats:
+ reply = QtGui.QMessageBox.question(self,
+ self.tr("Anonymous Stats"),
+ self.tr(
+ "Is it okay if ComicTagger occasionally sends some anonymous usage statistics? Nothing nefarious, "
+ "just trying to get a better idea of how the app is being used.\n\nThanks for your support!"
+ ),
+ QtGui.QMessageBox.Yes|QtGui.QMessageBox.Default, QtGui.QMessageBox.No )
- self.actionRepackage.setShortcut( 'Ctrl+E' )
- self.actionRepackage.setStatusTip( 'Re-create archive as CBZ' )
- self.actionRepackage.triggered.connect( self.repackageArchive )
+ if reply == QtGui.QMessageBox.Yes:
+ self.settings.send_usage_stats = True
+ self.settings.ask_about_usage_stats = False
- self.actionRename.setShortcut( 'Ctrl+N' )
- self.actionRename.setStatusTip( 'Rename archive based on tags' )
- self.actionRename.triggered.connect( self.renameArchive )
-
- self.actionSettings.setShortcut( 'Ctrl+Shift+S' )
- self.actionSettings.setStatusTip( 'Configure ComicTagger' )
- self.actionSettings.triggered.connect( self.showSettings )
-
- # Tag Menu
- self.actionParse_Filename.setShortcut( 'Ctrl+F' )
- self.actionParse_Filename.setStatusTip( 'Try to extract tags from filename' )
- self.actionParse_Filename.triggered.connect( self.useFilename )
+ if self.settings.check_for_new_version:
+ self.checkLatestVersionOnline()
- self.actionSearchOnline.setShortcut( 'Ctrl+W' )
- self.actionSearchOnline.setStatusTip( 'Search online for tags' )
- self.actionSearchOnline.triggered.connect( self.queryOnline )
+ def sigint_handler(self, *args):
+ # defer the actual close in the app loop thread
+ QtCore.QTimer.singleShot(200, self.close)
- self.actionAutoIdentify.setShortcut( 'Ctrl+I' )
- self.actionAutoIdentify.triggered.connect( self.autoIdentifySearch )
-
- self.actionApplyCBLTransform.setShortcut( 'Ctrl+L' )
- self.actionApplyCBLTransform.setStatusTip( 'Modify tags specifically for CBL format' )
- self.actionApplyCBLTransform.triggered.connect( self.applyCBLTransform )
+ def resetApp( self ):
- self.actionClearEntryForm.setShortcut( 'Ctrl+Shift+C' )
- self.actionClearEntryForm.setStatusTip( 'Clear all the data on the screen' )
- self.actionClearEntryForm.triggered.connect( self.clearForm )
+ self.archiveCoverWidget.clear()
+ self.comic_archive = None
+ self.dirtyFlag = False
+ self.clearForm()
+ self.pageListEditor.resetPage()
+ if self.page_browser is not None:
+ self.page_browser.reset()
+ self.updateAppTitle()
+ self.updateMenus()
+ self.updateInfoBox()
- # Window Menu
- self.actionPageBrowser.setShortcut( 'Ctrl+P' )
- self.actionPageBrowser.setStatusTip( 'Show the page browser' )
- self.actionPageBrowser.triggered.connect( self.showPageBrowser )
-
- # Help Menu
- self.actionAbout.setStatusTip( 'Show the ' + self.appName + ' info' )
- self.actionAbout.triggered.connect( self.aboutApp )
- self.actionWiki.triggered.connect( self.showWiki )
- self.actionReportBug.triggered.connect( self.reportBug )
- self.actionComicTaggerForum.triggered.connect( self.showForum )
-
- # ToolBar
-
- self.actionLoad.setIcon(QtGui.QIcon( ComicTaggerSettings.getGraphic('open.png')))
- self.actionLoadFolder.setIcon(QtGui.QIcon( ComicTaggerSettings.getGraphic('longbox.png')))
- self.actionWrite_Tags.setIcon(QtGui.QIcon( ComicTaggerSettings.getGraphic('save.png')))
- self.actionParse_Filename.setIcon(QtGui.QIcon( ComicTaggerSettings.getGraphic('parse.png')))
- self.actionSearchOnline.setIcon(QtGui.QIcon( ComicTaggerSettings.getGraphic('search.png')))
- self.actionAutoIdentify.setIcon(QtGui.QIcon( ComicTaggerSettings.getGraphic('auto.png')))
- self.actionAutoTag.setIcon(QtGui.QIcon( ComicTaggerSettings.getGraphic('autotag.png')))
- self.actionClearEntryForm.setIcon(QtGui.QIcon( ComicTaggerSettings.getGraphic('clear.png')))
- self.actionPageBrowser.setIcon(QtGui.QIcon( ComicTaggerSettings.getGraphic('browse.png')))
-
- self.toolBar.addAction( self.actionLoad )
- self.toolBar.addAction( self.actionLoadFolder )
- self.toolBar.addAction( self.actionWrite_Tags )
- self.toolBar.addAction( self.actionSearchOnline )
- self.toolBar.addAction( self.actionAutoIdentify )
- self.toolBar.addAction( self.actionAutoTag )
- self.toolBar.addAction( self.actionClearEntryForm )
- self.toolBar.addAction( self.actionPageBrowser )
-
- def repackageArchive( self ):
- ca_list = self.fileSelectionList.getSelectedArchiveList()
- rar_count = 0
- for ca in ca_list:
- if ca.isRar( ):
- rar_count += 1
-
- if rar_count == 0:
- QtGui.QMessageBox.information(self, self.tr("Export as Zip Archive"), self.tr("No RAR archives selected!"))
- return
-
- if not self.dirtyFlagVerification( "Export as Zip Archive",
- "If you export archives as Zip now, unsaved data in the form may be lost. Are you sure?"):
- return
-
- if rar_count != 0:
- dlg = ExportWindow( self, self.settings,
- self.tr("You have selected {0} archive(s) to export to Zip format. New archives will be created in the same folder as the original.\n\nPlease choose options below, and select OK.\n".format(rar_count) ))
- dlg.adjustSize( )
- dlg.setModal( True )
- if not dlg.exec_():
- return
-
- progdialog = QtGui.QProgressDialog("", "Cancel", 0, rar_count, self)
- progdialog.setWindowTitle( "Exporting as ZIP" )
- progdialog.setWindowModality(QtCore.Qt.ApplicationModal)
- progdialog.show()
- prog_idx = 0
-
- new_archives_to_add = []
- archives_to_remove = []
- skipped_list = []
- failed_list = []
- success_count = 0
-
- for ca in ca_list:
- if ca.isRar():
- QtCore.QCoreApplication.processEvents()
- if progdialog.wasCanceled():
- break
- progdialog.setValue(prog_idx)
- prog_idx += 1
- progdialog.setLabelText( ca.path )
- centerWindowOnParent( progdialog )
- QtCore.QCoreApplication.processEvents()
-
- original_path = os.path.abspath( ca.path )
- export_name = os.path.splitext(original_path)[0] + ".cbz"
-
- if os.path.lexists( export_name ):
- if dlg.fileConflictBehavior == ExportConflictOpts.dontCreate:
- export_name = None
- skipped_list.append( ca.path )
- elif dlg.fileConflictBehavior == ExportConflictOpts.createUnique:
- export_name = utils.unique_file( export_name )
-
- if export_name is not None:
- if ca.exportAsZip( export_name ):
- success_count += 1
- if dlg.addToList:
- new_archives_to_add.append( export_name )
- if dlg.deleteOriginal:
- archives_to_remove.append( ca )
- os.unlink( ca.path )
-
- else:
- # last export failed, so remove the zip, if it exists
- failed_list.append( ca.path )
- if os.path.lexists( export_name ):
- os.remove( export_name )
-
- progdialog.close()
-
- self.fileSelectionList.addPathList( new_archives_to_add )
- self.fileSelectionList.removeArchiveList( archives_to_remove )
-
- summary = u"Successfully created {0} Zip archive(s).".format( success_count )
- if len( skipped_list ) > 0:
- summary += u"\n\nThe following {0} RAR archive(s) were skipped due to file name conflicts:\n".format( len( skipped_list ) )
- for f in skipped_list:
- summary += u"\t{0}\n".format( f )
- if len( failed_list ) > 0:
- summary += u"\n\nThe following {0} RAR archive(s) failed to export due to read/write errors:\n".format( len( failed_list ) )
- for f in failed_list:
- summary += u"\t{0}\n".format( f )
-
- dlg = LogWindow( self )
- dlg.setText( summary )
- dlg.setWindowTitle( "Archive Export to Zip Summary" )
- dlg.exec_()
-
-
- def aboutApp( self ):
-
- website = "http://code.google.com/p/comictagger"
- email = "comictagger@gmail.com"
- license_link = "http://www.apache.org/licenses/LICENSE-2.0"
- license_name = "Apache License 2.0"
-
- msgBox = QtGui.QMessageBox()
- msgBox.setWindowTitle( self.tr("About " + self.appName ) )
- msgBox.setTextFormat( QtCore.Qt.RichText )
- msgBox.setIconPixmap( QtGui.QPixmap(ComicTaggerSettings.getGraphic('about.png')) )
- msgBox.setText( "
"
- + self.appName + " v" + self.version + "
"
- + "(c)2014 Anthony Beville
"
- + "{0}
".format(website)
- + "{0}
".format(email)
- + "License: {1}".format(license_link, license_name) )
-
- msgBox.setStandardButtons( QtGui.QMessageBox.Ok )
- msgBox.exec_()
-
- def dragEnterEvent(self, event):
- self.droppedFiles = None
- if event.mimeData().hasUrls():
-
- # walk through the URL list and build a file list
- for url in event.mimeData().urls():
- if url.isValid() and url.scheme() == "file":
- if self.droppedFiles is None:
- self.droppedFiles = []
- self.droppedFiles.append(url.toLocalFile())
-
- if self.droppedFiles is not None:
- event.accept()
-
- def dropEvent(self, event):
- #if self.dirtyFlagVerification( "Open Archive",
- # "If you open a new archive now, data in the form will be lost. Are you sure?"):
- self.fileSelectionList.addPathList( self.droppedFiles )
- event.accept()
-
- def actualLoadCurrentArchive( self ):
- if self.metadata.isEmpty:
- self.metadata = self.comic_archive.metadataFromFilename( self.settings.parse_scan_info)
- if len(self.metadata.pages) == 0:
- self.metadata.setDefaultPageList( self.comic_archive.getNumberOfPages() )
-
- self.updateCoverImage()
-
- if self.page_browser is not None:
- self.page_browser.setComicArchive( self.comic_archive )
- self.page_browser.metadata = self.metadata
-
- self.metadataToForm()
- self.pageListEditor.setData( self.comic_archive, self.metadata.pages )
- self.clearDirtyFlag() # also updates the app title
- self.updateInfoBox()
- self.updateMenus()
- self.updateAppTitle()
-
- def updateCoverImage( self ):
- cover_idx = self.metadata.getCoverPageIndexList()[0]
- self.archiveCoverWidget.setArchive( self.comic_archive, cover_idx)
-
- def updateMenus( self ):
-
- # First just disable all the questionable items
- self.actionAutoTag.setEnabled( False )
- self.actionCopyTags.setEnabled( False )
- self.actionRemoveAuto.setEnabled( False )
- self.actionRemoveCRTags.setEnabled( False )
- self.actionRemoveCBLTags.setEnabled( False )
- self.actionWrite_Tags.setEnabled( False )
- self.actionRepackage.setEnabled(False)
- self.actionViewRawCBLTags.setEnabled( False )
- self.actionViewRawCRTags.setEnabled( False )
- self.actionParse_Filename.setEnabled( False )
- self.actionAutoIdentify.setEnabled( False )
- self.actionRename.setEnabled( False )
- self.actionApplyCBLTransform.setEnabled( False )
-
- # now, selectively re-enable
- if self.comic_archive is not None :
- has_cix = self.comic_archive.hasCIX()
- has_cbi = self.comic_archive.hasCBI()
-
- self.actionParse_Filename.setEnabled( True )
- self.actionAutoIdentify.setEnabled( True )
- self.actionAutoTag.setEnabled( True )
- self.actionRename.setEnabled( True )
- self.actionApplyCBLTransform.setEnabled( True )
- self.actionRepackage.setEnabled(True)
- self.actionRemoveAuto.setEnabled( True )
- self.actionRemoveCRTags.setEnabled( True )
- self.actionRemoveCBLTags.setEnabled( True )
- self.actionCopyTags.setEnabled( True )
-
- if has_cix:
- self.actionViewRawCRTags.setEnabled( True )
- if has_cbi:
- self.actionViewRawCBLTags.setEnabled( True )
-
- if self.comic_archive.isWritable():
- self.actionWrite_Tags.setEnabled( True )
+ self.droppedFile = None
+ self.page_loader = None
- def updateInfoBox( self ):
-
- ca = self.comic_archive
-
- if ca is None:
- self.lblFilename.setText( "" )
- self.lblArchiveType.setText( "" )
- self.lblTagList.setText( "" )
- self.lblPageCount.setText( "" )
- return
-
- filename = os.path.basename( ca.path )
- filename = os.path.splitext(filename)[0]
- filename = FileNameParser().fixSpaces(filename, False)
+ def updateAppTitle( self ):
- self.lblFilename.setText( filename )
+ if self.comic_archive is None:
+ self.setWindowTitle( self.appName )
+ else:
+ mod_str = ""
+ ro_str = ""
- if ca.isZip():
- self.lblArchiveType.setText( "ZIP archive" )
- elif ca.isRar():
- self.lblArchiveType.setText( "RAR archive" )
- elif ca.isFolder():
- self.lblArchiveType.setText( "Folder archive" )
- else:
- self.lblArchiveType.setText( "" )
-
- page_count = " ({0} pages)".format(ca.getNumberOfPages())
- self.lblPageCount.setText( page_count)
-
- tag_info = ""
- if ca.hasCIX():
- tag_info = u"• ComicRack tags"
- if ca.hasCBI():
- if tag_info != "":
- tag_info += "\n"
- tag_info += u"• ComicBookLover tags"
+ if self.dirtyFlag:
+ mod_str = " [modified]"
- self.lblTagList.setText( tag_info )
+ if not self.comic_archive.isWritable():
+ ro_str = " [read only]"
+
+ self.setWindowTitle( self.appName + " - " + self.comic_archive.path + mod_str + ro_str)
+
+ def configMenus( self):
+
+ # File Menu
+ self.actionExit.setShortcut( 'Ctrl+Q' )
+ self.actionExit.setStatusTip( 'Exit application' )
+ self.actionExit.triggered.connect( self.close )
+
+ self.actionLoad.setShortcut( 'Ctrl+O' )
+ self.actionLoad.setStatusTip( 'Load comic archive' )
+ self.actionLoad.triggered.connect( self.selectFile )
+
+ self.actionLoadFolder.setShortcut( 'Ctrl+Shift+O' )
+ self.actionLoadFolder.setStatusTip( 'Load folder with comic archives' )
+ self.actionLoadFolder.triggered.connect( self.selectFolder )
+
+ self.actionWrite_Tags.setShortcut( 'Ctrl+S' )
+ self.actionWrite_Tags.setStatusTip( 'Save tags to comic archive' )
+ self.actionWrite_Tags.triggered.connect( self.commitMetadata )
+
+ self.actionAutoTag.setShortcut( 'Ctrl+T' )
+ self.actionAutoTag.setStatusTip( 'Auto-tag multiple archives' )
+ self.actionAutoTag.triggered.connect( self.autoTag )
+
+ self.actionCopyTags.setShortcut( 'Ctrl+C' )
+ self.actionCopyTags.setStatusTip( 'Copy one tag style to another' )
+ self.actionCopyTags.triggered.connect( self.copyTags )
+
+ self.actionRemoveAuto.setShortcut( 'Ctrl+D' )
+ self.actionRemoveAuto.setStatusTip( 'Remove currently selected modify tag style from the archive' )
+ self.actionRemoveAuto.triggered.connect( self.removeAuto )
+
+ self.actionRemoveCBLTags.setStatusTip( 'Remove ComicBookLover tags from comic archive' )
+ self.actionRemoveCBLTags.triggered.connect( self.removeCBLTags )
+
+ self.actionRemoveCRTags.setStatusTip( 'Remove ComicRack tags from comic archive' )
+ self.actionRemoveCRTags.triggered.connect( self.removeCRTags )
+
+ self.actionViewRawCRTags.setStatusTip( 'View raw ComicRack tag block from file' )
+ self.actionViewRawCRTags.triggered.connect( self.viewRawCRTags )
+
+ self.actionViewRawCBLTags.setStatusTip( 'View raw ComicBookLover tag block from file' )
+ self.actionViewRawCBLTags.triggered.connect( self.viewRawCBLTags )
+
+ self.actionRepackage.setShortcut( 'Ctrl+E' )
+ self.actionRepackage.setStatusTip( 'Re-create archive as CBZ' )
+ self.actionRepackage.triggered.connect( self.repackageArchive )
+
+ self.actionRename.setShortcut( 'Ctrl+N' )
+ self.actionRename.setStatusTip( 'Rename archive based on tags' )
+ self.actionRename.triggered.connect( self.renameArchive )
+
+ self.actionSettings.setShortcut( 'Ctrl+Shift+S' )
+ self.actionSettings.setStatusTip( 'Configure ComicTagger' )
+ self.actionSettings.triggered.connect( self.showSettings )
+
+ # Tag Menu
+ self.actionParse_Filename.setShortcut( 'Ctrl+F' )
+ self.actionParse_Filename.setStatusTip( 'Try to extract tags from filename' )
+ self.actionParse_Filename.triggered.connect( self.useFilename )
+
+ self.actionSearchOnline.setShortcut( 'Ctrl+W' )
+ self.actionSearchOnline.setStatusTip( 'Search online for tags' )
+ self.actionSearchOnline.triggered.connect( self.queryOnline )
+
+ self.actionAutoIdentify.setShortcut( 'Ctrl+I' )
+ self.actionAutoIdentify.triggered.connect( self.autoIdentifySearch )
+
+ self.actionApplyCBLTransform.setShortcut( 'Ctrl+L' )
+ self.actionApplyCBLTransform.setStatusTip( 'Modify tags specifically for CBL format' )
+ self.actionApplyCBLTransform.triggered.connect( self.applyCBLTransform )
+
+ self.actionClearEntryForm.setShortcut( 'Ctrl+Shift+C' )
+ self.actionClearEntryForm.setStatusTip( 'Clear all the data on the screen' )
+ self.actionClearEntryForm.triggered.connect( self.clearForm )
+
+ # Window Menu
+ self.actionPageBrowser.setShortcut( 'Ctrl+P' )
+ self.actionPageBrowser.setStatusTip( 'Show the page browser' )
+ self.actionPageBrowser.triggered.connect( self.showPageBrowser )
+
+ # Help Menu
+ self.actionAbout.setStatusTip( 'Show the ' + self.appName + ' info' )
+ self.actionAbout.triggered.connect( self.aboutApp )
+ self.actionWiki.triggered.connect( self.showWiki )
+ self.actionReportBug.triggered.connect( self.reportBug )
+ self.actionComicTaggerForum.triggered.connect( self.showForum )
+
+ # ToolBar
+
+ self.actionLoad.setIcon(QtGui.QIcon( ComicTaggerSettings.getGraphic('open.png')))
+ self.actionLoadFolder.setIcon(QtGui.QIcon( ComicTaggerSettings.getGraphic('longbox.png')))
+ self.actionWrite_Tags.setIcon(QtGui.QIcon( ComicTaggerSettings.getGraphic('save.png')))
+ self.actionParse_Filename.setIcon(QtGui.QIcon( ComicTaggerSettings.getGraphic('parse.png')))
+ self.actionSearchOnline.setIcon(QtGui.QIcon( ComicTaggerSettings.getGraphic('search.png')))
+ self.actionAutoIdentify.setIcon(QtGui.QIcon( ComicTaggerSettings.getGraphic('auto.png')))
+ self.actionAutoTag.setIcon(QtGui.QIcon( ComicTaggerSettings.getGraphic('autotag.png')))
+ self.actionClearEntryForm.setIcon(QtGui.QIcon( ComicTaggerSettings.getGraphic('clear.png')))
+ self.actionPageBrowser.setIcon(QtGui.QIcon( ComicTaggerSettings.getGraphic('browse.png')))
+
+ self.toolBar.addAction( self.actionLoad )
+ self.toolBar.addAction( self.actionLoadFolder )
+ self.toolBar.addAction( self.actionWrite_Tags )
+ self.toolBar.addAction( self.actionSearchOnline )
+ self.toolBar.addAction( self.actionAutoIdentify )
+ self.toolBar.addAction( self.actionAutoTag )
+ self.toolBar.addAction( self.actionClearEntryForm )
+ self.toolBar.addAction( self.actionPageBrowser )
+
+ def repackageArchive( self ):
+ ca_list = self.fileSelectionList.getSelectedArchiveList()
+ rar_count = 0
+ for ca in ca_list:
+ if ca.isRar( ):
+ rar_count += 1
+
+ if rar_count == 0:
+ QtGui.QMessageBox.information(self, self.tr("Export as Zip Archive"), self.tr("No RAR archives selected!"))
+ return
+
+ if not self.dirtyFlagVerification( "Export as Zip Archive",
+ "If you export archives as Zip now, unsaved data in the form may be lost. Are you sure?"):
+ return
+
+ if rar_count != 0:
+ dlg = ExportWindow( self, self.settings,
+ self.tr("You have selected {0} archive(s) to export to Zip format. New archives will be created in the same folder as the original.\n\nPlease choose options below, and select OK.\n".format(rar_count) ))
+ dlg.adjustSize( )
+ dlg.setModal( True )
+ if not dlg.exec_():
+ return
+
+ progdialog = QtGui.QProgressDialog("", "Cancel", 0, rar_count, self)
+ progdialog.setWindowTitle( "Exporting as ZIP" )
+ progdialog.setWindowModality(QtCore.Qt.ApplicationModal)
+ progdialog.show()
+ prog_idx = 0
+
+ new_archives_to_add = []
+ archives_to_remove = []
+ skipped_list = []
+ failed_list = []
+ success_count = 0
+
+ for ca in ca_list:
+ if ca.isRar():
+ QtCore.QCoreApplication.processEvents()
+ if progdialog.wasCanceled():
+ break
+ progdialog.setValue(prog_idx)
+ prog_idx += 1
+ progdialog.setLabelText( ca.path )
+ centerWindowOnParent( progdialog )
+ QtCore.QCoreApplication.processEvents()
+
+ original_path = os.path.abspath( ca.path )
+ export_name = os.path.splitext(original_path)[0] + ".cbz"
+
+ if os.path.lexists( export_name ):
+ if dlg.fileConflictBehavior == ExportConflictOpts.dontCreate:
+ export_name = None
+ skipped_list.append( ca.path )
+ elif dlg.fileConflictBehavior == ExportConflictOpts.createUnique:
+ export_name = utils.unique_file( export_name )
+
+ if export_name is not None:
+ if ca.exportAsZip( export_name ):
+ success_count += 1
+ if dlg.addToList:
+ new_archives_to_add.append( export_name )
+ if dlg.deleteOriginal:
+ archives_to_remove.append( ca )
+ os.unlink( ca.path )
+
+ else:
+ # last export failed, so remove the zip, if it exists
+ failed_list.append( ca.path )
+ if os.path.lexists( export_name ):
+ os.remove( export_name )
+
+ progdialog.close()
+
+ self.fileSelectionList.addPathList( new_archives_to_add )
+ self.fileSelectionList.removeArchiveList( archives_to_remove )
+
+ summary = u"Successfully created {0} Zip archive(s).".format( success_count )
+ if len( skipped_list ) > 0:
+ summary += u"\n\nThe following {0} RAR archive(s) were skipped due to file name conflicts:\n".format( len( skipped_list ) )
+ for f in skipped_list:
+ summary += u"\t{0}\n".format( f )
+ if len( failed_list ) > 0:
+ summary += u"\n\nThe following {0} RAR archive(s) failed to export due to read/write errors:\n".format( len( failed_list ) )
+ for f in failed_list:
+ summary += u"\t{0}\n".format( f )
+
+ dlg = LogWindow( self )
+ dlg.setText( summary )
+ dlg.setWindowTitle( "Archive Export to Zip Summary" )
+ dlg.exec_()
+
+
+ def aboutApp( self ):
+
+ website = "http://code.google.com/p/comictagger"
+ email = "comictagger@gmail.com"
+ license_link = "http://www.apache.org/licenses/LICENSE-2.0"
+ license_name = "Apache License 2.0"
+
+ msgBox = QtGui.QMessageBox()
+ msgBox.setWindowTitle( self.tr("About " + self.appName ) )
+ msgBox.setTextFormat( QtCore.Qt.RichText )
+ msgBox.setIconPixmap( QtGui.QPixmap(ComicTaggerSettings.getGraphic('about.png')) )
+ msgBox.setText( "
"
+ + self.appName + " v" + self.version + "
"
+ + "(c)2014 Anthony Beville
"
+ + "{0}
".format(website)
+ + "{0}
".format(email)
+ + "License: {1}".format(license_link, license_name) )
+
+ msgBox.setStandardButtons( QtGui.QMessageBox.Ok )
+ msgBox.exec_()
+
+ def dragEnterEvent(self, event):
+ self.droppedFiles = None
+ if event.mimeData().hasUrls():
+
+ # walk through the URL list and build a file list
+ for url in event.mimeData().urls():
+ if url.isValid() and url.scheme() == "file":
+ if self.droppedFiles is None:
+ self.droppedFiles = []
+ self.droppedFiles.append(url.toLocalFile())
+
+ if self.droppedFiles is not None:
+ event.accept()
+
+ def dropEvent(self, event):
+ #if self.dirtyFlagVerification( "Open Archive",
+ # "If you open a new archive now, data in the form will be lost. Are you sure?"):
+ self.fileSelectionList.addPathList( self.droppedFiles )
+ event.accept()
+
+ def actualLoadCurrentArchive( self ):
+ if self.metadata.isEmpty:
+ self.metadata = self.comic_archive.metadataFromFilename( self.settings.parse_scan_info)
+ if len(self.metadata.pages) == 0:
+ self.metadata.setDefaultPageList( self.comic_archive.getNumberOfPages() )
+
+ self.updateCoverImage()
+
+ if self.page_browser is not None:
+ self.page_browser.setComicArchive( self.comic_archive )
+ self.page_browser.metadata = self.metadata
+
+ self.metadataToForm()
+ self.pageListEditor.setData( self.comic_archive, self.metadata.pages )
+ self.clearDirtyFlag() # also updates the app title
+ self.updateInfoBox()
+ self.updateMenus()
+ self.updateAppTitle()
+
+ def updateCoverImage( self ):
+ cover_idx = self.metadata.getCoverPageIndexList()[0]
+ self.archiveCoverWidget.setArchive( self.comic_archive, cover_idx)
+
+ def updateMenus( self ):
+
+ # First just disable all the questionable items
+ self.actionAutoTag.setEnabled( False )
+ self.actionCopyTags.setEnabled( False )
+ self.actionRemoveAuto.setEnabled( False )
+ self.actionRemoveCRTags.setEnabled( False )
+ self.actionRemoveCBLTags.setEnabled( False )
+ self.actionWrite_Tags.setEnabled( False )
+ self.actionRepackage.setEnabled(False)
+ self.actionViewRawCBLTags.setEnabled( False )
+ self.actionViewRawCRTags.setEnabled( False )
+ self.actionParse_Filename.setEnabled( False )
+ self.actionAutoIdentify.setEnabled( False )
+ self.actionRename.setEnabled( False )
+ self.actionApplyCBLTransform.setEnabled( False )
+
+ # now, selectively re-enable
+ if self.comic_archive is not None :
+ has_cix = self.comic_archive.hasCIX()
+ has_cbi = self.comic_archive.hasCBI()
+
+ self.actionParse_Filename.setEnabled( True )
+ self.actionAutoIdentify.setEnabled( True )
+ self.actionAutoTag.setEnabled( True )
+ self.actionRename.setEnabled( True )
+ self.actionApplyCBLTransform.setEnabled( True )
+ self.actionRepackage.setEnabled(True)
+ self.actionRemoveAuto.setEnabled( True )
+ self.actionRemoveCRTags.setEnabled( True )
+ self.actionRemoveCBLTags.setEnabled( True )
+ self.actionCopyTags.setEnabled( True )
+
+ if has_cix:
+ self.actionViewRawCRTags.setEnabled( True )
+ if has_cbi:
+ self.actionViewRawCBLTags.setEnabled( True )
+
+ if self.comic_archive.isWritable():
+ self.actionWrite_Tags.setEnabled( True )
+
+
+ def updateInfoBox( self ):
+
+ ca = self.comic_archive
+
+ if ca is None:
+ self.lblFilename.setText( "" )
+ self.lblArchiveType.setText( "" )
+ self.lblTagList.setText( "" )
+ self.lblPageCount.setText( "" )
+ return
+
+ filename = os.path.basename( ca.path )
+ filename = os.path.splitext(filename)[0]
+ filename = FileNameParser().fixSpaces(filename, False)
+
+ self.lblFilename.setText( filename )
+
+ if ca.isZip():
+ self.lblArchiveType.setText( "ZIP archive" )
+ elif ca.isRar():
+ self.lblArchiveType.setText( "RAR archive" )
+ elif ca.isFolder():
+ self.lblArchiveType.setText( "Folder archive" )
+ else:
+ self.lblArchiveType.setText( "" )
+
+ page_count = " ({0} pages)".format(ca.getNumberOfPages())
+ self.lblPageCount.setText( page_count)
+
+ tag_info = ""
+ if ca.hasCIX():
+ tag_info = u"• ComicRack tags"
+ if ca.hasCBI():
+ if tag_info != "":
+ tag_info += "\n"
+ tag_info += u"• ComicBookLover tags"
+
+ self.lblTagList.setText( tag_info )
- def setDirtyFlag( self, param1=None, param2=None, param3=None ):
- if not self.dirtyFlag:
- self.dirtyFlag = True
- self.fileSelectionList.setModifiedFlag( True )
- self.updateAppTitle()
-
- def clearDirtyFlag( self ):
- if self.dirtyFlag:
- self.dirtyFlag = False
- self.fileSelectionList.setModifiedFlag( False )
- self.updateAppTitle()
-
- def connectDirtyFlagSignals( self ):
- # recursivly connect the tab form child slots
- self.connectChildDirtyFlagSignals( self.tabWidget )
-
- def connectChildDirtyFlagSignals (self, widget ):
-
- if ( isinstance(widget, QtGui.QLineEdit)):
- widget.textEdited.connect(self.setDirtyFlag)
- if ( isinstance(widget, QtGui.QTextEdit)):
- widget.textChanged.connect(self.setDirtyFlag)
- if ( isinstance(widget, QtGui.QComboBox) ):
- widget.currentIndexChanged.connect(self.setDirtyFlag)
- if ( isinstance(widget, QtGui.QCheckBox) ):
- widget.stateChanged.connect(self.setDirtyFlag)
-
- # recursive call on chillun
- for child in widget.children():
- if child != self.pageListEditor:
- self.connectChildDirtyFlagSignals( child )
-
-
- def clearForm( self ):
-
- # get a minty fresh metadata object
- self.metadata = GenericMetadata()
- if self.comic_archive is not None:
- self.metadata.setDefaultPageList( self.comic_archive.getNumberOfPages() )
-
- # recursivly clear the tab form
- self.clearChildren( self.tabWidget )
-
- # clear the dirty flag, since there is nothing in there now to lose
- self.clearDirtyFlag()
-
- self.pageListEditor.setData( self.comic_archive, self.metadata.pages )
-
- def clearChildren (self, widget ):
-
- if ( isinstance(widget, QtGui.QLineEdit) or
- isinstance(widget, QtGui.QTextEdit)):
- widget.setText("")
- if ( isinstance(widget, QtGui.QComboBox) ):
- widget.setCurrentIndex( 0 )
- if ( isinstance(widget, QtGui.QCheckBox) ):
- widget.setChecked( False )
- if ( isinstance(widget, QtGui.QTableWidget) ):
- while widget.rowCount() > 0:
- widget.removeRow(0)
-
- # recursive call on chillun
- for child in widget.children():
- self.clearChildren( child )
-
-
- def metadataToForm( self ):
- # copy the the metadata object into to the form
-
- #helper func
- def assignText( field, value):
- if value is not None:
- field.setText( unicode(value) )
-
- md = self.metadata
-
- assignText( self.leSeries, md.series )
- assignText( self.leIssueNum, md.issue )
- assignText( self.leIssueCount, md.issueCount )
- assignText( self.leVolumeNum, md.volume )
- assignText( self.leVolumeCount, md.volumeCount )
- assignText( self.leTitle, md.title )
- assignText( self.lePublisher, md.publisher )
- assignText( self.lePubMonth, md.month )
- assignText( self.lePubYear, md.year )
- assignText( self.leGenre, md.genre )
- assignText( self.leImprint, md.imprint )
- assignText( self.teComments, md.comments )
- assignText( self.teNotes, md.notes )
- assignText( self.leCriticalRating, md.criticalRating )
- assignText( self.leStoryArc, md.storyArc )
- assignText( self.leScanInfo, md.scanInfo )
- assignText( self.leSeriesGroup, md.seriesGroup )
- assignText( self.leAltSeries, md.alternateSeries )
- assignText( self.leAltIssueNum, md.alternateNumber )
- assignText( self.leAltIssueCount, md.alternateCount )
- assignText( self.leWebLink, md.webLink )
- assignText( self.teCharacters, md.characters )
- assignText( self.teTeams, md.teams )
- assignText( self.teLocations, md.locations )
-
- if md.format is not None and md.format != "":
- i = self.cbFormat.findText( md.format )
- if i == -1:
- self.cbFormat.setEditText( md.format )
- else:
- self.cbFormat.setCurrentIndex( i )
-
- if md.maturityRating is not None and md.maturityRating != "":
- i = self.cbMaturityRating.findText( md.maturityRating )
- if i == -1:
- self.cbMaturityRating.setEditText( md.maturityRating )
- else:
- self.cbMaturityRating.setCurrentIndex( i )
-
- if md.language is not None:
- i = self.cbLanguage.findData( md.language )
- self.cbLanguage.setCurrentIndex( i )
-
- if md.country is not None:
- i = self.cbCountry.findText( md.country )
- self.cbCountry.setCurrentIndex( i )
-
- if md.manga is not None:
- i = self.cbManga.findData( md.manga )
- self.cbManga.setCurrentIndex( i )
-
- if md.blackAndWhite is not None and md.blackAndWhite:
- self.cbBW.setChecked( True )
-
- assignText( self.teTags, utils.listToString( md.tags ) )
-
- # !!! Should we clear the credits table or just avoid duplicates?
- while self.twCredits.rowCount() > 0:
- self.twCredits.removeRow(0)
-
- if md.credits is not None and len(md.credits) != 0:
-
- self.twCredits.setSortingEnabled( False )
-
- row = 0
- for credit in md.credits:
- # if the role-person pair already exists, just skip adding it to the list
- if self.isDupeCredit( credit['role'].title(), credit['person']):
- continue
-
- self.addNewCreditEntry( row, credit['role'].title(), credit['person'], (credit['primary'] if credit.has_key('primary') else False ) )
-
- row += 1
-
- self.twCredits.setSortingEnabled( True )
- self.updateCreditColors()
-
- def addNewCreditEntry( self, row, role, name, primary_flag=False ):
- self.twCredits.insertRow(row)
-
- item_text = role
- item = QtGui.QTableWidgetItem(item_text)
- item.setFlags(QtCore.Qt.ItemIsSelectable| QtCore.Qt.ItemIsEnabled)
- item.setData( QtCore.Qt.ToolTipRole, item_text )
- self.twCredits.setItem(row, 1, item)
-
-
- item_text = name
- item = QtGui.QTableWidgetItem(item_text)
- item.setData( QtCore.Qt.ToolTipRole, item_text )
- item.setFlags(QtCore.Qt.ItemIsSelectable| QtCore.Qt.ItemIsEnabled)
- self.twCredits.setItem(row, 2, item)
-
- item = QtGui.QTableWidgetItem("")
- item.setFlags(QtCore.Qt.ItemIsSelectable| QtCore.Qt.ItemIsEnabled)
- self.twCredits.setItem(row, 0, item)
- self.updateCreditPrimaryFlag( row, primary_flag )
-
- def isDupeCredit( self, role, name ):
- r = 0
- while r < self.twCredits.rowCount():
- if ( self.twCredits.item(r, 1).text() == role and
- self.twCredits.item(r, 2).text() == name ):
- return True
- r = r + 1
-
- return False
-
- def formToMetadata( self ):
-
- #helper func
- def xlate( data, type_str):
- s = u"{0}".format(data).strip()
- if s == "":
- return None
- elif type_str == "str":
- return s
- else:
- return int(s)
-
- # copy the data from the form into the metadata
- md = self.metadata
- md.series = xlate( self.leSeries.text(), "str" )
- md.issue = xlate( self.leIssueNum.text(), "str" )
- md.issueCount = xlate( self.leIssueCount.text(), "int" )
- md.volume = xlate( self.leVolumeNum.text(), "int" )
- md.volumeCount = xlate( self.leVolumeCount.text(), "int" )
- md.title = xlate( self.leTitle.text(), "str" )
- md.publisher = xlate( self.lePublisher.text(), "str" )
- md.month = xlate( self.lePubMonth.text(), "int" )
- md.year = xlate( self.lePubYear.text(), "int" )
- md.genre = xlate( self.leGenre.text(), "str" )
- md.imprint = xlate( self.leImprint.text(), "str" )
- md.comments = xlate( self.teComments.toPlainText(), "str" )
- md.notes = xlate( self.teNotes.toPlainText(), "str" )
- md.criticalRating = xlate( self.leCriticalRating.text(), "int" )
- md.maturityRating = xlate( self.cbMaturityRating.currentText(), "str" )
-
- md.storyArc = xlate( self.leStoryArc.text(), "str" )
- md.scanInfo = xlate( self.leScanInfo.text(), "str" )
- md.seriesGroup = xlate( self.leSeriesGroup.text(), "str" )
- md.alternateSeries = xlate( self.leAltSeries.text(), "str" )
- md.alternateNumber = xlate( self.leAltIssueNum.text(), "int" )
- md.alternateCount = xlate( self.leAltIssueCount.text(), "int" )
- md.webLink = xlate( self.leWebLink.text(), "str" )
- md.characters = xlate( self.teCharacters.toPlainText(), "str" )
- md.teams = xlate( self.teTeams.toPlainText(), "str" )
- md.locations = xlate( self.teLocations.toPlainText(), "str" )
-
- md.format = xlate( self.cbFormat.currentText(), "str" )
- md.country = xlate( self.cbCountry.currentText(), "str" )
-
- langiso = self.cbLanguage.itemData(self.cbLanguage.currentIndex()).toString()
- md.language = xlate( langiso, "str" )
-
- manga_code = self.cbManga.itemData(self.cbManga.currentIndex()).toString()
- md.manga = xlate( manga_code, "str" )
-
- # Make a list from the coma delimited tags string
- tmp = xlate( self.teTags.toPlainText(), "str" )
- if tmp != None:
- def striplist(l):
- return([x.strip() for x in l])
-
- md.tags = striplist(tmp.split( "," ))
-
- if ( self.cbBW.isChecked() ):
- md.blackAndWhite = True
- else:
- md.blackAndWhite = False
-
- # get the credits from the table
- md.credits = list()
- row = 0
- while row < self.twCredits.rowCount():
- role = u"{0}".format(self.twCredits.item(row, 1).text())
- name = u"{0}".format(self.twCredits.item(row, 2).text())
- primary_flag = self.twCredits.item( row, 0 ).text() != ""
-
- md.addCredit( name, role, bool(primary_flag) )
- row += 1
-
- md.pages = self.pageListEditor.getPageList()
-
- def useFilename( self ):
- if self.comic_archive is not None:
- #copy the form onto metadata object
- self.formToMetadata()
- new_metadata = self.comic_archive.metadataFromFilename(self.settings.parse_scan_info)
- if new_metadata is not None:
- self.metadata.overlay( new_metadata )
- self.metadataToForm()
-
- def selectFolder( self ):
- self.selectFile( folder_mode=True )
-
- def selectFile( self , folder_mode = False):
-
- dialog = QtGui.QFileDialog(self)
- if folder_mode:
- dialog.setFileMode(QtGui.QFileDialog.Directory)
- else:
- dialog.setFileMode(QtGui.QFileDialog.ExistingFiles)
-
- if self.settings.last_opened_folder is not None:
- dialog.setDirectory( self.settings.last_opened_folder )
- #dialog.setFileMode(QtGui.QFileDialog.Directory )
-
- if not folder_mode:
- if platform.system() != "Windows" and utils.which("unrar") is None:
- archive_filter = "Comic archive files (*.cbz *.zip)"
- else:
- archive_filter = "Comic archive files (*.cbz *.zip *.cbr *.rar)"
- filters = [
- archive_filter,
- "Any files (*)"
- ]
- dialog.setNameFilters(filters)
-
- if (dialog.exec_()):
- fileList = dialog.selectedFiles()
- #if self.dirtyFlagVerification( "Open Archive",
- # "If you open a new archive now, data in the form will be lost. Are you sure?"):
- self.fileSelectionList.addPathList( fileList )
-
- def autoIdentifySearch(self):
- if self.comic_archive is None:
- QtGui.QMessageBox.warning(self, self.tr("Automatic Identify Search"),
- self.tr("You need to load a comic first!"))
- return
-
- self.queryOnline( autoselect=True )
-
- def queryOnline(self, autoselect=False):
-
- issue_number = unicode(self.leIssueNum.text()).strip()
-
- if autoselect and issue_number == "":
- QtGui.QMessageBox.information(self,"Automatic Identify Search", "Can't auto-identify without an issue number (yet!)")
- return
-
- if unicode(self.leSeries.text()).strip() != "":
- series_name = unicode(self.leSeries.text()).strip()
- else:
- QtGui.QMessageBox.information(self, self.tr("Online Search"), self.tr("Need to enter a series name to search."))
- return
-
- year = str(self.lePubYear.text()).strip()
- if year == "":
- year = None
-
- issue_count = str(self.leIssueCount.text()).strip()
- if issue_count == "":
- issue_count = None
-
- cover_index_list = self.metadata.getCoverPageIndexList()
- selector = VolumeSelectionWindow( self, series_name, issue_number, year, issue_count, cover_index_list, self.comic_archive, self.settings, autoselect )
-
- title = "Search: '" + series_name + "' - "
- selector.setWindowTitle( title + "Select Series")
-
- selector.setModal(True)
- selector.exec_()
-
- if selector.result():
- #we should now have a volume ID
- QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
-
- #copy the form onto metadata object
- self.formToMetadata()
-
- try:
- comicVine = ComicVineTalker()
- new_metadata = comicVine.fetchIssueData( selector.volume_id, selector.issue_number, self.settings )
- except ComicVineTalkerException as e:
- QtGui.QApplication.restoreOverrideCursor()
- if e.code == ComicVineTalkerException.RateLimit:
- QtGui.QMessageBox.critical(self, self.tr("Comic Vine Error"), ComicVineTalker.getRateLimitMessage())
- else:
- QtGui.QMessageBox.critical(self, self.tr("Network Issue"), self.tr("Could not connect to ComicVine to get issue details.!"))
- else:
- QtGui.QApplication.restoreOverrideCursor()
- if new_metadata is not None:
-
- if self.settings.apply_cbl_transform_on_cv_import:
- new_metadata = CBLTransformer( new_metadata, self.settings ).apply()
-
- if self.settings.clear_form_before_populating_from_cv:
- self.clearForm()
-
- self.metadata.overlay( new_metadata )
- # Now push the new combined data into the edit controls
- self.metadataToForm()
- else:
- QtGui.QMessageBox.critical(self, self.tr("Search"), self.tr("Could not find an issue {0} for that series".format(selector.issue_number)))
-
-
- def commitMetadata(self):
-
- if ( self.metadata is not None and self.comic_archive is not None):
- reply = QtGui.QMessageBox.question(self,
- self.tr("Save Tags"),
- self.tr("Are you sure you wish to save " + MetaDataStyle.name[self.save_data_style] + " tags to this archive?"),
- QtGui.QMessageBox.Yes, QtGui.QMessageBox.No )
-
- if reply == QtGui.QMessageBox.Yes:
- QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
- self.formToMetadata()
-
- success = self.comic_archive.writeMetadata( self.metadata, self.save_data_style )
- self.comic_archive.loadCache( [ MetaDataStyle.CBI, MetaDataStyle.CIX ] )
- QtGui.QApplication.restoreOverrideCursor()
-
- if not success:
- QtGui.QMessageBox.warning(self, self.tr("Save failed"), self.tr("The tag save operation seemed to fail!"))
- else:
- self.clearDirtyFlag()
- self.updateInfoBox()
- self.updateMenus()
- #QtGui.QMessageBox.information(self, self.tr("Yeah!"), self.tr("File written."))
- self.fileSelectionList.updateCurrentRow()
-
- else:
- QtGui.QMessageBox.information(self, self.tr("Whoops!"), self.tr("No data to commit!"))
-
-
- def setLoadDataStyle(self, s):
- if self.dirtyFlagVerification( "Change Tag Read Style",
- "If you change read tag style now, data in the form will be lost. Are you sure?"):
- self.load_data_style, b = self.cbLoadDataStyle.itemData(s).toInt()
- self.settings.last_selected_load_data_style = self.load_data_style
- self.updateMenus()
- if self.comic_archive is not None:
- self.loadArchive( self.comic_archive )
- else:
- self.cbLoadDataStyle.currentIndexChanged.disconnect(self.setLoadDataStyle)
- self.adjustLoadStyleCombo()
- self.cbLoadDataStyle.currentIndexChanged.connect(self.setLoadDataStyle)
-
- def setSaveDataStyle(self, s):
- self.save_data_style, b = self.cbSaveDataStyle.itemData(s).toInt()
-
- self.settings.last_selected_save_data_style = self.save_data_style
- self.updateStyleTweaks()
- self.updateMenus()
-
- def updateCreditColors( self ):
- inactive_color = QtGui.QColor(255, 170, 150)
- active_palette = self.leSeries.palette()
- active_color = active_palette.color( QtGui.QPalette.Base )
-
- cix_credits = ComicInfoXml().getParseableCredits()
-
- if self.save_data_style == MetaDataStyle.CIX:
- #loop over credit table, mark selected rows
- r = 0
- while r < self.twCredits.rowCount():
- if str(self.twCredits.item(r, 1).text()).lower() not in cix_credits:
- self.twCredits.item(r, 1).setBackgroundColor( inactive_color )
- else:
- self.twCredits.item(r, 1).setBackgroundColor( active_color )
- # turn off entire primary column
- self.twCredits.item(r, 0).setBackgroundColor( inactive_color )
- r = r + 1
-
- if self.save_data_style == MetaDataStyle.CBI:
- #loop over credit table, make all active color
- r = 0
- while r < self.twCredits.rowCount():
- self.twCredits.item(r, 0).setBackgroundColor( active_color )
- self.twCredits.item(r, 1).setBackgroundColor( active_color )
- r = r + 1
-
-
- def updateStyleTweaks( self ):
-
- # depending on the current data style, certain fields are disabled
-
- inactive_color = QtGui.QColor(255, 170, 150)
- active_palette = self.leSeries.palette()
-
- inactive_palette1 = self.leSeries.palette()
- inactive_palette1.setColor(QtGui.QPalette.Base, inactive_color)
-
- inactive_palette2 = self.leSeries.palette()
-
- inactive_palette3 = self.leSeries.palette()
- inactive_palette3.setColor(QtGui.QPalette.Base, inactive_color)
-
- inactive_palette3.setColor(QtGui.QPalette.Base, inactive_color)
-
- #helper func
- def enableWidget( item, enable ):
- inactive_palette3.setColor(item.backgroundRole(), inactive_color)
- inactive_palette2.setColor(item.backgroundRole(), inactive_color)
- inactive_palette3.setColor(item.foregroundRole(), inactive_color)
-
- if enable:
- item.setPalette(active_palette)
- item.setAutoFillBackground( False )
- if type(item) == QtGui.QCheckBox:
- item.setEnabled( True )
- elif type(item) == QtGui.QComboBox:
- item.setEnabled( True )
- else:
- item.setReadOnly( False )
- else:
- item.setAutoFillBackground( True )
- if type(item) == QtGui.QCheckBox:
- item.setPalette(inactive_palette2)
- item.setEnabled( False )
- elif type(item) == QtGui.QComboBox:
- item.setPalette(inactive_palette3)
- item.setEnabled( False )
- else:
- item.setReadOnly( True )
- item.setPalette(inactive_palette1)
-
-
- cbi_only = [ self.leVolumeCount, self.cbCountry, self.leCriticalRating, self.teTags ]
- cix_only = [
- self.leImprint, self.teNotes, self.cbBW, self.cbManga,
- self.leStoryArc, self.leScanInfo, self.leSeriesGroup,
- self.leAltSeries, self.leAltIssueNum, self.leAltIssueCount,
- self.leWebLink, self.teCharacters, self.teTeams,
- self.teLocations, self.cbMaturityRating, self.cbFormat
- ]
-
- if self.save_data_style == MetaDataStyle.CIX:
- for item in cix_only:
- enableWidget( item, True )
- for item in cbi_only:
- enableWidget(item, False )
-
- if self.save_data_style == MetaDataStyle.CBI:
- for item in cbi_only:
- enableWidget( item, True )
- for item in cix_only:
- enableWidget(item, False )
-
- self.updateCreditColors()
- self.pageListEditor.setMetadataStyle( self.save_data_style )
-
- def cellDoubleClicked( self, r, c ):
- self.editCredit()
-
- def addCredit( self ):
- self.modifyCredits( "add" )
-
- def editCredit( self ):
- if ( self.twCredits.currentRow() > -1 ):
- self.modifyCredits( "edit" )
-
- def updateCreditPrimaryFlag( self, row, primary ):
-
- # if we're clearing a flagm do it and quit
- if not primary:
- self.twCredits.item(row, 0).setText( "" )
- return
-
- # otherwise, we need to check for, and clear, other primaries with same role
- role = str(self.twCredits.item(row, 1).text())
- r = 0
- while r < self.twCredits.rowCount():
- if ( self.twCredits.item(r, 0).text() != "" and
- str(self.twCredits.item(r, 1).text()).lower() == role.lower() ):
- self.twCredits.item(r, 0).setText( "" )
- r = r + 1
-
- # Now set our new primary
- self.twCredits.item(row, 0).setText( "Yes" )
-
- def modifyCredits( self , action ):
-
- if action == "edit":
- row = self.twCredits.currentRow()
- role = self.twCredits.item( row, 1 ).text()
- name = self.twCredits.item( row, 2 ).text()
- primary = self.twCredits.item( row, 0 ).text() != ""
- else:
- role = ""
- name = ""
- primary = False
-
- editor = CreditEditorWindow( self, CreditEditorWindow.ModeEdit, role, name, primary )
- editor.setModal(True)
- editor.exec_()
- if editor.result():
- new_role, new_name, new_primary = editor.getCredits()
-
- if new_name == name and new_role == role and new_primary == primary:
- #nothing has changed, just quit
- return
-
- # name and role is the same, but primary flag changed
- if new_name == name and new_role == role:
- self.updateCreditPrimaryFlag( row, new_primary )
- return
-
- # check for dupes
- ok_to_mod = True
- if self.isDupeCredit( new_role, new_name):
- # delete the dupe credit from list
- reply = QtGui.QMessageBox.question(self,
- self.tr("Duplicate Credit!"),
- self.tr("This will create a duplicate credit entry. Would you like to merge the entries, or create a duplicate?"),
- self.tr("Merge"), self.tr("Duplicate" ))
-
- if reply == 0:
- # merge
- if action == "edit":
- # just remove the row that would be same
- self.twCredits.removeRow( row )
- # TODO -- need to find the row of the dupe, and possible change the primary flag
-
- ok_to_mod = False
-
-
- if ok_to_mod:
- #modify it
- if action == "edit":
- self.twCredits.item(row, 1).setText( new_role )
- self.twCredits.item(row, 2).setText( new_name )
- self.updateCreditPrimaryFlag( row, new_primary )
- else:
- # add new entry
- row = self.twCredits.rowCount()
- self.addNewCreditEntry( row, new_role, new_name, new_primary)
-
- self.updateCreditColors()
- self.setDirtyFlag()
-
- def removeCredit( self ):
- row = self.twCredits.currentRow()
- if row != -1 :
- self.twCredits.removeRow( row )
- self.setDirtyFlag()
-
- def showSettings( self ):
-
- settingswin = SettingsWindow( self, self.settings )
- settingswin.setModal(True)
- settingswin.exec_()
- if settingswin.result():
- pass
-
- def setAppPosition( self ):
- if self.settings.last_main_window_width != 0:
- self.move( self.settings.last_main_window_x, self.settings.last_main_window_y )
- self.resize( self.settings.last_main_window_width, self.settings.last_main_window_height )
- else:
- screen = QtGui.QDesktopWidget().screenGeometry()
- size = self.frameGeometry()
- self.move((screen.width()-size.width())/2, (screen.height()-size.height())/2)
-
-
- def adjustLoadStyleCombo( self ):
- # select the current style
- if ( self.load_data_style == MetaDataStyle.CBI ):
- self.cbLoadDataStyle.setCurrentIndex ( 0 )
- elif ( self.load_data_style == MetaDataStyle.CIX ):
- self.cbLoadDataStyle.setCurrentIndex ( 1 )
-
- def adjustSaveStyleCombo( self ):
- # select the current style
- if ( self.save_data_style == MetaDataStyle.CBI ):
- self.cbSaveDataStyle.setCurrentIndex ( 0 )
- elif ( self.save_data_style == MetaDataStyle.CIX ):
- self.cbSaveDataStyle.setCurrentIndex ( 1 )
- self.updateStyleTweaks()
-
-
- def populateComboBoxes( self ):
-
- # Add the entries to the tag style combobox
- self.cbLoadDataStyle.addItem( "ComicBookLover", MetaDataStyle.CBI )
- self.cbLoadDataStyle.addItem( "ComicRack", MetaDataStyle.CIX )
- self.adjustLoadStyleCombo()
-
- self.cbSaveDataStyle.addItem( "ComicBookLover", MetaDataStyle.CBI )
- self.cbSaveDataStyle.addItem( "ComicRack", MetaDataStyle.CIX )
- self.adjustSaveStyleCombo()
-
- # Add the entries to the country combobox
- self.cbCountry.addItem( "", "" )
- for c in utils.countries:
- self.cbCountry.addItem( c[1], c[0] )
-
- # Add the entries to the language combobox
- self.cbLanguage.addItem( "", "" )
- lang_dict = utils.getLanguageDict()
- for key in sorted(lang_dict, cmp=locale.strcoll, key=lang_dict.get):
- self.cbLanguage.addItem( lang_dict[key], key )
-
- # Add the entries to the manga combobox
- self.cbManga.addItem( "", "" )
- self.cbManga.addItem( "Yes", "Yes" )
- self.cbManga.addItem( "Yes (Right to Left)", "YesAndRightToLeft" )
- self.cbManga.addItem( "No", "No" )
-
- # Add the entries to the maturity combobox
- self.cbMaturityRating.addItem( "", "" )
- self.cbMaturityRating.addItem( "Everyone", "" )
- self.cbMaturityRating.addItem( "G", "" )
- self.cbMaturityRating.addItem( "Early Childhood", "" )
- self.cbMaturityRating.addItem( "Everyone 10+", "" )
- self.cbMaturityRating.addItem( "PG", "" )
- self.cbMaturityRating.addItem( "Kids to Adults", "" )
- self.cbMaturityRating.addItem( "Teen", "" )
- self.cbMaturityRating.addItem( "MA15+", "" )
- self.cbMaturityRating.addItem( "Mature 17+", "" )
- self.cbMaturityRating.addItem( "R18+", "" )
- self.cbMaturityRating.addItem( "X18+", "" )
- self.cbMaturityRating.addItem( "Adults Only 18+", "" )
- self.cbMaturityRating.addItem( "Rating Pending", "" )
-
- # Add entries to the format combobox
- self.cbFormat.addItem("")
- self.cbFormat.addItem(".1")
- self.cbFormat.addItem("-1")
- self.cbFormat.addItem("1 Shot")
- self.cbFormat.addItem("1/2")
- self.cbFormat.addItem("1-Shot")
- self.cbFormat.addItem("Annotation")
- self.cbFormat.addItem("Annotations")
- self.cbFormat.addItem("Annual")
- self.cbFormat.addItem("Anthology")
- self.cbFormat.addItem("B&W")
- self.cbFormat.addItem("B/W")
- self.cbFormat.addItem("B&&W")
- self.cbFormat.addItem("Black & White")
- self.cbFormat.addItem("Box Set")
- self.cbFormat.addItem("Box-Set")
- self.cbFormat.addItem("Crossover")
- self.cbFormat.addItem("Director's Cut")
- self.cbFormat.addItem("Epilogue")
- self.cbFormat.addItem("Event")
- self.cbFormat.addItem("FCBD")
- self.cbFormat.addItem("Flyer")
- self.cbFormat.addItem("Giant")
- self.cbFormat.addItem("Giant Size")
- self.cbFormat.addItem("Giant-Size")
- self.cbFormat.addItem("Graphic Novel")
- self.cbFormat.addItem("Hardcover")
- self.cbFormat.addItem("Hard-Cover")
- self.cbFormat.addItem("King")
- self.cbFormat.addItem("King Size")
- self.cbFormat.addItem("King-Size")
- self.cbFormat.addItem("Limited Series")
- self.cbFormat.addItem("Magazine")
- self.cbFormat.addItem("-1")
- self.cbFormat.addItem("NSFW")
- self.cbFormat.addItem("One Shot")
- self.cbFormat.addItem("One-Shot")
- self.cbFormat.addItem("Point 1")
- self.cbFormat.addItem("Preview")
- self.cbFormat.addItem("Prologue")
- self.cbFormat.addItem("Reference")
- self.cbFormat.addItem("Review")
- self.cbFormat.addItem("Reviewed")
- self.cbFormat.addItem("Scanlation")
- self.cbFormat.addItem("Script")
- self.cbFormat.addItem("Series")
- self.cbFormat.addItem("Sketch")
- self.cbFormat.addItem("Special")
- self.cbFormat.addItem("TPB")
- self.cbFormat.addItem("Trade Paper Back")
- self.cbFormat.addItem("WebComic")
- self.cbFormat.addItem("Web Comic")
- self.cbFormat.addItem("Year 1")
- self.cbFormat.addItem("Year One")
-
- def removeAuto( self ):
- self.removeTags( self.save_data_style )
-
- def removeCBLTags( self ):
- self.removeTags( MetaDataStyle.CBI )
-
- def removeCRTags( self ):
- self.removeTags( MetaDataStyle.CIX )
-
- def removeTags( self, style):
- # remove the indicated tags from the archive
- ca_list = self.fileSelectionList.getSelectedArchiveList()
- has_md_count = 0
- for ca in ca_list:
- if ca.hasMetadata( style ):
- has_md_count += 1
-
- if has_md_count == 0:
- QtGui.QMessageBox.information(self, self.tr("Remove Tags"),
- self.tr("No archives with {0} tags selected!".format(MetaDataStyle.name[style])))
- return
-
- if has_md_count != 0 and not self.dirtyFlagVerification( "Remove Tags",
- "If you remove tags now, unsaved data in the form will be lost. Are you sure?"):
- return
-
- if has_md_count != 0:
- reply = QtGui.QMessageBox.question(self,
- self.tr("Remove Tags"),
- self.tr("Are you sure you wish to remove the {0} tags from {1} archive(s)?".format(MetaDataStyle.name[style], has_md_count)),
- QtGui.QMessageBox.Yes, QtGui.QMessageBox.No )
-
- if reply == QtGui.QMessageBox.Yes:
- progdialog = QtGui.QProgressDialog("", "Cancel", 0, has_md_count, self)
- progdialog.setWindowTitle( "Removing Tags" )
- progdialog.setWindowModality(QtCore.Qt.ApplicationModal)
- progdialog.show()
- prog_idx = 0
-
- failed_list = []
- success_count = 0
- for ca in ca_list:
- if ca.hasMetadata( style ):
- QtCore.QCoreApplication.processEvents()
- if progdialog.wasCanceled():
- break
- progdialog.setValue(prog_idx)
- prog_idx += 1
- progdialog.setLabelText( ca.path )
- centerWindowOnParent( progdialog )
- QtCore.QCoreApplication.processEvents()
-
- if ca.hasMetadata( style ) and ca.isWritable():
- if not ca.removeMetadata( style ):
- failed_list.append( ca.path )
- else:
- success_count += 1
- ca.loadCache( [ MetaDataStyle.CBI, MetaDataStyle.CIX ] )
-
- progdialog.close()
- self.fileSelectionList.updateSelectedRows()
- self.updateInfoBox()
- self.updateMenus()
-
- summary = u"Successfully removed tags in {0} archive(s).".format( success_count )
- if len( failed_list ) > 0:
- summary += u"\n\nThe remove operation failed in the following {0} archive(s):\n".format( len( failed_list ) )
- for f in failed_list:
- summary += u"\t{0}\n".format( f )
-
- dlg = LogWindow( self )
- dlg.setText( summary )
- dlg.setWindowTitle( "Tag Remove Summary" )
- #dlg.adjustSize()
- dlg.exec_()
-
- def copyTags( self ):
- # copy the indicated tags in the archive
- ca_list = self.fileSelectionList.getSelectedArchiveList()
- has_src_count = 0
-
- src_style = self.load_data_style
- dest_style = self.save_data_style
-
- if src_style == dest_style:
- QtGui.QMessageBox.information(self, self.tr("Copy Tags"), self.tr("Can't copy tag style onto itself." +
- " Read style and modify style must be different."))
- return
-
- for ca in ca_list:
- if ca.hasMetadata( src_style ):
- has_src_count += 1
-
- if has_src_count == 0:
- QtGui.QMessageBox.information(self, self.tr("Copy Tags"), self.tr("No archives with {0} tags selected!".format(
- MetaDataStyle.name[src_style])))
- return
-
- if has_src_count != 0 and not self.dirtyFlagVerification( "Copy Tags",
- "If you copy tags now, unsaved data in the form may be lost. Are you sure?"):
- return
-
- if has_src_count != 0:
- reply = QtGui.QMessageBox.question(self,
- self.tr("Copy Tags"),
- self.tr("Are you sure you wish to copy the {0} tags to {1} tags in {2} archive(s)?".format(
- MetaDataStyle.name[src_style], MetaDataStyle.name[dest_style], has_src_count)),
- QtGui.QMessageBox.Yes, QtGui.QMessageBox.No )
-
- if reply == QtGui.QMessageBox.Yes:
- progdialog = QtGui.QProgressDialog("", "Cancel", 0, has_src_count, self)
- progdialog.setWindowTitle( "Copying Tags" )
- progdialog.setWindowModality(QtCore.Qt.ApplicationModal)
- progdialog.show()
- prog_idx = 0
-
- failed_list = []
- success_count = 0
- for ca in ca_list:
- if ca.hasMetadata( src_style ):
- QtCore.QCoreApplication.processEvents()
- if progdialog.wasCanceled():
- break
- progdialog.setValue(prog_idx)
- prog_idx += 1
- progdialog.setLabelText( ca.path )
- centerWindowOnParent( progdialog )
- QtCore.QCoreApplication.processEvents()
-
- if ca.hasMetadata( src_style ) and ca.isWritable():
- md = ca.readMetadata( src_style )
-
- if dest_style == MetaDataStyle.CBI and self.settings.apply_cbl_transform_on_bulk_operation:
- md = CBLTransformer( md, self.settings ).apply()
-
- if not ca.writeMetadata( md, dest_style ):
- failed_list.append( ca.path )
- else:
- success_count += 1
-
- ca.loadCache( [ MetaDataStyle.CBI, MetaDataStyle.CIX ] )
-
- progdialog.close()
- self.fileSelectionList.updateSelectedRows()
- self.updateInfoBox()
- self.updateMenus()
-
- summary = u"Successfully copied tags in {0} archive(s).".format( success_count )
- if len( failed_list ) > 0:
- summary += u"\n\nThe copy operation failed in the following {0} archive(s):\n".format( len( failed_list ) )
- for f in failed_list:
- summary += u"\t{0}\n".format( f )
-
- dlg = LogWindow( self )
- dlg.setText( summary )
- dlg.setWindowTitle( "Tag Copy Summary" )
- dlg.exec_()
-
- def actualIssueDataFetch( self, match ):
-
- # now get the particular issue data
- cv_md = None
- QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
-
- try:
- comicVine = ComicVineTalker( )
- comicVine.wait_for_rate_limit = self.settings.wait_and_retry_on_rate_limit
- cv_md = comicVine.fetchIssueData( match['volume_id'], match['issue_number'], self.settings )
- except ComicVineTalkerException:
- print "Network error while getting issue details. Save aborted"
-
- if cv_md is not None:
- if self.settings.apply_cbl_transform_on_cv_import:
- cv_md = CBLTransformer( cv_md, self.settings ).apply()
-
- QtGui.QApplication.restoreOverrideCursor()
-
- return cv_md
-
- def autoTagLog( self, text ):
- IssueIdentifier.defaultWriteOutput( text )
- if self.atprogdialog is not None:
- self.atprogdialog.textEdit.insertPlainText(text)
- self.atprogdialog.textEdit.ensureCursorVisible()
- QtCore.QCoreApplication.processEvents()
- QtCore.QCoreApplication.processEvents()
- QtCore.QCoreApplication.processEvents()
-
- def identifyAndTagSingleArchive( self, ca, match_results, dlg):
- success = False
- ii = IssueIdentifier( ca, self.settings )
-
- # read in metadata, and parse file name if not there
- md = ca.readMetadata( self.save_data_style )
- if md.isEmpty:
- md = ca.metadataFromFilename(self.settings.parse_scan_info)
- if dlg.ignoreLeadingDigitsInFilename and md.series is not None:
- #remove all leading numbers
- md.series = re.sub( "([\d.]*)(.*)", "\\2", md.series)
-
- # use the dialog specified search string
- if dlg.searchString is not None:
- md.series = dlg.searchString
-
- if md is None or md.isEmpty:
- print "!!!!No metadata given to search online with!"
- return False, match_results
-
- if dlg.dontUseYear:
- md.year = None
- if dlg.assumeIssueOne and ( md.issue is None or md.issue == ""):
- md.issue = "1"
- ii.setAdditionalMetadata( md )
- ii.onlyUseAdditionalMetaData = True
- ii.waitAndRetryOnRateLimit = dlg.waitAndRetryOnRateLimit
- ii.setOutputFunction( self.autoTagLog )
- ii.cover_page_index = md.getCoverPageIndexList()[0]
- ii.setCoverURLCallback( self.atprogdialog.setTestImage )
- ii.setNameLengthDeltaThreshold( dlg.nameLengthMatchTolerance )
-
- matches = ii.search()
-
- result = ii.search_result
-
- found_match = False
- choices = False
- low_confidence = False
- no_match = False
-
- if result == ii.ResultNoMatches:
- pass
- elif result == ii.ResultFoundMatchButBadCoverScore:
- low_confidence = True
- found_match = True
- elif result == ii.ResultFoundMatchButNotFirstPage :
- found_match = True
- elif result == ii.ResultMultipleMatchesWithBadImageScores:
- low_confidence = True
- choices = True
- elif result == ii.ResultOneGoodMatch:
- found_match = True
- elif result == ii.ResultMultipleGoodMatches:
- choices = True
-
- if choices:
- if low_confidence:
- self.autoTagLog( "Online search: Multiple low-confidence matches. Save aborted\n" )
- match_results.lowConfidenceMatches.append(MultipleMatch(ca,matches))
- else:
- self.autoTagLog( "Online search: Multiple matches. Save aborted\n" )
- match_results.multipleMatches.append(MultipleMatch(ca,matches))
- elif low_confidence and not dlg.autoSaveOnLow:
- self.autoTagLog( "Online search: Low confidence match. Save aborted\n" )
- match_results.lowConfidenceMatches.append(MultipleMatch(ca,matches))
- elif not found_match:
- self.autoTagLog( "Online search: No match found. Save aborted\n" )
- match_results.noMatches.append(ca.path)
- else:
- # a single match!
- if low_confidence:
- self.autoTagLog( "Online search: Low confidence match, but saving anyways, as indicated...\n" )
-
- # now get the particular issue data
- cv_md = self.actualIssueDataFetch( matches[0] )
- if cv_md is None:
- match_results.fetchDataFailures.append(ca.path)
-
- if cv_md is not None:
- md.overlay( cv_md )
-
- if not ca.writeMetadata( md, self.save_data_style ):
- match_results.writeFailures.append(ca.path)
- self.autoTagLog( "Save failed ;-(\n" )
- else:
- match_results.goodMatches.append(ca.path)
- success = True
- self.autoTagLog( "Save complete!\n" )
- ca.loadCache( [ MetaDataStyle.CBI, MetaDataStyle.CIX ] )
-
- return success, match_results
-
- def autoTag( self ):
- ca_list = self.fileSelectionList.getSelectedArchiveList()
- style = self.save_data_style
-
- if len(ca_list) == 0:
- QtGui.QMessageBox.information(self, self.tr("Auto-Tag"), self.tr("No archives selected!"))
- return
-
- if not self.dirtyFlagVerification( "Auto-Tag",
- "If you auto-tag now, unsaved data in the form will be lost. Are you sure?"):
- return
-
- atstartdlg = AutoTagStartWindow( self, self.settings,
- self.tr("You have selected {0} archive(s) to automatically identify and write {1} tags to.\n\n".format(len(ca_list), MetaDataStyle.name[style]) +
- "Please choose options below, and select OK to Auto-Tag.\n" ))
-
- atstartdlg.adjustSize( )
- atstartdlg.setModal( True )
- if not atstartdlg.exec_():
- return
-
- self.atprogdialog = AutoTagProgressWindow( self)
- self.atprogdialog.setModal(True)
- self.atprogdialog.show()
- self.atprogdialog.progressBar.setMaximum( len(ca_list) )
- self.atprogdialog.setWindowTitle( "Auto-Tagging" )
-
- self.autoTagLog( u"========================================================================\n" )
- self.autoTagLog( u"Auto-Tagging Started for {0} items\n".format(len(ca_list)))
-
- prog_idx = 0
-
- match_results = OnlineMatchResults()
- archives_to_remove = []
- for ca in ca_list:
- self.autoTagLog( u"============================================================\n" )
- self.autoTagLog( u"Auto-Tagging {0} of {1}\n".format(prog_idx+1, len(ca_list)))
- self.autoTagLog( u"{0}\n".format(ca.path) )
- cover_idx = ca.readMetadata(style).getCoverPageIndexList()[0]
- image_data = ca.getPage( cover_idx )
- self.atprogdialog.setArchiveImage( image_data )
- self.atprogdialog.setTestImage( None )
-
- QtCore.QCoreApplication.processEvents()
- if self.atprogdialog.isdone:
- break
- self.atprogdialog.progressBar.setValue( prog_idx )
- prog_idx += 1
- self.atprogdialog.label.setText( ca.path )
- centerWindowOnParent( self.atprogdialog )
- QtCore.QCoreApplication.processEvents()
-
- if ca.isWritable():
- success, match_results = self.identifyAndTagSingleArchive( ca, match_results, atstartdlg )
-
- if success and atstartdlg.removeAfterSuccess:
- archives_to_remove.append( ca )
-
- self.atprogdialog.close()
-
- if atstartdlg.removeAfterSuccess:
- self.fileSelectionList.removeArchiveList( archives_to_remove )
- self.fileSelectionList.updateSelectedRows()
-
- self.loadArchive( self.fileSelectionList.getCurrentArchive() )
- self.atprogdialog = None
-
- summary = u""
- summary += u"Successfully tagged archives: {0}\n".format( len(match_results.goodMatches))
-
- if len ( match_results.multipleMatches ) > 0:
- summary += u"Archives with multiple matches: {0}\n".format( len(match_results.multipleMatches))
- if len ( match_results.lowConfidenceMatches ) > 0:
- summary += u"Archives with one or more low-confidence matches: {0}\n".format( len(match_results.lowConfidenceMatches))
- if len ( match_results.noMatches ) > 0:
- summary += u"Archives with no matches: {0}\n".format( len(match_results.noMatches))
- if len ( match_results.fetchDataFailures ) > 0:
- summary += u"Archives that failed due to data fetch errors: {0}\n".format( len(match_results.fetchDataFailures))
- if len ( match_results.writeFailures ) > 0:
- summary += u"Archives that failed due to file writing errors: {0}\n".format( len(match_results.writeFailures))
-
- self.autoTagLog( summary )
-
- sum_selectable = len ( match_results.multipleMatches ) + len(match_results.lowConfidenceMatches)
- if sum_selectable > 0:
- summary += u"\n\nDo you want to manually select the ones with multiple matches and/or low-confidence matches now?"
-
- reply = QtGui.QMessageBox.question(self,
- self.tr(u"Auto-Tag Summary"),
- self.tr(summary),
- QtGui.QMessageBox.Yes, QtGui.QMessageBox.No )
-
- match_results.multipleMatches.extend( match_results.lowConfidenceMatches )
- if reply == QtGui.QMessageBox.Yes:
- matchdlg = AutoTagMatchWindow( self, match_results.multipleMatches, style, self.actualIssueDataFetch)
- matchdlg.setModal( True )
- matchdlg.exec_()
- self.fileSelectionList.updateSelectedRows()
- self.loadArchive( self.fileSelectionList.getCurrentArchive() )
-
- else:
- QtGui.QMessageBox.information(self, self.tr("Auto-Tag Summary"), self.tr(summary))
-
-
-
-
-
- def dirtyFlagVerification( self, title, desc):
- if self.dirtyFlag:
- reply = QtGui.QMessageBox.question(self,
- self.tr(title),
- self.tr(desc),
- QtGui.QMessageBox.Yes, QtGui.QMessageBox.No )
-
- if reply != QtGui.QMessageBox.Yes:
- return False
- return True
-
- def closeEvent(self, event):
-
- if self.dirtyFlagVerification( "Exit " + self.appName,
- "If you quit now, data in the form will be lost. Are you sure?"):
- appsize = self.size()
- self.settings.last_main_window_width = appsize.width()
- self.settings.last_main_window_height = appsize.height()
- self.settings.last_main_window_x = self.x()
- self.settings.last_main_window_y = self.y()
- self.settings.last_form_side_width = self.splitter.sizes()[0]
- self.settings.last_list_side_width = self.splitter.sizes()[1]
- self.settings.last_filelist_sorted_column, self.settings.last_filelist_sorted_order = self.fileSelectionList.getSorting()
- self.settings.save()
-
-
- event.accept()
- else:
- event.ignore()
-
- def showPageBrowser( self ):
- if self.page_browser is None:
- self.page_browser = PageBrowserWindow( self, self.metadata )
- if self.comic_archive is not None:
- self.page_browser.setComicArchive( self.comic_archive )
- self.page_browser.finished.connect(self.pageBrowserClosed)
-
- def pageBrowserClosed( self ):
- self.page_browser = None
-
- def viewRawCRTags( self ):
- if self.comic_archive is not None and self.comic_archive.hasCIX():
- dlg = LogWindow( self )
- dlg.setText( self.comic_archive.readRawCIX() )
- dlg.setWindowTitle( "Raw ComicRack Tag View" )
- dlg.exec_()
-
- def viewRawCBLTags( self ):
- if self.comic_archive is not None and self.comic_archive.hasCBI():
- dlg = LogWindow( self )
- text = pprint.pformat( json.loads(self.comic_archive.readRawCBI()), indent=4 )
- dlg.setText(text )
- dlg.setWindowTitle( "Raw ComicBookLover Tag View" )
- dlg.exec_()
-
- def showWiki( self ):
- webbrowser.open("http://code.google.com/p/comictagger/wiki/Home?tm=6")
-
- def reportBug( self ):
- webbrowser.open("http://code.google.com/p/comictagger/issues/list")
-
- def showForum( self ):
- webbrowser.open("http://comictagger.forumotion.com/")
-
- def frontCoverChanged( self, int ):
- self.metadata.pages = self.pageListEditor.getPageList()
- self.updateCoverImage()
-
- def pageListOrderChanged( self ):
- self.metadata.pages = self.pageListEditor.getPageList()
-
- def applyCBLTransform(self):
- self.formToMetadata()
- self.metadata = CBLTransformer( self.metadata, self.settings ).apply()
- self.metadataToForm()
-
- def renameArchive(self):
- ca_list = self.fileSelectionList.getSelectedArchiveList()
-
- if len(ca_list) == 0:
- QtGui.QMessageBox.information(self, self.tr("Rename"), self.tr("No archives selected!"))
- return
-
- if self.dirtyFlagVerification( "File Rename",
- "If you rename files now, unsaved data in the form will be lost. Are you sure?"):
-
- dlg = RenameWindow( self, ca_list, self.load_data_style, self.settings )
- dlg.setModal( True )
- if dlg.exec_():
- self.fileSelectionList.updateSelectedRows()
- self.loadArchive( self.comic_archive )
-
-
-
- def fileListSelectionChanged( self, qvarFI ):
- fi = qvarFI.toPyObject()
- self.loadArchive( fi.ca )
-
- def loadArchive( self, comic_archive ):
- self.comic_archive = None
- self.clearForm()
-
- self.settings.last_opened_folder = os.path.abspath(os.path.split(comic_archive.path)[0])
- self.comic_archive = comic_archive
- self.metadata = self.comic_archive.readMetadata(self.load_data_style)
- if self.metadata is None:
- self.metadata = GenericMetadata()
-
- self.actualLoadCurrentArchive()
-
- def fileListCleared( self ):
- self.resetApp()
-
- def splitterMovedEvent( self, w1, w2 ):
- scrollbar_w = 0
- if self.scrollArea.verticalScrollBar().isVisible():
- scrollbar_w = self.scrollArea.verticalScrollBar().width()
-
- new_w = self.scrollArea.width() - scrollbar_w - 5
- self.scrollAreaWidgetContents.resize( new_w, self.scrollAreaWidgetContents.height())
-
- def resizeEvent( self, ev ):
- self.splitterMovedEvent( 0, 0)
-
- def tabChanged( self, idx ):
- if idx == 0:
- self.splitterMovedEvent( 0, 0)
-
- def checkLatestVersionOnline( self ):
- self.versionChecker = VersionChecker()
- self.versionChecker.versionRequestComplete.connect( self.versionCheckComplete )
- self.versionChecker.asyncGetLatestVersion( self.settings.install_id, self.settings.send_usage_stats )
-
- def versionCheckComplete( self, new_version ):
- if ( new_version != self.version and
- new_version != self.settings.dont_notify_about_this_version):
- website = "http://code.google.com/p/comictagger"
- checked = OptionalMessageDialog.msg( self, "New version available!",
- "New version ({0}) available!
(You are currently running {1})
".format( new_version, self.version) +
- "Visit {0} for more info.
".format(website),
- QtCore.Qt.Unchecked,
- "Don't tell me about this version again")
- if checked:
- self.settings.dont_notify_about_this_version = new_version
-
- def onIncomingSocketConnection(self):
- # accept connection from other instance.
- # read in the file list if they're giving it,
- # and add to our own list
- localSocket = self.socketServer.nextPendingConnection()
- if localSocket.waitForReadyRead(3000):
- byteArray = localSocket.readAll()
- if len(byteArray) > 0:
- obj = pickle.loads(byteArray)
- localSocket.disconnectFromServer()
- if type(obj) is list:
- self.fileSelectionList.addPathList( obj )
- else:
- #print localSocket.errorString().toLatin1()
- pass
-
- self.bringToTop()
-
- def bringToTop(self):
- if platform.system() == "Windows":
- self.showNormal()
- self.raise_()
- self.activateWindow()
- try:
- import win32con
- import win32gui
- hwnd = self.effectiveWinId()
- rect = win32gui.GetWindowRect(hwnd)
- x = rect[0]
- y = rect[1]
- w = rect[2] - x
- h = rect[3] - y
- # mark it "always on top", just for a moment, to force it to the top
- win32gui.SetWindowPos(hwnd,win32con.HWND_TOPMOST, x, y, w, h, 0)
- win32gui.SetWindowPos(hwnd,win32con.HWND_NOTOPMOST, x, y, w, h, 0)
- except Exception as e:
- print "Whoops", e
- elif platform.system() == "Darwin":
- self.raise_()
- self.showNormal()
- self.activateWindow()
- else:
- flags = self.windowFlags()
- self.setWindowFlags( flags | QtCore.Qt.WindowStaysOnTopHint | QtCore.Qt.X11BypassWindowManagerHint)
- QtCore.QCoreApplication.processEvents()
- #self.show()
- self.setWindowFlags( flags )
- self.show()
-
\ No newline at end of file
+ def setDirtyFlag( self, param1=None, param2=None, param3=None ):
+ if not self.dirtyFlag:
+ self.dirtyFlag = True
+ self.fileSelectionList.setModifiedFlag( True )
+ self.updateAppTitle()
+
+ def clearDirtyFlag( self ):
+ if self.dirtyFlag:
+ self.dirtyFlag = False
+ self.fileSelectionList.setModifiedFlag( False )
+ self.updateAppTitle()
+
+ def connectDirtyFlagSignals( self ):
+ # recursivly connect the tab form child slots
+ self.connectChildDirtyFlagSignals( self.tabWidget )
+
+ def connectChildDirtyFlagSignals (self, widget ):
+
+ if ( isinstance(widget, QtGui.QLineEdit)):
+ widget.textEdited.connect(self.setDirtyFlag)
+ if ( isinstance(widget, QtGui.QTextEdit)):
+ widget.textChanged.connect(self.setDirtyFlag)
+ if ( isinstance(widget, QtGui.QComboBox) ):
+ widget.currentIndexChanged.connect(self.setDirtyFlag)
+ if ( isinstance(widget, QtGui.QCheckBox) ):
+ widget.stateChanged.connect(self.setDirtyFlag)
+
+ # recursive call on chillun
+ for child in widget.children():
+ if child != self.pageListEditor:
+ self.connectChildDirtyFlagSignals( child )
+
+
+ def clearForm( self ):
+
+ # get a minty fresh metadata object
+ self.metadata = GenericMetadata()
+ if self.comic_archive is not None:
+ self.metadata.setDefaultPageList( self.comic_archive.getNumberOfPages() )
+
+ # recursivly clear the tab form
+ self.clearChildren( self.tabWidget )
+
+ # clear the dirty flag, since there is nothing in there now to lose
+ self.clearDirtyFlag()
+
+ self.pageListEditor.setData( self.comic_archive, self.metadata.pages )
+
+ def clearChildren (self, widget ):
+
+ if ( isinstance(widget, QtGui.QLineEdit) or
+ isinstance(widget, QtGui.QTextEdit)):
+ widget.setText("")
+ if ( isinstance(widget, QtGui.QComboBox) ):
+ widget.setCurrentIndex( 0 )
+ if ( isinstance(widget, QtGui.QCheckBox) ):
+ widget.setChecked( False )
+ if ( isinstance(widget, QtGui.QTableWidget) ):
+ while widget.rowCount() > 0:
+ widget.removeRow(0)
+
+ # recursive call on chillun
+ for child in widget.children():
+ self.clearChildren( child )
+
+
+ def metadataToForm( self ):
+ # copy the the metadata object into to the form
+
+ #helper func
+ def assignText( field, value):
+ if value is not None:
+ field.setText( unicode(value) )
+
+ md = self.metadata
+
+ assignText( self.leSeries, md.series )
+ assignText( self.leIssueNum, md.issue )
+ assignText( self.leIssueCount, md.issueCount )
+ assignText( self.leVolumeNum, md.volume )
+ assignText( self.leVolumeCount, md.volumeCount )
+ assignText( self.leTitle, md.title )
+ assignText( self.lePublisher, md.publisher )
+ assignText( self.lePubMonth, md.month )
+ assignText( self.lePubYear, md.year )
+ assignText( self.lePubDay, md.day )
+ assignText( self.leGenre, md.genre )
+ assignText( self.leImprint, md.imprint )
+ assignText( self.teComments, md.comments )
+ assignText( self.teNotes, md.notes )
+ assignText( self.leCriticalRating, md.criticalRating )
+ assignText( self.leStoryArc, md.storyArc )
+ assignText( self.leScanInfo, md.scanInfo )
+ assignText( self.leSeriesGroup, md.seriesGroup )
+ assignText( self.leAltSeries, md.alternateSeries )
+ assignText( self.leAltIssueNum, md.alternateNumber )
+ assignText( self.leAltIssueCount, md.alternateCount )
+ assignText( self.leWebLink, md.webLink )
+ assignText( self.teCharacters, md.characters )
+ assignText( self.teTeams, md.teams )
+ assignText( self.teLocations, md.locations )
+
+ if md.format is not None and md.format != "":
+ i = self.cbFormat.findText( md.format )
+ if i == -1:
+ self.cbFormat.setEditText( md.format )
+ else:
+ self.cbFormat.setCurrentIndex( i )
+
+ if md.maturityRating is not None and md.maturityRating != "":
+ i = self.cbMaturityRating.findText( md.maturityRating )
+ if i == -1:
+ self.cbMaturityRating.setEditText( md.maturityRating )
+ else:
+ self.cbMaturityRating.setCurrentIndex( i )
+
+ if md.language is not None:
+ i = self.cbLanguage.findData( md.language )
+ self.cbLanguage.setCurrentIndex( i )
+
+ if md.country is not None:
+ i = self.cbCountry.findText( md.country )
+ self.cbCountry.setCurrentIndex( i )
+
+ if md.manga is not None:
+ i = self.cbManga.findData( md.manga )
+ self.cbManga.setCurrentIndex( i )
+
+ if md.blackAndWhite is not None and md.blackAndWhite:
+ self.cbBW.setChecked( True )
+
+ assignText( self.teTags, utils.listToString( md.tags ) )
+
+ # !!! Should we clear the credits table or just avoid duplicates?
+ while self.twCredits.rowCount() > 0:
+ self.twCredits.removeRow(0)
+
+ if md.credits is not None and len(md.credits) != 0:
+
+ self.twCredits.setSortingEnabled( False )
+
+ row = 0
+ for credit in md.credits:
+ # if the role-person pair already exists, just skip adding it to the list
+ if self.isDupeCredit( credit['role'].title(), credit['person']):
+ continue
+
+ self.addNewCreditEntry( row, credit['role'].title(), credit['person'], (credit['primary'] if credit.has_key('primary') else False ) )
+
+ row += 1
+
+ self.twCredits.setSortingEnabled( True )
+ self.updateCreditColors()
+
+ def addNewCreditEntry( self, row, role, name, primary_flag=False ):
+ self.twCredits.insertRow(row)
+
+ item_text = role
+ item = QtGui.QTableWidgetItem(item_text)
+ item.setFlags(QtCore.Qt.ItemIsSelectable| QtCore.Qt.ItemIsEnabled)
+ item.setData( QtCore.Qt.ToolTipRole, item_text )
+ self.twCredits.setItem(row, 1, item)
+
+
+ item_text = name
+ item = QtGui.QTableWidgetItem(item_text)
+ item.setData( QtCore.Qt.ToolTipRole, item_text )
+ item.setFlags(QtCore.Qt.ItemIsSelectable| QtCore.Qt.ItemIsEnabled)
+ self.twCredits.setItem(row, 2, item)
+
+ item = QtGui.QTableWidgetItem("")
+ item.setFlags(QtCore.Qt.ItemIsSelectable| QtCore.Qt.ItemIsEnabled)
+ self.twCredits.setItem(row, 0, item)
+ self.updateCreditPrimaryFlag( row, primary_flag )
+
+ def isDupeCredit( self, role, name ):
+ r = 0
+ while r < self.twCredits.rowCount():
+ if ( self.twCredits.item(r, 1).text() == role and
+ self.twCredits.item(r, 2).text() == name ):
+ return True
+ r = r + 1
+
+ return False
+
+ def formToMetadata( self ):
+
+ #helper func
+ def xlate( data, type_str):
+ s = u"{0}".format(data).strip()
+ if s == "":
+ return None
+ elif type_str == "str":
+ return s
+ else:
+ return int(s)
+
+ # copy the data from the form into the metadata
+ md = self.metadata
+ md.series = xlate( self.leSeries.text(), "str" )
+ md.issue = xlate( self.leIssueNum.text(), "str" )
+ md.issueCount = xlate( self.leIssueCount.text(), "int" )
+ md.volume = xlate( self.leVolumeNum.text(), "int" )
+ md.volumeCount = xlate( self.leVolumeCount.text(), "int" )
+ md.title = xlate( self.leTitle.text(), "str" )
+ md.publisher = xlate( self.lePublisher.text(), "str" )
+ md.month = xlate( self.lePubMonth.text(), "int" )
+ md.year = xlate( self.lePubYear.text(), "int" )
+ md.day = xlate( self.lePubDay.text(), "int" )
+ md.genre = xlate( self.leGenre.text(), "str" )
+ md.imprint = xlate( self.leImprint.text(), "str" )
+ md.comments = xlate( self.teComments.toPlainText(), "str" )
+ md.notes = xlate( self.teNotes.toPlainText(), "str" )
+ md.criticalRating = xlate( self.leCriticalRating.text(), "int" )
+ md.maturityRating = xlate( self.cbMaturityRating.currentText(), "str" )
+
+ md.storyArc = xlate( self.leStoryArc.text(), "str" )
+ md.scanInfo = xlate( self.leScanInfo.text(), "str" )
+ md.seriesGroup = xlate( self.leSeriesGroup.text(), "str" )
+ md.alternateSeries = xlate( self.leAltSeries.text(), "str" )
+ md.alternateNumber = xlate( self.leAltIssueNum.text(), "int" )
+ md.alternateCount = xlate( self.leAltIssueCount.text(), "int" )
+ md.webLink = xlate( self.leWebLink.text(), "str" )
+ md.characters = xlate( self.teCharacters.toPlainText(), "str" )
+ md.teams = xlate( self.teTeams.toPlainText(), "str" )
+ md.locations = xlate( self.teLocations.toPlainText(), "str" )
+
+ md.format = xlate( self.cbFormat.currentText(), "str" )
+ md.country = xlate( self.cbCountry.currentText(), "str" )
+
+ langiso = self.cbLanguage.itemData(self.cbLanguage.currentIndex()).toString()
+ md.language = xlate( langiso, "str" )
+
+ manga_code = self.cbManga.itemData(self.cbManga.currentIndex()).toString()
+ md.manga = xlate( manga_code, "str" )
+
+ # Make a list from the coma delimited tags string
+ tmp = xlate( self.teTags.toPlainText(), "str" )
+ if tmp != None:
+ def striplist(l):
+ return([x.strip() for x in l])
+
+ md.tags = striplist(tmp.split( "," ))
+
+ if ( self.cbBW.isChecked() ):
+ md.blackAndWhite = True
+ else:
+ md.blackAndWhite = False
+
+ # get the credits from the table
+ md.credits = list()
+ row = 0
+ while row < self.twCredits.rowCount():
+ role = u"{0}".format(self.twCredits.item(row, 1).text())
+ name = u"{0}".format(self.twCredits.item(row, 2).text())
+ primary_flag = self.twCredits.item( row, 0 ).text() != ""
+
+ md.addCredit( name, role, bool(primary_flag) )
+ row += 1
+
+ md.pages = self.pageListEditor.getPageList()
+
+ def useFilename( self ):
+ if self.comic_archive is not None:
+ #copy the form onto metadata object
+ self.formToMetadata()
+ new_metadata = self.comic_archive.metadataFromFilename(self.settings.parse_scan_info)
+ if new_metadata is not None:
+ self.metadata.overlay( new_metadata )
+ self.metadataToForm()
+
+ def selectFolder( self ):
+ self.selectFile( folder_mode=True )
+
+ def selectFile( self , folder_mode = False):
+
+ dialog = QtGui.QFileDialog(self)
+ if folder_mode:
+ dialog.setFileMode(QtGui.QFileDialog.Directory)
+ else:
+ dialog.setFileMode(QtGui.QFileDialog.ExistingFiles)
+
+ if self.settings.last_opened_folder is not None:
+ dialog.setDirectory( self.settings.last_opened_folder )
+ #dialog.setFileMode(QtGui.QFileDialog.Directory )
+
+ if not folder_mode:
+ if platform.system() != "Windows" and utils.which("unrar") is None:
+ archive_filter = "Comic archive files (*.cbz *.zip)"
+ else:
+ archive_filter = "Comic archive files (*.cbz *.zip *.cbr *.rar)"
+ filters = [
+ archive_filter,
+ "Any files (*)"
+ ]
+ dialog.setNameFilters(filters)
+
+ if (dialog.exec_()):
+ fileList = dialog.selectedFiles()
+ #if self.dirtyFlagVerification( "Open Archive",
+ # "If you open a new archive now, data in the form will be lost. Are you sure?"):
+ self.fileSelectionList.addPathList( fileList )
+
+ def autoIdentifySearch(self):
+ if self.comic_archive is None:
+ QtGui.QMessageBox.warning(self, self.tr("Automatic Identify Search"),
+ self.tr("You need to load a comic first!"))
+ return
+
+ self.queryOnline( autoselect=True )
+
+ def queryOnline(self, autoselect=False):
+
+ issue_number = unicode(self.leIssueNum.text()).strip()
+
+ if autoselect and issue_number == "":
+ QtGui.QMessageBox.information(self,"Automatic Identify Search", "Can't auto-identify without an issue number (yet!)")
+ return
+
+ if unicode(self.leSeries.text()).strip() != "":
+ series_name = unicode(self.leSeries.text()).strip()
+ else:
+ QtGui.QMessageBox.information(self, self.tr("Online Search"), self.tr("Need to enter a series name to search."))
+ return
+
+ year = str(self.lePubYear.text()).strip()
+ if year == "":
+ year = None
+
+ issue_count = str(self.leIssueCount.text()).strip()
+ if issue_count == "":
+ issue_count = None
+
+ cover_index_list = self.metadata.getCoverPageIndexList()
+ selector = VolumeSelectionWindow( self, series_name, issue_number, year, issue_count, cover_index_list, self.comic_archive, self.settings, autoselect )
+
+ title = "Search: '" + series_name + "' - "
+ selector.setWindowTitle( title + "Select Series")
+
+ selector.setModal(True)
+ selector.exec_()
+
+ if selector.result():
+ #we should now have a volume ID
+ QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
+
+ #copy the form onto metadata object
+ self.formToMetadata()
+
+ try:
+ comicVine = ComicVineTalker()
+ new_metadata = comicVine.fetchIssueData( selector.volume_id, selector.issue_number, self.settings )
+ except ComicVineTalkerException as e:
+ QtGui.QApplication.restoreOverrideCursor()
+ if e.code == ComicVineTalkerException.RateLimit:
+ QtGui.QMessageBox.critical(self, self.tr("Comic Vine Error"), ComicVineTalker.getRateLimitMessage())
+ else:
+ QtGui.QMessageBox.critical(self, self.tr("Network Issue"), self.tr("Could not connect to ComicVine to get issue details.!"))
+ else:
+ QtGui.QApplication.restoreOverrideCursor()
+ if new_metadata is not None:
+
+ if self.settings.apply_cbl_transform_on_cv_import:
+ new_metadata = CBLTransformer( new_metadata, self.settings ).apply()
+
+ if self.settings.clear_form_before_populating_from_cv:
+ self.clearForm()
+
+ self.metadata.overlay( new_metadata )
+ # Now push the new combined data into the edit controls
+ self.metadataToForm()
+ else:
+ QtGui.QMessageBox.critical(self, self.tr("Search"), self.tr("Could not find an issue {0} for that series".format(selector.issue_number)))
+
+
+ def commitMetadata(self):
+
+ if ( self.metadata is not None and self.comic_archive is not None):
+ reply = QtGui.QMessageBox.question(self,
+ self.tr("Save Tags"),
+ self.tr("Are you sure you wish to save " + MetaDataStyle.name[self.save_data_style] + " tags to this archive?"),
+ QtGui.QMessageBox.Yes, QtGui.QMessageBox.No )
+
+ if reply == QtGui.QMessageBox.Yes:
+ QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
+ self.formToMetadata()
+
+ success = self.comic_archive.writeMetadata( self.metadata, self.save_data_style )
+ self.comic_archive.loadCache( [ MetaDataStyle.CBI, MetaDataStyle.CIX ] )
+ QtGui.QApplication.restoreOverrideCursor()
+
+ if not success:
+ QtGui.QMessageBox.warning(self, self.tr("Save failed"), self.tr("The tag save operation seemed to fail!"))
+ else:
+ self.clearDirtyFlag()
+ self.updateInfoBox()
+ self.updateMenus()
+ #QtGui.QMessageBox.information(self, self.tr("Yeah!"), self.tr("File written."))
+ self.fileSelectionList.updateCurrentRow()
+
+ else:
+ QtGui.QMessageBox.information(self, self.tr("Whoops!"), self.tr("No data to commit!"))
+
+
+ def setLoadDataStyle(self, s):
+ if self.dirtyFlagVerification( "Change Tag Read Style",
+ "If you change read tag style now, data in the form will be lost. Are you sure?"):
+ self.load_data_style, b = self.cbLoadDataStyle.itemData(s).toInt()
+ self.settings.last_selected_load_data_style = self.load_data_style
+ self.updateMenus()
+ if self.comic_archive is not None:
+ self.loadArchive( self.comic_archive )
+ else:
+ self.cbLoadDataStyle.currentIndexChanged.disconnect(self.setLoadDataStyle)
+ self.adjustLoadStyleCombo()
+ self.cbLoadDataStyle.currentIndexChanged.connect(self.setLoadDataStyle)
+
+ def setSaveDataStyle(self, s):
+ self.save_data_style, b = self.cbSaveDataStyle.itemData(s).toInt()
+
+ self.settings.last_selected_save_data_style = self.save_data_style
+ self.updateStyleTweaks()
+ self.updateMenus()
+
+ def updateCreditColors( self ):
+ inactive_color = QtGui.QColor(255, 170, 150)
+ active_palette = self.leSeries.palette()
+ active_color = active_palette.color( QtGui.QPalette.Base )
+
+ cix_credits = ComicInfoXml().getParseableCredits()
+
+ if self.save_data_style == MetaDataStyle.CIX:
+ #loop over credit table, mark selected rows
+ r = 0
+ while r < self.twCredits.rowCount():
+ if str(self.twCredits.item(r, 1).text()).lower() not in cix_credits:
+ self.twCredits.item(r, 1).setBackgroundColor( inactive_color )
+ else:
+ self.twCredits.item(r, 1).setBackgroundColor( active_color )
+ # turn off entire primary column
+ self.twCredits.item(r, 0).setBackgroundColor( inactive_color )
+ r = r + 1
+
+ if self.save_data_style == MetaDataStyle.CBI:
+ #loop over credit table, make all active color
+ r = 0
+ while r < self.twCredits.rowCount():
+ self.twCredits.item(r, 0).setBackgroundColor( active_color )
+ self.twCredits.item(r, 1).setBackgroundColor( active_color )
+ r = r + 1
+
+
+ def updateStyleTweaks( self ):
+
+ # depending on the current data style, certain fields are disabled
+
+ inactive_color = QtGui.QColor(255, 170, 150)
+ active_palette = self.leSeries.palette()
+
+ inactive_palette1 = self.leSeries.palette()
+ inactive_palette1.setColor(QtGui.QPalette.Base, inactive_color)
+
+ inactive_palette2 = self.leSeries.palette()
+
+ inactive_palette3 = self.leSeries.palette()
+ inactive_palette3.setColor(QtGui.QPalette.Base, inactive_color)
+
+ inactive_palette3.setColor(QtGui.QPalette.Base, inactive_color)
+
+ #helper func
+ def enableWidget( item, enable ):
+ inactive_palette3.setColor(item.backgroundRole(), inactive_color)
+ inactive_palette2.setColor(item.backgroundRole(), inactive_color)
+ inactive_palette3.setColor(item.foregroundRole(), inactive_color)
+
+ if enable:
+ item.setPalette(active_palette)
+ item.setAutoFillBackground( False )
+ if type(item) == QtGui.QCheckBox:
+ item.setEnabled( True )
+ elif type(item) == QtGui.QComboBox:
+ item.setEnabled( True )
+ else:
+ item.setReadOnly( False )
+ else:
+ item.setAutoFillBackground( True )
+ if type(item) == QtGui.QCheckBox:
+ item.setPalette(inactive_palette2)
+ item.setEnabled( False )
+ elif type(item) == QtGui.QComboBox:
+ item.setPalette(inactive_palette3)
+ item.setEnabled( False )
+ else:
+ item.setReadOnly( True )
+ item.setPalette(inactive_palette1)
+
+
+ cbi_only = [ self.leVolumeCount, self.cbCountry, self.leCriticalRating, self.teTags ]
+ cix_only = [
+ self.leImprint, self.teNotes, self.cbBW, self.cbManga,
+ self.leStoryArc, self.leScanInfo, self.leSeriesGroup,
+ self.leAltSeries, self.leAltIssueNum, self.leAltIssueCount,
+ self.leWebLink, self.teCharacters, self.teTeams,
+ self.teLocations, self.cbMaturityRating, self.cbFormat
+ ]
+
+ if self.save_data_style == MetaDataStyle.CIX:
+ for item in cix_only:
+ enableWidget( item, True )
+ for item in cbi_only:
+ enableWidget(item, False )
+
+ if self.save_data_style == MetaDataStyle.CBI:
+ for item in cbi_only:
+ enableWidget( item, True )
+ for item in cix_only:
+ enableWidget(item, False )
+
+ self.updateCreditColors()
+ self.pageListEditor.setMetadataStyle( self.save_data_style )
+
+ def cellDoubleClicked( self, r, c ):
+ self.editCredit()
+
+ def addCredit( self ):
+ self.modifyCredits( "add" )
+
+ def editCredit( self ):
+ if ( self.twCredits.currentRow() > -1 ):
+ self.modifyCredits( "edit" )
+
+ def updateCreditPrimaryFlag( self, row, primary ):
+
+ # if we're clearing a flagm do it and quit
+ if not primary:
+ self.twCredits.item(row, 0).setText( "" )
+ return
+
+ # otherwise, we need to check for, and clear, other primaries with same role
+ role = str(self.twCredits.item(row, 1).text())
+ r = 0
+ while r < self.twCredits.rowCount():
+ if ( self.twCredits.item(r, 0).text() != "" and
+ str(self.twCredits.item(r, 1).text()).lower() == role.lower() ):
+ self.twCredits.item(r, 0).setText( "" )
+ r = r + 1
+
+ # Now set our new primary
+ self.twCredits.item(row, 0).setText( "Yes" )
+
+ def modifyCredits( self , action ):
+
+ if action == "edit":
+ row = self.twCredits.currentRow()
+ role = self.twCredits.item( row, 1 ).text()
+ name = self.twCredits.item( row, 2 ).text()
+ primary = self.twCredits.item( row, 0 ).text() != ""
+ else:
+ role = ""
+ name = ""
+ primary = False
+
+ editor = CreditEditorWindow( self, CreditEditorWindow.ModeEdit, role, name, primary )
+ editor.setModal(True)
+ editor.exec_()
+ if editor.result():
+ new_role, new_name, new_primary = editor.getCredits()
+
+ if new_name == name and new_role == role and new_primary == primary:
+ #nothing has changed, just quit
+ return
+
+ # name and role is the same, but primary flag changed
+ if new_name == name and new_role == role:
+ self.updateCreditPrimaryFlag( row, new_primary )
+ return
+
+ # check for dupes
+ ok_to_mod = True
+ if self.isDupeCredit( new_role, new_name):
+ # delete the dupe credit from list
+ reply = QtGui.QMessageBox.question(self,
+ self.tr("Duplicate Credit!"),
+ self.tr("This will create a duplicate credit entry. Would you like to merge the entries, or create a duplicate?"),
+ self.tr("Merge"), self.tr("Duplicate" ))
+
+ if reply == 0:
+ # merge
+ if action == "edit":
+ # just remove the row that would be same
+ self.twCredits.removeRow( row )
+ # TODO -- need to find the row of the dupe, and possible change the primary flag
+
+ ok_to_mod = False
+
+
+ if ok_to_mod:
+ #modify it
+ if action == "edit":
+ self.twCredits.item(row, 1).setText( new_role )
+ self.twCredits.item(row, 2).setText( new_name )
+ self.updateCreditPrimaryFlag( row, new_primary )
+ else:
+ # add new entry
+ row = self.twCredits.rowCount()
+ self.addNewCreditEntry( row, new_role, new_name, new_primary)
+
+ self.updateCreditColors()
+ self.setDirtyFlag()
+
+ def removeCredit( self ):
+ row = self.twCredits.currentRow()
+ if row != -1 :
+ self.twCredits.removeRow( row )
+ self.setDirtyFlag()
+
+ def showSettings( self ):
+
+ settingswin = SettingsWindow( self, self.settings )
+ settingswin.setModal(True)
+ settingswin.exec_()
+ if settingswin.result():
+ pass
+
+ def setAppPosition( self ):
+ if self.settings.last_main_window_width != 0:
+ self.move( self.settings.last_main_window_x, self.settings.last_main_window_y )
+ self.resize( self.settings.last_main_window_width, self.settings.last_main_window_height )
+ else:
+ screen = QtGui.QDesktopWidget().screenGeometry()
+ size = self.frameGeometry()
+ self.move((screen.width()-size.width())/2, (screen.height()-size.height())/2)
+
+
+ def adjustLoadStyleCombo( self ):
+ # select the current style
+ if ( self.load_data_style == MetaDataStyle.CBI ):
+ self.cbLoadDataStyle.setCurrentIndex ( 0 )
+ elif ( self.load_data_style == MetaDataStyle.CIX ):
+ self.cbLoadDataStyle.setCurrentIndex ( 1 )
+
+ def adjustSaveStyleCombo( self ):
+ # select the current style
+ if ( self.save_data_style == MetaDataStyle.CBI ):
+ self.cbSaveDataStyle.setCurrentIndex ( 0 )
+ elif ( self.save_data_style == MetaDataStyle.CIX ):
+ self.cbSaveDataStyle.setCurrentIndex ( 1 )
+ self.updateStyleTweaks()
+
+
+ def populateComboBoxes( self ):
+
+ # Add the entries to the tag style combobox
+ self.cbLoadDataStyle.addItem( "ComicBookLover", MetaDataStyle.CBI )
+ self.cbLoadDataStyle.addItem( "ComicRack", MetaDataStyle.CIX )
+ self.adjustLoadStyleCombo()
+
+ self.cbSaveDataStyle.addItem( "ComicBookLover", MetaDataStyle.CBI )
+ self.cbSaveDataStyle.addItem( "ComicRack", MetaDataStyle.CIX )
+ self.adjustSaveStyleCombo()
+
+ # Add the entries to the country combobox
+ self.cbCountry.addItem( "", "" )
+ for c in utils.countries:
+ self.cbCountry.addItem( c[1], c[0] )
+
+ # Add the entries to the language combobox
+ self.cbLanguage.addItem( "", "" )
+ lang_dict = utils.getLanguageDict()
+ for key in sorted(lang_dict, cmp=locale.strcoll, key=lang_dict.get):
+ self.cbLanguage.addItem( lang_dict[key], key )
+
+ # Add the entries to the manga combobox
+ self.cbManga.addItem( "", "" )
+ self.cbManga.addItem( "Yes", "Yes" )
+ self.cbManga.addItem( "Yes (Right to Left)", "YesAndRightToLeft" )
+ self.cbManga.addItem( "No", "No" )
+
+ # Add the entries to the maturity combobox
+ self.cbMaturityRating.addItem( "", "" )
+ self.cbMaturityRating.addItem( "Everyone", "" )
+ self.cbMaturityRating.addItem( "G", "" )
+ self.cbMaturityRating.addItem( "Early Childhood", "" )
+ self.cbMaturityRating.addItem( "Everyone 10+", "" )
+ self.cbMaturityRating.addItem( "PG", "" )
+ self.cbMaturityRating.addItem( "Kids to Adults", "" )
+ self.cbMaturityRating.addItem( "Teen", "" )
+ self.cbMaturityRating.addItem( "MA15+", "" )
+ self.cbMaturityRating.addItem( "Mature 17+", "" )
+ self.cbMaturityRating.addItem( "R18+", "" )
+ self.cbMaturityRating.addItem( "X18+", "" )
+ self.cbMaturityRating.addItem( "Adults Only 18+", "" )
+ self.cbMaturityRating.addItem( "Rating Pending", "" )
+
+ # Add entries to the format combobox
+ self.cbFormat.addItem("")
+ self.cbFormat.addItem(".1")
+ self.cbFormat.addItem("-1")
+ self.cbFormat.addItem("1 Shot")
+ self.cbFormat.addItem("1/2")
+ self.cbFormat.addItem("1-Shot")
+ self.cbFormat.addItem("Annotation")
+ self.cbFormat.addItem("Annotations")
+ self.cbFormat.addItem("Annual")
+ self.cbFormat.addItem("Anthology")
+ self.cbFormat.addItem("B&W")
+ self.cbFormat.addItem("B/W")
+ self.cbFormat.addItem("B&&W")
+ self.cbFormat.addItem("Black & White")
+ self.cbFormat.addItem("Box Set")
+ self.cbFormat.addItem("Box-Set")
+ self.cbFormat.addItem("Crossover")
+ self.cbFormat.addItem("Director's Cut")
+ self.cbFormat.addItem("Epilogue")
+ self.cbFormat.addItem("Event")
+ self.cbFormat.addItem("FCBD")
+ self.cbFormat.addItem("Flyer")
+ self.cbFormat.addItem("Giant")
+ self.cbFormat.addItem("Giant Size")
+ self.cbFormat.addItem("Giant-Size")
+ self.cbFormat.addItem("Graphic Novel")
+ self.cbFormat.addItem("Hardcover")
+ self.cbFormat.addItem("Hard-Cover")
+ self.cbFormat.addItem("King")
+ self.cbFormat.addItem("King Size")
+ self.cbFormat.addItem("King-Size")
+ self.cbFormat.addItem("Limited Series")
+ self.cbFormat.addItem("Magazine")
+ self.cbFormat.addItem("-1")
+ self.cbFormat.addItem("NSFW")
+ self.cbFormat.addItem("One Shot")
+ self.cbFormat.addItem("One-Shot")
+ self.cbFormat.addItem("Point 1")
+ self.cbFormat.addItem("Preview")
+ self.cbFormat.addItem("Prologue")
+ self.cbFormat.addItem("Reference")
+ self.cbFormat.addItem("Review")
+ self.cbFormat.addItem("Reviewed")
+ self.cbFormat.addItem("Scanlation")
+ self.cbFormat.addItem("Script")
+ self.cbFormat.addItem("Series")
+ self.cbFormat.addItem("Sketch")
+ self.cbFormat.addItem("Special")
+ self.cbFormat.addItem("TPB")
+ self.cbFormat.addItem("Trade Paper Back")
+ self.cbFormat.addItem("WebComic")
+ self.cbFormat.addItem("Web Comic")
+ self.cbFormat.addItem("Year 1")
+ self.cbFormat.addItem("Year One")
+
+ def removeAuto( self ):
+ self.removeTags( self.save_data_style )
+
+ def removeCBLTags( self ):
+ self.removeTags( MetaDataStyle.CBI )
+
+ def removeCRTags( self ):
+ self.removeTags( MetaDataStyle.CIX )
+
+ def removeTags( self, style):
+ # remove the indicated tags from the archive
+ ca_list = self.fileSelectionList.getSelectedArchiveList()
+ has_md_count = 0
+ for ca in ca_list:
+ if ca.hasMetadata( style ):
+ has_md_count += 1
+
+ if has_md_count == 0:
+ QtGui.QMessageBox.information(self, self.tr("Remove Tags"),
+ self.tr("No archives with {0} tags selected!".format(MetaDataStyle.name[style])))
+ return
+
+ if has_md_count != 0 and not self.dirtyFlagVerification( "Remove Tags",
+ "If you remove tags now, unsaved data in the form will be lost. Are you sure?"):
+ return
+
+ if has_md_count != 0:
+ reply = QtGui.QMessageBox.question(self,
+ self.tr("Remove Tags"),
+ self.tr("Are you sure you wish to remove the {0} tags from {1} archive(s)?".format(MetaDataStyle.name[style], has_md_count)),
+ QtGui.QMessageBox.Yes, QtGui.QMessageBox.No )
+
+ if reply == QtGui.QMessageBox.Yes:
+ progdialog = QtGui.QProgressDialog("", "Cancel", 0, has_md_count, self)
+ progdialog.setWindowTitle( "Removing Tags" )
+ progdialog.setWindowModality(QtCore.Qt.ApplicationModal)
+ progdialog.show()
+ prog_idx = 0
+
+ failed_list = []
+ success_count = 0
+ for ca in ca_list:
+ if ca.hasMetadata( style ):
+ QtCore.QCoreApplication.processEvents()
+ if progdialog.wasCanceled():
+ break
+ progdialog.setValue(prog_idx)
+ prog_idx += 1
+ progdialog.setLabelText( ca.path )
+ centerWindowOnParent( progdialog )
+ QtCore.QCoreApplication.processEvents()
+
+ if ca.hasMetadata( style ) and ca.isWritable():
+ if not ca.removeMetadata( style ):
+ failed_list.append( ca.path )
+ else:
+ success_count += 1
+ ca.loadCache( [ MetaDataStyle.CBI, MetaDataStyle.CIX ] )
+
+ progdialog.close()
+ self.fileSelectionList.updateSelectedRows()
+ self.updateInfoBox()
+ self.updateMenus()
+
+ summary = u"Successfully removed tags in {0} archive(s).".format( success_count )
+ if len( failed_list ) > 0:
+ summary += u"\n\nThe remove operation failed in the following {0} archive(s):\n".format( len( failed_list ) )
+ for f in failed_list:
+ summary += u"\t{0}\n".format( f )
+
+ dlg = LogWindow( self )
+ dlg.setText( summary )
+ dlg.setWindowTitle( "Tag Remove Summary" )
+ #dlg.adjustSize()
+ dlg.exec_()
+
+ def copyTags( self ):
+ # copy the indicated tags in the archive
+ ca_list = self.fileSelectionList.getSelectedArchiveList()
+ has_src_count = 0
+
+ src_style = self.load_data_style
+ dest_style = self.save_data_style
+
+ if src_style == dest_style:
+ QtGui.QMessageBox.information(self, self.tr("Copy Tags"), self.tr("Can't copy tag style onto itself." +
+ " Read style and modify style must be different."))
+ return
+
+ for ca in ca_list:
+ if ca.hasMetadata( src_style ):
+ has_src_count += 1
+
+ if has_src_count == 0:
+ QtGui.QMessageBox.information(self, self.tr("Copy Tags"), self.tr("No archives with {0} tags selected!".format(
+ MetaDataStyle.name[src_style])))
+ return
+
+ if has_src_count != 0 and not self.dirtyFlagVerification( "Copy Tags",
+ "If you copy tags now, unsaved data in the form may be lost. Are you sure?"):
+ return
+
+ if has_src_count != 0:
+ reply = QtGui.QMessageBox.question(self,
+ self.tr("Copy Tags"),
+ self.tr("Are you sure you wish to copy the {0} tags to {1} tags in {2} archive(s)?".format(
+ MetaDataStyle.name[src_style], MetaDataStyle.name[dest_style], has_src_count)),
+ QtGui.QMessageBox.Yes, QtGui.QMessageBox.No )
+
+ if reply == QtGui.QMessageBox.Yes:
+ progdialog = QtGui.QProgressDialog("", "Cancel", 0, has_src_count, self)
+ progdialog.setWindowTitle( "Copying Tags" )
+ progdialog.setWindowModality(QtCore.Qt.ApplicationModal)
+ progdialog.show()
+ prog_idx = 0
+
+ failed_list = []
+ success_count = 0
+ for ca in ca_list:
+ if ca.hasMetadata( src_style ):
+ QtCore.QCoreApplication.processEvents()
+ if progdialog.wasCanceled():
+ break
+ progdialog.setValue(prog_idx)
+ prog_idx += 1
+ progdialog.setLabelText( ca.path )
+ centerWindowOnParent( progdialog )
+ QtCore.QCoreApplication.processEvents()
+
+ if ca.hasMetadata( src_style ) and ca.isWritable():
+ md = ca.readMetadata( src_style )
+
+ if dest_style == MetaDataStyle.CBI and self.settings.apply_cbl_transform_on_bulk_operation:
+ md = CBLTransformer( md, self.settings ).apply()
+
+ if not ca.writeMetadata( md, dest_style ):
+ failed_list.append( ca.path )
+ else:
+ success_count += 1
+
+ ca.loadCache( [ MetaDataStyle.CBI, MetaDataStyle.CIX ] )
+
+ progdialog.close()
+ self.fileSelectionList.updateSelectedRows()
+ self.updateInfoBox()
+ self.updateMenus()
+
+ summary = u"Successfully copied tags in {0} archive(s).".format( success_count )
+ if len( failed_list ) > 0:
+ summary += u"\n\nThe copy operation failed in the following {0} archive(s):\n".format( len( failed_list ) )
+ for f in failed_list:
+ summary += u"\t{0}\n".format( f )
+
+ dlg = LogWindow( self )
+ dlg.setText( summary )
+ dlg.setWindowTitle( "Tag Copy Summary" )
+ dlg.exec_()
+
+ def actualIssueDataFetch( self, match ):
+
+ # now get the particular issue data
+ cv_md = None
+ QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
+
+ try:
+ comicVine = ComicVineTalker( )
+ comicVine.wait_for_rate_limit = self.settings.wait_and_retry_on_rate_limit
+ cv_md = comicVine.fetchIssueData( match['volume_id'], match['issue_number'], self.settings )
+ except ComicVineTalkerException:
+ print "Network error while getting issue details. Save aborted"
+
+ if cv_md is not None:
+ if self.settings.apply_cbl_transform_on_cv_import:
+ cv_md = CBLTransformer( cv_md, self.settings ).apply()
+
+ QtGui.QApplication.restoreOverrideCursor()
+
+ return cv_md
+
+ def autoTagLog( self, text ):
+ IssueIdentifier.defaultWriteOutput( text )
+ if self.atprogdialog is not None:
+ self.atprogdialog.textEdit.insertPlainText(text)
+ self.atprogdialog.textEdit.ensureCursorVisible()
+ QtCore.QCoreApplication.processEvents()
+ QtCore.QCoreApplication.processEvents()
+ QtCore.QCoreApplication.processEvents()
+
+ def identifyAndTagSingleArchive( self, ca, match_results, dlg):
+ success = False
+ ii = IssueIdentifier( ca, self.settings )
+
+ # read in metadata, and parse file name if not there
+ md = ca.readMetadata( self.save_data_style )
+ if md.isEmpty:
+ md = ca.metadataFromFilename(self.settings.parse_scan_info)
+ if dlg.ignoreLeadingDigitsInFilename and md.series is not None:
+ #remove all leading numbers
+ md.series = re.sub( "([\d.]*)(.*)", "\\2", md.series)
+
+ # use the dialog specified search string
+ if dlg.searchString is not None:
+ md.series = dlg.searchString
+
+ if md is None or md.isEmpty:
+ print "!!!!No metadata given to search online with!"
+ return False, match_results
+
+ if dlg.dontUseYear:
+ md.year = None
+ if dlg.assumeIssueOne and ( md.issue is None or md.issue == ""):
+ md.issue = "1"
+ ii.setAdditionalMetadata( md )
+ ii.onlyUseAdditionalMetaData = True
+ ii.waitAndRetryOnRateLimit = dlg.waitAndRetryOnRateLimit
+ ii.setOutputFunction( self.autoTagLog )
+ ii.cover_page_index = md.getCoverPageIndexList()[0]
+ ii.setCoverURLCallback( self.atprogdialog.setTestImage )
+ ii.setNameLengthDeltaThreshold( dlg.nameLengthMatchTolerance )
+
+ matches = ii.search()
+
+ result = ii.search_result
+
+ found_match = False
+ choices = False
+ low_confidence = False
+ no_match = False
+
+ if result == ii.ResultNoMatches:
+ pass
+ elif result == ii.ResultFoundMatchButBadCoverScore:
+ low_confidence = True
+ found_match = True
+ elif result == ii.ResultFoundMatchButNotFirstPage :
+ found_match = True
+ elif result == ii.ResultMultipleMatchesWithBadImageScores:
+ low_confidence = True
+ choices = True
+ elif result == ii.ResultOneGoodMatch:
+ found_match = True
+ elif result == ii.ResultMultipleGoodMatches:
+ choices = True
+
+ if choices:
+ if low_confidence:
+ self.autoTagLog( "Online search: Multiple low-confidence matches. Save aborted\n" )
+ match_results.lowConfidenceMatches.append(MultipleMatch(ca,matches))
+ else:
+ self.autoTagLog( "Online search: Multiple matches. Save aborted\n" )
+ match_results.multipleMatches.append(MultipleMatch(ca,matches))
+ elif low_confidence and not dlg.autoSaveOnLow:
+ self.autoTagLog( "Online search: Low confidence match. Save aborted\n" )
+ match_results.lowConfidenceMatches.append(MultipleMatch(ca,matches))
+ elif not found_match:
+ self.autoTagLog( "Online search: No match found. Save aborted\n" )
+ match_results.noMatches.append(ca.path)
+ else:
+ # a single match!
+ if low_confidence:
+ self.autoTagLog( "Online search: Low confidence match, but saving anyways, as indicated...\n" )
+
+ # now get the particular issue data
+ cv_md = self.actualIssueDataFetch( matches[0] )
+ if cv_md is None:
+ match_results.fetchDataFailures.append(ca.path)
+
+ if cv_md is not None:
+ md.overlay( cv_md )
+
+ if not ca.writeMetadata( md, self.save_data_style ):
+ match_results.writeFailures.append(ca.path)
+ self.autoTagLog( "Save failed ;-(\n" )
+ else:
+ match_results.goodMatches.append(ca.path)
+ success = True
+ self.autoTagLog( "Save complete!\n" )
+ ca.loadCache( [ MetaDataStyle.CBI, MetaDataStyle.CIX ] )
+
+ return success, match_results
+
+ def autoTag( self ):
+ ca_list = self.fileSelectionList.getSelectedArchiveList()
+ style = self.save_data_style
+
+ if len(ca_list) == 0:
+ QtGui.QMessageBox.information(self, self.tr("Auto-Tag"), self.tr("No archives selected!"))
+ return
+
+ if not self.dirtyFlagVerification( "Auto-Tag",
+ "If you auto-tag now, unsaved data in the form will be lost. Are you sure?"):
+ return
+
+ atstartdlg = AutoTagStartWindow( self, self.settings,
+ self.tr("You have selected {0} archive(s) to automatically identify and write {1} tags to.\n\n".format(len(ca_list), MetaDataStyle.name[style]) +
+ "Please choose options below, and select OK to Auto-Tag.\n" ))
+
+ atstartdlg.adjustSize( )
+ atstartdlg.setModal( True )
+ if not atstartdlg.exec_():
+ return
+
+ self.atprogdialog = AutoTagProgressWindow( self)
+ self.atprogdialog.setModal(True)
+ self.atprogdialog.show()
+ self.atprogdialog.progressBar.setMaximum( len(ca_list) )
+ self.atprogdialog.setWindowTitle( "Auto-Tagging" )
+
+ self.autoTagLog( u"========================================================================\n" )
+ self.autoTagLog( u"Auto-Tagging Started for {0} items\n".format(len(ca_list)))
+
+ prog_idx = 0
+
+ match_results = OnlineMatchResults()
+ archives_to_remove = []
+ for ca in ca_list:
+ self.autoTagLog( u"============================================================\n" )
+ self.autoTagLog( u"Auto-Tagging {0} of {1}\n".format(prog_idx+1, len(ca_list)))
+ self.autoTagLog( u"{0}\n".format(ca.path) )
+ cover_idx = ca.readMetadata(style).getCoverPageIndexList()[0]
+ image_data = ca.getPage( cover_idx )
+ self.atprogdialog.setArchiveImage( image_data )
+ self.atprogdialog.setTestImage( None )
+
+ QtCore.QCoreApplication.processEvents()
+ if self.atprogdialog.isdone:
+ break
+ self.atprogdialog.progressBar.setValue( prog_idx )
+ prog_idx += 1
+ self.atprogdialog.label.setText( ca.path )
+ centerWindowOnParent( self.atprogdialog )
+ QtCore.QCoreApplication.processEvents()
+
+ if ca.isWritable():
+ success, match_results = self.identifyAndTagSingleArchive( ca, match_results, atstartdlg )
+
+ if success and atstartdlg.removeAfterSuccess:
+ archives_to_remove.append( ca )
+
+ self.atprogdialog.close()
+
+ if atstartdlg.removeAfterSuccess:
+ self.fileSelectionList.removeArchiveList( archives_to_remove )
+ self.fileSelectionList.updateSelectedRows()
+
+ self.loadArchive( self.fileSelectionList.getCurrentArchive() )
+ self.atprogdialog = None
+
+ summary = u""
+ summary += u"Successfully tagged archives: {0}\n".format( len(match_results.goodMatches))
+
+ if len ( match_results.multipleMatches ) > 0:
+ summary += u"Archives with multiple matches: {0}\n".format( len(match_results.multipleMatches))
+ if len ( match_results.lowConfidenceMatches ) > 0:
+ summary += u"Archives with one or more low-confidence matches: {0}\n".format( len(match_results.lowConfidenceMatches))
+ if len ( match_results.noMatches ) > 0:
+ summary += u"Archives with no matches: {0}\n".format( len(match_results.noMatches))
+ if len ( match_results.fetchDataFailures ) > 0:
+ summary += u"Archives that failed due to data fetch errors: {0}\n".format( len(match_results.fetchDataFailures))
+ if len ( match_results.writeFailures ) > 0:
+ summary += u"Archives that failed due to file writing errors: {0}\n".format( len(match_results.writeFailures))
+
+ self.autoTagLog( summary )
+
+ sum_selectable = len ( match_results.multipleMatches ) + len(match_results.lowConfidenceMatches)
+ if sum_selectable > 0:
+ summary += u"\n\nDo you want to manually select the ones with multiple matches and/or low-confidence matches now?"
+
+ reply = QtGui.QMessageBox.question(self,
+ self.tr(u"Auto-Tag Summary"),
+ self.tr(summary),
+ QtGui.QMessageBox.Yes, QtGui.QMessageBox.No )
+
+ match_results.multipleMatches.extend( match_results.lowConfidenceMatches )
+ if reply == QtGui.QMessageBox.Yes:
+ matchdlg = AutoTagMatchWindow( self, match_results.multipleMatches, style, self.actualIssueDataFetch)
+ matchdlg.setModal( True )
+ matchdlg.exec_()
+ self.fileSelectionList.updateSelectedRows()
+ self.loadArchive( self.fileSelectionList.getCurrentArchive() )
+
+ else:
+ QtGui.QMessageBox.information(self, self.tr("Auto-Tag Summary"), self.tr(summary))
+
+
+
+
+
+ def dirtyFlagVerification( self, title, desc):
+ if self.dirtyFlag:
+ reply = QtGui.QMessageBox.question(self,
+ self.tr(title),
+ self.tr(desc),
+ QtGui.QMessageBox.Yes, QtGui.QMessageBox.No )
+
+ if reply != QtGui.QMessageBox.Yes:
+ return False
+ return True
+
+ def closeEvent(self, event):
+
+ if self.dirtyFlagVerification( "Exit " + self.appName,
+ "If you quit now, data in the form will be lost. Are you sure?"):
+ appsize = self.size()
+ self.settings.last_main_window_width = appsize.width()
+ self.settings.last_main_window_height = appsize.height()
+ self.settings.last_main_window_x = self.x()
+ self.settings.last_main_window_y = self.y()
+ self.settings.last_form_side_width = self.splitter.sizes()[0]
+ self.settings.last_list_side_width = self.splitter.sizes()[1]
+ self.settings.last_filelist_sorted_column, self.settings.last_filelist_sorted_order = self.fileSelectionList.getSorting()
+ self.settings.save()
+
+
+ event.accept()
+ else:
+ event.ignore()
+
+ def showPageBrowser( self ):
+ if self.page_browser is None:
+ self.page_browser = PageBrowserWindow( self, self.metadata )
+ if self.comic_archive is not None:
+ self.page_browser.setComicArchive( self.comic_archive )
+ self.page_browser.finished.connect(self.pageBrowserClosed)
+
+ def pageBrowserClosed( self ):
+ self.page_browser = None
+
+ def viewRawCRTags( self ):
+ if self.comic_archive is not None and self.comic_archive.hasCIX():
+ dlg = LogWindow( self )
+ dlg.setText( self.comic_archive.readRawCIX() )
+ dlg.setWindowTitle( "Raw ComicRack Tag View" )
+ dlg.exec_()
+
+ def viewRawCBLTags( self ):
+ if self.comic_archive is not None and self.comic_archive.hasCBI():
+ dlg = LogWindow( self )
+ text = pprint.pformat( json.loads(self.comic_archive.readRawCBI()), indent=4 )
+ dlg.setText(text )
+ dlg.setWindowTitle( "Raw ComicBookLover Tag View" )
+ dlg.exec_()
+
+ def showWiki( self ):
+ webbrowser.open("http://code.google.com/p/comictagger/wiki/Home?tm=6")
+
+ def reportBug( self ):
+ webbrowser.open("http://code.google.com/p/comictagger/issues/list")
+
+ def showForum( self ):
+ webbrowser.open("http://comictagger.forumotion.com/")
+
+ def frontCoverChanged( self, int ):
+ self.metadata.pages = self.pageListEditor.getPageList()
+ self.updateCoverImage()
+
+ def pageListOrderChanged( self ):
+ self.metadata.pages = self.pageListEditor.getPageList()
+
+ def applyCBLTransform(self):
+ self.formToMetadata()
+ self.metadata = CBLTransformer( self.metadata, self.settings ).apply()
+ self.metadataToForm()
+
+ def renameArchive(self):
+ ca_list = self.fileSelectionList.getSelectedArchiveList()
+
+ if len(ca_list) == 0:
+ QtGui.QMessageBox.information(self, self.tr("Rename"), self.tr("No archives selected!"))
+ return
+
+ if self.dirtyFlagVerification( "File Rename",
+ "If you rename files now, unsaved data in the form will be lost. Are you sure?"):
+
+ dlg = RenameWindow( self, ca_list, self.load_data_style, self.settings )
+ dlg.setModal( True )
+ if dlg.exec_():
+ self.fileSelectionList.updateSelectedRows()
+ self.loadArchive( self.comic_archive )
+
+
+
+ def fileListSelectionChanged( self, qvarFI ):
+ fi = qvarFI.toPyObject()
+ self.loadArchive( fi.ca )
+
+ def loadArchive( self, comic_archive ):
+ self.comic_archive = None
+ self.clearForm()
+
+ self.settings.last_opened_folder = os.path.abspath(os.path.split(comic_archive.path)[0])
+ self.comic_archive = comic_archive
+ self.metadata = self.comic_archive.readMetadata(self.load_data_style)
+ if self.metadata is None:
+ self.metadata = GenericMetadata()
+
+ self.actualLoadCurrentArchive()
+
+ def fileListCleared( self ):
+ self.resetApp()
+
+ def splitterMovedEvent( self, w1, w2 ):
+ scrollbar_w = 0
+ if self.scrollArea.verticalScrollBar().isVisible():
+ scrollbar_w = self.scrollArea.verticalScrollBar().width()
+
+ new_w = self.scrollArea.width() - scrollbar_w - 5
+ self.scrollAreaWidgetContents.resize( new_w, self.scrollAreaWidgetContents.height())
+
+ def resizeEvent( self, ev ):
+ self.splitterMovedEvent( 0, 0)
+
+ def tabChanged( self, idx ):
+ if idx == 0:
+ self.splitterMovedEvent( 0, 0)
+
+ def checkLatestVersionOnline( self ):
+ self.versionChecker = VersionChecker()
+ self.versionChecker.versionRequestComplete.connect( self.versionCheckComplete )
+ self.versionChecker.asyncGetLatestVersion( self.settings.install_id, self.settings.send_usage_stats )
+
+ def versionCheckComplete( self, new_version ):
+ if ( new_version != self.version and
+ new_version != self.settings.dont_notify_about_this_version):
+ website = "http://code.google.com/p/comictagger"
+ checked = OptionalMessageDialog.msg( self, "New version available!",
+ "New version ({0}) available!
(You are currently running {1})
".format( new_version, self.version) +
+ "Visit {0} for more info.
".format(website),
+ QtCore.Qt.Unchecked,
+ "Don't tell me about this version again")
+ if checked:
+ self.settings.dont_notify_about_this_version = new_version
+
+ def onIncomingSocketConnection(self):
+ # accept connection from other instance.
+ # read in the file list if they're giving it,
+ # and add to our own list
+ localSocket = self.socketServer.nextPendingConnection()
+ if localSocket.waitForReadyRead(3000):
+ byteArray = localSocket.readAll()
+ if len(byteArray) > 0:
+ obj = pickle.loads(byteArray)
+ localSocket.disconnectFromServer()
+ if type(obj) is list:
+ self.fileSelectionList.addPathList( obj )
+ else:
+ #print localSocket.errorString().toLatin1()
+ pass
+
+ self.bringToTop()
+
+ def bringToTop(self):
+ if platform.system() == "Windows":
+ self.showNormal()
+ self.raise_()
+ self.activateWindow()
+ try:
+ import win32con
+ import win32gui
+ hwnd = self.effectiveWinId()
+ rect = win32gui.GetWindowRect(hwnd)
+ x = rect[0]
+ y = rect[1]
+ w = rect[2] - x
+ h = rect[3] - y
+ # mark it "always on top", just for a moment, to force it to the top
+ win32gui.SetWindowPos(hwnd,win32con.HWND_TOPMOST, x, y, w, h, 0)
+ win32gui.SetWindowPos(hwnd,win32con.HWND_NOTOPMOST, x, y, w, h, 0)
+ except Exception as e:
+ print "Whoops", e
+ elif platform.system() == "Darwin":
+ self.raise_()
+ self.showNormal()
+ self.activateWindow()
+ else:
+ flags = self.windowFlags()
+ self.setWindowFlags( flags | QtCore.Qt.WindowStaysOnTopHint | QtCore.Qt.X11BypassWindowManagerHint)
+ QtCore.QCoreApplication.processEvents()
+ #self.show()
+ self.setWindowFlags( flags )
+ self.show()
diff --git a/comictaggerlib/ui/taggerwindow.ui b/comictaggerlib/ui/taggerwindow.ui
index 0ff3f56..d5fcc3b 100644
--- a/comictaggerlib/ui/taggerwindow.ui
+++ b/comictaggerlib/ui/taggerwindow.ui
@@ -402,14 +402,26 @@
-
-
+
- # Issues
+ Day
-
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 16777215
+ 16777215
+
+
false
@@ -419,27 +431,44 @@
-
+
+
+ # Issues
+
+
+
+ -
+
+
+ false
+
+
+ Qt::ImhDigitsOnly
+
+
+
+ -
Volume
- -
+
-
false
- -
+
-
# Volumes
- -
+
-
false
@@ -449,14 +478,14 @@
- -
+
-
Alt.Issue
- -
+
-
@@ -469,14 +498,14 @@
- -
+
-
Alt. # Issues
- -
+
-
false