forked from openlp/openlp
This branch implements the 'images groups' feature. It has the following features:
- convert old 'images files' setting to SQLite database - existing images are loaded as top-level items to minimize the change for users - groups can be nested - images can be moved between groups by drag&drop - new images can be added directly to a group by drag&drop, or by clicking the 'Add images' button - image groups can be used as service items with multiple images bzr-revno: 2214
This commit is contained in:
commit
ce528140ae
@ -377,6 +377,7 @@ from uistrings import UiStrings
|
|||||||
from screen import ScreenList
|
from screen import ScreenList
|
||||||
from settings import Settings
|
from settings import Settings
|
||||||
from listwidgetwithdnd import ListWidgetWithDnD
|
from listwidgetwithdnd import ListWidgetWithDnD
|
||||||
|
from treewidgetwithdnd import TreeWidgetWithDnD
|
||||||
from formattingtags import FormattingTags
|
from formattingtags import FormattingTags
|
||||||
from spelltextedit import SpellTextEdit
|
from spelltextedit import SpellTextEdit
|
||||||
from plugin import PluginStatus, StringContent, Plugin
|
from plugin import PluginStatus, StringContent, Plugin
|
||||||
|
@ -311,16 +311,16 @@ class MediaManagerItem(QtGui.QWidget):
|
|||||||
self.validateAndLoad(files)
|
self.validateAndLoad(files)
|
||||||
self.application.set_normal_cursor()
|
self.application.set_normal_cursor()
|
||||||
|
|
||||||
def loadFile(self, files):
|
def loadFile(self, data):
|
||||||
"""
|
"""
|
||||||
Turn file from Drag and Drop into an array so the Validate code can run it.
|
Turn file from Drag and Drop into an array so the Validate code can run it.
|
||||||
|
|
||||||
``files``
|
``data``
|
||||||
The list of files to be loaded
|
A dictionary containing the list of files to be loaded and the target
|
||||||
"""
|
"""
|
||||||
new_files = []
|
new_files = []
|
||||||
error_shown = False
|
error_shown = False
|
||||||
for file_name in files:
|
for file_name in data['files']:
|
||||||
file_type = file_name.split(u'.')[-1]
|
file_type = file_name.split(u'.')[-1]
|
||||||
if file_type.lower() not in self.onNewFileMasks:
|
if file_type.lower() not in self.onNewFileMasks:
|
||||||
if not error_shown:
|
if not error_shown:
|
||||||
@ -330,15 +330,27 @@ class MediaManagerItem(QtGui.QWidget):
|
|||||||
else:
|
else:
|
||||||
new_files.append(file_name)
|
new_files.append(file_name)
|
||||||
if new_files:
|
if new_files:
|
||||||
self.validateAndLoad(new_files)
|
self.validateAndLoad(new_files, data['target'])
|
||||||
|
|
||||||
def validateAndLoad(self, files):
|
def dnd_move_internal(self, target):
|
||||||
|
"""
|
||||||
|
Handle internal moving of media manager items
|
||||||
|
|
||||||
|
``target``
|
||||||
|
The target of the DnD action
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def validateAndLoad(self, files, target_group=None):
|
||||||
"""
|
"""
|
||||||
Process a list for files either from the File Dialog or from Drag and
|
Process a list for files either from the File Dialog or from Drag and
|
||||||
Drop
|
Drop
|
||||||
|
|
||||||
``files``
|
``files``
|
||||||
The files to be loaded.
|
The files to be loaded.
|
||||||
|
|
||||||
|
``target_group``
|
||||||
|
The QTreeWidgetItem of the group that will be the parent of the added files
|
||||||
"""
|
"""
|
||||||
names = []
|
names = []
|
||||||
full_list = []
|
full_list = []
|
||||||
@ -347,16 +359,17 @@ class MediaManagerItem(QtGui.QWidget):
|
|||||||
full_list.append(self.listView.item(count).data(QtCore.Qt.UserRole))
|
full_list.append(self.listView.item(count).data(QtCore.Qt.UserRole))
|
||||||
duplicates_found = False
|
duplicates_found = False
|
||||||
files_added = False
|
files_added = False
|
||||||
for file in files:
|
for file_path in files:
|
||||||
filename = os.path.split(unicode(file))[1]
|
filename = os.path.split(unicode(file_path))[1]
|
||||||
if filename in names:
|
if filename in names:
|
||||||
duplicates_found = True
|
duplicates_found = True
|
||||||
else:
|
else:
|
||||||
files_added = True
|
files_added = True
|
||||||
full_list.append(file)
|
full_list.append(filename)
|
||||||
if full_list and files_added:
|
if full_list and files_added:
|
||||||
|
if target_group is None:
|
||||||
self.listView.clear()
|
self.listView.clear()
|
||||||
self.loadList(full_list)
|
self.loadList(full_list, target_group)
|
||||||
last_dir = os.path.split(unicode(files[0]))[0]
|
last_dir = os.path.split(unicode(files[0]))[0]
|
||||||
Settings().setValue(self.settingsSection + u'/last directory', last_dir)
|
Settings().setValue(self.settingsSection + u'/last directory', last_dir)
|
||||||
Settings().setValue(u'%s/%s files' % (self.settingsSection, self.settingsSection), self.getFileList())
|
Settings().setValue(u'%s/%s files' % (self.settingsSection, self.settingsSection), self.getFileList())
|
||||||
@ -387,7 +400,7 @@ class MediaManagerItem(QtGui.QWidget):
|
|||||||
file_list.append(filename)
|
file_list.append(filename)
|
||||||
return file_list
|
return file_list
|
||||||
|
|
||||||
def loadList(self, list):
|
def loadList(self, list, target_group):
|
||||||
"""
|
"""
|
||||||
Load a list. Needs to be implemented by the plugin.
|
Load a list. Needs to be implemented by the plugin.
|
||||||
"""
|
"""
|
||||||
|
@ -216,6 +216,15 @@ class Plugin(QtCore.QObject):
|
|||||||
if self.mediaItemClass:
|
if self.mediaItemClass:
|
||||||
self.mediaItem = self.mediaItemClass(self.main_window.media_dock_manager.media_dock, self)
|
self.mediaItem = self.mediaItemClass(self.main_window.media_dock_manager.media_dock, self)
|
||||||
|
|
||||||
|
def upgrade_settings(self, settings):
|
||||||
|
"""
|
||||||
|
Upgrade the settings of this plugin.
|
||||||
|
|
||||||
|
``settings``
|
||||||
|
The Settings object containing the old settings.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
def addImportMenuItem(self, importMenu):
|
def addImportMenuItem(self, importMenu):
|
||||||
"""
|
"""
|
||||||
Create a menu item and add it to the "Import" menu.
|
Create a menu item and add it to the "Import" menu.
|
||||||
@ -300,24 +309,10 @@ class Plugin(QtCore.QObject):
|
|||||||
# FIXME: Remove after 2.2 release.
|
# FIXME: Remove after 2.2 release.
|
||||||
# This is needed to load the list of images/media/presentation from the config saved
|
# This is needed to load the list of images/media/presentation from the config saved
|
||||||
# before the settings rewrite.
|
# before the settings rewrite.
|
||||||
if self.mediaItemClass is not None:
|
if self.mediaItemClass is not None and self.name != u'images':
|
||||||
# We need QSettings instead of Settings here to bypass our central settings dict.
|
loaded_list = Settings().get_files_from_config(self)
|
||||||
# Do NOT do this anywhere else!
|
|
||||||
settings = QtCore.QSettings()
|
|
||||||
settings.beginGroup(self.settingsSection)
|
|
||||||
if settings.contains(u'%s count' % self.name):
|
|
||||||
list_count = int(settings.value(u'%s count' % self.name, 0))
|
|
||||||
loaded_list = []
|
|
||||||
if list_count:
|
|
||||||
for counter in range(list_count):
|
|
||||||
item = settings.value(u'%s %d' % (self.name, counter), u'')
|
|
||||||
if item:
|
|
||||||
loaded_list.append(item)
|
|
||||||
settings.remove(u'%s %d' % (self.name, counter))
|
|
||||||
settings.remove(u'%s count' % self.name)
|
|
||||||
# Now save the list to the config using our Settings class.
|
# Now save the list to the config using our Settings class.
|
||||||
Settings().setValue(u'%s/%s files' % (self.settingsSection, self.name), loaded_list)
|
Settings().setValue(u'%s/%s files' % (self.settingsSection, self.name), loaded_list)
|
||||||
settings.endGroup()
|
|
||||||
|
|
||||||
def uses_theme(self, theme):
|
def uses_theme(self, theme):
|
||||||
"""
|
"""
|
||||||
|
@ -183,6 +183,17 @@ class PluginManager(object):
|
|||||||
if plugin.status is not PluginStatus.Disabled:
|
if plugin.status is not PluginStatus.Disabled:
|
||||||
plugin.addToolsMenuItem(self.main_window.tools_menu)
|
plugin.addToolsMenuItem(self.main_window.tools_menu)
|
||||||
|
|
||||||
|
def hook_upgrade_plugin_settings(self, settings):
|
||||||
|
"""
|
||||||
|
Loop through all the plugins and give them an opportunity to upgrade their settings.
|
||||||
|
|
||||||
|
``settings``
|
||||||
|
The Settings object containing the old settings.
|
||||||
|
"""
|
||||||
|
for plugin in self.plugins:
|
||||||
|
if plugin.status is not PluginStatus.Disabled:
|
||||||
|
plugin.upgrade_settings(settings)
|
||||||
|
|
||||||
def initialise_plugins(self):
|
def initialise_plugins(self):
|
||||||
"""
|
"""
|
||||||
Loop through all the plugins and give them an opportunity to
|
Loop through all the plugins and give them an opportunity to
|
||||||
|
@ -439,3 +439,32 @@ class Settings(QtCore.QSettings):
|
|||||||
if isinstance(default_value, int):
|
if isinstance(default_value, int):
|
||||||
return int(setting)
|
return int(setting)
|
||||||
return setting
|
return setting
|
||||||
|
|
||||||
|
def get_files_from_config(self, plugin):
|
||||||
|
"""
|
||||||
|
This removes the settings needed for old way we saved files (e. g. the image paths for the image plugin). A list
|
||||||
|
of file paths are returned.
|
||||||
|
|
||||||
|
**Note**: Only a list of paths is returned; this does not convert anything!
|
||||||
|
|
||||||
|
``plugin``
|
||||||
|
The Plugin object.The caller has to convert/save the list himself; o
|
||||||
|
"""
|
||||||
|
files_list = []
|
||||||
|
# We need QSettings instead of Settings here to bypass our central settings dict.
|
||||||
|
# Do NOT do this anywhere else!
|
||||||
|
settings = QtCore.QSettings(self.fileName(), Settings.IniFormat)
|
||||||
|
settings.beginGroup(plugin.settingsSection)
|
||||||
|
if settings.contains(u'%s count' % plugin.name):
|
||||||
|
# Get the count.
|
||||||
|
list_count = int(settings.value(u'%s count' % plugin.name, 0))
|
||||||
|
if list_count:
|
||||||
|
for counter in range(list_count):
|
||||||
|
# The keys were named e. g.: "image 0"
|
||||||
|
item = settings.value(u'%s %d' % (plugin.name, counter), u'')
|
||||||
|
if item:
|
||||||
|
files_list.append(item)
|
||||||
|
settings.remove(u'%s %d' % (plugin.name, counter))
|
||||||
|
settings.remove(u'%s count' % plugin.name)
|
||||||
|
settings.endGroup()
|
||||||
|
return files_list
|
||||||
|
151
openlp/core/lib/treewidgetwithdnd.py
Normal file
151
openlp/core/lib/treewidgetwithdnd.py
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||||
|
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
|
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||||
|
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||||
|
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||||
|
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||||
|
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||||
|
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
"""
|
||||||
|
Extend QTreeWidget to handle drag and drop functionality
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
|
||||||
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
|
from openlp.core.lib import Registry
|
||||||
|
|
||||||
|
|
||||||
|
class TreeWidgetWithDnD(QtGui.QTreeWidget):
|
||||||
|
"""
|
||||||
|
Provide a tree widget to store objects and handle drag and drop events
|
||||||
|
"""
|
||||||
|
def __init__(self, parent=None, name=u''):
|
||||||
|
"""
|
||||||
|
Initialise the tree widget
|
||||||
|
"""
|
||||||
|
QtGui.QTreeWidget.__init__(self, parent)
|
||||||
|
self.mimeDataText = name
|
||||||
|
self.allow_internal_dnd = False
|
||||||
|
self.header().close()
|
||||||
|
self.defaultIndentation = self.indentation()
|
||||||
|
self.setIndentation(0)
|
||||||
|
self.setAnimated(True)
|
||||||
|
assert(self.mimeDataText)
|
||||||
|
|
||||||
|
def activateDnD(self):
|
||||||
|
"""
|
||||||
|
Activate DnD of widget
|
||||||
|
"""
|
||||||
|
self.setAcceptDrops(True)
|
||||||
|
self.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
|
||||||
|
Registry().register_function((u'%s_dnd' % self.mimeDataText), self.parent().loadFile)
|
||||||
|
Registry().register_function((u'%s_dnd_internal' % self.mimeDataText), self.parent().dnd_move_internal)
|
||||||
|
|
||||||
|
def mouseMoveEvent(self, event):
|
||||||
|
"""
|
||||||
|
Drag and drop event does not care what data is selected as the recipient will use events to request the data
|
||||||
|
move just tell it what plugin to call
|
||||||
|
|
||||||
|
``event``
|
||||||
|
The event that occurred
|
||||||
|
"""
|
||||||
|
if event.buttons() != QtCore.Qt.LeftButton:
|
||||||
|
event.ignore()
|
||||||
|
return
|
||||||
|
if not self.selectedItems():
|
||||||
|
event.ignore()
|
||||||
|
return
|
||||||
|
drag = QtGui.QDrag(self)
|
||||||
|
mimeData = QtCore.QMimeData()
|
||||||
|
drag.setMimeData(mimeData)
|
||||||
|
mimeData.setText(self.mimeDataText)
|
||||||
|
drag.start(QtCore.Qt.CopyAction)
|
||||||
|
|
||||||
|
def dragEnterEvent(self, event):
|
||||||
|
"""
|
||||||
|
Receive drag enter event, check if it is a file or internal object and allow it if it is.
|
||||||
|
|
||||||
|
``event``
|
||||||
|
The event that occurred
|
||||||
|
"""
|
||||||
|
if event.mimeData().hasUrls():
|
||||||
|
event.accept()
|
||||||
|
elif self.allow_internal_dnd:
|
||||||
|
event.accept()
|
||||||
|
else:
|
||||||
|
event.ignore()
|
||||||
|
|
||||||
|
def dragMoveEvent(self, event):
|
||||||
|
"""
|
||||||
|
Receive drag move event, check if it is a file or internal object and allow it if it is.
|
||||||
|
|
||||||
|
``event``
|
||||||
|
The event that occurred
|
||||||
|
"""
|
||||||
|
QtGui.QTreeWidget.dragMoveEvent(self, event)
|
||||||
|
if event.mimeData().hasUrls():
|
||||||
|
event.setDropAction(QtCore.Qt.CopyAction)
|
||||||
|
event.accept()
|
||||||
|
elif self.allow_internal_dnd:
|
||||||
|
event.setDropAction(QtCore.Qt.CopyAction)
|
||||||
|
event.accept()
|
||||||
|
else:
|
||||||
|
event.ignore()
|
||||||
|
|
||||||
|
def dropEvent(self, event):
|
||||||
|
"""
|
||||||
|
Receive drop event, check if it is a file or internal object and process it if it is.
|
||||||
|
|
||||||
|
``event``
|
||||||
|
Handle of the event pint passed
|
||||||
|
"""
|
||||||
|
if event.mimeData().hasUrls():
|
||||||
|
event.setDropAction(QtCore.Qt.CopyAction)
|
||||||
|
event.accept()
|
||||||
|
files = []
|
||||||
|
for url in event.mimeData().urls():
|
||||||
|
localFile = url.toLocalFile()
|
||||||
|
if os.path.isfile(localFile):
|
||||||
|
files.append(localFile)
|
||||||
|
elif os.path.isdir(localFile):
|
||||||
|
listing = os.listdir(localFile)
|
||||||
|
for file_name in listing:
|
||||||
|
files.append(os.path.join(localFile, file_name))
|
||||||
|
Registry().execute(u'%s_dnd' % self.mimeDataText, {'files': files, 'target': self.itemAt(event.pos())})
|
||||||
|
elif self.allow_internal_dnd:
|
||||||
|
event.setDropAction(QtCore.Qt.CopyAction)
|
||||||
|
event.accept()
|
||||||
|
Registry().execute(u'%s_dnd_internal' % self.mimeDataText, self.itemAt(event.pos()))
|
||||||
|
else:
|
||||||
|
event.ignore()
|
||||||
|
|
||||||
|
# Convenience methods for emulating a QListWidget. This helps keeping MediaManagerItem simple.
|
||||||
|
def addItem(self, item):
|
||||||
|
self.addTopLevelItem(item)
|
||||||
|
|
||||||
|
def count(self):
|
||||||
|
return self.topLevelItemCount()
|
||||||
|
|
||||||
|
def item(self, index):
|
||||||
|
return self.topLevelItem(index)
|
@ -58,6 +58,7 @@ class UiStrings(object):
|
|||||||
"""
|
"""
|
||||||
self.About = translate('OpenLP.Ui', 'About')
|
self.About = translate('OpenLP.Ui', 'About')
|
||||||
self.Add = translate('OpenLP.Ui', '&Add')
|
self.Add = translate('OpenLP.Ui', '&Add')
|
||||||
|
self.AddGroup = translate('OpenLP.Ui', 'Add group')
|
||||||
self.Advanced = translate('OpenLP.Ui', 'Advanced')
|
self.Advanced = translate('OpenLP.Ui', 'Advanced')
|
||||||
self.AllFiles = translate('OpenLP.Ui', 'All Files')
|
self.AllFiles = translate('OpenLP.Ui', 'All Files')
|
||||||
self.Automatic = translate('OpenLP.Ui', 'Automatic')
|
self.Automatic = translate('OpenLP.Ui', 'Automatic')
|
||||||
|
@ -686,6 +686,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||||||
``message``
|
``message``
|
||||||
The message to be displayed.
|
The message to be displayed.
|
||||||
"""
|
"""
|
||||||
|
if hasattr(self.application, u'splash'):
|
||||||
self.application.splash.close()
|
self.application.splash.close()
|
||||||
QtGui.QMessageBox.critical(self, title, message)
|
QtGui.QMessageBox.critical(self, title, message)
|
||||||
|
|
||||||
@ -699,6 +700,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||||||
``message``
|
``message``
|
||||||
The message to be displayed.
|
The message to be displayed.
|
||||||
"""
|
"""
|
||||||
|
if hasattr(self.application, u'splash'):
|
||||||
self.application.splash.close()
|
self.application.splash.close()
|
||||||
QtGui.QMessageBox.warning(self, title, message)
|
QtGui.QMessageBox.warning(self, title, message)
|
||||||
|
|
||||||
@ -712,6 +714,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||||||
``message``
|
``message``
|
||||||
The message to be displayed.
|
The message to be displayed.
|
||||||
"""
|
"""
|
||||||
|
if hasattr(self.application, u'splash'):
|
||||||
self.application.splash.close()
|
self.application.splash.close()
|
||||||
QtGui.QMessageBox.information(self, title, message)
|
QtGui.QMessageBox.information(self, title, message)
|
||||||
|
|
||||||
@ -816,8 +819,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||||||
setting_sections.extend([self.header_section])
|
setting_sections.extend([self.header_section])
|
||||||
setting_sections.extend([u'crashreport'])
|
setting_sections.extend([u'crashreport'])
|
||||||
# Add plugin sections.
|
# Add plugin sections.
|
||||||
for plugin in self.plugin_manager.plugins:
|
setting_sections.extend([plugin.name for plugin in self.plugin_manager.plugins])
|
||||||
setting_sections.extend([plugin.name])
|
|
||||||
# Copy the settings file to the tmp dir, because we do not want to change the original one.
|
# Copy the settings file to the tmp dir, because we do not want to change the original one.
|
||||||
temp_directory = os.path.join(unicode(gettempdir()), u'openlp')
|
temp_directory = os.path.join(unicode(gettempdir()), u'openlp')
|
||||||
check_directory_exists(temp_directory)
|
check_directory_exists(temp_directory)
|
||||||
@ -825,11 +827,13 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||||||
shutil.copyfile(import_file_name, temp_config)
|
shutil.copyfile(import_file_name, temp_config)
|
||||||
settings = Settings()
|
settings = Settings()
|
||||||
import_settings = Settings(temp_config, Settings.IniFormat)
|
import_settings = Settings(temp_config, Settings.IniFormat)
|
||||||
|
# Convert image files
|
||||||
|
log.info(u'hook upgrade_plugin_settings')
|
||||||
|
self.plugin_manager.hook_upgrade_plugin_settings(import_settings)
|
||||||
# Remove/rename old settings to prepare the import.
|
# Remove/rename old settings to prepare the import.
|
||||||
import_settings.remove_obsolete_settings()
|
import_settings.remove_obsolete_settings()
|
||||||
# Lets do a basic sanity check. If it contains this string we can
|
# Lets do a basic sanity check. If it contains this string we can assume it was created by OpenLP and so we'll
|
||||||
# assume it was created by OpenLP and so we'll load what we can
|
# load what we can from it, and just silently ignore anything we don't recognise.
|
||||||
# from it, and just silently ignore anything we don't recognise
|
|
||||||
if import_settings.value(u'SettingsImport/type') != u'OpenLP_settings_export':
|
if import_settings.value(u'SettingsImport/type') != u'OpenLP_settings_export':
|
||||||
QtGui.QMessageBox.critical(self, translate('OpenLP.MainWindow', 'Import settings'),
|
QtGui.QMessageBox.critical(self, translate('OpenLP.MainWindow', 'Import settings'),
|
||||||
translate('OpenLP.MainWindow', 'The file you have selected does not appear to be a valid OpenLP '
|
translate('OpenLP.MainWindow', 'The file you have selected does not appear to be a valid OpenLP '
|
||||||
@ -864,9 +868,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||||||
settings.setValue(u'file_date_imported', now.strftime("%Y-%m-%d %H:%M"))
|
settings.setValue(u'file_date_imported', now.strftime("%Y-%m-%d %H:%M"))
|
||||||
settings.endGroup()
|
settings.endGroup()
|
||||||
settings.sync()
|
settings.sync()
|
||||||
# We must do an immediate restart or current configuration will
|
# We must do an immediate restart or current configuration will overwrite what was just imported when
|
||||||
# overwrite what was just imported when application terminates
|
# application terminates normally. We need to exit without saving configuration.
|
||||||
# normally. We need to exit without saving configuration.
|
|
||||||
QtGui.QMessageBox.information(self, translate('OpenLP.MainWindow', 'Import settings'),
|
QtGui.QMessageBox.information(self, translate('OpenLP.MainWindow', 'Import settings'),
|
||||||
translate('OpenLP.MainWindow', 'OpenLP will now close. Imported settings will '
|
translate('OpenLP.MainWindow', 'OpenLP will now close. Imported settings will '
|
||||||
'be applied the next time you start OpenLP.'),
|
'be applied the next time you start OpenLP.'),
|
||||||
|
@ -99,7 +99,7 @@ class CustomMediaItem(MediaManagerItem):
|
|||||||
self.loadList(self.manager.get_all_objects(CustomSlide, order_by_ref=CustomSlide.title))
|
self.loadList(self.manager.get_all_objects(CustomSlide, order_by_ref=CustomSlide.title))
|
||||||
self.config_update()
|
self.config_update()
|
||||||
|
|
||||||
def loadList(self, custom_slides):
|
def loadList(self, custom_slides, target_group=None):
|
||||||
# Sort out what custom we want to select after loading the list.
|
# Sort out what custom we want to select after loading the list.
|
||||||
self.saveAutoSelectId()
|
self.saveAutoSelectId()
|
||||||
self.listView.clear()
|
self.listView.clear()
|
||||||
|
57
openlp/plugins/images/forms/__init__.py
Normal file
57
openlp/plugins/images/forms/__init__.py
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||||
|
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
|
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||||
|
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||||
|
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||||
|
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||||
|
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||||
|
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
"""
|
||||||
|
Forms in OpenLP are made up of two classes. One class holds all the graphical
|
||||||
|
elements, like buttons and lists, and the other class holds all the functional
|
||||||
|
code, like slots and loading and saving.
|
||||||
|
|
||||||
|
The first class, commonly known as the **Dialog** class, is typically named
|
||||||
|
``Ui_<name>Dialog``. It is a slightly modified version of the class that the
|
||||||
|
``pyuic4`` command produces from Qt4's .ui file. Typical modifications will be
|
||||||
|
converting most strings from "" to u'' and using OpenLP's ``translate()``
|
||||||
|
function for translating strings.
|
||||||
|
|
||||||
|
The second class, commonly known as the **Form** class, is typically named
|
||||||
|
``<name>Form``. This class is the one which is instantiated and used. It uses
|
||||||
|
dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class
|
||||||
|
mentioned above, like so::
|
||||||
|
|
||||||
|
class AuthorsForm(QtGui.QDialog, Ui_AuthorsDialog):
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
QtGui.QDialog.__init__(self, parent)
|
||||||
|
self.setupUi(self)
|
||||||
|
|
||||||
|
This allows OpenLP to use ``self.object`` for all the GUI elements while keeping
|
||||||
|
them separate from the functionality, so that it is easier to recreate the GUI
|
||||||
|
from the .ui files later if necessary.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from addgroupform import AddGroupForm
|
||||||
|
from choosegroupform import ChooseGroupForm
|
64
openlp/plugins/images/forms/addgroupdialog.py
Normal file
64
openlp/plugins/images/forms/addgroupdialog.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||||
|
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
|
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||||
|
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||||
|
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||||
|
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||||
|
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||||
|
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
from PyQt4 import QtGui
|
||||||
|
|
||||||
|
from openlp.core.lib import translate
|
||||||
|
from openlp.core.lib.ui import create_button_box
|
||||||
|
|
||||||
|
|
||||||
|
class Ui_AddGroupDialog(object):
|
||||||
|
def setupUi(self, add_group_dialog):
|
||||||
|
add_group_dialog.setObjectName(u'add_group_dialog')
|
||||||
|
add_group_dialog.resize(300, 10)
|
||||||
|
self.dialog_layout = QtGui.QVBoxLayout(add_group_dialog)
|
||||||
|
self.dialog_layout.setObjectName(u'dialog_layout')
|
||||||
|
self.name_layout = QtGui.QFormLayout()
|
||||||
|
self.name_layout.setObjectName(u'name_layout')
|
||||||
|
self.parent_group_label = QtGui.QLabel(add_group_dialog)
|
||||||
|
self.parent_group_label.setObjectName(u'parent_group_label')
|
||||||
|
self.parent_group_combobox = QtGui.QComboBox(add_group_dialog)
|
||||||
|
self.parent_group_combobox.setObjectName(u'parent_group_combobox')
|
||||||
|
self.name_layout.addRow(self.parent_group_label, self.parent_group_combobox)
|
||||||
|
self.name_label = QtGui.QLabel(add_group_dialog)
|
||||||
|
self.name_label.setObjectName(u'name_label')
|
||||||
|
self.name_edit = QtGui.QLineEdit(add_group_dialog)
|
||||||
|
self.name_edit.setObjectName(u'name_edit')
|
||||||
|
self.name_label.setBuddy(self.name_edit)
|
||||||
|
self.name_layout.addRow(self.name_label, self.name_edit)
|
||||||
|
self.dialog_layout.addLayout(self.name_layout)
|
||||||
|
self.button_box = create_button_box(add_group_dialog, u'button_box', [u'cancel', u'save'])
|
||||||
|
self.dialog_layout.addWidget(self.button_box)
|
||||||
|
self.retranslateUi(add_group_dialog)
|
||||||
|
add_group_dialog.setMaximumHeight(add_group_dialog.sizeHint().height())
|
||||||
|
|
||||||
|
def retranslateUi(self, add_group_dialog):
|
||||||
|
add_group_dialog.setWindowTitle(translate('ImagePlugin.AddGroupForm', 'Add group'))
|
||||||
|
self.parent_group_label.setText(translate('ImagePlugin.AddGroupForm', 'Parent group:'))
|
||||||
|
self.name_label.setText(translate('ImagePlugin.AddGroupForm', 'Group name:'))
|
83
openlp/plugins/images/forms/addgroupform.py
Normal file
83
openlp/plugins/images/forms/addgroupform.py
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||||
|
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
|
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||||
|
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||||
|
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||||
|
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||||
|
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||||
|
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
from PyQt4 import QtGui
|
||||||
|
|
||||||
|
from openlp.core.lib import translate
|
||||||
|
from openlp.core.lib.ui import critical_error_message_box
|
||||||
|
from openlp.plugins.images.forms.addgroupdialog import Ui_AddGroupDialog
|
||||||
|
|
||||||
|
|
||||||
|
class AddGroupForm(QtGui.QDialog, Ui_AddGroupDialog):
|
||||||
|
"""
|
||||||
|
This class implements the 'Add group' form for the Images plugin.
|
||||||
|
"""
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
"""
|
||||||
|
Constructor
|
||||||
|
"""
|
||||||
|
QtGui.QDialog.__init__(self, parent)
|
||||||
|
self.setupUi(self)
|
||||||
|
|
||||||
|
def exec_(self, clear=True, show_top_level_group=False, selected_group=None):
|
||||||
|
"""
|
||||||
|
Show the form
|
||||||
|
|
||||||
|
``clear``
|
||||||
|
Set to False if the text input box should not be cleared when showing the dialog (default: True)
|
||||||
|
|
||||||
|
``show_top_level_group``
|
||||||
|
Set to True when "-- Top level group --" should be showed as first item (default: False)
|
||||||
|
|
||||||
|
``selected_group``
|
||||||
|
The ID of the group that should be selected by default when showing the dialog
|
||||||
|
"""
|
||||||
|
if clear:
|
||||||
|
self.name_edit.clear()
|
||||||
|
self.name_edit.setFocus()
|
||||||
|
if show_top_level_group and not self.parent_group_combobox.top_level_group_added:
|
||||||
|
self.parent_group_combobox.insertItem(0, translate('ImagePlugin.MediaItem', '-- Top-level group --'), 0)
|
||||||
|
self.parent_group_combobox.top_level_group_added = True
|
||||||
|
if selected_group is not None:
|
||||||
|
for i in range(self.parent_group_combobox.count()):
|
||||||
|
if self.parent_group_combobox.itemData(i) == selected_group:
|
||||||
|
self.parent_group_combobox.setCurrentIndex(i)
|
||||||
|
return QtGui.QDialog.exec_(self)
|
||||||
|
|
||||||
|
def accept(self):
|
||||||
|
"""
|
||||||
|
Override the accept() method from QDialog to make sure something is entered in the text input box
|
||||||
|
"""
|
||||||
|
if not self.name_edit.text():
|
||||||
|
critical_error_message_box(message=translate('ImagePlugin.AddGroupForm',
|
||||||
|
'You need to type in a group name.'))
|
||||||
|
self.name_edit.setFocus()
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return QtGui.QDialog.accept(self)
|
94
openlp/plugins/images/forms/choosegroupdialog.py
Normal file
94
openlp/plugins/images/forms/choosegroupdialog.py
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||||
|
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
|
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||||
|
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||||
|
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||||
|
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||||
|
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||||
|
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
|
from openlp.core.lib import translate
|
||||||
|
from openlp.core.lib.ui import create_button_box
|
||||||
|
|
||||||
|
|
||||||
|
class Ui_ChooseGroupDialog(object):
|
||||||
|
"""
|
||||||
|
The UI for the "Choose Image Group" form.
|
||||||
|
"""
|
||||||
|
def setupUi(self, choose_group_dialog):
|
||||||
|
"""
|
||||||
|
Set up the UI.
|
||||||
|
|
||||||
|
``choose_group_dialog``
|
||||||
|
The form object (not the class).
|
||||||
|
"""
|
||||||
|
choose_group_dialog.setObjectName(u'choose_group_dialog')
|
||||||
|
choose_group_dialog.resize(399, 119)
|
||||||
|
self.choose_group_layout = QtGui.QFormLayout(choose_group_dialog)
|
||||||
|
self.choose_group_layout.setFieldGrowthPolicy(QtGui.QFormLayout.ExpandingFieldsGrow)
|
||||||
|
self.choose_group_layout.setMargin(8)
|
||||||
|
self.choose_group_layout.setSpacing(8)
|
||||||
|
self.choose_group_layout.setLabelAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
|
||||||
|
self.choose_group_layout.setObjectName(u'choose_group_layout')
|
||||||
|
self.group_question_label = QtGui.QLabel(choose_group_dialog)
|
||||||
|
self.group_question_label.setWordWrap(True)
|
||||||
|
self.group_question_label.setObjectName(u'group_question_label')
|
||||||
|
self.choose_group_layout.setWidget(1, QtGui.QFormLayout.SpanningRole, self.group_question_label)
|
||||||
|
self.nogroup_radio_button = QtGui.QRadioButton(choose_group_dialog)
|
||||||
|
self.nogroup_radio_button.setChecked(True)
|
||||||
|
self.nogroup_radio_button.setObjectName(u'nogroup_radio_button')
|
||||||
|
self.choose_group_layout.setWidget(2, QtGui.QFormLayout.LabelRole, self.nogroup_radio_button)
|
||||||
|
self.existing_radio_button = QtGui.QRadioButton(choose_group_dialog)
|
||||||
|
self.existing_radio_button.setChecked(False)
|
||||||
|
self.existing_radio_button.setObjectName(u'existing_radio_button')
|
||||||
|
self.choose_group_layout.setWidget(3, QtGui.QFormLayout.LabelRole, self.existing_radio_button)
|
||||||
|
self.group_combobox = QtGui.QComboBox(choose_group_dialog)
|
||||||
|
self.group_combobox.setObjectName(u'group_combobox')
|
||||||
|
self.choose_group_layout.setWidget(3, QtGui.QFormLayout.FieldRole, self.group_combobox)
|
||||||
|
self.new_radio_button = QtGui.QRadioButton(choose_group_dialog)
|
||||||
|
self.new_radio_button.setChecked(False)
|
||||||
|
self.new_radio_button.setObjectName(u'new_radio_button')
|
||||||
|
self.choose_group_layout.setWidget(4, QtGui.QFormLayout.LabelRole, self.new_radio_button)
|
||||||
|
self.new_group_edit = QtGui.QLineEdit(choose_group_dialog)
|
||||||
|
self.new_group_edit.setObjectName(u'new_group_edit')
|
||||||
|
self.choose_group_layout.setWidget(4, QtGui.QFormLayout.FieldRole, self.new_group_edit)
|
||||||
|
self.group_button_box = create_button_box(choose_group_dialog, u'buttonBox', [u'ok'])
|
||||||
|
self.choose_group_layout.setWidget(5, QtGui.QFormLayout.FieldRole, self.group_button_box)
|
||||||
|
|
||||||
|
self.retranslateUi(choose_group_dialog)
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(choose_group_dialog)
|
||||||
|
|
||||||
|
def retranslateUi(self, choose_group_dialog):
|
||||||
|
"""
|
||||||
|
Translate the UI on the fly.
|
||||||
|
|
||||||
|
``choose_group_dialog``
|
||||||
|
The form object (not the class).
|
||||||
|
"""
|
||||||
|
choose_group_dialog.setWindowTitle(translate('ImagePlugin.ChooseGroupForm', 'Select Image Group'))
|
||||||
|
self.group_question_label.setText(translate('ImagePlugin.ChooseGroupForm', 'Add images to group:'))
|
||||||
|
self.nogroup_radio_button.setText(translate('ImagePlugin.ChooseGroupForm', 'No group'))
|
||||||
|
self.existing_radio_button.setText(translate('ImagePlugin.ChooseGroupForm', 'Existing group'))
|
||||||
|
self.new_radio_button.setText(translate('ImagePlugin.ChooseGroupForm', 'New group'))
|
57
openlp/plugins/images/forms/choosegroupform.py
Normal file
57
openlp/plugins/images/forms/choosegroupform.py
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||||
|
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
|
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||||
|
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||||
|
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||||
|
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||||
|
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||||
|
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
from PyQt4 import QtGui
|
||||||
|
|
||||||
|
from openlp.plugins.images.forms.choosegroupdialog import Ui_ChooseGroupDialog
|
||||||
|
|
||||||
|
|
||||||
|
class ChooseGroupForm(QtGui.QDialog, Ui_ChooseGroupDialog):
|
||||||
|
"""
|
||||||
|
This class implements the 'Choose group' form for the Images plugin.
|
||||||
|
"""
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
"""
|
||||||
|
Constructor
|
||||||
|
"""
|
||||||
|
QtGui.QDialog.__init__(self, parent)
|
||||||
|
self.setupUi(self)
|
||||||
|
|
||||||
|
def exec_(self, selected_group=None):
|
||||||
|
"""
|
||||||
|
Show the form
|
||||||
|
|
||||||
|
``selected_group``
|
||||||
|
The ID of the group that should be selected by default when showing the dialog
|
||||||
|
"""
|
||||||
|
if selected_group is not None:
|
||||||
|
for i in range(self.group_combobox.count()):
|
||||||
|
if self.group_combobox.itemData(i) == selected_group:
|
||||||
|
self.group_combobox.setCurrentIndex(i)
|
||||||
|
return QtGui.QDialog.exec_(self)
|
@ -32,13 +32,16 @@ from PyQt4 import QtGui
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from openlp.core.lib import Plugin, StringContent, Registry, ImageSource, Settings, build_icon, translate
|
from openlp.core.lib import Plugin, StringContent, Registry, ImageSource, Settings, build_icon, translate
|
||||||
|
from openlp.core.lib.db import Manager
|
||||||
from openlp.plugins.images.lib import ImageMediaItem, ImageTab
|
from openlp.plugins.images.lib import ImageMediaItem, ImageTab
|
||||||
|
from openlp.plugins.images.lib.db import init_schema, ImageFilenames
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
__default_settings__ = {
|
__default_settings__ = {
|
||||||
u'images/images files': []
|
u'images/db type': u'sqlite',
|
||||||
}
|
u'images/background color': u'#000000',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class ImagePlugin(Plugin):
|
class ImagePlugin(Plugin):
|
||||||
@ -46,6 +49,7 @@ class ImagePlugin(Plugin):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
Plugin.__init__(self, u'images', __default_settings__, ImageMediaItem, ImageTab)
|
Plugin.__init__(self, u'images', __default_settings__, ImageMediaItem, ImageTab)
|
||||||
|
self.manager = Manager(u'images', init_schema)
|
||||||
self.weight = -7
|
self.weight = -7
|
||||||
self.iconPath = u':/plugins/plugin_images.png'
|
self.iconPath = u':/plugins/plugin_images.png'
|
||||||
self.icon = build_icon(self.iconPath)
|
self.icon = build_icon(self.iconPath)
|
||||||
@ -64,6 +68,29 @@ class ImagePlugin(Plugin):
|
|||||||
'provided by the theme.')
|
'provided by the theme.')
|
||||||
return about_text
|
return about_text
|
||||||
|
|
||||||
|
def app_startup(self):
|
||||||
|
"""
|
||||||
|
Perform tasks on application startup
|
||||||
|
"""
|
||||||
|
Plugin.app_startup(self)
|
||||||
|
# Convert old settings-based image list to the database
|
||||||
|
files_from_config = Settings().get_files_from_config(self)
|
||||||
|
if files_from_config:
|
||||||
|
log.debug(u'Importing images list from old config: %s' % files_from_config)
|
||||||
|
self.mediaItem.save_new_images_list(files_from_config)
|
||||||
|
|
||||||
|
def upgrade_settings(self, settings):
|
||||||
|
"""
|
||||||
|
Upgrade the settings of this plugin.
|
||||||
|
|
||||||
|
``settings``
|
||||||
|
The Settings object containing the old settings.
|
||||||
|
"""
|
||||||
|
files_from_config = settings.get_files_from_config(self)
|
||||||
|
if files_from_config:
|
||||||
|
log.debug(u'Importing images list from old config: %s' % files_from_config)
|
||||||
|
self.mediaItem.save_new_images_list(files_from_config)
|
||||||
|
|
||||||
def set_plugin_text_strings(self):
|
def set_plugin_text_strings(self):
|
||||||
"""
|
"""
|
||||||
Called to define all translatable texts of the plugin
|
Called to define all translatable texts of the plugin
|
||||||
@ -74,8 +101,7 @@ class ImagePlugin(Plugin):
|
|||||||
u'plural': translate('ImagePlugin', 'Images', 'name plural')
|
u'plural': translate('ImagePlugin', 'Images', 'name plural')
|
||||||
}
|
}
|
||||||
## Name for MediaDockManager, SettingsManager ##
|
## Name for MediaDockManager, SettingsManager ##
|
||||||
self.textStrings[StringContent.VisibleName] = {u'title': translate('ImagePlugin', 'Images', 'container title')
|
self.textStrings[StringContent.VisibleName] = {u'title': translate('ImagePlugin', 'Images', 'container title')}
|
||||||
}
|
|
||||||
# Middle Header Bar
|
# Middle Header Bar
|
||||||
tooltips = {
|
tooltips = {
|
||||||
u'load': translate('ImagePlugin', 'Load a new image.'),
|
u'load': translate('ImagePlugin', 'Load a new image.'),
|
||||||
|
99
openlp/plugins/images/lib/db.py
Normal file
99
openlp/plugins/images/lib/db.py
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||||
|
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
|
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||||
|
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||||
|
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||||
|
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||||
|
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||||
|
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
"""
|
||||||
|
The :mod:`db` module provides the database and schema that is the backend for the Images plugin
|
||||||
|
"""
|
||||||
|
|
||||||
|
from sqlalchemy import Column, ForeignKey, Table, types
|
||||||
|
from sqlalchemy.orm import mapper, relation, reconstructor
|
||||||
|
|
||||||
|
from openlp.core.lib.db import BaseModel, init_db
|
||||||
|
|
||||||
|
|
||||||
|
class ImageGroups(BaseModel):
|
||||||
|
"""
|
||||||
|
ImageGroups model
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ImageFilenames(BaseModel):
|
||||||
|
"""
|
||||||
|
ImageFilenames model
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def init_schema(url):
|
||||||
|
"""
|
||||||
|
Setup the images database connection and initialise the database schema.
|
||||||
|
|
||||||
|
``url``
|
||||||
|
The database to setup
|
||||||
|
|
||||||
|
The images database contains the following tables:
|
||||||
|
|
||||||
|
* image_groups
|
||||||
|
* image_filenames
|
||||||
|
|
||||||
|
**image_groups Table**
|
||||||
|
This table holds the names of the images groups. It has the following columns:
|
||||||
|
|
||||||
|
* id
|
||||||
|
* parent_id
|
||||||
|
* group_name
|
||||||
|
|
||||||
|
**image_filenames Table**
|
||||||
|
This table holds the filenames of the images and the group they belong to. It has the following columns:
|
||||||
|
|
||||||
|
* id
|
||||||
|
* group_id
|
||||||
|
* filename
|
||||||
|
"""
|
||||||
|
session, metadata = init_db(url)
|
||||||
|
|
||||||
|
# Definition of the "image_groups" table
|
||||||
|
image_groups_table = Table(u'image_groups', metadata,
|
||||||
|
Column(u'id', types.Integer(), primary_key=True),
|
||||||
|
Column(u'parent_id', types.Integer()),
|
||||||
|
Column(u'group_name', types.Unicode(128))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Definition of the "image_filenames" table
|
||||||
|
image_filenames_table = Table(u'image_filenames', metadata,
|
||||||
|
Column(u'id', types.Integer(), primary_key=True),
|
||||||
|
Column(u'group_id', types.Integer(), ForeignKey(u'image_groups.id'), default=None),
|
||||||
|
Column(u'filename', types.Unicode(255), nullable=False)
|
||||||
|
)
|
||||||
|
|
||||||
|
mapper(ImageGroups, image_groups_table)
|
||||||
|
mapper(ImageFilenames, image_filenames_table)
|
||||||
|
|
||||||
|
metadata.create_all(checkfirst=True)
|
||||||
|
return session
|
@ -32,6 +32,7 @@ from PyQt4 import QtGui
|
|||||||
from openlp.core.lib import Registry, SettingsTab, Settings, UiStrings, translate
|
from openlp.core.lib import Registry, SettingsTab, Settings, UiStrings, translate
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ImageTab(SettingsTab):
|
class ImageTab(SettingsTab):
|
||||||
"""
|
"""
|
||||||
ImageTab is the images settings tab in the settings dialog.
|
ImageTab is the images settings tab in the settings dialog.
|
||||||
|
@ -32,13 +32,17 @@ import os
|
|||||||
|
|
||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
from openlp.core.lib import MediaManagerItem, ItemCapabilities, Registry, ServiceItemContext, Settings, UiStrings, \
|
from openlp.core.lib import ItemCapabilities, MediaManagerItem, Registry, ServiceItemContext, Settings, \
|
||||||
build_icon, check_item_selected, check_directory_exists, create_thumb, translate, validate_thumb
|
StringContent, TreeWidgetWithDnD, UiStrings, build_icon, check_directory_exists, check_item_selected, \
|
||||||
from openlp.core.lib.ui import critical_error_message_box
|
create_thumb, translate, validate_thumb
|
||||||
|
from openlp.core.lib.ui import create_widget_action, critical_error_message_box
|
||||||
from openlp.core.utils import AppLocation, delete_file, locale_compare, get_images_filter
|
from openlp.core.utils import AppLocation, delete_file, locale_compare, get_images_filter
|
||||||
|
from openlp.plugins.images.forms import AddGroupForm, ChooseGroupForm
|
||||||
|
from openlp.plugins.images.lib.db import ImageFilenames, ImageGroups
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ImageMediaItem(MediaManagerItem):
|
class ImageMediaItem(MediaManagerItem):
|
||||||
"""
|
"""
|
||||||
This is the custom media manager item for images.
|
This is the custom media manager item for images.
|
||||||
@ -50,6 +54,11 @@ class ImageMediaItem(MediaManagerItem):
|
|||||||
MediaManagerItem.__init__(self, parent, plugin)
|
MediaManagerItem.__init__(self, parent, plugin)
|
||||||
self.quickPreviewAllowed = True
|
self.quickPreviewAllowed = True
|
||||||
self.hasSearch = True
|
self.hasSearch = True
|
||||||
|
self.manager = plugin.manager
|
||||||
|
self.choose_group_form = ChooseGroupForm(self)
|
||||||
|
self.add_group_form = AddGroupForm(self)
|
||||||
|
self.fill_groups_combobox(self.choose_group_form.group_combobox)
|
||||||
|
self.fill_groups_combobox(self.add_group_form.parent_group_combobox)
|
||||||
Registry().register_function(u'live_theme_changed', self.live_theme_changed)
|
Registry().register_function(u'live_theme_changed', self.live_theme_changed)
|
||||||
# Allow DnD from the desktop
|
# Allow DnD from the desktop
|
||||||
self.listView.activateDnD()
|
self.listView.activateDnD()
|
||||||
@ -59,6 +68,8 @@ class ImageMediaItem(MediaManagerItem):
|
|||||||
'Select Image(s)')
|
'Select Image(s)')
|
||||||
file_formats = get_images_filter()
|
file_formats = get_images_filter()
|
||||||
self.onNewFileMasks = u'%s;;%s (*.*) (*)' % (file_formats, UiStrings().AllFiles)
|
self.onNewFileMasks = u'%s;;%s (*.*) (*)' % (file_formats, UiStrings().AllFiles)
|
||||||
|
self.addGroupAction.setText(UiStrings().AddGroup)
|
||||||
|
self.addGroupAction.setToolTip(UiStrings().AddGroup)
|
||||||
self.replaceAction.setText(UiStrings().ReplaceBG)
|
self.replaceAction.setText(UiStrings().ReplaceBG)
|
||||||
self.replaceAction.setToolTip(UiStrings().ReplaceLiveBG)
|
self.replaceAction.setToolTip(UiStrings().ReplaceLiveBG)
|
||||||
self.resetAction.setText(UiStrings().ResetBG)
|
self.resetAction.setText(UiStrings().ResetBG)
|
||||||
@ -75,70 +86,444 @@ class ImageMediaItem(MediaManagerItem):
|
|||||||
log.debug(u'initialise')
|
log.debug(u'initialise')
|
||||||
self.listView.clear()
|
self.listView.clear()
|
||||||
self.listView.setIconSize(QtCore.QSize(88, 50))
|
self.listView.setIconSize(QtCore.QSize(88, 50))
|
||||||
|
self.listView.setIndentation(self.listView.defaultIndentation)
|
||||||
|
self.listView.allow_internal_dnd = True
|
||||||
self.servicePath = os.path.join(AppLocation.get_section_data_path(self.settingsSection), u'thumbnails')
|
self.servicePath = os.path.join(AppLocation.get_section_data_path(self.settingsSection), u'thumbnails')
|
||||||
check_directory_exists(self.servicePath)
|
check_directory_exists(self.servicePath)
|
||||||
self.loadList(Settings().value(self.settingsSection + u'/images files'), True)
|
# Load images from the database
|
||||||
|
self.loadFullList(
|
||||||
|
self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename), initial_load=True)
|
||||||
|
|
||||||
def addListViewToToolBar(self):
|
def addListViewToToolBar(self):
|
||||||
MediaManagerItem.addListViewToToolBar(self)
|
"""
|
||||||
|
Creates the main widget for listing items the media item is tracking.
|
||||||
|
This method overloads MediaManagerItem.addListViewToToolBar
|
||||||
|
"""
|
||||||
|
# Add the List widget
|
||||||
|
self.listView = TreeWidgetWithDnD(self, self.plugin.name)
|
||||||
|
self.listView.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
|
||||||
|
self.listView.setAlternatingRowColors(True)
|
||||||
|
self.listView.setObjectName(u'%sTreeView' % self.plugin.name)
|
||||||
|
# Add to pageLayout
|
||||||
|
self.pageLayout.addWidget(self.listView)
|
||||||
|
# define and add the context menu
|
||||||
|
self.listView.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
||||||
|
if self.hasEditIcon:
|
||||||
|
create_widget_action(self.listView,
|
||||||
|
text=self.plugin.getString(StringContent.Edit)[u'title'],
|
||||||
|
icon=u':/general/general_edit.png',
|
||||||
|
triggers=self.onEditClick)
|
||||||
|
create_widget_action(self.listView, separator=True)
|
||||||
|
if self.hasDeleteIcon:
|
||||||
|
create_widget_action(self.listView,
|
||||||
|
text=self.plugin.getString(StringContent.Delete)[u'title'],
|
||||||
|
icon=u':/general/general_delete.png',
|
||||||
|
shortcuts=[QtCore.Qt.Key_Delete], triggers=self.onDeleteClick)
|
||||||
|
create_widget_action(self.listView, separator=True)
|
||||||
|
create_widget_action(self.listView,
|
||||||
|
text=self.plugin.getString(StringContent.Preview)[u'title'],
|
||||||
|
icon=u':/general/general_preview.png',
|
||||||
|
shortcuts=[QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return],
|
||||||
|
triggers=self.onPreviewClick)
|
||||||
|
create_widget_action(self.listView,
|
||||||
|
text=self.plugin.getString(StringContent.Live)[u'title'],
|
||||||
|
icon=u':/general/general_live.png',
|
||||||
|
shortcuts=[QtCore.Qt.ShiftModifier | QtCore.Qt.Key_Enter,
|
||||||
|
QtCore.Qt.ShiftModifier | QtCore.Qt.Key_Return],
|
||||||
|
triggers=self.onLiveClick)
|
||||||
|
create_widget_action(self.listView,
|
||||||
|
text=self.plugin.getString(StringContent.Service)[u'title'],
|
||||||
|
icon=u':/general/general_add.png',
|
||||||
|
shortcuts=[QtCore.Qt.Key_Plus, QtCore.Qt.Key_Equal],
|
||||||
|
triggers=self.onAddClick)
|
||||||
|
if self.addToServiceItem:
|
||||||
|
create_widget_action(self.listView, separator=True)
|
||||||
|
create_widget_action(self.listView,
|
||||||
|
text=translate('OpenLP.MediaManagerItem', '&Add to selected Service Item'),
|
||||||
|
icon=u':/general/general_add.png',
|
||||||
|
triggers=self.onAddEditClick)
|
||||||
|
self.addCustomContextActions()
|
||||||
|
# Create the context menu and add all actions from the listView.
|
||||||
|
self.menu = QtGui.QMenu()
|
||||||
|
self.menu.addActions(self.listView.actions())
|
||||||
|
self.listView.doubleClicked.connect(self.onDoubleClicked)
|
||||||
|
self.listView.itemSelectionChanged.connect(self.onSelectionChange)
|
||||||
|
self.listView.customContextMenuRequested.connect(self.contextMenu)
|
||||||
self.listView.addAction(self.replaceAction)
|
self.listView.addAction(self.replaceAction)
|
||||||
|
|
||||||
|
def addCustomContextActions(self):
|
||||||
|
"""
|
||||||
|
Add custom actions to the context menu
|
||||||
|
"""
|
||||||
|
create_widget_action(self.listView, separator=True)
|
||||||
|
create_widget_action(self.listView,
|
||||||
|
text=UiStrings().AddGroup,
|
||||||
|
icon=u':/images/image_new_group.png',
|
||||||
|
triggers=self.onAddGroupClick)
|
||||||
|
create_widget_action(self.listView,
|
||||||
|
text=self.plugin.getString(StringContent.Load)[u'tooltip'],
|
||||||
|
icon=u':/general/general_open.png',
|
||||||
|
triggers=self.onFileClick)
|
||||||
|
|
||||||
|
def addStartHeaderBar(self):
|
||||||
|
"""
|
||||||
|
Add custom buttons to the start of the toolbar
|
||||||
|
"""
|
||||||
|
self.addGroupAction = self.toolbar.add_toolbar_action(u'addGroupAction',
|
||||||
|
icon=u':/images/image_new_group.png', triggers=self.onAddGroupClick)
|
||||||
|
|
||||||
def addEndHeaderBar(self):
|
def addEndHeaderBar(self):
|
||||||
|
"""
|
||||||
|
Add custom buttons to the end of the toolbar
|
||||||
|
"""
|
||||||
self.replaceAction = self.toolbar.add_toolbar_action(u'replaceAction',
|
self.replaceAction = self.toolbar.add_toolbar_action(u'replaceAction',
|
||||||
icon=u':/slides/slide_blank.png', triggers=self.onReplaceClick)
|
icon=u':/slides/slide_blank.png', triggers=self.onReplaceClick)
|
||||||
self.resetAction = self.toolbar.add_toolbar_action(u'resetAction',
|
self.resetAction = self.toolbar.add_toolbar_action(u'resetAction',
|
||||||
icon=u':/system/system_close.png', visible=False, triggers=self.onResetClick)
|
icon=u':/system/system_close.png', visible=False, triggers=self.onResetClick)
|
||||||
|
|
||||||
|
def recursively_delete_group(self, image_group):
|
||||||
|
"""
|
||||||
|
Recursively deletes a group and all groups and images in it
|
||||||
|
|
||||||
|
``image_group``
|
||||||
|
The ImageGroups instance of the group that will be deleted
|
||||||
|
"""
|
||||||
|
images = self.manager.get_all_objects(ImageFilenames, ImageFilenames.group_id == image_group.id)
|
||||||
|
for image in images:
|
||||||
|
delete_file(os.path.join(self.servicePath, os.path.split(image.filename)[1]))
|
||||||
|
self.manager.delete_object(ImageFilenames, image.id)
|
||||||
|
image_groups = self.manager.get_all_objects(ImageGroups, ImageGroups.parent_id == image_group.id)
|
||||||
|
for group in image_groups:
|
||||||
|
self.recursively_delete_group(group)
|
||||||
|
self.manager.delete_object(ImageGroups, group.id)
|
||||||
|
|
||||||
def onDeleteClick(self):
|
def onDeleteClick(self):
|
||||||
"""
|
"""
|
||||||
Remove an image item from the list
|
Remove an image item from the list
|
||||||
"""
|
"""
|
||||||
# Turn off auto preview triggers.
|
# Turn off auto preview triggers.
|
||||||
self.listView.blockSignals(True)
|
self.listView.blockSignals(True)
|
||||||
if check_item_selected(self.listView, translate('ImagePlugin.MediaItem','You must select an image to delete.')):
|
if check_item_selected(self.listView, translate('ImagePlugin.MediaItem',
|
||||||
row_list = [item.row() for item in self.listView.selectedIndexes()]
|
'You must select an image or group to delete.')):
|
||||||
row_list.sort(reverse=True)
|
item_list = self.listView.selectedItems()
|
||||||
self.application.set_busy_cursor()
|
self.application.set_busy_cursor()
|
||||||
self.main_window.display_progress_bar(len(row_list))
|
self.main_window.display_progress_bar(len(item_list))
|
||||||
for row in row_list:
|
for row_item in item_list:
|
||||||
text = self.listView.item(row)
|
if row_item:
|
||||||
if text:
|
item_data = row_item.data(0, QtCore.Qt.UserRole)
|
||||||
delete_file(os.path.join(self.servicePath, text.text()))
|
if isinstance(item_data, ImageFilenames):
|
||||||
self.listView.takeItem(row)
|
delete_file(os.path.join(self.servicePath, row_item.text(0)))
|
||||||
|
if item_data.group_id == 0:
|
||||||
|
self.listView.takeTopLevelItem(self.listView.indexOfTopLevelItem(row_item))
|
||||||
|
else:
|
||||||
|
row_item.parent().removeChild(row_item)
|
||||||
|
self.manager.delete_object(ImageFilenames, row_item.data(0, QtCore.Qt.UserRole).id)
|
||||||
|
elif isinstance(item_data, ImageGroups):
|
||||||
|
if QtGui.QMessageBox.question(self.listView.parent(),
|
||||||
|
translate('ImagePlugin.MediaItem', 'Remove group'),
|
||||||
|
translate('ImagePlugin.MediaItem',
|
||||||
|
'Are you sure you want to remove "%s" and everything in it?') % item_data.group_name,
|
||||||
|
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes |
|
||||||
|
QtGui.QMessageBox.No)) == QtGui.QMessageBox.Yes:
|
||||||
|
self.recursively_delete_group(item_data)
|
||||||
|
self.manager.delete_object(ImageGroups, row_item.data(0, QtCore.Qt.UserRole).id)
|
||||||
|
if item_data.parent_id == 0:
|
||||||
|
self.listView.takeTopLevelItem(self.listView.indexOfTopLevelItem(row_item))
|
||||||
|
else:
|
||||||
|
row_item.parent().removeChild(row_item)
|
||||||
|
self.fill_groups_combobox(self.choose_group_form.group_combobox)
|
||||||
|
self.fill_groups_combobox(self.add_group_form.parent_group_combobox)
|
||||||
self.main_window.increment_progress_bar()
|
self.main_window.increment_progress_bar()
|
||||||
Settings.setValue(self.settingsSection + u'/images files', self.getFileList())
|
|
||||||
self.main_window.finished_progress_bar()
|
self.main_window.finished_progress_bar()
|
||||||
self.application.set_normal_cursor()
|
self.application.set_normal_cursor()
|
||||||
self.listView.blockSignals(False)
|
self.listView.blockSignals(False)
|
||||||
|
|
||||||
def loadList(self, images, initialLoad=False):
|
def add_sub_groups(self, group_list, parent_group_id):
|
||||||
|
"""
|
||||||
|
Recursively add subgroups to the given parent group in a QTreeWidget
|
||||||
|
|
||||||
|
``group_list``
|
||||||
|
The List object that contains all QTreeWidgetItems
|
||||||
|
|
||||||
|
``parent_group_id``
|
||||||
|
The ID of the group that will be added recursively
|
||||||
|
"""
|
||||||
|
image_groups = self.manager.get_all_objects(ImageGroups, ImageGroups.parent_id == parent_group_id)
|
||||||
|
image_groups.sort(cmp=locale_compare, key=lambda group_object: group_object.group_name)
|
||||||
|
folder_icon = build_icon(u':/images/image_group.png')
|
||||||
|
for image_group in image_groups:
|
||||||
|
group = QtGui.QTreeWidgetItem()
|
||||||
|
group.setText(0, image_group.group_name)
|
||||||
|
group.setData(0, QtCore.Qt.UserRole, image_group)
|
||||||
|
group.setIcon(0, folder_icon)
|
||||||
|
if parent_group_id == 0:
|
||||||
|
self.listView.addTopLevelItem(group)
|
||||||
|
else:
|
||||||
|
group_list[parent_group_id].addChild(group)
|
||||||
|
group_list[image_group.id] = group
|
||||||
|
self.add_sub_groups(group_list, image_group.id)
|
||||||
|
|
||||||
|
def fill_groups_combobox(self, combobox, parent_group_id=0, prefix=''):
|
||||||
|
"""
|
||||||
|
Recursively add groups to the combobox in the 'Add group' dialog
|
||||||
|
|
||||||
|
``combobox``
|
||||||
|
The QComboBox to add the options to
|
||||||
|
|
||||||
|
``parent_group_id``
|
||||||
|
The ID of the group that will be added
|
||||||
|
|
||||||
|
``prefix``
|
||||||
|
A string containing the prefix that will be added in front of the groupname for each level of the tree
|
||||||
|
"""
|
||||||
|
if parent_group_id == 0:
|
||||||
|
combobox.clear()
|
||||||
|
combobox.top_level_group_added = False
|
||||||
|
image_groups = self.manager.get_all_objects(ImageGroups, ImageGroups.parent_id == parent_group_id)
|
||||||
|
image_groups.sort(cmp=locale_compare, key=lambda group_object: group_object.group_name)
|
||||||
|
for image_group in image_groups:
|
||||||
|
combobox.addItem(prefix + image_group.group_name, image_group.id)
|
||||||
|
self.fill_groups_combobox(combobox, image_group.id, prefix + ' ')
|
||||||
|
|
||||||
|
def expand_group(self, group_id, root_item=None):
|
||||||
|
"""
|
||||||
|
Expand groups in the widget recursively
|
||||||
|
|
||||||
|
``group_id``
|
||||||
|
The ID of the group that will be expanded
|
||||||
|
|
||||||
|
``root_item``
|
||||||
|
This option is only used for recursion purposes
|
||||||
|
"""
|
||||||
|
return_value = False
|
||||||
|
if root_item is None:
|
||||||
|
root_item = self.listView.invisibleRootItem()
|
||||||
|
for i in range(root_item.childCount()):
|
||||||
|
child = root_item.child(i)
|
||||||
|
if self.expand_group(group_id, child):
|
||||||
|
child.setExpanded(True)
|
||||||
|
return_value = True
|
||||||
|
if isinstance(root_item.data(0, QtCore.Qt.UserRole), ImageGroups):
|
||||||
|
if root_item.data(0, QtCore.Qt.UserRole).id == group_id:
|
||||||
|
return True
|
||||||
|
return return_value
|
||||||
|
|
||||||
|
def loadFullList(self, images, initial_load=False, open_group=None):
|
||||||
|
"""
|
||||||
|
Replace the list of images and groups in the interface.
|
||||||
|
|
||||||
|
``images``
|
||||||
|
A List of ImageFilenames objects that will be used to reload the mediamanager list
|
||||||
|
|
||||||
|
``initial_load``
|
||||||
|
When set to False, the busy cursor and progressbar will be shown while loading images
|
||||||
|
|
||||||
|
``open_group``
|
||||||
|
ImageGroups object of the group that must be expanded after reloading the list in the interface
|
||||||
|
"""
|
||||||
|
if not initial_load:
|
||||||
self.application.set_busy_cursor()
|
self.application.set_busy_cursor()
|
||||||
if not initialLoad:
|
|
||||||
self.main_window.display_progress_bar(len(images))
|
self.main_window.display_progress_bar(len(images))
|
||||||
|
self.listView.clear()
|
||||||
|
# Load the list of groups and add them to the treeView
|
||||||
|
group_items = {}
|
||||||
|
self.add_sub_groups(group_items, parent_group_id=0)
|
||||||
|
if open_group is not None:
|
||||||
|
self.expand_group(open_group.id)
|
||||||
# Sort the images by its filename considering language specific
|
# Sort the images by its filename considering language specific
|
||||||
# characters.
|
# characters.
|
||||||
images.sort(cmp=locale_compare, key=lambda filename: os.path.split(unicode(filename))[1])
|
images.sort(cmp=locale_compare, key=lambda image_object: os.path.split(unicode(image_object.filename))[1])
|
||||||
for imageFile in images:
|
for imageFile in images:
|
||||||
filename = os.path.split(unicode(imageFile))[1]
|
log.debug(u'Loading image: %s', imageFile.filename)
|
||||||
|
filename = os.path.split(imageFile.filename)[1]
|
||||||
thumb = os.path.join(self.servicePath, filename)
|
thumb = os.path.join(self.servicePath, filename)
|
||||||
if not os.path.exists(unicode(imageFile)):
|
if not os.path.exists(imageFile.filename):
|
||||||
icon = build_icon(u':/general/general_delete.png')
|
icon = build_icon(u':/general/general_delete.png')
|
||||||
else:
|
else:
|
||||||
if validate_thumb(unicode(imageFile), thumb):
|
if validate_thumb(imageFile.filename, thumb):
|
||||||
icon = build_icon(thumb)
|
icon = build_icon(thumb)
|
||||||
else:
|
else:
|
||||||
icon = create_thumb(unicode(imageFile), thumb)
|
icon = create_thumb(imageFile.filename, thumb)
|
||||||
item_name = QtGui.QListWidgetItem(filename)
|
item_name = QtGui.QTreeWidgetItem(filename)
|
||||||
item_name.setIcon(icon)
|
item_name.setText(0, filename)
|
||||||
item_name.setToolTip(imageFile)
|
item_name.setIcon(0, icon)
|
||||||
item_name.setData(QtCore.Qt.UserRole, imageFile)
|
item_name.setToolTip(0, imageFile.filename)
|
||||||
self.listView.addItem(item_name)
|
item_name.setData(0, QtCore.Qt.UserRole, imageFile)
|
||||||
if not initialLoad:
|
if imageFile.group_id == 0:
|
||||||
|
self.listView.addTopLevelItem(item_name)
|
||||||
|
else:
|
||||||
|
group_items[imageFile.group_id].addChild(item_name)
|
||||||
|
if not initial_load:
|
||||||
self.main_window.increment_progress_bar()
|
self.main_window.increment_progress_bar()
|
||||||
if not initialLoad:
|
if not initial_load:
|
||||||
self.main_window.finished_progress_bar()
|
self.main_window.finished_progress_bar()
|
||||||
self.application.set_normal_cursor()
|
self.application.set_normal_cursor()
|
||||||
|
|
||||||
|
def validateAndLoad(self, files, target_group=None):
|
||||||
|
"""
|
||||||
|
Process a list for files either from the File Dialog or from Drag and Drop.
|
||||||
|
This method is overloaded from MediaManagerItem.
|
||||||
|
|
||||||
|
``files``
|
||||||
|
A List of strings containing the filenames of the files to be loaded
|
||||||
|
|
||||||
|
``target_group``
|
||||||
|
The QTreeWidgetItem of the group that will be the parent of the added files
|
||||||
|
"""
|
||||||
|
self.application.set_normal_cursor()
|
||||||
|
self.loadList(files, target_group)
|
||||||
|
last_dir = os.path.split(unicode(files[0]))[0]
|
||||||
|
Settings().setValue(self.settingsSection + u'/last directory', last_dir)
|
||||||
|
|
||||||
|
def loadList(self, images, target_group=None, initial_load=False):
|
||||||
|
"""
|
||||||
|
Add new images to the database. This method is called when adding images using the Add button or DnD.
|
||||||
|
|
||||||
|
``images``
|
||||||
|
A List of strings containing the filenames of the files to be loaded
|
||||||
|
|
||||||
|
``target_group``
|
||||||
|
The QTreeWidgetItem of the group that will be the parent of the added files
|
||||||
|
|
||||||
|
``initial_load``
|
||||||
|
When set to False, the busy cursor and progressbar will be shown while loading images
|
||||||
|
"""
|
||||||
|
if target_group is None:
|
||||||
|
# Find out if a group must be pre-selected
|
||||||
|
preselect_group = None
|
||||||
|
selected_items = self.listView.selectedItems()
|
||||||
|
if selected_items:
|
||||||
|
selected_item = selected_items[0]
|
||||||
|
if isinstance(selected_item.data(0, QtCore.Qt.UserRole), ImageFilenames):
|
||||||
|
selected_item = selected_item.parent()
|
||||||
|
if isinstance(selected_item, QtGui.QTreeWidgetItem):
|
||||||
|
if isinstance(selected_item.data(0, QtCore.Qt.UserRole), ImageGroups):
|
||||||
|
preselect_group = selected_item.data(0, QtCore.Qt.UserRole).id
|
||||||
|
# Enable and disable parts of the 'choose group' form
|
||||||
|
if preselect_group is None:
|
||||||
|
self.choose_group_form.nogroup_radio_button.setChecked(True)
|
||||||
|
self.choose_group_form.nogroup_radio_button.setFocus()
|
||||||
|
self.choose_group_form.existing_radio_button.setChecked(False)
|
||||||
|
self.choose_group_form.new_radio_button.setChecked(False)
|
||||||
|
else:
|
||||||
|
self.choose_group_form.nogroup_radio_button.setChecked(False)
|
||||||
|
self.choose_group_form.existing_radio_button.setChecked(True)
|
||||||
|
self.choose_group_form.new_radio_button.setChecked(False)
|
||||||
|
self.choose_group_form.group_combobox.setFocus()
|
||||||
|
if self.manager.get_object_count(ImageGroups) == 0:
|
||||||
|
self.choose_group_form.existing_radio_button.setDisabled(True)
|
||||||
|
self.choose_group_form.group_combobox.setDisabled(True)
|
||||||
|
else:
|
||||||
|
self.choose_group_form.existing_radio_button.setDisabled(False)
|
||||||
|
self.choose_group_form.group_combobox.setDisabled(False)
|
||||||
|
# Ask which group the images should be saved in
|
||||||
|
if self.choose_group_form.exec_(selected_group=preselect_group):
|
||||||
|
if self.choose_group_form.nogroup_radio_button.isChecked():
|
||||||
|
# User chose 'No group'
|
||||||
|
parent_group = ImageGroups()
|
||||||
|
parent_group.id = 0
|
||||||
|
elif self.choose_group_form.existing_radio_button.isChecked():
|
||||||
|
# User chose 'Existing group'
|
||||||
|
group_id = self.choose_group_form.group_combobox.itemData(
|
||||||
|
self.choose_group_form.group_combobox.currentIndex(), QtCore.Qt.UserRole)
|
||||||
|
parent_group = self.manager.get_object_filtered(ImageGroups, ImageGroups.id == group_id)
|
||||||
|
elif self.choose_group_form.new_radio_button.isChecked():
|
||||||
|
# User chose 'New group'
|
||||||
|
parent_group = ImageGroups()
|
||||||
|
parent_group.parent_id = 0
|
||||||
|
parent_group.group_name = self.choose_group_form.new_group_edit.text()
|
||||||
|
self.manager.save_object(parent_group)
|
||||||
|
else:
|
||||||
|
parent_group = target_group.data(0, QtCore.Qt.UserRole)
|
||||||
|
if isinstance(parent_group, ImageFilenames):
|
||||||
|
if parent_group.group_id == 0:
|
||||||
|
parent_group = ImageGroups()
|
||||||
|
parent_group.id = 0
|
||||||
|
else:
|
||||||
|
parent_group = target_group.parent().data(0, QtCore.Qt.UserRole)
|
||||||
|
# If no valid parent group is found, do nothing
|
||||||
|
if not isinstance(parent_group, ImageGroups):
|
||||||
|
return
|
||||||
|
# Initialize busy cursor and progress bar
|
||||||
|
self.application.set_busy_cursor()
|
||||||
|
self.main_window.display_progress_bar(len(images))
|
||||||
|
# Save the new images in the database
|
||||||
|
self.save_new_images_list(images, group_id=parent_group.id, reload_list=False)
|
||||||
|
self.loadFullList(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename),
|
||||||
|
initial_load=initial_load, open_group=parent_group)
|
||||||
|
self.application.set_normal_cursor()
|
||||||
|
|
||||||
|
def save_new_images_list(self, images_list, group_id=0, reload_list=True):
|
||||||
|
"""
|
||||||
|
Convert a list of image filenames to ImageFilenames objects and save them in the database.
|
||||||
|
|
||||||
|
``images_list``
|
||||||
|
A List of strings containing image filenames
|
||||||
|
|
||||||
|
``group_id``
|
||||||
|
The ID of the group to save the images in
|
||||||
|
|
||||||
|
``reload_list``
|
||||||
|
This boolean is set to True when the list in the interface should be reloaded after saving the new images
|
||||||
|
"""
|
||||||
|
for filename in images_list:
|
||||||
|
if type(filename) is not str and type(filename) is not unicode:
|
||||||
|
continue
|
||||||
|
log.debug(u'Adding new image: %s', filename)
|
||||||
|
imageFile = ImageFilenames()
|
||||||
|
imageFile.group_id = group_id
|
||||||
|
imageFile.filename = unicode(filename)
|
||||||
|
self.manager.save_object(imageFile)
|
||||||
|
self.main_window.increment_progress_bar()
|
||||||
|
if reload_list and images_list:
|
||||||
|
self.loadFullList(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename))
|
||||||
|
|
||||||
|
def dnd_move_internal(self, target):
|
||||||
|
"""
|
||||||
|
Handle drag-and-drop moving of images within the media manager
|
||||||
|
|
||||||
|
``target``
|
||||||
|
This contains the QTreeWidget that is the target of the DnD action
|
||||||
|
"""
|
||||||
|
items_to_move = self.listView.selectedItems()
|
||||||
|
# Determine group to move images to
|
||||||
|
target_group = target
|
||||||
|
if target_group is not None and isinstance(target_group.data(0, QtCore.Qt.UserRole), ImageFilenames):
|
||||||
|
target_group = target.parent()
|
||||||
|
# Move to toplevel
|
||||||
|
if target_group is None:
|
||||||
|
target_group = self.listView.invisibleRootItem()
|
||||||
|
target_group.setData(0, QtCore.Qt.UserRole, ImageGroups())
|
||||||
|
target_group.data(0, QtCore.Qt.UserRole).id = 0
|
||||||
|
# Move images in the treeview
|
||||||
|
items_to_save = []
|
||||||
|
for item in items_to_move:
|
||||||
|
if isinstance(item.data(0, QtCore.Qt.UserRole), ImageFilenames):
|
||||||
|
if isinstance(item.parent(), QtGui.QTreeWidgetItem):
|
||||||
|
item.parent().removeChild(item)
|
||||||
|
else:
|
||||||
|
self.listView.invisibleRootItem().removeChild(item)
|
||||||
|
target_group.addChild(item)
|
||||||
|
item.setSelected(True)
|
||||||
|
item_data = item.data(0, QtCore.Qt.UserRole)
|
||||||
|
item_data.group_id = target_group.data(0, QtCore.Qt.UserRole).id
|
||||||
|
items_to_save.append(item_data)
|
||||||
|
target_group.setExpanded(True)
|
||||||
|
# Update the group ID's of the images in the database
|
||||||
|
self.manager.save_objects(items_to_save)
|
||||||
|
# Sort the target group
|
||||||
|
group_items = []
|
||||||
|
image_items = []
|
||||||
|
for item in target_group.takeChildren():
|
||||||
|
if isinstance(item.data(0, QtCore.Qt.UserRole), ImageGroups):
|
||||||
|
group_items.append(item)
|
||||||
|
if isinstance(item.data(0, QtCore.Qt.UserRole), ImageFilenames):
|
||||||
|
image_items.append(item)
|
||||||
|
group_items.sort(cmp=locale_compare, key=lambda item: item.text(0))
|
||||||
|
target_group.addChildren(group_items)
|
||||||
|
image_items.sort(cmp=locale_compare, key=lambda item: item.text(0))
|
||||||
|
target_group.addChildren(image_items)
|
||||||
|
|
||||||
def generateSlideData(self, service_item, item=None, xmlVersion=False,
|
def generateSlideData(self, service_item, item=None, xmlVersion=False,
|
||||||
remote=False, context=ServiceItemContext.Service):
|
remote=False, context=ServiceItemContext.Service):
|
||||||
background = QtGui.QColor(Settings().value(self.settingsSection + u'/background color'))
|
background = QtGui.QColor(Settings().value(self.settingsSection + u'/background color'))
|
||||||
@ -148,6 +533,10 @@ class ImageMediaItem(MediaManagerItem):
|
|||||||
items = self.listView.selectedItems()
|
items = self.listView.selectedItems()
|
||||||
if not items:
|
if not items:
|
||||||
return False
|
return False
|
||||||
|
# Determine service item title
|
||||||
|
if isinstance(items[0].data(0, QtCore.Qt.UserRole), ImageGroups):
|
||||||
|
service_item.title = items[0].text(0)
|
||||||
|
else:
|
||||||
service_item.title = unicode(self.plugin.nameStrings[u'plural'])
|
service_item.title = unicode(self.plugin.nameStrings[u'plural'])
|
||||||
service_item.add_capability(ItemCapabilities.CanMaintain)
|
service_item.add_capability(ItemCapabilities.CanMaintain)
|
||||||
service_item.add_capability(ItemCapabilities.CanPreview)
|
service_item.add_capability(ItemCapabilities.CanPreview)
|
||||||
@ -157,8 +546,19 @@ class ImageMediaItem(MediaManagerItem):
|
|||||||
service_item.theme = -1
|
service_item.theme = -1
|
||||||
missing_items = []
|
missing_items = []
|
||||||
missing_items_filenames = []
|
missing_items_filenames = []
|
||||||
|
# Expand groups to images
|
||||||
for bitem in items:
|
for bitem in items:
|
||||||
filename = bitem.data(QtCore.Qt.UserRole)
|
if isinstance(bitem.data(0, QtCore.Qt.UserRole), ImageGroups) or bitem.data(0, QtCore.Qt.UserRole) is None:
|
||||||
|
for index in range(0, bitem.childCount()):
|
||||||
|
if isinstance(bitem.child(index).data(0, QtCore.Qt.UserRole), ImageFilenames):
|
||||||
|
items.append(bitem.child(index))
|
||||||
|
items.remove(bitem)
|
||||||
|
# Don't try to display empty groups
|
||||||
|
if not items:
|
||||||
|
return False
|
||||||
|
# Find missing files
|
||||||
|
for bitem in items:
|
||||||
|
filename = bitem.data(0, QtCore.Qt.UserRole).filename
|
||||||
if not os.path.exists(filename):
|
if not os.path.exists(filename):
|
||||||
missing_items.append(bitem)
|
missing_items.append(bitem)
|
||||||
missing_items_filenames.append(filename)
|
missing_items_filenames.append(filename)
|
||||||
@ -181,11 +581,57 @@ class ImageMediaItem(MediaManagerItem):
|
|||||||
return False
|
return False
|
||||||
# Continue with the existing images.
|
# Continue with the existing images.
|
||||||
for bitem in items:
|
for bitem in items:
|
||||||
filename = bitem.data(QtCore.Qt.UserRole)
|
filename = bitem.data(0, QtCore.Qt.UserRole).filename
|
||||||
name = os.path.split(filename)[1]
|
name = os.path.split(filename)[1]
|
||||||
service_item.add_from_image(filename, name, background)
|
service_item.add_from_image(filename, name, background)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def check_group_exists(self, new_group):
|
||||||
|
"""
|
||||||
|
Returns *True* if the given Group already exists in the database, otherwise *False*.
|
||||||
|
|
||||||
|
``new_group``
|
||||||
|
The ImageGroups object that contains the name of the group that will be checked
|
||||||
|
"""
|
||||||
|
groups = self.manager.get_all_objects(ImageGroups, ImageGroups.group_name == new_group.group_name)
|
||||||
|
if groups:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def onAddGroupClick(self):
|
||||||
|
"""
|
||||||
|
Called to add a new group
|
||||||
|
"""
|
||||||
|
# Find out if a group must be pre-selected
|
||||||
|
preselect_group = 0
|
||||||
|
selected_items = self.listView.selectedItems()
|
||||||
|
if selected_items:
|
||||||
|
selected_item = selected_items[0]
|
||||||
|
if isinstance(selected_item.data(0, QtCore.Qt.UserRole), ImageFilenames):
|
||||||
|
selected_item = selected_item.parent()
|
||||||
|
if isinstance(selected_item, QtGui.QTreeWidgetItem):
|
||||||
|
if isinstance(selected_item.data(0, QtCore.Qt.UserRole), ImageGroups):
|
||||||
|
preselect_group = selected_item.data(0, QtCore.Qt.UserRole).id
|
||||||
|
# Show 'add group' dialog
|
||||||
|
if self.add_group_form.exec_(show_top_level_group=True, selected_group=preselect_group):
|
||||||
|
new_group = ImageGroups.populate(parent_id=self.add_group_form.parent_group_combobox.itemData(
|
||||||
|
self.add_group_form.parent_group_combobox.currentIndex(), QtCore.Qt.UserRole),
|
||||||
|
group_name=self.add_group_form.name_edit.text())
|
||||||
|
if not self.check_group_exists(new_group):
|
||||||
|
if self.manager.save_object(new_group):
|
||||||
|
self.loadFullList(self.manager.get_all_objects(ImageFilenames,
|
||||||
|
order_by_ref=ImageFilenames.filename))
|
||||||
|
self.expand_group(new_group.id)
|
||||||
|
self.fill_groups_combobox(self.choose_group_form.group_combobox)
|
||||||
|
self.fill_groups_combobox(self.add_group_form.parent_group_combobox)
|
||||||
|
else:
|
||||||
|
critical_error_message_box(
|
||||||
|
message=translate('ImagePlugin.AddGroupForm', 'Could not add the new group.'))
|
||||||
|
else:
|
||||||
|
critical_error_message_box(
|
||||||
|
message=translate('ImagePlugin.AddGroupForm', 'This group already exists.'))
|
||||||
|
|
||||||
def onResetClick(self):
|
def onResetClick(self):
|
||||||
"""
|
"""
|
||||||
Called to reset the Live background with the image selected,
|
Called to reset the Live background with the image selected,
|
||||||
@ -206,9 +652,11 @@ class ImageMediaItem(MediaManagerItem):
|
|||||||
if check_item_selected(self.listView,
|
if check_item_selected(self.listView,
|
||||||
translate('ImagePlugin.MediaItem', 'You must select an image to replace the background with.')):
|
translate('ImagePlugin.MediaItem', 'You must select an image to replace the background with.')):
|
||||||
background = QtGui.QColor(Settings().value(self.settingsSection + u'/background color'))
|
background = QtGui.QColor(Settings().value(self.settingsSection + u'/background color'))
|
||||||
item = self.listView.selectedIndexes()[0]
|
bitem = self.listView.selectedItems()[0]
|
||||||
bitem = self.listView.item(item.row())
|
if not isinstance(bitem.data(0, QtCore.Qt.UserRole), ImageFilenames):
|
||||||
filename = bitem.data(QtCore.Qt.UserRole)
|
# Only continue when an image is selected
|
||||||
|
return
|
||||||
|
filename = bitem.data(0, QtCore.Qt.UserRole).filename
|
||||||
if os.path.exists(filename):
|
if os.path.exists(filename):
|
||||||
if self.live_controller.display.direct_image(filename, background):
|
if self.live_controller.display.direct_image(filename, background):
|
||||||
self.resetAction.setVisible(True)
|
self.resetAction.setVisible(True)
|
||||||
@ -221,11 +669,10 @@ class ImageMediaItem(MediaManagerItem):
|
|||||||
'the image file "%s" no longer exists.') % filename)
|
'the image file "%s" no longer exists.') % filename)
|
||||||
|
|
||||||
def search(self, string, showError):
|
def search(self, string, showError):
|
||||||
files = Settings().value(self.settingsSection + u'/images files')
|
files = self.manager.get_all_objects(ImageFilenames, filter_clause=ImageFilenames.filename.contains(string),
|
||||||
|
order_by_ref=ImageFilenames.filename)
|
||||||
results = []
|
results = []
|
||||||
string = string.lower()
|
for file_object in files:
|
||||||
for file in files:
|
filename = os.path.split(unicode(file_object.filename))[1]
|
||||||
filename = os.path.split(unicode(file))[1]
|
results.append([file_object.filename, filename])
|
||||||
if filename.lower().find(string) > -1:
|
|
||||||
results.append([file, filename])
|
|
||||||
return results
|
return results
|
||||||
|
@ -252,7 +252,7 @@ class MediaMediaItem(MediaManagerItem):
|
|||||||
self.listView.takeItem(row)
|
self.listView.takeItem(row)
|
||||||
Settings().setValue(self.settingsSection + u'/media files', self.getFileList())
|
Settings().setValue(self.settingsSection + u'/media files', self.getFileList())
|
||||||
|
|
||||||
def loadList(self, media):
|
def loadList(self, media, target_group=None):
|
||||||
# Sort the media by its filename considering language specific
|
# Sort the media by its filename considering language specific
|
||||||
# characters.
|
# characters.
|
||||||
media.sort(cmp=locale_compare, key=lambda filename: os.path.split(unicode(filename))[1])
|
media.sort(cmp=locale_compare, key=lambda filename: os.path.split(unicode(filename))[1])
|
||||||
|
@ -120,7 +120,7 @@ class PresentationMediaItem(MediaManagerItem):
|
|||||||
"""
|
"""
|
||||||
self.listView.setIconSize(QtCore.QSize(88, 50))
|
self.listView.setIconSize(QtCore.QSize(88, 50))
|
||||||
files = Settings().value(self.settingsSection + u'/presentations files')
|
files = Settings().value(self.settingsSection + u'/presentations files')
|
||||||
self.loadList(files, True)
|
self.loadList(files, initialLoad=True)
|
||||||
self.populate_display_types()
|
self.populate_display_types()
|
||||||
|
|
||||||
def populate_display_types(self):
|
def populate_display_types(self):
|
||||||
@ -141,7 +141,7 @@ class PresentationMediaItem(MediaManagerItem):
|
|||||||
else:
|
else:
|
||||||
self.presentationWidget.hide()
|
self.presentationWidget.hide()
|
||||||
|
|
||||||
def loadList(self, files, initialLoad=False):
|
def loadList(self, files, target_group=None, initialLoad=False):
|
||||||
"""
|
"""
|
||||||
Add presentations into the media manager
|
Add presentations into the media manager
|
||||||
This is called both on initial load of the plugin to populate with
|
This is called both on initial load of the plugin to populate with
|
||||||
|
62
resources/forms/imagesaddgroupdialog.ui
Normal file
62
resources/forms/imagesaddgroupdialog.ui
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>AddGroupDialog</class>
|
||||||
|
<widget class="QDialog" name="AddGroupDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>365</width>
|
||||||
|
<height>119</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Add group</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QFormLayout" name="AddGroupLayout">
|
||||||
|
<property name="fieldGrowthPolicy">
|
||||||
|
<enum>QFormLayout::ExpandingFieldsGrow</enum>
|
||||||
|
</property>
|
||||||
|
<property name="horizontalSpacing">
|
||||||
|
<number>8</number>
|
||||||
|
</property>
|
||||||
|
<property name="verticalSpacing">
|
||||||
|
<number>8</number>
|
||||||
|
</property>
|
||||||
|
<property name="margin">
|
||||||
|
<number>8</number>
|
||||||
|
</property>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="ParentGroupLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Parent group:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QComboBox" name="ParentGroupComboBox"/>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="GroupNameLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Group name:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLineEdit" name="GroupNameEdit"/>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QDialogButtonBox" name="GroupButtonBox">
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Save</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="../images/openlp-2.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
55
resources/forms/imageschoosegroupdialog.ui
Normal file
55
resources/forms/imageschoosegroupdialog.ui
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>ChooseGroupDialog</class>
|
||||||
|
<widget class="QDialog" name="ChooseGroupDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>440</width>
|
||||||
|
<height>119</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Choose group</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QFormLayout" name="addGroupLayout">
|
||||||
|
<property name="fieldGrowthPolicy">
|
||||||
|
<enum>QFormLayout::ExpandingFieldsGrow</enum>
|
||||||
|
</property>
|
||||||
|
<property name="horizontalSpacing">
|
||||||
|
<number>8</number>
|
||||||
|
</property>
|
||||||
|
<property name="verticalSpacing">
|
||||||
|
<number>8</number>
|
||||||
|
</property>
|
||||||
|
<property name="margin">
|
||||||
|
<number>8</number>
|
||||||
|
</property>
|
||||||
|
<item row="1" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="groupQuestionLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>To which group do you want these images to be added?</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QComboBox" name="groupComboBox"/>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QDialogButtonBox" name="groupButtonBox">
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Save</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="../images/openlp-2.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
BIN
resources/images/image_group.png
Normal file
BIN
resources/images/image_group.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
BIN
resources/images/image_new_group.png
Normal file
BIN
resources/images/image_new_group.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 762 B |
@ -21,6 +21,10 @@
|
|||||||
<file>song_topic_edit.png</file>
|
<file>song_topic_edit.png</file>
|
||||||
<file>song_book_edit.png</file>
|
<file>song_book_edit.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
|
<qresource prefix="images">
|
||||||
|
<file>image_group.png</file>
|
||||||
|
<file>image_new_group.png</file>
|
||||||
|
</qresource>
|
||||||
<qresource prefix="bibles">
|
<qresource prefix="bibles">
|
||||||
<file>bibles_search_text.png</file>
|
<file>bibles_search_text.png</file>
|
||||||
<file>bibles_search_reference.png</file>
|
<file>bibles_search_reference.png</file>
|
||||||
|
@ -6,7 +6,7 @@ from unittest import TestCase
|
|||||||
from mock import MagicMock
|
from mock import MagicMock
|
||||||
|
|
||||||
from openlp.core.lib.pluginmanager import PluginManager
|
from openlp.core.lib.pluginmanager import PluginManager
|
||||||
from openlp.core.lib import Registry, PluginStatus
|
from openlp.core.lib import Settings, Registry, PluginStatus
|
||||||
|
|
||||||
|
|
||||||
class TestPluginManager(TestCase):
|
class TestPluginManager(TestCase):
|
||||||
@ -184,7 +184,7 @@ class TestPluginManager(TestCase):
|
|||||||
# WHEN: We run hook_export_menu()
|
# WHEN: We run hook_export_menu()
|
||||||
plugin_manager.hook_export_menu()
|
plugin_manager.hook_export_menu()
|
||||||
|
|
||||||
# THEN: The addExportMenuItem() method should have been called
|
# THEN: The addExportMenuItem() method should not have been called
|
||||||
assert mocked_plugin.addExportMenuItem.call_count == 0, \
|
assert mocked_plugin.addExportMenuItem.call_count == 0, \
|
||||||
u'The addExportMenuItem() method should not have been called.'
|
u'The addExportMenuItem() method should not have been called.'
|
||||||
|
|
||||||
@ -204,6 +204,41 @@ class TestPluginManager(TestCase):
|
|||||||
# THEN: The addExportMenuItem() method should have been called
|
# THEN: The addExportMenuItem() method should have been called
|
||||||
mocked_plugin.addExportMenuItem.assert_called_with(self.mocked_main_window.file_export_menu)
|
mocked_plugin.addExportMenuItem.assert_called_with(self.mocked_main_window.file_export_menu)
|
||||||
|
|
||||||
|
def hook_upgrade_plugin_settings_with_disabled_plugin_test(self):
|
||||||
|
"""
|
||||||
|
Test running the hook_upgrade_plugin_settings() method with a disabled plugin
|
||||||
|
"""
|
||||||
|
# GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled
|
||||||
|
mocked_plugin = MagicMock()
|
||||||
|
mocked_plugin.status = PluginStatus.Disabled
|
||||||
|
plugin_manager = PluginManager()
|
||||||
|
plugin_manager.plugins = [mocked_plugin]
|
||||||
|
settings = Settings()
|
||||||
|
|
||||||
|
# WHEN: We run hook_upgrade_plugin_settings()
|
||||||
|
plugin_manager.hook_upgrade_plugin_settings(settings)
|
||||||
|
|
||||||
|
# THEN: The upgrade_settings() method should not have been called
|
||||||
|
assert mocked_plugin.upgrade_settings.call_count == 0, \
|
||||||
|
u'The upgrade_settings() method should not have been called.'
|
||||||
|
|
||||||
|
def hook_upgrade_plugin_settings_with_active_plugin_test(self):
|
||||||
|
"""
|
||||||
|
Test running the hook_upgrade_plugin_settings() method with an active plugin
|
||||||
|
"""
|
||||||
|
# GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active
|
||||||
|
mocked_plugin = MagicMock()
|
||||||
|
mocked_plugin.status = PluginStatus.Active
|
||||||
|
plugin_manager = PluginManager()
|
||||||
|
plugin_manager.plugins = [mocked_plugin]
|
||||||
|
settings = Settings()
|
||||||
|
|
||||||
|
# WHEN: We run hook_upgrade_plugin_settings()
|
||||||
|
plugin_manager.hook_upgrade_plugin_settings(settings)
|
||||||
|
|
||||||
|
# THEN: The addExportMenuItem() method should have been called
|
||||||
|
mocked_plugin.upgrade_settings.assert_called_with(settings)
|
||||||
|
|
||||||
def hook_tools_menu_with_disabled_plugin_test(self):
|
def hook_tools_menu_with_disabled_plugin_test(self):
|
||||||
"""
|
"""
|
||||||
Test running the hook_tools_menu() method with a disabled plugin
|
Test running the hook_tools_menu() method with a disabled plugin
|
||||||
|
0
tests/functional/openlp_plugins/images/__init__.py
Normal file
0
tests/functional/openlp_plugins/images/__init__.py
Normal file
112
tests/functional/openlp_plugins/images/test_lib.py
Normal file
112
tests/functional/openlp_plugins/images/test_lib.py
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||||
|
"""
|
||||||
|
This module contains tests for the lib submodule of the Images plugin.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from unittest import TestCase
|
||||||
|
|
||||||
|
from mock import MagicMock, patch
|
||||||
|
|
||||||
|
from openlp.core.lib import Registry
|
||||||
|
from openlp.plugins.images.lib.db import ImageFilenames
|
||||||
|
from openlp.plugins.images.lib.mediaitem import ImageMediaItem
|
||||||
|
|
||||||
|
|
||||||
|
class TestImageMediaItem(TestCase):
|
||||||
|
"""
|
||||||
|
This is a test case to test various methods in the ImageMediaItem class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.mocked_main_window = MagicMock()
|
||||||
|
Registry.create()
|
||||||
|
Registry().register(u'service_list', MagicMock())
|
||||||
|
Registry().register(u'main_window', self.mocked_main_window)
|
||||||
|
mocked_parent = MagicMock()
|
||||||
|
mocked_plugin = MagicMock()
|
||||||
|
with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.__init__') as mocked_init:
|
||||||
|
mocked_init.return_value = None
|
||||||
|
self.mediaitem = ImageMediaItem(mocked_parent, mocked_plugin)
|
||||||
|
|
||||||
|
def save_new_images_list_empty_list_test(self):
|
||||||
|
"""
|
||||||
|
Test that the save_new_images_list() method handles empty lists gracefully
|
||||||
|
"""
|
||||||
|
# GIVEN: An empty image_list
|
||||||
|
image_list = []
|
||||||
|
with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.loadFullList') as mocked_loadFullList:
|
||||||
|
self.mediaitem.manager = MagicMock()
|
||||||
|
|
||||||
|
# WHEN: We run save_new_images_list with the empty list
|
||||||
|
self.mediaitem.save_new_images_list(image_list)
|
||||||
|
|
||||||
|
# THEN: The save_object() method should not have been called
|
||||||
|
assert self.mediaitem.manager.save_object.call_count == 0, \
|
||||||
|
u'The save_object() method should not have been called'
|
||||||
|
|
||||||
|
def save_new_images_list_single_image_with_reload_test(self):
|
||||||
|
"""
|
||||||
|
Test that the save_new_images_list() calls loadFullList() when reload_list is set to True
|
||||||
|
"""
|
||||||
|
# GIVEN: A list with 1 image
|
||||||
|
image_list = [ u'test_image.jpg' ]
|
||||||
|
with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.loadFullList') as mocked_loadFullList:
|
||||||
|
ImageFilenames.filename = ''
|
||||||
|
self.mediaitem.manager = MagicMock()
|
||||||
|
|
||||||
|
# WHEN: We run save_new_images_list with reload_list=True
|
||||||
|
self.mediaitem.save_new_images_list(image_list, reload_list=True)
|
||||||
|
|
||||||
|
# THEN: loadFullList() should have been called
|
||||||
|
assert mocked_loadFullList.call_count == 1, u'loadFullList() should have been called'
|
||||||
|
|
||||||
|
# CLEANUP: Remove added attribute from ImageFilenames
|
||||||
|
delattr(ImageFilenames, 'filename')
|
||||||
|
|
||||||
|
def save_new_images_list_single_image_without_reload_test(self):
|
||||||
|
"""
|
||||||
|
Test that the save_new_images_list() doesn't call loadFullList() when reload_list is set to False
|
||||||
|
"""
|
||||||
|
# GIVEN: A list with 1 image
|
||||||
|
image_list = [ u'test_image.jpg' ]
|
||||||
|
with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.loadFullList') as mocked_loadFullList:
|
||||||
|
self.mediaitem.manager = MagicMock()
|
||||||
|
|
||||||
|
# WHEN: We run save_new_images_list with reload_list=False
|
||||||
|
self.mediaitem.save_new_images_list(image_list, reload_list=False)
|
||||||
|
|
||||||
|
# THEN: loadFullList() should not have been called
|
||||||
|
assert mocked_loadFullList.call_count == 0, u'loadFullList() should not have been called'
|
||||||
|
|
||||||
|
def save_new_images_list_multiple_images_test(self):
|
||||||
|
"""
|
||||||
|
Test that the save_new_images_list() saves all images in the list
|
||||||
|
"""
|
||||||
|
# GIVEN: A list with 3 images
|
||||||
|
image_list = [ u'test_image_1.jpg', u'test_image_2.jpg', u'test_image_3.jpg' ]
|
||||||
|
with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.loadFullList') as mocked_loadFullList:
|
||||||
|
self.mediaitem.manager = MagicMock()
|
||||||
|
|
||||||
|
# WHEN: We run save_new_images_list with the list of 3 images
|
||||||
|
self.mediaitem.save_new_images_list(image_list, reload_list=False)
|
||||||
|
|
||||||
|
# THEN: loadFullList() should not have been called
|
||||||
|
assert self.mediaitem.manager.save_object.call_count == 3, \
|
||||||
|
u'loadFullList() should have been called three times'
|
||||||
|
|
||||||
|
def save_new_images_list_other_objects_in_list_test(self):
|
||||||
|
"""
|
||||||
|
Test that the save_new_images_list() ignores everything in the provided list except strings
|
||||||
|
"""
|
||||||
|
# GIVEN: A list with images and objects
|
||||||
|
image_list = [ u'test_image_1.jpg', None, True, ImageFilenames(), 'test_image_2.jpg' ]
|
||||||
|
with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.loadFullList') as mocked_loadFullList:
|
||||||
|
self.mediaitem.manager = MagicMock()
|
||||||
|
|
||||||
|
# WHEN: We run save_new_images_list with the list of images and objects
|
||||||
|
self.mediaitem.save_new_images_list(image_list, reload_list=False)
|
||||||
|
|
||||||
|
# THEN: loadFullList() should not have been called
|
||||||
|
assert self.mediaitem.manager.save_object.call_count == 2, \
|
||||||
|
u'loadFullList() should have been called only once'
|
Loading…
Reference in New Issue
Block a user