From a770f90320559d8e1e83064652b8904576466035 Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Fri, 25 Jan 2013 15:06:49 +0100 Subject: [PATCH] Implemented adding image groups --- openlp/core/lib/treewidgetwithdnd.py | 1 + openlp/core/lib/uistrings.py | 1 + openlp/plugins/images/forms/__init__.py | 57 +++++++ openlp/plugins/images/forms/addgroupdialog.py | 63 ++++++++ openlp/plugins/images/forms/addgroupform.py | 60 +++++++ openlp/plugins/images/lib/mediaitem.py | 151 +++++++++++++++--- resources/forms/imagesaddgroupdialog.ui | 62 +++++++ resources/images/image_new_group.png | Bin 0 -> 762 bytes resources/images/openlp-2.qrc | 3 + 9 files changed, 379 insertions(+), 19 deletions(-) create mode 100644 openlp/plugins/images/forms/__init__.py create mode 100644 openlp/plugins/images/forms/addgroupdialog.py create mode 100644 openlp/plugins/images/forms/addgroupform.py create mode 100644 resources/forms/imagesaddgroupdialog.ui create mode 100644 resources/images/image_new_group.png diff --git a/openlp/core/lib/treewidgetwithdnd.py b/openlp/core/lib/treewidgetwithdnd.py index b62246dc3..fe53bb43a 100644 --- a/openlp/core/lib/treewidgetwithdnd.py +++ b/openlp/core/lib/treewidgetwithdnd.py @@ -46,6 +46,7 @@ class TreeWidgetWithDnD(QtGui.QTreeWidget): QtGui.QTreeWidget.__init__(self, parent) self.mimeDataText = name self.header().close() + self.defaultIndentation = self.indentation() self.setIndentation(0) assert(self.mimeDataText) diff --git a/openlp/core/lib/uistrings.py b/openlp/core/lib/uistrings.py index f89b52a98..9a60d3ff9 100644 --- a/openlp/core/lib/uistrings.py +++ b/openlp/core/lib/uistrings.py @@ -58,6 +58,7 @@ class UiStrings(object): """ self.About = translate('OpenLP.Ui', 'About') self.Add = translate('OpenLP.Ui', '&Add') + self.AddGroup = translate('OpenLP.Ui', 'Add group') self.Advanced = translate('OpenLP.Ui', 'Advanced') self.AllFiles = translate('OpenLP.Ui', 'All Files') self.Automatic = translate('OpenLP.Ui', 'Automatic') diff --git a/openlp/plugins/images/forms/__init__.py b/openlp/plugins/images/forms/__init__.py new file mode 100644 index 000000000..8c33c656e --- /dev/null +++ b/openlp/plugins/images/forms/__init__.py @@ -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_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 +``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 + diff --git a/openlp/plugins/images/forms/addgroupdialog.py b/openlp/plugins/images/forms/addgroupdialog.py new file mode 100644 index 000000000..1ba019911 --- /dev/null +++ b/openlp/plugins/images/forms/addgroupdialog.py @@ -0,0 +1,63 @@ +# -*- 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, addGroupDialog): + addGroupDialog.setObjectName(u'addGroupDialog') + addGroupDialog.resize(300, 10) + self.dialogLayout = QtGui.QVBoxLayout(addGroupDialog) + self.dialogLayout.setObjectName(u'dialogLayout') + self.nameLayout = QtGui.QFormLayout() + self.nameLayout.setObjectName(u'nameLayout') + self.parentGroupLabel = QtGui.QLabel(addGroupDialog) + self.parentGroupLabel.setObjectName(u'parentGroupLabel') + self.parentGroupComboBox = QtGui.QComboBox(addGroupDialog) + self.parentGroupComboBox.setObjectName(u'parentGroupComboBox') + self.nameLayout.addRow(self.parentGroupLabel, self.parentGroupComboBox) + self.nameLabel = QtGui.QLabel(addGroupDialog) + self.nameLabel.setObjectName(u'nameLabel') + self.nameEdit = QtGui.QLineEdit(addGroupDialog) + self.nameEdit.setObjectName(u'nameEdit') + self.nameLabel.setBuddy(self.nameEdit) + self.nameLayout.addRow(self.nameLabel, self.nameEdit) + self.dialogLayout.addLayout(self.nameLayout) + self.buttonBox = create_button_box(addGroupDialog, u'buttonBox', [u'cancel', u'save']) + self.dialogLayout.addWidget(self.buttonBox) + self.retranslateUi(addGroupDialog) + addGroupDialog.setMaximumHeight(addGroupDialog.sizeHint().height()) + + def retranslateUi(self, addGroupDialog): + addGroupDialog.setWindowTitle(translate('ImagePlugin.AddGroupForm', 'Add group')) + self.parentGroupLabel.setText(translate('ImagePlugin.AddGroupForm', 'Parent group:')) + self.nameLabel.setText(translate('ImagePlugin.AddGroupForm', 'Group name:')) diff --git a/openlp/plugins/images/forms/addgroupform.py b/openlp/plugins/images/forms/addgroupform.py new file mode 100644 index 000000000..eb6595546 --- /dev/null +++ b/openlp/plugins/images/forms/addgroupform.py @@ -0,0 +1,60 @@ +# -*- 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): + """ + Class documentation goes here. + """ + def __init__(self, parent=None): + """ + Constructor + """ + QtGui.QDialog.__init__(self, parent) + self.setupUi(self) + + def exec_(self, clear=True): + if clear: + self.nameEdit.clear() + self.nameEdit.setFocus() + return QtGui.QDialog.exec_(self) + + def accept(self): + if not self.nameEdit.text(): + critical_error_message_box(message=translate('ImagePlugin.AddGroupForm', + 'You need to type in a group name.')) + self.nameEdit.setFocus() + return False + else: + return QtGui.QDialog.accept(self) diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 37036c668..871f88714 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -37,7 +37,8 @@ from openlp.core.lib import MediaManagerItem, build_icon, ItemCapabilities, Sett UiStrings from openlp.core.lib.ui import critical_error_message_box from openlp.core.utils import AppLocation, delete_file, locale_compare, get_images_filter -from openlp.plugins.images.lib.db import ImageFilenames +from openlp.plugins.images.forms import AddGroupForm +from openlp.plugins.images.lib.db import ImageFilenames, ImageGroups log = logging.getLogger(__name__) @@ -53,6 +54,8 @@ class ImageMediaItem(MediaManagerItem): self.quickPreviewAllowed = True self.hasSearch = True self.manager = plugin.manager + self.addgroupform = AddGroupForm(self) + self.fillGroupsComboBox() QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'live_theme_changed'), self.liveThemeChanged) # Allow DnD from the desktop self.listView.activateDnD() @@ -62,6 +65,8 @@ class ImageMediaItem(MediaManagerItem): 'Select Image(s)') file_formats = get_images_filter() 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.setToolTip(UiStrings().ReplaceLiveBG) self.resetAction.setText(UiStrings().ResetBG) @@ -78,6 +83,7 @@ class ImageMediaItem(MediaManagerItem): log.debug(u'initialise') self.listView.clear() self.listView.setIconSize(QtCore.QSize(88, 50)) + self.listView.setIndentation(self.listView.defaultIndentation) self.servicePath = os.path.join(AppLocation.get_section_data_path(self.settingsSection), u'thumbnails') check_directory_exists(self.servicePath) # Import old images list @@ -92,12 +98,16 @@ class ImageMediaItem(MediaManagerItem): Settings().remove(self.settingsSection + u'/images files') Settings().remove(self.settingsSection + u'/images count') # Load images from the database - self.loadList(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename)) + self.loadFullList(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename), True) def addListViewToToolBar(self): MediaManagerItem.addListViewToToolBar(self) self.listView.addAction(self.replaceAction) + def addStartHeaderBar(self): + self.addGroupAction = self.toolbar.addToolbarAction(u'addGroupAction', + icon=u':/images/image_new_group.png', triggers=self.onAddGroupClick) + def addEndHeaderBar(self): self.replaceAction = self.toolbar.addToolbarAction(u'replaceAction', icon=u':/slides/slide_blank.png', triggers=self.onReplaceClick) @@ -111,15 +121,15 @@ class ImageMediaItem(MediaManagerItem): # Turn off auto preview triggers. self.listView.blockSignals(True) if check_item_selected(self.listView, translate('ImagePlugin.MediaItem','You must select an image to delete.')): - row_list = [item.row() for item in self.listView.selectedIndexes()] - row_list.sort(reverse=True) + item_list = self.listView.selectedItems() Receiver.send_message(u'cursor_busy') self.main_window.displayProgressBar(len(row_list)) - for row in row_list: + self.plugin.formParent.displayProgressBar(len(item_list)) + for row_item in item_list: row_item = self.listView.topLevelItem(row) if row_item: delete_file(os.path.join(self.servicePath, row_item.text(0))) - self.listView.takeTopLevelItem(row) + row_item.parent().removeChild(row_item) self.main_window.incrementProgressBar() self.manager.delete_object(ImageFilenames, row_item.data(0, QtCore.Qt.UserRole).id) SettingsManager.setValue(self.settingsSection + u'/images files', self.getFileList()) @@ -127,20 +137,51 @@ class ImageMediaItem(MediaManagerItem): Receiver.send_message(u'cursor_normal') self.listView.blockSignals(False) - def loadList(self, images, initialLoad=False): + def addSubGroups(self, groupList, parentGroupId): + """ + Recursively add subgroups to the given parent group + """ + image_groups = self.manager.get_all_objects(ImageGroups, ImageGroups.parent_id == parentGroupId) + image_groups.sort(cmp=locale_compare, key=lambda group_object: group_object.group_name) + for image_group in image_groups: + group = QtGui.QTreeWidgetItem() + group.setText(0, image_group.group_name) + if parentGroupId is 0: + self.listView.addTopLevelItem(group) + else: + groupList[parentGroupId].addChild(group) + groupList[image_group.id] = group + self.addSubGroups(groupList, image_group.id) + + def fillGroupsComboBox(self, parentGroupId=0, prefix=''): + """ + Recursively add groups to the combobox in the 'Add group' dialog + """ + if parentGroupId is 0: + self.addgroupform.parentGroupComboBox.clear() + self.addgroupform.parentGroupComboBox.addItem(translate('ImagePlugin.MediaItem', '-- Top-level group --'), 0) + image_groups = self.manager.get_all_objects(ImageGroups, ImageGroups.parent_id == parentGroupId) + image_groups.sort(cmp=locale_compare, key=lambda group_object: group_object.group_name) + for image_group in image_groups: + self.addgroupform.parentGroupComboBox.addItem(prefix+image_group.group_name, image_group.id) + self.fillGroupsComboBox(image_group.id, prefix+' ') + + def loadFullList(self, images, initialLoad=False): + """ + Replace the list of images and groups in the interface. + """ if not initialLoad: Receiver.send_message(u'cursor_busy') self.main_window.displayProgressBar(len(images)) + self.listView.clear() + # Load the list of groups and add them to the treeView + group_items = {} + self.addSubGroups(group_items, 0) # Sort the images by its filename considering language specific # 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: - if type(imageFile) is str or type(imageFile) is unicode: - filename = imageFile - imageFile = ImageFilenames() - imageFile.group_id = 0 - imageFile.filename = unicode(filename) - success = self.manager.save_object(imageFile) + log.debug(u'Loading image: %s', imageFile.filename) filename = os.path.split(imageFile.filename)[1] thumb = os.path.join(self.servicePath, filename) if not os.path.exists(imageFile.filename): @@ -150,19 +191,40 @@ class ImageMediaItem(MediaManagerItem): icon = build_icon(thumb) else: icon = create_thumb(imageFile.filename, thumb) - log.debug(u'Loading image: %s', imageFile.filename) item_name = QtGui.QTreeWidgetItem(filename) item_name.setText(0, filename) item_name.setIcon(0, icon) item_name.setToolTip(0, imageFile.filename) item_name.setData(0, QtCore.Qt.UserRole, imageFile) - self.listView.addTopLevelItem(item_name) + if imageFile.group_id is 0: + if 0 not in group_items: + # The 'Imported' group is only displayed when there are files that were imported from the + # configuration file + imported_group = QtGui.QTreeWidgetItem() + imported_group.setText(0, translate('ImagePlugin.MediaItem', 'Imported')) + self.listView.insertTopLevelItem(0, imported_group) + group_items[0] = imported_group + group_items[imageFile.group_id].addChild(item_name) if not initialLoad: self.main_window.incrementProgressBar() if not initialLoad: self.main_window.finishedProgressBar() Receiver.send_message(u'cursor_normal') + def loadList(self, images, initialLoad=False): + """ + Add new images to the database. This method is called when adding images using the Add button or DnD. + """ + for filename in images: + if filename is None: + continue + imageFile = ImageFilenames() + imageFile.group_id = 0 + imageFile.filename = unicode(filename) + success = self.manager.save_object(imageFile) + self.loadFullList(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename), + initialLoad) + def generateSlideData(self, service_item, item=None, xmlVersion=False, remote=False, context=ServiceItemContext.Service): background = QtGui.QColor(Settings().value(self.settingsSection + u'/background color')) @@ -182,6 +244,9 @@ class ImageMediaItem(MediaManagerItem): missing_items = [] missing_items_filenames = [] for bitem in items: + if bitem.data(0, QtCore.Qt.UserRole) is None: + # Ignore double-clicked groups + continue filename = bitem.data(0, QtCore.Qt.UserRole).filename if not os.path.exists(filename): missing_items.append(bitem) @@ -205,11 +270,60 @@ class ImageMediaItem(MediaManagerItem): return False # Continue with the existing images. for bitem in items: + if bitem.data(0, QtCore.Qt.UserRole) is None: + # Ignore double-clicked groups + continue filename = bitem.data(0, QtCore.Qt.UserRole).filename name = os.path.split(filename)[1] service_item.add_from_image(filename, name, background) return True + def __checkObject(self, objects, newObject, edit): + """ + Utility method to check for an existing object. + + ``edit`` + If we edit an item, this should be *True*. + """ + if objects: + # If we edit an existing object, we need to make sure that we do + # not return False when nothing has changed. + if edit: + for object in objects: + if object.id != newObject.id: + return False + return True + else: + return False + else: + return True + + def checkGroupName(self, newGroup, edit=False): + """ + Returns *False* if the given Group already exists, otherwise *True*. + """ + groups = self.manager.get_all_objects(ImageGroups, ImageGroups.group_name == newGroup.group_name) + return self.__checkObject(groups, newGroup, edit) + + def onAddGroupClick(self): + """ + Called to add a new group + """ + if self.addgroupform.exec_(): + new_group = ImageGroups.populate(parent_id=self.addgroupform.parentGroupComboBox.itemData( + self.addgroupform.parentGroupComboBox.currentIndex(), QtCore.Qt.UserRole), + group_name=self.addgroupform.nameEdit.text()) + if self.checkGroupName(new_group): + if self.manager.save_object(new_group): + self.loadList([]) + self.fillGroupsComboBox() + 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): """ Called to reset the Live backgound with the image selected, @@ -230,9 +344,8 @@ class ImageMediaItem(MediaManagerItem): if check_item_selected(self.listView, translate('ImagePlugin.MediaItem', 'You must select an image to replace the background with.')): background = QtGui.QColor(Settings().value(self.settingsSection + u'/background color')) - item = self.listView.selectedIndexes()[0] - bitem = self.listView.topLevelItem(item.row()) - filename = bitem.data(QtCore.Qt.UserRole).filename + bitem = self.listView.selectedItems()[0] + filename = bitem.data(0, QtCore.Qt.UserRole).filename if os.path.exists(filename): if self.plugin.liveController.display.directImage(filename, background): self.resetAction.setVisible(True) diff --git a/resources/forms/imagesaddgroupdialog.ui b/resources/forms/imagesaddgroupdialog.ui new file mode 100644 index 000000000..639804dba --- /dev/null +++ b/resources/forms/imagesaddgroupdialog.ui @@ -0,0 +1,62 @@ + + + AddGroupDialog + + + + 0 + 0 + 365 + 119 + + + + Add group + + + + QFormLayout::ExpandingFieldsGrow + + + 8 + + + 8 + + + 8 + + + + + Parent group: + + + + + + + + + + Group name: + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Save + + + + + + + + + + diff --git a/resources/images/image_new_group.png b/resources/images/image_new_group.png new file mode 100644 index 0000000000000000000000000000000000000000..ae2e0f86b07d23df44b6e3cfc908e4ce5809efe5 GIT binary patch literal 762 zcmVPx#24YJ`L;(K){{a7>y{D4^000SaNLh0L01ejw01ejxLMWSf00007bV*G`2ipY* z5)3D=WNfMc00MeRL_t(I%XO1WNR&|&$A9O3*U86359omv5!5Ox=;5x?N<)Gu8iNRG z)v8@VBm`{&3tB`Gw5wJ0SV&+LNiIyJpd@-g3KqRg94p6pnDc%2o)+JXj8J!T;lTO* zf9G&7{O6?3ch4U|2tZQjAHHy68c`4c6>K7SfA3sN!))_a z4zz4->Dskqi=JKEDHlgNw0HcK^bGW*r0vvgZQa|3x)5W8Ah0~{?bAdeA&6m~Tfp5_ ztu(Am>cZTp^tCHH?hBL2dVM=KI&5>938o8EydIgrU|6%PhMkR@#3LwBt4p{uJjI(6 z87T|(wJU(gWm#vZevOgI=82~B1O|paXK|q;o&H8HGf7o6AKE}}2z9?9*P$dkh5RC5 z4Z}7wG0L047kvDhCNKgBf2OnS+r62C`wt@C@HA_<@Z}i!l0kkEumV!%R9aFUH}&+% zBV-FR7!zBn3NtfBhDUxAMoKlc3??6N+n*u)kt0!`WrU z7!fppDuE&eHsss5qKB~y5=8kKymR!J>sVF62vAB709OWiC;}EGBqZcl39nT7I#95x z5P8LUMSut}fKw2O-$tU6uXv1FF=FIA>J=*@7!wc} zKRZG>&6jSefW>vi1OW!Yj{VH===nR;M{|-)g@yj(uh>Kl-@cDyS1(`8rj6@yQ8=4a zsajd{72w3Y8V1InW2XwmC&kdWMxi1CUgJep5ihZ<6qZ0%09720La1HB(rtT)!tH>d sY7hifnH^shL`nh@%ZYdtrMkBNFJcfTonXt~p#T5?07*qoM6N<$g0JI4!vFvP literal 0 HcmV?d00001 diff --git a/resources/images/openlp-2.qrc b/resources/images/openlp-2.qrc index beebcff67..d98f1e534 100644 --- a/resources/images/openlp-2.qrc +++ b/resources/images/openlp-2.qrc @@ -21,6 +21,9 @@ song_topic_edit.png song_book_edit.png + + image_new_group.png + bibles_search_text.png bibles_search_reference.png