From dbce056688adcfe14f94bfdaba09e63c788eaca3 Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Fri, 25 Jan 2013 12:29:32 +0100 Subject: [PATCH 01/39] Change QListWidget to QTreeWidget in media manager --- openlp/core/lib/__init__.py | 2 +- openlp/core/lib/mediamanageritem.py | 22 ++-- openlp/core/lib/treewidgetwithdnd.py | 113 ++++++++++++++++++ openlp/plugins/bibles/lib/mediaitem.py | 11 +- openlp/plugins/custom/lib/mediaitem.py | 7 +- openlp/plugins/images/lib/mediaitem.py | 15 +-- openlp/plugins/media/lib/mediaitem.py | 27 +++-- openlp/plugins/presentations/lib/mediaitem.py | 22 ++-- openlp/plugins/songs/lib/mediaitem.py | 23 ++-- 9 files changed, 183 insertions(+), 59 deletions(-) create mode 100644 openlp/core/lib/treewidgetwithdnd.py diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 5cbfec11f..c18e3d0a1 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -391,7 +391,7 @@ from uistrings import UiStrings from eventreceiver import Receiver from screen import ScreenList from settings import Settings -from listwidgetwithdnd import ListWidgetWithDnD +from treewidgetwithdnd import TreeWidgetWithDnD from formattingtags import FormattingTags from spelltextedit import SpellTextEdit from settingsmanager import SettingsManager diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 100f248c0..3c4507280 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -36,7 +36,7 @@ import re from PyQt4 import QtCore, QtGui from openlp.core.lib import OpenLPToolbar, ServiceItem, StringContent, build_icon, translate, Receiver, \ - ListWidgetWithDnD, ServiceItemContext, Settings, Registry, UiStrings + TreeWidgetWithDnD, ServiceItemContext, Settings, Registry, UiStrings from openlp.core.lib.searchedit import SearchEdit from openlp.core.lib.ui import create_widget_action, critical_error_message_box @@ -213,8 +213,7 @@ class MediaManagerItem(QtGui.QWidget): Creates the main widget for listing items the media item is tracking """ # Add the List widget - self.listView = ListWidgetWithDnD(self, self.plugin.name) - self.listView.setSpacing(1) + self.listView = TreeWidgetWithDnD(self, self.plugin.name) self.listView.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) self.listView.setAlternatingRowColors(True) self.listView.setObjectName(u'%sListView' % self.plugin.name) @@ -404,7 +403,7 @@ class MediaManagerItem(QtGui.QWidget): """ count = 0 file_list = [] - while count < self.listView.count(): + while count < self.listView.topLevelItemCount(): bitem = self.listView.item(count) filename = bitem.data(QtCore.Qt.UserRole) file_list.append(filename) @@ -571,15 +570,16 @@ class MediaManagerItem(QtGui.QWidget): """ Checks if the listView is empty and adds a "No Search Results" item. """ - if self.listView.count(): + if self.listView.topLevelItemCount(): return message = translate('OpenLP.MediaManagerItem', 'No Search Results') - item = QtGui.QListWidgetItem(message) + item = QtGui.QTreeWidgetItem(message) + item.setText(0, message) item.setFlags(QtCore.Qt.NoItemFlags) font = QtGui.QFont() font.setItalic(True) - item.setFont(font) - self.listView.addItem(item) + item.setFont(0, font) + self.listView.addTopLevelItem(item) def _getIdOfItemToGenerate(self, item, remoteItem): """ @@ -596,7 +596,7 @@ class MediaManagerItem(QtGui.QWidget): item = self.listView.currentItem() if item is None: return False - item_id = item.data(QtCore.Qt.UserRole) + item_id = item.data(0, QtCore.Qt.UserRole) else: item_id = remoteItem else: @@ -611,7 +611,7 @@ class MediaManagerItem(QtGui.QWidget): if self.autoSelectId == -1: item = self.listView.currentItem() if item: - self.autoSelectId = item.data(QtCore.Qt.UserRole) + self.autoSelectId = item.data(0, QtCore.Qt.UserRole) def search(self, string, showError=True): """ @@ -687,4 +687,4 @@ class MediaManagerItem(QtGui.QWidget): self._service_manager = Registry().get(u'service_manager') return self._service_manager - service_manager = property(_get_service_manager) \ No newline at end of file + service_manager = property(_get_service_manager) diff --git a/openlp/core/lib/treewidgetwithdnd.py b/openlp/core/lib/treewidgetwithdnd.py new file mode 100644 index 000000000..b62246dc3 --- /dev/null +++ b/openlp/core/lib/treewidgetwithdnd.py @@ -0,0 +1,113 @@ +# -*- 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 Receiver + +class TreeWidgetWithDnD(QtGui.QTreeWidget): + """ + Provide a list widget to store objects and handle drag and drop events + """ + def __init__(self, parent=None, name=u''): + """ + Initialise the list widget + """ + QtGui.QTreeWidget.__init__(self, parent) + self.mimeDataText = name + self.header().close() + self.setIndentation(0) + assert(self.mimeDataText) + + def activateDnD(self): + """ + Activate DnD of widget + """ + self.setAcceptDrops(True) + self.setDragDropMode(QtGui.QAbstractItemView.DragDrop) + QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'%s_dnd' % self.mimeDataText), + self.parent().loadFile) + + 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 + """ + 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): + if event.mimeData().hasUrls(): + event.accept() + else: + event.ignore() + + def dragMoveEvent(self, event): + if event.mimeData().hasUrls(): + event.setDropAction(QtCore.Qt.CopyAction) + event.accept() + else: + event.ignore() + + def dropEvent(self, event): + """ + Receive drop event check if it is a file 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 in listing: + files.append(os.path.join(localFile, file)) + Receiver.send_message(u'%s_dnd' % self.mimeDataText, files) + else: + event.ignore() diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index fd1109221..3d616181a 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -95,7 +95,7 @@ class BibleMediaItem(MediaManagerItem): self.displayResults(bible, second_bible) def _decodeQtObject(self, bitem, key): - reference = bitem.data(QtCore.Qt.UserRole) + reference = bitem.data(0, QtCore.Qt.UserRole) obj = reference[unicode(key)] return unicode(obj).strip() @@ -691,7 +691,7 @@ class BibleMediaItem(MediaManagerItem): self.second_search_results = bibles[second_bible].get_verses(text) if not self.quickLockButton.isChecked(): self.listView.clear() - if self.listView.count() != 0 and self.search_results: + if self.listView.topLevelItemCount() != 0 and self.search_results: self.__checkSecondBible(bible, second_bible) elif self.search_results: self.displayResults(bible, second_bible) @@ -707,7 +707,7 @@ class BibleMediaItem(MediaManagerItem): """ items = self.buildDisplayResults(bible, second_bible, self.search_results) for bible_verse in items: - self.listView.addItem(bible_verse) + self.listView.addTopLevelItem(bible_verse) self.listView.selectAll() self.search_results = {} self.second_search_results = {} @@ -766,8 +766,9 @@ class BibleMediaItem(MediaManagerItem): second_version) else: bible_text = u'%s %d%s%d (%s)' % (book, verse.chapter, verse_separator, verse.verse, version) - bible_verse = QtGui.QListWidgetItem(bible_text) - bible_verse.setData(QtCore.Qt.UserRole, data) + bible_verse = QtGui.QTreeWidgetItem(bible_text) + bible_verse.setText(0, bible_text) + bible_verse.setData(0, QtCore.Qt.UserRole, data) items.append(bible_verse) return items diff --git a/openlp/plugins/custom/lib/mediaitem.py b/openlp/plugins/custom/lib/mediaitem.py index b4938e929..2cf6bd92e 100644 --- a/openlp/plugins/custom/lib/mediaitem.py +++ b/openlp/plugins/custom/lib/mediaitem.py @@ -105,9 +105,10 @@ class CustomMediaItem(MediaManagerItem): self.listView.clear() custom_slides.sort() for custom_slide in custom_slides: - custom_name = QtGui.QListWidgetItem(custom_slide.title) - custom_name.setData(QtCore.Qt.UserRole, custom_slide.id) - self.listView.addItem(custom_name) + custom_name = QtGui.QTreeWidgetItem(custom_slide.title) + custom_name.setText(0, custom_slide.title) + custom_name.setData(0, QtCore.Qt.UserRole, custom_slide.id) + self.listView.addTopLevelItem(custom_name) # Auto-select the custom. if custom_slide.id == self.autoSelectId: self.listView.setCurrentItem(custom_name) diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 092103c86..4924092e2 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -129,11 +129,12 @@ class ImageMediaItem(MediaManagerItem): icon = build_icon(thumb) else: icon = create_thumb(unicode(imageFile), thumb) - item_name = QtGui.QListWidgetItem(filename) - item_name.setIcon(icon) - item_name.setToolTip(imageFile) - item_name.setData(QtCore.Qt.UserRole, imageFile) - self.listView.addItem(item_name) + item_name = QtGui.QTreeWidgetItem(filename) + item_name.setText(0, filename) + item_name.setIcon(0, icon) + item_name.setToolTip(0, imageFile) + item_name.setData(0, QtCore.Qt.UserRole, imageFile) + self.listView.addTopLevelItem(item_name) if not initialLoad: self.main_window.incrementProgressBar() if not initialLoad: @@ -159,7 +160,7 @@ class ImageMediaItem(MediaManagerItem): missing_items = [] missing_items_filenames = [] for bitem in items: - filename = bitem.data(QtCore.Qt.UserRole) + filename = bitem.data(0, QtCore.Qt.UserRole) if not os.path.exists(filename): missing_items.append(bitem) missing_items_filenames.append(filename) @@ -182,7 +183,7 @@ class ImageMediaItem(MediaManagerItem): return False # Continue with the existing images. for bitem in items: - filename = bitem.data(QtCore.Qt.UserRole) + filename = bitem.data(0, QtCore.Qt.UserRole) name = os.path.split(filename)[1] service_item.add_from_image(filename, name, background) return True diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 443087a09..773027847 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -264,24 +264,27 @@ class MediaMediaItem(MediaManagerItem): track_info = QtCore.QFileInfo(track) if not os.path.exists(track): filename = os.path.split(unicode(track))[1] - item_name = QtGui.QListWidgetItem(filename) - item_name.setIcon(ERROR) - item_name.setData(QtCore.Qt.UserRole, track) + item_name = QtGui.QTreeWidgetItem(filename) + item_name.setText(0, filename) + item_name.setIcon(0, ERROR) + item_name.setData(0, QtCore.Qt.UserRole, track) elif track_info.isFile(): filename = os.path.split(unicode(track))[1] - item_name = QtGui.QListWidgetItem(filename) + item_name = QtGui.QTreeWidgetItem(filename) + item_name.setText(0, filename) if u'*.%s' % (filename.split(u'.')[-1].lower()) in self.media_controller.audio_extensions_list: - item_name.setIcon(AUDIO) + item_name.setIcon(0, AUDIO) else: - item_name.setIcon(VIDEO) - item_name.setData(QtCore.Qt.UserRole, track) + item_name.setIcon(0, VIDEO) + item_name.setData(0, QtCore.Qt.UserRole, track) else: filename = os.path.split(unicode(track))[1] - item_name = QtGui.QListWidgetItem(filename) - item_name.setIcon(build_icon(DVDICON)) - item_name.setData(QtCore.Qt.UserRole, track) - item_name.setToolTip(track) - self.listView.addItem(item_name) + item_name = QtGui.QTreeWidgetItem(filename) + item_name.setText(0, filename) + item_name.setIcon(0, build_icon(DVDICON)) + item_name.setData(0, QtCore.Qt.UserRole, track) + item_name.setToolTip(0, track) + self.listView.addTopLevelItem(item_name) def getList(self, type=MediaType.Audio): media = Settings().value(self.settingsSection + u'/media files') diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index fe533a791..f7358e8f0 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -164,11 +164,12 @@ class PresentationMediaItem(MediaManagerItem): continue filename = os.path.split(unicode(file))[1] if not os.path.exists(file): - item_name = QtGui.QListWidgetItem(filename) - item_name.setIcon(build_icon(ERROR)) - item_name.setData(QtCore.Qt.UserRole, file) - item_name.setToolTip(file) - self.listView.addItem(item_name) + item_name = QtGui.QTreeWidgetItem(filename) + item_name.setText(0, filename) + item_name.setIcon(0, build_icon(ERROR)) + item_name.setData(0, QtCore.Qt.UserRole, file) + item_name.setToolTip(0, file) + self.listView.addTopLevelItem(item_name) else: if titles.count(filename) > 0: if not initialLoad: @@ -201,11 +202,12 @@ class PresentationMediaItem(MediaManagerItem): critical_error_message_box(UiStrings().UnsupportedFile, translate('PresentationPlugin.MediaItem', 'This type of presentation is not supported.')) continue - item_name = QtGui.QListWidgetItem(filename) - item_name.setData(QtCore.Qt.UserRole, file) - item_name.setIcon(icon) - item_name.setToolTip(file) - self.listView.addItem(item_name) + item_name = QtGui.QTreeWidgetItem(filename) + item_name.setText(0, filename) + item_name.setData(0, QtCore.Qt.UserRole, file) + item_name.setIcon(0, icon) + item_name.setToolTip(0, file) + self.listView.addTopLevelItem(item_name) Receiver.send_message(u'cursor_normal') if not initialLoad: self.main_window.finishedProgressBar() diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index cd23ad731..d974f79e8 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -236,9 +236,10 @@ class SongMediaItem(MediaManagerItem): author_list = [author.display_name for author in song.authors] song_title = unicode(song.title) song_detail = u'%s (%s)' % (song_title, create_separated_list(author_list)) - song_name = QtGui.QListWidgetItem(song_detail) - song_name.setData(QtCore.Qt.UserRole, song.id) - self.listView.addItem(song_name) + song_name = QtGui.QTreeWidgetItem(song_detail) + song_name.setText(0, song_detail) + song_name.setData(0, QtCore.Qt.UserRole, song.id) + self.listView.addTopLevelItem(song_name) # Auto-select the item if name has been set if song.id == self.autoSelectId: self.listView.setCurrentItem(song_name) @@ -253,9 +254,10 @@ class SongMediaItem(MediaManagerItem): if song.temporary: continue song_detail = u'%s (%s)' % (author.display_name, song.title) - song_name = QtGui.QListWidgetItem(song_detail) - song_name.setData(QtCore.Qt.UserRole, song.id) - self.listView.addItem(song_name) + song_name = QtGui.QTreeWidgetItem(song_detail) + song_name.setText(0, song_detail) + song_name.setData(0, QtCore.Qt.UserRole, song.id) + self.listView.addTopLevelItem(song_name) def displayResultsBook(self, searchresults, song_number=False): log.debug(u'display results Book') @@ -270,9 +272,10 @@ class SongMediaItem(MediaManagerItem): if song_number and not song_number in song.song_number: continue song_detail = u'%s - %s (%s)' % (book.name, song.song_number, song.title) - song_name = QtGui.QListWidgetItem(song_detail) - song_name.setData(QtCore.Qt.UserRole, song.id) - self.listView.addItem(song_name) + song_name = QtGui.QTreeWidgetItem(song_detail) + song_name.setText(0, song_detail) + song_name.setData(0, QtCore.Qt.UserRole, song.id) + self.listView.addTopLevelItem(song_name) def onClearTextButtonClick(self): """ @@ -351,7 +354,7 @@ class SongMediaItem(MediaManagerItem): log.debug(u'onEditClick') if check_item_selected(self.listView, UiStrings().SelectEdit): self.editItem = self.listView.currentItem() - item_id = self.editItem.data(QtCore.Qt.UserRole) + item_id = self.editItem.data(0, QtCore.Qt.UserRole) self.editSongForm.loadSong(item_id, False) self.editSongForm.exec_() self.autoSelectId = -1 From 32a77e8313e96e5a96d5c5d7ce5b29e34908f08b Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Fri, 25 Jan 2013 12:30:22 +0100 Subject: [PATCH 02/39] Two more fixes for QTreeWidget --- openlp/core/lib/mediamanageritem.py | 2 +- openlp/plugins/bibles/lib/mediaitem.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 3c4507280..0cc22c540 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -366,7 +366,7 @@ class MediaManagerItem(QtGui.QWidget): """ names = [] full_list = [] - for count in range(self.listView.count()): + for count in range(self.listView.topLevelItemCount()): names.append(self.listView.item(count).text()) full_list.append(self.listView.item(count).data(QtCore.Qt.UserRole)) duplicates_found = False diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 3d616181a..5fd80748b 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -634,7 +634,7 @@ class BibleMediaItem(MediaManagerItem): self.second_search_results = self.plugin.manager.get_verses(second_bible, versetext, book_ref_id) if not self.advancedLockButton.isChecked(): self.listView.clear() - if self.listView.count() != 0: + if self.listView.topLevelItemCount() != 0: self.__checkSecondBible(bible, second_bible) elif self.search_results: self.displayResults(bible, second_bible) From 5631a68c40cc16ba2456417671546dcbcc8eab51 Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Fri, 25 Jan 2013 12:38:48 +0100 Subject: [PATCH 03/39] Fixes for QTreeWidget --- openlp/core/lib/mediamanageritem.py | 12 ++++++------ openlp/plugins/bibles/lib/mediaitem.py | 4 ++-- openlp/plugins/custom/lib/mediaitem.py | 2 +- openlp/plugins/images/lib/mediaitem.py | 8 ++++---- openlp/plugins/media/lib/mediaitem.py | 6 +++--- openlp/plugins/presentations/lib/mediaitem.py | 2 +- openlp/plugins/songs/lib/mediaitem.py | 2 +- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 0cc22c540..cedf86f6a 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -367,8 +367,8 @@ class MediaManagerItem(QtGui.QWidget): names = [] full_list = [] for count in range(self.listView.topLevelItemCount()): - names.append(self.listView.item(count).text()) - full_list.append(self.listView.item(count).data(QtCore.Qt.UserRole)) + names.append(self.listView.topLevelItem(count).text(0)) + full_list.append(self.listView.topLevelItem(count).data(0, QtCore.Qt.UserRole)) duplicates_found = False files_added = False for file in files: @@ -404,8 +404,8 @@ class MediaManagerItem(QtGui.QWidget): count = 0 file_list = [] while count < self.listView.topLevelItemCount(): - bitem = self.listView.item(count) - filename = bitem.data(QtCore.Qt.UserRole) + bitem = self.listView.topLevelItem(count) + filename = bitem.data(0, QtCore.Qt.UserRole) file_list.append(filename) count += 1 return file_list @@ -498,8 +498,8 @@ class MediaManagerItem(QtGui.QWidget): self.live_controller.addServiceItem(serviceItem) def createItemFromId(self, item_id): - item = QtGui.QListWidgetItem() - item.setData(QtCore.Qt.UserRole, item_id) + item = QtGui.QTreeWidgetItem() + item.setData(0, QtCore.Qt.UserRole, item_id) return item def onAddClick(self): diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 5fd80748b..626b595d6 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -76,7 +76,7 @@ class BibleMediaItem(MediaManagerItem): """ Check if the first item is a second bible item or not. """ - bitem = self.listView.item(0) + bitem = self.listView.topLevelItem(0) if not bitem.flags() & QtCore.Qt.ItemIsSelectable: # The item is the "No Search Results" item. self.listView.clear() @@ -972,7 +972,7 @@ class BibleMediaItem(MediaManagerItem): return [] def createItemFromId(self, item_id): - item = QtGui.QListWidgetItem() + item = QtGui.QTreeWidgetItem() bible = self.quickVersionComboBox.currentText() search_results = self.plugin.manager.get_verses(bible, item_id, False) items = self.buildDisplayResults(bible, u'', search_results) diff --git a/openlp/plugins/custom/lib/mediaitem.py b/openlp/plugins/custom/lib/mediaitem.py index 2cf6bd92e..7006ad561 100644 --- a/openlp/plugins/custom/lib/mediaitem.py +++ b/openlp/plugins/custom/lib/mediaitem.py @@ -155,7 +155,7 @@ class CustomMediaItem(MediaManagerItem): """ if check_item_selected(self.listView, UiStrings().SelectEdit): item = self.listView.currentItem() - item_id = item.data(QtCore.Qt.UserRole) + item_id = item.data(0, QtCore.Qt.UserRole) self.edit_custom_form.loadCustom(item_id, False) self.edit_custom_form.exec_() self.autoSelectId = -1 diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 4924092e2..c3904b2b7 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -102,10 +102,10 @@ class ImageMediaItem(MediaManagerItem): Receiver.send_message(u'cursor_busy') self.main_window.displayProgressBar(len(row_list)) for row in row_list: - text = self.listView.item(row) + text = self.listView.topLevelItem(row) if text: - delete_file(os.path.join(self.servicePath, text.text())) - self.listView.takeItem(row) + delete_file(os.path.join(self.servicePath, text.text(0))) + self.listView.takeTopLevelItem(row) self.main_window.incrementProgressBar() SettingsManager.setValue(self.settingsSection + u'/images files', self.getFileList()) self.main_window.finishedProgressBar() @@ -209,7 +209,7 @@ class ImageMediaItem(MediaManagerItem): 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.item(item.row()) + bitem = self.listView.topLevelItem(item.row()) filename = bitem.data(QtCore.Qt.UserRole) if os.path.exists(filename): if self.plugin.liveController.display.directImage(filename, background): diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 773027847..e6ae464b3 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -147,7 +147,7 @@ class MediaMediaItem(MediaManagerItem): if check_item_selected(self.listView, translate('MediaPlugin.MediaItem', 'You must select a media file to replace the background with.')): item = self.listView.currentItem() - filename = item.data(QtCore.Qt.UserRole) + filename = item.data(0, QtCore.Qt.UserRole) if os.path.exists(filename): service_item = ServiceItem() service_item.title = u'webkit' @@ -171,7 +171,7 @@ class MediaMediaItem(MediaManagerItem): item = self.listView.currentItem() if item is None: return False - filename = item.data(QtCore.Qt.UserRole) + filename = item.data(0, QtCore.Qt.UserRole) if not os.path.exists(filename): if not remote: # File is no longer present @@ -253,7 +253,7 @@ class MediaMediaItem(MediaManagerItem): row_list = [item.row() for item in self.listView.selectedIndexes()] row_list.sort(reverse=True) for row in row_list: - self.listView.takeItem(row) + self.listView.takeTopLevelItem(row) Settings().setValue(self.settingsSection + u'/media files', self.getFileList()) def loadList(self, media): diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index f7358e8f0..79868e9ed 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -233,7 +233,7 @@ class PresentationMediaItem(MediaManagerItem): self.main_window.finishedProgressBar() Receiver.send_message(u'cursor_normal') for row in row_list: - self.listView.takeItem(row) + self.listView.takeTopLevelItem(row) Settings().setValue(self.settingsSection + u'/presentations files', self.getFileList()) def generateSlideData(self, service_item, item=None, xmlVersion=False, diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index d974f79e8..9566ea9d3 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -403,7 +403,7 @@ class SongMediaItem(MediaManagerItem): log.debug(u'onCloneClick') if check_item_selected(self.listView, UiStrings().SelectEdit): self.editItem = self.listView.currentItem() - item_id = self.editItem.data(QtCore.Qt.UserRole) + item_id = self.editItem.data(0, QtCore.Qt.UserRole) old_song = self.plugin.manager.get_object(Song, item_id) song_xml = self.openLyrics.song_to_xml(old_song) new_song = self.openLyrics.xml_to_song(song_xml) From 9b707d2351d54ea34e03a822e138a0afcbb54100 Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Fri, 25 Jan 2013 12:52:32 +0100 Subject: [PATCH 04/39] Moved images filenames storage from configfile to sqlite db --- openlp/core/lib/mediamanageritem.py | 1 - openlp/plugins/images/imageplugin.py | 4 + openlp/plugins/images/lib/db.py | 97 +++++++++++++++++++ openlp/plugins/images/lib/mediaitem.py | 46 ++++++--- openlp/plugins/media/lib/mediaitem.py | 1 + openlp/plugins/presentations/lib/mediaitem.py | 1 + 6 files changed, 137 insertions(+), 13 deletions(-) create mode 100644 openlp/plugins/images/lib/db.py diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index cedf86f6a..936a2ef0a 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -383,7 +383,6 @@ class MediaManagerItem(QtGui.QWidget): self.loadList(full_list) last_dir = os.path.split(unicode(files[0]))[0] Settings().setValue(self.settingsSection + u'/last directory', last_dir) - Settings().setValue(u'%s/%s files' % (self.settingsSection, self.settingsSection), self.getFileList()) if duplicates_found: critical_error_message_box(UiStrings().Duplicate, translate('OpenLP.MediaManagerItem', 'Duplicate files were found on import and were ignored.')) diff --git a/openlp/plugins/images/imageplugin.py b/openlp/plugins/images/imageplugin.py index 11e542e60..cbbdc39ec 100644 --- a/openlp/plugins/images/imageplugin.py +++ b/openlp/plugins/images/imageplugin.py @@ -32,11 +32,14 @@ from PyQt4 import QtCore, QtGui import logging from openlp.core.lib import Plugin, StringContent, build_icon, translate, Receiver, ImageSource, Settings +from openlp.core.lib.db import Manager from openlp.plugins.images.lib import ImageMediaItem, ImageTab +from openlp.plugins.images.lib.db import init_schema log = logging.getLogger(__name__) __default_settings__ = { + u'images/db type': u'sqlite', u'images/background color': u'#000000', u'images/images files': [] } @@ -47,6 +50,7 @@ class ImagePlugin(Plugin): def __init__(self): Plugin.__init__(self, u'images', __default_settings__, ImageMediaItem, ImageTab) + self.manager = Manager(u'images', init_schema) self.weight = -7 self.iconPath = u':/plugins/plugin_images.png' self.icon = build_icon(self.iconPath) diff --git a/openlp/plugins/images/lib/db.py b/openlp/plugins/images/lib/db.py new file mode 100644 index 000000000..a5716f648 --- /dev/null +++ b/openlp/plugins/images/lib/db.py @@ -0,0 +1,97 @@ +# -*- 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 diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index c3904b2b7..37036c668 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -37,6 +37,7 @@ 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 log = logging.getLogger(__name__) @@ -51,6 +52,7 @@ class ImageMediaItem(MediaManagerItem): MediaManagerItem.__init__(self, parent, plugin, icon) self.quickPreviewAllowed = True self.hasSearch = True + self.manager = plugin.manager QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'live_theme_changed'), self.liveThemeChanged) # Allow DnD from the desktop self.listView.activateDnD() @@ -78,7 +80,19 @@ class ImageMediaItem(MediaManagerItem): self.listView.setIconSize(QtCore.QSize(88, 50)) self.servicePath = os.path.join(AppLocation.get_section_data_path(self.settingsSection), u'thumbnails') check_directory_exists(self.servicePath) - self.loadList(Settings().value(self.settingsSection + u'/images files'), True) + # Import old images list + images_old = Settings().value(self.settingsSection + u'/images files') + if len(images_old) > 0: + for imageFile in images_old: + imagefilename = ImageFilenames() + imagefilename.group_id = 0 + imagefilename.filename = imageFile + success = self.manager.save_object(imagefilename) + Settings().setValue(self.settingsSection + u'/images files', []) + 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)) def addListViewToToolBar(self): MediaManagerItem.addListViewToToolBar(self) @@ -102,11 +116,12 @@ class ImageMediaItem(MediaManagerItem): Receiver.send_message(u'cursor_busy') self.main_window.displayProgressBar(len(row_list)) for row in row_list: - text = self.listView.topLevelItem(row) - if text: - delete_file(os.path.join(self.servicePath, text.text(0))) + row_item = self.listView.topLevelItem(row) + if row_item: + delete_file(os.path.join(self.servicePath, row_item.text(0))) self.listView.takeTopLevelItem(row) 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()) self.main_window.finishedProgressBar() Receiver.send_message(u'cursor_normal') @@ -120,19 +135,26 @@ class ImageMediaItem(MediaManagerItem): # characters. images.sort(cmp=locale_compare, key=lambda filename: os.path.split(unicode(filename))[1]) for imageFile in images: - filename = os.path.split(unicode(imageFile))[1] + 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) + filename = os.path.split(imageFile.filename)[1] 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') else: - if validate_thumb(unicode(imageFile), thumb): + if validate_thumb(imageFile.filename, thumb): icon = build_icon(thumb) else: - icon = create_thumb(unicode(imageFile), thumb) + 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) + item_name.setToolTip(0, imageFile.filename) item_name.setData(0, QtCore.Qt.UserRole, imageFile) self.listView.addTopLevelItem(item_name) if not initialLoad: @@ -160,7 +182,7 @@ class ImageMediaItem(MediaManagerItem): missing_items = [] missing_items_filenames = [] for bitem in items: - filename = bitem.data(0, QtCore.Qt.UserRole) + filename = bitem.data(0, QtCore.Qt.UserRole).filename if not os.path.exists(filename): missing_items.append(bitem) missing_items_filenames.append(filename) @@ -183,7 +205,7 @@ class ImageMediaItem(MediaManagerItem): return False # Continue with the existing images. for bitem in items: - filename = bitem.data(0, QtCore.Qt.UserRole) + filename = bitem.data(0, QtCore.Qt.UserRole).filename name = os.path.split(filename)[1] service_item.add_from_image(filename, name, background) return True @@ -210,7 +232,7 @@ class ImageMediaItem(MediaManagerItem): 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.data(QtCore.Qt.UserRole).filename if os.path.exists(filename): if self.plugin.liveController.display.directImage(filename, background): self.resetAction.setVisible(True) diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index e6ae464b3..4fa94e2a2 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -285,6 +285,7 @@ class MediaMediaItem(MediaManagerItem): item_name.setData(0, QtCore.Qt.UserRole, track) item_name.setToolTip(0, track) self.listView.addTopLevelItem(item_name) + Settings().setValue(self.settingsSection + u'/media files', self.getFileList()) def getList(self, type=MediaType.Audio): media = Settings().value(self.settingsSection + u'/media files') diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index 79868e9ed..cd2628cf1 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -208,6 +208,7 @@ class PresentationMediaItem(MediaManagerItem): item_name.setIcon(0, icon) item_name.setToolTip(0, file) self.listView.addTopLevelItem(item_name) + Settings().setValue(self.settingsSection + u'/presentations files', self.getFileList()) Receiver.send_message(u'cursor_normal') if not initialLoad: self.main_window.finishedProgressBar() From a770f90320559d8e1e83064652b8904576466035 Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Fri, 25 Jan 2013 15:06:49 +0100 Subject: [PATCH 05/39] 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 From 3c3b7bb2941bfc7abdf63f97b566c8fb55905ba1 Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Fri, 25 Jan 2013 15:09:22 +0100 Subject: [PATCH 06/39] Implemented adding images into groups --- openlp/plugins/images/forms/__init__.py | 1 + .../plugins/images/forms/choosegroupdialog.py | 61 +++++++++++++++++++ .../plugins/images/forms/choosegroupform.py | 46 ++++++++++++++ openlp/plugins/images/lib/mediaitem.py | 42 +++++++------ resources/forms/imageschoosegroupdialog.ui | 55 +++++++++++++++++ 5 files changed, 188 insertions(+), 17 deletions(-) create mode 100644 openlp/plugins/images/forms/choosegroupdialog.py create mode 100644 openlp/plugins/images/forms/choosegroupform.py create mode 100644 resources/forms/imageschoosegroupdialog.ui diff --git a/openlp/plugins/images/forms/__init__.py b/openlp/plugins/images/forms/__init__.py index 8c33c656e..0904f4c63 100644 --- a/openlp/plugins/images/forms/__init__.py +++ b/openlp/plugins/images/forms/__init__.py @@ -54,4 +54,5 @@ from the .ui files later if necessary. """ from addgroupform import AddGroupForm +from choosegroupform import ChooseGroupForm diff --git a/openlp/plugins/images/forms/choosegroupdialog.py b/openlp/plugins/images/forms/choosegroupdialog.py new file mode 100644 index 000000000..6c6f253c2 --- /dev/null +++ b/openlp/plugins/images/forms/choosegroupdialog.py @@ -0,0 +1,61 @@ +# -*- 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): + def setupUi(self, chooseGroupDialog): + chooseGroupDialog.setObjectName(u'chooseGroupDialog') + chooseGroupDialog.resize(440, 119) + self.chooseGroupLayout = QtGui.QFormLayout(chooseGroupDialog) + self.chooseGroupLayout.setFieldGrowthPolicy(QtGui.QFormLayout.ExpandingFieldsGrow) + self.chooseGroupLayout.setMargin(8) + self.chooseGroupLayout.setSpacing(8) + self.chooseGroupLayout.setObjectName(u'chooseGroupLayout') + self.groupQuestionLabel = QtGui.QLabel(chooseGroupDialog) + self.groupQuestionLabel.setWordWrap(True) + self.groupQuestionLabel.setObjectName(u'groupQuestionLabel') + self.chooseGroupLayout.setWidget(1, QtGui.QFormLayout.SpanningRole, self.groupQuestionLabel) + self.groupComboBox = QtGui.QComboBox(chooseGroupDialog) + self.groupComboBox.setObjectName(u'groupComboBox') + self.chooseGroupLayout.setWidget(2, QtGui.QFormLayout.FieldRole, self.groupComboBox) + self.groupButtonBox = create_button_box(chooseGroupDialog, u'buttonBox', [u'ok']) + self.chooseGroupLayout.setWidget(3, QtGui.QFormLayout.FieldRole, self.groupButtonBox) + + self.retranslateUi(chooseGroupDialog) + QtCore.QMetaObject.connectSlotsByName(chooseGroupDialog) + + def retranslateUi(self, chooseGroupDialog): + chooseGroupDialog.setWindowTitle(translate('ImagePlugin.ChooseGroupForm', 'Choose group')) + self.groupQuestionLabel.setText(translate('ImagePlugin.ChooseGroupForm', + 'To which group do you want these images to be added?')) + diff --git a/openlp/plugins/images/forms/choosegroupform.py b/openlp/plugins/images/forms/choosegroupform.py new file mode 100644 index 000000000..0226c42f8 --- /dev/null +++ b/openlp/plugins/images/forms/choosegroupform.py @@ -0,0 +1,46 @@ +# -*- 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.choosegroupdialog import Ui_ChooseGroupDialog + +class ChooseGroupForm(QtGui.QDialog, Ui_ChooseGroupDialog): + """ + Class documentation goes here. + """ + def __init__(self, parent=None): + """ + Constructor + """ + QtGui.QDialog.__init__(self, parent) + self.setupUi(self) + diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 871f88714..08e166887 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -37,7 +37,7 @@ 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.forms import AddGroupForm +from openlp.plugins.images.forms import AddGroupForm, ChooseGroupForm from openlp.plugins.images.lib.db import ImageFilenames, ImageGroups log = logging.getLogger(__name__) @@ -54,8 +54,9 @@ class ImageMediaItem(MediaManagerItem): self.quickPreviewAllowed = True self.hasSearch = True self.manager = plugin.manager + self.choosegroupform = ChooseGroupForm(self) self.addgroupform = AddGroupForm(self) - self.fillGroupsComboBox() + self.fillGroupsComboBox(self.addgroupform.parentGroupComboBox) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'live_theme_changed'), self.liveThemeChanged) # Allow DnD from the desktop self.listView.activateDnD() @@ -153,18 +154,19 @@ class ImageMediaItem(MediaManagerItem): groupList[image_group.id] = group self.addSubGroups(groupList, image_group.id) - def fillGroupsComboBox(self, parentGroupId=0, prefix=''): + def fillGroupsComboBox(self, comboBox, parentGroupId=0, prefix='', showTopLevelGroup=True): """ 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) + comboBox.clear() + if showTopLevelGroup is True: + comboBox.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+' ') + comboBox.addItem(prefix+image_group.group_name, image_group.id) + self.fillGroupsComboBox(comboBox, image_group.id, prefix+' ') def loadFullList(self, images, initialLoad=False): """ @@ -215,15 +217,21 @@ class ImageMediaItem(MediaManagerItem): """ 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) + # Ask which group the images should be saved in + self.fillGroupsComboBox(self.choosegroupform.groupComboBox, showTopLevelGroup=False) + if self.choosegroupform.exec_(): + group_id = self.choosegroupform.groupComboBox.itemData( + self.choosegroupform.groupComboBox.currentIndex(), QtCore.Qt.UserRole) + # Save the new images in the database + for filename in images: + if filename is None: + continue + imageFile = ImageFilenames() + imageFile.group_id = group_id + 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): @@ -316,7 +324,7 @@ class ImageMediaItem(MediaManagerItem): if self.checkGroupName(new_group): if self.manager.save_object(new_group): self.loadList([]) - self.fillGroupsComboBox() + self.fillGroupsComboBox(self.addgroupform.parentGroupComboBox) else: critical_error_message_box( message=translate('ImagePlugin.AddGroupForm', 'Could not add the new group.')) diff --git a/resources/forms/imageschoosegroupdialog.ui b/resources/forms/imageschoosegroupdialog.ui new file mode 100644 index 000000000..6f4eb5b41 --- /dev/null +++ b/resources/forms/imageschoosegroupdialog.ui @@ -0,0 +1,55 @@ + + + ChooseGroupDialog + + + + 0 + 0 + 440 + 119 + + + + Choose group + + + + QFormLayout::ExpandingFieldsGrow + + + 8 + + + 8 + + + 8 + + + + + To which group do you want these images to be added? + + + true + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Save + + + + + + + + + + From 140250576be27a79c8f7b20f858114dcbd9b5c9a Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Fri, 25 Jan 2013 15:22:52 +0100 Subject: [PATCH 07/39] Implemented recursive group deleting and fixed a bug in adding new images into a group --- openlp/plugins/images/lib/mediaitem.py | 41 ++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 08e166887..a0b5f3a14 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -115,6 +115,19 @@ class ImageMediaItem(MediaManagerItem): self.resetAction = self.toolbar.addToolbarAction(u'resetAction', icon=u':/system/system_close.png', visible=False, triggers=self.onResetClick) + def recursivelyDeleteGroup(self, image_group): + """ + Recursively deletes a group and all groups and images in it + """ + 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.recursivelyDeleteGroup(group) + self.manager.delete_object(ImageGroups, group.id) + def onDeleteClick(self): """ Remove an image item from the list @@ -129,11 +142,24 @@ class ImageMediaItem(MediaManagerItem): 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))) - row_item.parent().removeChild(row_item) + item_data = row_item.data(0, QtCore.Qt.UserRole) + if isinstance(item_data, ImageFilenames): + delete_file(os.path.join(self.servicePath, row_item.text(0))) + 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.recursivelyDeleteGroup(item_data) + self.manager.delete_object(ImageGroups, row_item.data(0, QtCore.Qt.UserRole).id) + if item_data.parent_id is 0: + self.listView.takeTopLevelItem(self.listView.indexOfTopLevelItem(row_item)) + else: + 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()) self.main_window.finishedProgressBar() Receiver.send_message(u'cursor_normal') self.listView.blockSignals(False) @@ -147,6 +173,7 @@ class ImageMediaItem(MediaManagerItem): for image_group in image_groups: group = QtGui.QTreeWidgetItem() group.setText(0, image_group.group_name) + group.setData(0, QtCore.Qt.UserRole, image_group) if parentGroupId is 0: self.listView.addTopLevelItem(group) else: @@ -224,8 +251,9 @@ class ImageMediaItem(MediaManagerItem): self.choosegroupform.groupComboBox.currentIndex(), QtCore.Qt.UserRole) # Save the new images in the database for filename in images: - if filename is None: + 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) @@ -323,7 +351,8 @@ class ImageMediaItem(MediaManagerItem): group_name=self.addgroupform.nameEdit.text()) if self.checkGroupName(new_group): if self.manager.save_object(new_group): - self.loadList([]) + self.loadFullList(self.manager.get_all_objects(ImageFilenames, + order_by_ref=ImageFilenames.filename)) self.fillGroupsComboBox(self.addgroupform.parentGroupComboBox) else: critical_error_message_box( From 53a23e9a045dc75318a8b51388848f6f94163e39 Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Fri, 25 Jan 2013 15:25:10 +0100 Subject: [PATCH 08/39] Image groups can now be used as service items with multiple images --- openlp/plugins/images/lib/mediaitem.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index a0b5f3a14..1e96cfff0 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -270,7 +270,11 @@ class ImageMediaItem(MediaManagerItem): items = self.listView.selectedItems() if not items: return False - service_item.title = unicode(self.plugin.nameStrings[u'plural']) + # 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.add_capability(ItemCapabilities.CanMaintain) service_item.add_capability(ItemCapabilities.CanPreview) service_item.add_capability(ItemCapabilities.CanLoop) @@ -279,10 +283,15 @@ class ImageMediaItem(MediaManagerItem): service_item.theme = -1 missing_items = [] missing_items_filenames = [] + # Expand groups to images + for bitem in items: + 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) + # Find missing files 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) @@ -306,9 +315,6 @@ 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) From 150fe6e15fa92330467a298bead38b977abeddbb Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Fri, 25 Jan 2013 16:30:18 +0100 Subject: [PATCH 09/39] Removed ListWidgetWithDnD and fixed some rebasing bugs --- openlp/core/lib/listwidgetwithdnd.py | 111 ------------------------- openlp/plugins/images/lib/mediaitem.py | 4 +- 2 files changed, 1 insertion(+), 114 deletions(-) delete mode 100644 openlp/core/lib/listwidgetwithdnd.py diff --git a/openlp/core/lib/listwidgetwithdnd.py b/openlp/core/lib/listwidgetwithdnd.py deleted file mode 100644 index ca6e10525..000000000 --- a/openlp/core/lib/listwidgetwithdnd.py +++ /dev/null @@ -1,111 +0,0 @@ -# -*- 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 QListWidget to handle drag and drop functionality -""" -import os - -from PyQt4 import QtCore, QtGui - -from openlp.core.lib import Receiver - -class ListWidgetWithDnD(QtGui.QListWidget): - """ - Provide a list widget to store objects and handle drag and drop events - """ - def __init__(self, parent=None, name=u''): - """ - Initialise the list widget - """ - QtGui.QListWidget.__init__(self, parent) - self.mimeDataText = name - assert(self.mimeDataText) - - def activateDnD(self): - """ - Activate DnD of widget - """ - self.setAcceptDrops(True) - self.setDragDropMode(QtGui.QAbstractItemView.DragDrop) - QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'%s_dnd' % self.mimeDataText), - self.parent().loadFile) - - 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 - """ - 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): - if event.mimeData().hasUrls(): - event.accept() - else: - event.ignore() - - def dragMoveEvent(self, event): - if event.mimeData().hasUrls(): - event.setDropAction(QtCore.Qt.CopyAction) - event.accept() - else: - event.ignore() - - def dropEvent(self, event): - """ - Receive drop event check if it is a file 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 in listing: - files.append(os.path.join(localFile, file)) - Receiver.send_message(u'%s_dnd' % self.mimeDataText, files) - else: - event.ignore() diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 1e96cfff0..6c7a25b8e 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -137,10 +137,8 @@ class ImageMediaItem(MediaManagerItem): if check_item_selected(self.listView, translate('ImagePlugin.MediaItem','You must select an image to delete.')): item_list = self.listView.selectedItems() Receiver.send_message(u'cursor_busy') - self.main_window.displayProgressBar(len(row_list)) - self.plugin.formParent.displayProgressBar(len(item_list)) + self.main_window.displayProgressBar(len(item_list)) for row_item in item_list: - row_item = self.listView.topLevelItem(row) if row_item: item_data = row_item.data(0, QtCore.Qt.UserRole) if isinstance(item_data, ImageFilenames): From 965785a8e747d4336bfe16151284d7b6709a04c4 Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Fri, 25 Jan 2013 20:20:27 +0100 Subject: [PATCH 10/39] Fixed the image search method --- openlp/plugins/images/lib/mediaitem.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 6c7a25b8e..d3f1d90a1 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -399,11 +399,9 @@ class ImageMediaItem(MediaManagerItem): 'the image file "%s" no longer exists.') % filename) 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 = [] - string = string.lower() for file in files: - filename = os.path.split(unicode(file))[1] - if filename.lower().find(string) > -1: - results.append([file, filename]) + filename = os.path.split(unicode(file.filename))[1] + results.append([file.filename, filename]) return results From d09b9edcc974e71ddc8be61fcf71c4b93e96610b Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Mon, 28 Jan 2013 20:52:26 +0100 Subject: [PATCH 11/39] Fixed two comments --- openlp/core/lib/treewidgetwithdnd.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/core/lib/treewidgetwithdnd.py b/openlp/core/lib/treewidgetwithdnd.py index fe53bb43a..75a64b515 100644 --- a/openlp/core/lib/treewidgetwithdnd.py +++ b/openlp/core/lib/treewidgetwithdnd.py @@ -37,11 +37,11 @@ from openlp.core.lib import Receiver class TreeWidgetWithDnD(QtGui.QTreeWidget): """ - Provide a list widget to store objects and handle drag and drop events + Provide a tree widget to store objects and handle drag and drop events """ def __init__(self, parent=None, name=u''): """ - Initialise the list widget + Initialise the tree widget """ QtGui.QTreeWidget.__init__(self, parent) self.mimeDataText = name From 34455239efd87657c6eaff058bd217944095884a Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Tue, 29 Jan 2013 13:02:20 +0100 Subject: [PATCH 12/39] Implemented drag&drop between image groups --- openlp/core/lib/mediamanageritem.py | 6 ++++++ openlp/core/lib/treewidgetwithdnd.py | 17 ++++++++++++++++- openlp/plugins/images/lib/mediaitem.py | 26 ++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 936a2ef0a..540011f68 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -356,6 +356,12 @@ class MediaManagerItem(QtGui.QWidget): if new_files: self.validateAndLoad(new_files) + def dndMoveInternal(self, target): + """ + Handle internal moving of media manager items + """ + pass + def validateAndLoad(self, files): """ Process a list for files either from the File Dialog or from Drag and diff --git a/openlp/core/lib/treewidgetwithdnd.py b/openlp/core/lib/treewidgetwithdnd.py index 75a64b515..dffb2a428 100644 --- a/openlp/core/lib/treewidgetwithdnd.py +++ b/openlp/core/lib/treewidgetwithdnd.py @@ -45,6 +45,7 @@ class TreeWidgetWithDnD(QtGui.QTreeWidget): """ QtGui.QTreeWidget.__init__(self, parent) self.mimeDataText = name + self.allowInternalDnD = False self.header().close() self.defaultIndentation = self.indentation() self.setIndentation(0) @@ -58,6 +59,11 @@ class TreeWidgetWithDnD(QtGui.QTreeWidget): self.setDragDropMode(QtGui.QAbstractItemView.DragDrop) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'%s_dnd' % self.mimeDataText), self.parent().loadFile) + QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'%s_dnd_internal' % self.mimeDataText), + self.parent().dndMoveInternal) + + def doInternalDnD(self, accept): + self.allowInternalDnD = accept def mouseMoveEvent(self, event): """ @@ -80,6 +86,8 @@ class TreeWidgetWithDnD(QtGui.QTreeWidget): def dragEnterEvent(self, event): if event.mimeData().hasUrls(): event.accept() + elif self.allowInternalDnD: + event.accept() else: event.ignore() @@ -87,12 +95,15 @@ class TreeWidgetWithDnD(QtGui.QTreeWidget): if event.mimeData().hasUrls(): event.setDropAction(QtCore.Qt.CopyAction) event.accept() + elif self.allowInternalDnD: + event.setDropAction(QtCore.Qt.CopyAction) + event.accept() else: event.ignore() def dropEvent(self, event): """ - Receive drop event check if it is a file and process it if it is. + 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 @@ -110,5 +121,9 @@ class TreeWidgetWithDnD(QtGui.QTreeWidget): for file in listing: files.append(os.path.join(localFile, file)) Receiver.send_message(u'%s_dnd' % self.mimeDataText, files) + elif self.allowInternalDnD: + event.setDropAction(QtCore.Qt.CopyAction) + event.accept() + Receiver.send_message(u'%s_dnd_internal' % self.mimeDataText, self.itemAt(event.pos())) else: event.ignore() diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index d3f1d90a1..61b3e003b 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -85,6 +85,7 @@ class ImageMediaItem(MediaManagerItem): self.listView.clear() self.listView.setIconSize(QtCore.QSize(88, 50)) self.listView.setIndentation(self.listView.defaultIndentation) + self.listView.doInternalDnD(True) self.servicePath = os.path.join(AppLocation.get_section_data_path(self.settingsSection), u'thumbnails') check_directory_exists(self.servicePath) # Import old images list @@ -259,6 +260,31 @@ class ImageMediaItem(MediaManagerItem): self.loadFullList(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename), initialLoad) + def dndMoveInternal(self, target): + """ + Handle drag-and-drop moving of images within the media manager + """ + items_to_move = self.listView.selectedItems() + # Determine group to move images to + target_group = target + if isinstance(target_group.data(0, QtCore.Qt.UserRole), ImageFilenames): + target_group = target.parent() + # Don't allow moving to the Imported group + if target_group.data(0, QtCore.Qt.UserRole) is None: + return + # Move images in the treeview + items_to_save = [] + for item in items_to_move: + if isinstance(item.data(0, QtCore.Qt.UserRole), ImageFilenames): + item.parent().removeChild(item) + target_group.addChild(item) + 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.sortChildren(0, QtCore.Qt.AscendingOrder) + # Update the group ID's of the images in the database + self.manager.save_objects(items_to_save) + def generateSlideData(self, service_item, item=None, xmlVersion=False, remote=False, context=ServiceItemContext.Service): background = QtGui.QColor(Settings().value(self.settingsSection + u'/background color')) From 42a75e545e70e86358c2c2ab2eb85f8922c10fb1 Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Tue, 29 Jan 2013 14:21:07 +0100 Subject: [PATCH 13/39] Implemented drag&drop from external directly into an image group --- openlp/core/lib/mediamanageritem.py | 15 ++++--- openlp/core/lib/treewidgetwithdnd.py | 2 +- openlp/plugins/custom/lib/mediaitem.py | 2 +- openlp/plugins/images/lib/mediaitem.py | 43 +++++++++++-------- openlp/plugins/media/lib/mediaitem.py | 2 +- openlp/plugins/presentations/lib/mediaitem.py | 4 +- 6 files changed, 39 insertions(+), 29 deletions(-) diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 540011f68..7708c1ce8 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -335,7 +335,7 @@ class MediaManagerItem(QtGui.QWidget): self.validateAndLoad(files) Receiver.send_message(u'cursor_normal') - def loadFile(self, files): + def loadFile(self, data): """ Turn file from Drag and Drop into an array so the Validate code can run it. @@ -344,7 +344,7 @@ class MediaManagerItem(QtGui.QWidget): """ new_files = [] error_shown = False - for file in files: + for file in data['files']: type = file.split(u'.')[-1] if type.lower() not in self.onNewFileMasks: if not error_shown: @@ -354,7 +354,7 @@ class MediaManagerItem(QtGui.QWidget): else: new_files.append(file) if new_files: - self.validateAndLoad(new_files) + self.validateAndLoad(new_files, data['target']) def dndMoveInternal(self, target): """ @@ -362,7 +362,7 @@ class MediaManagerItem(QtGui.QWidget): """ pass - def validateAndLoad(self, files): + def validateAndLoad(self, files, target_group=None): """ Process a list for files either from the File Dialog or from Drag and Drop @@ -385,8 +385,9 @@ class MediaManagerItem(QtGui.QWidget): files_added = True full_list.append(file) if full_list and files_added: - self.listView.clear() - self.loadList(full_list) + if target_group is None: + self.listView.clear() + self.loadList(full_list, target_group) last_dir = os.path.split(unicode(files[0]))[0] Settings().setValue(self.settingsSection + u'/last directory', last_dir) if duplicates_found: @@ -415,7 +416,7 @@ class MediaManagerItem(QtGui.QWidget): count += 1 return file_list - def loadList(self, list): + def loadList(self, list, target_group): raise NotImplementedError(u'MediaManagerItem.loadList needs to be defined by the plugin') def onNewClick(self): diff --git a/openlp/core/lib/treewidgetwithdnd.py b/openlp/core/lib/treewidgetwithdnd.py index dffb2a428..b1c6acd97 100644 --- a/openlp/core/lib/treewidgetwithdnd.py +++ b/openlp/core/lib/treewidgetwithdnd.py @@ -120,7 +120,7 @@ class TreeWidgetWithDnD(QtGui.QTreeWidget): listing = os.listdir(localFile) for file in listing: files.append(os.path.join(localFile, file)) - Receiver.send_message(u'%s_dnd' % self.mimeDataText, files) + Receiver.send_message(u'%s_dnd' % self.mimeDataText, {'files':files, 'target':self.itemAt(event.pos())}) elif self.allowInternalDnD: event.setDropAction(QtCore.Qt.CopyAction) event.accept() diff --git a/openlp/plugins/custom/lib/mediaitem.py b/openlp/plugins/custom/lib/mediaitem.py index 7006ad561..c7e8c6d50 100644 --- a/openlp/plugins/custom/lib/mediaitem.py +++ b/openlp/plugins/custom/lib/mediaitem.py @@ -99,7 +99,7 @@ class CustomMediaItem(MediaManagerItem): self.searchTextEdit.setCurrentSearchType(Settings().value( u'%s/last search type' % self.settingsSection)) self.config_updated() - 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. self.saveAutoSelectId() self.listView.clear() diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 61b3e003b..0f44deff5 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -239,26 +239,35 @@ class ImageMediaItem(MediaManagerItem): self.main_window.finishedProgressBar() Receiver.send_message(u'cursor_normal') - def loadList(self, images, initialLoad=False): + def loadList(self, images, target_group=None, initialLoad=False): """ Add new images to the database. This method is called when adding images using the Add button or DnD. """ - # Ask which group the images should be saved in - self.fillGroupsComboBox(self.choosegroupform.groupComboBox, showTopLevelGroup=False) - if self.choosegroupform.exec_(): - group_id = self.choosegroupform.groupComboBox.itemData( - self.choosegroupform.groupComboBox.currentIndex(), QtCore.Qt.UserRole) - # Save the new images in the database - for filename in images: - 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) - success = self.manager.save_object(imageFile) - self.loadFullList(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename), - initialLoad) + if target_group is None: + # Ask which group the images should be saved in + self.fillGroupsComboBox(self.choosegroupform.groupComboBox, showTopLevelGroup=False) + if self.choosegroupform.exec_(): + group_id = self.choosegroupform.groupComboBox.itemData( + self.choosegroupform.groupComboBox.currentIndex(), QtCore.Qt.UserRole) + parent_group = self.manager.get_object_filtered(ImageGroups, ImageGroups.id == group_id) + else: + parent_group = target_group.data(0, QtCore.Qt.UserRole) + if isinstance(parent_group, ImageFilenames): + 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 + # Save the new images in the database + for filename in images: + 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 = parent_group.id + 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 dndMoveInternal(self, target): """ diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 4fa94e2a2..3cce9ea03 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -256,7 +256,7 @@ class MediaMediaItem(MediaManagerItem): self.listView.takeTopLevelItem(row) 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 # characters. media.sort(cmp=locale_compare, key=lambda filename: os.path.split(unicode(filename))[1]) diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index cd2628cf1..2dab04c47 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -121,7 +121,7 @@ class PresentationMediaItem(MediaManagerItem): """ self.listView.setIconSize(QtCore.QSize(88, 50)) files = Settings().value(self.settingsSection + u'/presentations files') - self.loadList(files, True) + self.loadList(files, initialLoad=True) self.populateDisplayTypes() def populateDisplayTypes(self): @@ -142,7 +142,7 @@ class PresentationMediaItem(MediaManagerItem): else: self.presentationWidget.hide() - def loadList(self, files, initialLoad=False): + def loadList(self, files, target_group=None, initialLoad=False): """ Add presentations into the media manager This is called both on initial load of the plugin to populate with From dbcd1080eaddc8b38495c980edc9469ff475dca4 Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Wed, 30 Jan 2013 10:56:06 +0100 Subject: [PATCH 14/39] Fixed bug where deleted group would still show up in the 'add group' combobox --- openlp/plugins/images/forms/addgroupform.py | 5 ++++- openlp/plugins/images/lib/mediaitem.py | 15 +++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/openlp/plugins/images/forms/addgroupform.py b/openlp/plugins/images/forms/addgroupform.py index eb6595546..fc2f74eeb 100644 --- a/openlp/plugins/images/forms/addgroupform.py +++ b/openlp/plugins/images/forms/addgroupform.py @@ -44,10 +44,13 @@ class AddGroupForm(QtGui.QDialog, Ui_AddGroupDialog): QtGui.QDialog.__init__(self, parent) self.setupUi(self) - def exec_(self, clear=True): + def exec_(self, clear=True, showTopLevelGroup=False): if clear: self.nameEdit.clear() self.nameEdit.setFocus() + if showTopLevelGroup and not self.parentGroupComboBox.topLevelGroupAdded: + self.parentGroupComboBox.insertItem(0, translate('ImagePlugin.MediaItem', '-- Top-level group --'), 0) + self.parentGroupComboBox.topLevelGroupAdded = True return QtGui.QDialog.exec_(self) def accept(self): diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 0f44deff5..76bde05c0 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -56,6 +56,7 @@ class ImageMediaItem(MediaManagerItem): self.manager = plugin.manager self.choosegroupform = ChooseGroupForm(self) self.addgroupform = AddGroupForm(self) + self.fillGroupsComboBox(self.choosegroupform.groupComboBox) self.fillGroupsComboBox(self.addgroupform.parentGroupComboBox) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'live_theme_changed'), self.liveThemeChanged) # Allow DnD from the desktop @@ -151,13 +152,16 @@ class ImageMediaItem(MediaManagerItem): 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: + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | + QtGui.QMessageBox.No)) == QtGui.QMessageBox.Yes: self.recursivelyDeleteGroup(item_data) self.manager.delete_object(ImageGroups, row_item.data(0, QtCore.Qt.UserRole).id) if item_data.parent_id is 0: self.listView.takeTopLevelItem(self.listView.indexOfTopLevelItem(row_item)) else: row_item.parent().removeChild(row_item) + self.fillGroupsComboBox(self.choosegroupform.groupComboBox) + self.fillGroupsComboBox(self.addgroupform.parentGroupComboBox) self.main_window.incrementProgressBar() self.main_window.finishedProgressBar() Receiver.send_message(u'cursor_normal') @@ -180,14 +184,13 @@ class ImageMediaItem(MediaManagerItem): groupList[image_group.id] = group self.addSubGroups(groupList, image_group.id) - def fillGroupsComboBox(self, comboBox, parentGroupId=0, prefix='', showTopLevelGroup=True): + def fillGroupsComboBox(self, comboBox, parentGroupId=0, prefix=''): """ Recursively add groups to the combobox in the 'Add group' dialog """ if parentGroupId is 0: comboBox.clear() - if showTopLevelGroup is True: - comboBox.addItem(translate('ImagePlugin.MediaItem', '-- Top-level group --'), 0) + comboBox.topLevelGroupAdded = False 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: @@ -245,7 +248,6 @@ class ImageMediaItem(MediaManagerItem): """ if target_group is None: # Ask which group the images should be saved in - self.fillGroupsComboBox(self.choosegroupform.groupComboBox, showTopLevelGroup=False) if self.choosegroupform.exec_(): group_id = self.choosegroupform.groupComboBox.itemData( self.choosegroupform.groupComboBox.currentIndex(), QtCore.Qt.UserRole) @@ -384,7 +386,7 @@ class ImageMediaItem(MediaManagerItem): """ Called to add a new group """ - if self.addgroupform.exec_(): + if self.addgroupform.exec_(showTopLevelGroup=True): new_group = ImageGroups.populate(parent_id=self.addgroupform.parentGroupComboBox.itemData( self.addgroupform.parentGroupComboBox.currentIndex(), QtCore.Qt.UserRole), group_name=self.addgroupform.nameEdit.text()) @@ -392,6 +394,7 @@ class ImageMediaItem(MediaManagerItem): if self.manager.save_object(new_group): self.loadFullList(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename)) + self.fillGroupsComboBox(self.choosegroupform.groupComboBox) self.fillGroupsComboBox(self.addgroupform.parentGroupComboBox) else: critical_error_message_box( From 260a6815c2b65f0771cd5a963b9d5a1b2904d4fd Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Wed, 30 Jan 2013 11:57:06 +0100 Subject: [PATCH 15/39] Lots of code style fixes --- openlp/core/lib/mediamanageritem.py | 15 ++--- openlp/core/lib/treewidgetwithdnd.py | 9 +-- openlp/plugins/images/forms/__init__.py | 1 - openlp/plugins/images/forms/addgroupdialog.py | 57 ++++++++++--------- openlp/plugins/images/forms/addgroupform.py | 17 +++--- .../plugins/images/forms/choosegroupdialog.py | 46 +++++++-------- .../plugins/images/forms/choosegroupform.py | 2 +- openlp/plugins/images/imageplugin.py | 11 ++-- openlp/plugins/images/lib/db.py | 2 + openlp/plugins/images/lib/imagetab.py | 2 +- openlp/plugins/images/lib/mediaitem.py | 49 ++++++++-------- 11 files changed, 109 insertions(+), 102 deletions(-) diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 7708c1ce8..21e5d3778 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -43,6 +43,7 @@ from openlp.core.lib.ui import create_widget_action, critical_error_message_box log = logging.getLogger(__name__) + class MediaManagerItem(QtGui.QWidget): """ MediaManagerItem is a helper widget for plugins. @@ -344,19 +345,19 @@ class MediaManagerItem(QtGui.QWidget): """ new_files = [] error_shown = False - for file in data['files']: - type = file.split(u'.')[-1] + for file_name in data['files']: + type = file_name.split(u'.')[-1] if type.lower() not in self.onNewFileMasks: if not error_shown: critical_error_message_box(translate('OpenLP.MediaManagerItem', 'Invalid File Type'), - translate('OpenLP.MediaManagerItem', 'Invalid File %s.\nSuffix not supported') % file) + translate('OpenLP.MediaManagerItem', 'Invalid File %s.\nSuffix not supported') % file_name) error_shown = True else: - new_files.append(file) + new_files.append(file_name) if new_files: self.validateAndLoad(new_files, data['target']) - def dndMoveInternal(self, target): + def dnd_move_internal(self, target): """ Handle internal moving of media manager items """ @@ -377,8 +378,8 @@ class MediaManagerItem(QtGui.QWidget): full_list.append(self.listView.topLevelItem(count).data(0, QtCore.Qt.UserRole)) duplicates_found = False files_added = False - for file in files: - filename = os.path.split(unicode(file))[1] + for file_path in files: + filename = os.path.split(unicode(file_path))[1] if filename in names: duplicates_found = True else: diff --git a/openlp/core/lib/treewidgetwithdnd.py b/openlp/core/lib/treewidgetwithdnd.py index b1c6acd97..df50bd54a 100644 --- a/openlp/core/lib/treewidgetwithdnd.py +++ b/openlp/core/lib/treewidgetwithdnd.py @@ -35,6 +35,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import Receiver + class TreeWidgetWithDnD(QtGui.QTreeWidget): """ Provide a tree widget to store objects and handle drag and drop events @@ -60,7 +61,7 @@ class TreeWidgetWithDnD(QtGui.QTreeWidget): QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'%s_dnd' % self.mimeDataText), self.parent().loadFile) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'%s_dnd_internal' % self.mimeDataText), - self.parent().dndMoveInternal) + self.parent().dnd_move_internal) def doInternalDnD(self, accept): self.allowInternalDnD = accept @@ -118,9 +119,9 @@ class TreeWidgetWithDnD(QtGui.QTreeWidget): files.append(localFile) elif os.path.isdir(localFile): listing = os.listdir(localFile) - for file in listing: - files.append(os.path.join(localFile, file)) - Receiver.send_message(u'%s_dnd' % self.mimeDataText, {'files':files, 'target':self.itemAt(event.pos())}) + for file_name in listing: + files.append(os.path.join(localFile, file_name)) + Receiver.send_message(u'%s_dnd' % self.mimeDataText, {'files': files, 'target': self.itemAt(event.pos())}) elif self.allowInternalDnD: event.setDropAction(QtCore.Qt.CopyAction) event.accept() diff --git a/openlp/plugins/images/forms/__init__.py b/openlp/plugins/images/forms/__init__.py index 0904f4c63..d308a1471 100644 --- a/openlp/plugins/images/forms/__init__.py +++ b/openlp/plugins/images/forms/__init__.py @@ -55,4 +55,3 @@ from the .ui files later if necessary. from addgroupform import AddGroupForm from choosegroupform import ChooseGroupForm - diff --git a/openlp/plugins/images/forms/addgroupdialog.py b/openlp/plugins/images/forms/addgroupdialog.py index 1ba019911..41a552f76 100644 --- a/openlp/plugins/images/forms/addgroupdialog.py +++ b/openlp/plugins/images/forms/addgroupdialog.py @@ -32,32 +32,33 @@ 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:')) +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:')) diff --git a/openlp/plugins/images/forms/addgroupform.py b/openlp/plugins/images/forms/addgroupform.py index fc2f74eeb..535434e5b 100644 --- a/openlp/plugins/images/forms/addgroupform.py +++ b/openlp/plugins/images/forms/addgroupform.py @@ -33,6 +33,7 @@ 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. @@ -44,20 +45,20 @@ class AddGroupForm(QtGui.QDialog, Ui_AddGroupDialog): QtGui.QDialog.__init__(self, parent) self.setupUi(self) - def exec_(self, clear=True, showTopLevelGroup=False): + def exec_(self, clear=True, show_top_level_group=False): if clear: - self.nameEdit.clear() - self.nameEdit.setFocus() - if showTopLevelGroup and not self.parentGroupComboBox.topLevelGroupAdded: - self.parentGroupComboBox.insertItem(0, translate('ImagePlugin.MediaItem', '-- Top-level group --'), 0) - self.parentGroupComboBox.topLevelGroupAdded = True + 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 return QtGui.QDialog.exec_(self) def accept(self): - if not self.nameEdit.text(): + if not self.name_edit.text(): critical_error_message_box(message=translate('ImagePlugin.AddGroupForm', 'You need to type in a group name.')) - self.nameEdit.setFocus() + self.name_edit.setFocus() return False else: return QtGui.QDialog.accept(self) diff --git a/openlp/plugins/images/forms/choosegroupdialog.py b/openlp/plugins/images/forms/choosegroupdialog.py index 6c6f253c2..0bbec6cc8 100644 --- a/openlp/plugins/images/forms/choosegroupdialog.py +++ b/openlp/plugins/images/forms/choosegroupdialog.py @@ -32,30 +32,30 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import translate from openlp.core.lib.ui import create_button_box + class Ui_ChooseGroupDialog(object): - def setupUi(self, chooseGroupDialog): - chooseGroupDialog.setObjectName(u'chooseGroupDialog') - chooseGroupDialog.resize(440, 119) - self.chooseGroupLayout = QtGui.QFormLayout(chooseGroupDialog) - self.chooseGroupLayout.setFieldGrowthPolicy(QtGui.QFormLayout.ExpandingFieldsGrow) - self.chooseGroupLayout.setMargin(8) - self.chooseGroupLayout.setSpacing(8) - self.chooseGroupLayout.setObjectName(u'chooseGroupLayout') - self.groupQuestionLabel = QtGui.QLabel(chooseGroupDialog) - self.groupQuestionLabel.setWordWrap(True) - self.groupQuestionLabel.setObjectName(u'groupQuestionLabel') - self.chooseGroupLayout.setWidget(1, QtGui.QFormLayout.SpanningRole, self.groupQuestionLabel) - self.groupComboBox = QtGui.QComboBox(chooseGroupDialog) - self.groupComboBox.setObjectName(u'groupComboBox') - self.chooseGroupLayout.setWidget(2, QtGui.QFormLayout.FieldRole, self.groupComboBox) - self.groupButtonBox = create_button_box(chooseGroupDialog, u'buttonBox', [u'ok']) - self.chooseGroupLayout.setWidget(3, QtGui.QFormLayout.FieldRole, self.groupButtonBox) + def setupUi(self, choose_group_dialog): + choose_group_dialog.setObjectName(u'choose_group_dialog') + choose_group_dialog.resize(440, 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.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.group_combobox = QtGui.QComboBox(choose_group_dialog) + self.group_combobox.setObjectName(u'group_combobox') + self.choose_group_layout.setWidget(2, QtGui.QFormLayout.FieldRole, self.group_combobox) + self.group_button_box = create_button_box(choose_group_dialog, u'buttonBox', [u'ok']) + self.choose_group_layout.setWidget(3, QtGui.QFormLayout.FieldRole, self.group_button_box) - self.retranslateUi(chooseGroupDialog) - QtCore.QMetaObject.connectSlotsByName(chooseGroupDialog) + self.retranslateUi(choose_group_dialog) + QtCore.QMetaObject.connectSlotsByName(choose_group_dialog) - def retranslateUi(self, chooseGroupDialog): - chooseGroupDialog.setWindowTitle(translate('ImagePlugin.ChooseGroupForm', 'Choose group')) - self.groupQuestionLabel.setText(translate('ImagePlugin.ChooseGroupForm', + def retranslateUi(self, choose_group_dialog): + choose_group_dialog.setWindowTitle(translate('ImagePlugin.ChooseGroupForm', 'Choose group')) + self.group_question_label.setText(translate('ImagePlugin.ChooseGroupForm', 'To which group do you want these images to be added?')) - diff --git a/openlp/plugins/images/forms/choosegroupform.py b/openlp/plugins/images/forms/choosegroupform.py index 0226c42f8..eed92e360 100644 --- a/openlp/plugins/images/forms/choosegroupform.py +++ b/openlp/plugins/images/forms/choosegroupform.py @@ -33,6 +33,7 @@ from openlp.core.lib import translate from openlp.core.lib.ui import critical_error_message_box from openlp.plugins.images.forms.choosegroupdialog import Ui_ChooseGroupDialog + class ChooseGroupForm(QtGui.QDialog, Ui_ChooseGroupDialog): """ Class documentation goes here. @@ -43,4 +44,3 @@ class ChooseGroupForm(QtGui.QDialog, Ui_ChooseGroupDialog): """ QtGui.QDialog.__init__(self, parent) self.setupUi(self) - diff --git a/openlp/plugins/images/imageplugin.py b/openlp/plugins/images/imageplugin.py index cbbdc39ec..cfed5e102 100644 --- a/openlp/plugins/images/imageplugin.py +++ b/openlp/plugins/images/imageplugin.py @@ -39,10 +39,10 @@ from openlp.plugins.images.lib.db import init_schema log = logging.getLogger(__name__) __default_settings__ = { - u'images/db type': u'sqlite', - u'images/background color': u'#000000', - u'images/images files': [] - } + u'images/db type': u'sqlite', + u'images/background color': u'#000000', + u'images/images files': [] +} class ImagePlugin(Plugin): @@ -80,8 +80,7 @@ class ImagePlugin(Plugin): u'plural': translate('ImagePlugin', 'Images', 'name plural') } ## 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 tooltips = { u'load': translate('ImagePlugin', 'Load a new image.'), diff --git a/openlp/plugins/images/lib/db.py b/openlp/plugins/images/lib/db.py index a5716f648..bc4a94f15 100644 --- a/openlp/plugins/images/lib/db.py +++ b/openlp/plugins/images/lib/db.py @@ -42,12 +42,14 @@ class ImageGroups(BaseModel): """ pass + class ImageFilenames(BaseModel): """ ImageFilenames model """ pass + def init_schema(url): """ Setup the images database connection and initialise the database schema. diff --git a/openlp/plugins/images/lib/imagetab.py b/openlp/plugins/images/lib/imagetab.py index 8e3f1c657..056cbfa6d 100644 --- a/openlp/plugins/images/lib/imagetab.py +++ b/openlp/plugins/images/lib/imagetab.py @@ -31,6 +31,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import SettingsTab, translate, Receiver, Settings, UiStrings + class ImageTab(SettingsTab): """ ImageTab is the images settings tab in the settings dialog. @@ -92,4 +93,3 @@ class ImageTab(SettingsTab): settings.endGroup() if self.initial_color != self.bg_color: Receiver.send_message(u'image_updated') - diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 76bde05c0..af3c6a301 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -42,6 +42,7 @@ from openlp.plugins.images.lib.db import ImageFilenames, ImageGroups log = logging.getLogger(__name__) + class ImageMediaItem(MediaManagerItem): """ This is the custom media manager item for images. @@ -56,8 +57,8 @@ class ImageMediaItem(MediaManagerItem): self.manager = plugin.manager self.choosegroupform = ChooseGroupForm(self) self.addgroupform = AddGroupForm(self) - self.fillGroupsComboBox(self.choosegroupform.groupComboBox) - self.fillGroupsComboBox(self.addgroupform.parentGroupComboBox) + self.fill_groups_combo_box(self.choosegroupform.group_combobox) + self.fill_groups_combo_box(self.addgroupform.parent_group_combobox) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'live_theme_changed'), self.liveThemeChanged) # Allow DnD from the desktop self.listView.activateDnD() @@ -90,7 +91,7 @@ class ImageMediaItem(MediaManagerItem): self.servicePath = os.path.join(AppLocation.get_section_data_path(self.settingsSection), u'thumbnails') check_directory_exists(self.servicePath) # Import old images list - images_old = Settings().value(self.settingsSection + u'/images files') + images_old = Settings().value(self.settingsSection + u'/images files') if len(images_old) > 0: for imageFile in images_old: imagefilename = ImageFilenames() @@ -136,7 +137,8 @@ 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.')): + if check_item_selected(self.listView, translate('ImagePlugin.MediaItem', + 'You must select an image to delete.')): item_list = self.listView.selectedItems() Receiver.send_message(u'cursor_busy') self.main_window.displayProgressBar(len(item_list)) @@ -160,8 +162,8 @@ class ImageMediaItem(MediaManagerItem): self.listView.takeTopLevelItem(self.listView.indexOfTopLevelItem(row_item)) else: row_item.parent().removeChild(row_item) - self.fillGroupsComboBox(self.choosegroupform.groupComboBox) - self.fillGroupsComboBox(self.addgroupform.parentGroupComboBox) + self.fill_groups_combo_box(self.choosegroupform.group_combobox) + self.fill_groups_combo_box(self.addgroupform.parent_group_combobox) self.main_window.incrementProgressBar() self.main_window.finishedProgressBar() Receiver.send_message(u'cursor_normal') @@ -184,18 +186,18 @@ class ImageMediaItem(MediaManagerItem): groupList[image_group.id] = group self.addSubGroups(groupList, image_group.id) - def fillGroupsComboBox(self, comboBox, parentGroupId=0, prefix=''): + def fill_groups_combo_box(self, comboBox, parentGroupId=0, prefix=''): """ Recursively add groups to the combobox in the 'Add group' dialog """ if parentGroupId is 0: comboBox.clear() - comboBox.topLevelGroupAdded = False + comboBox.top_level_group_added = False 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: - comboBox.addItem(prefix+image_group.group_name, image_group.id) - self.fillGroupsComboBox(comboBox, image_group.id, prefix+' ') + comboBox.addItem(prefix + image_group.group_name, image_group.id) + self.fill_groups_combo_box(comboBox, image_group.id, prefix + ' ') def loadFullList(self, images, initialLoad=False): """ @@ -249,8 +251,8 @@ class ImageMediaItem(MediaManagerItem): if target_group is None: # Ask which group the images should be saved in if self.choosegroupform.exec_(): - group_id = self.choosegroupform.groupComboBox.itemData( - self.choosegroupform.groupComboBox.currentIndex(), QtCore.Qt.UserRole) + group_id = self.choosegroupform.group_combobox.itemData( + self.choosegroupform.group_combobox.currentIndex(), QtCore.Qt.UserRole) parent_group = self.manager.get_object_filtered(ImageGroups, ImageGroups.id == group_id) else: parent_group = target_group.data(0, QtCore.Qt.UserRole) @@ -271,7 +273,7 @@ class ImageMediaItem(MediaManagerItem): self.loadFullList(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename), initialLoad) - def dndMoveInternal(self, target): + def dnd_move_internal(self, target): """ Handle drag-and-drop moving of images within the media manager """ @@ -386,16 +388,16 @@ class ImageMediaItem(MediaManagerItem): """ Called to add a new group """ - if self.addgroupform.exec_(showTopLevelGroup=True): - 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.addgroupform.exec_(show_top_level_group=True): + new_group = ImageGroups.populate(parent_id=self.addgroupform.parent_group_combobox.itemData( + self.addgroupform.parent_group_combobox.currentIndex(), QtCore.Qt.UserRole), + group_name=self.addgroupform.name_edit.text()) if self.checkGroupName(new_group): if self.manager.save_object(new_group): self.loadFullList(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename)) - self.fillGroupsComboBox(self.choosegroupform.groupComboBox) - self.fillGroupsComboBox(self.addgroupform.parentGroupComboBox) + self.fill_groups_combo_box(self.choosegroupform.group_combobox) + self.fill_groups_combo_box(self.addgroupform.parent_group_combobox) else: critical_error_message_box( message=translate('ImagePlugin.AddGroupForm', 'Could not add the new group.')) @@ -437,9 +439,10 @@ class ImageMediaItem(MediaManagerItem): 'the image file "%s" no longer exists.') % filename) def search(self, string, showError): - files = self.manager.get_all_objects(ImageFilenames, filter_clause=ImageFilenames.filename.contains(string), order_by_ref=ImageFilenames.filename) + files = self.manager.get_all_objects(ImageFilenames, filter_clause=ImageFilenames.filename.contains(string), + order_by_ref=ImageFilenames.filename) results = [] - for file in files: - filename = os.path.split(unicode(file.filename))[1] - results.append([file.filename, filename]) + for file_object in files: + filename = os.path.split(unicode(file_object.filename))[1] + results.append([file_object.filename, filename]) return results From d95aef4b9714d5daa06c50c3cfbc79c9a64395cd Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Wed, 30 Jan 2013 20:33:22 +0100 Subject: [PATCH 16/39] - Scroll images list automatically when dragging - Expand group where new images were added - Select dropped images - More code style fixes - Reverted earlier change to validateAndLoad --- openlp/core/lib/mediamanageritem.py | 1 + openlp/core/lib/treewidgetwithdnd.py | 12 ++- openlp/plugins/images/lib/mediaitem.py | 89 +++++++++++++------ openlp/plugins/media/lib/mediaitem.py | 1 - openlp/plugins/presentations/lib/mediaitem.py | 1 - 5 files changed, 67 insertions(+), 37 deletions(-) diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 21e5d3778..17594d1b7 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -391,6 +391,7 @@ class MediaManagerItem(QtGui.QWidget): self.loadList(full_list, target_group) last_dir = os.path.split(unicode(files[0]))[0] Settings().setValue(self.settingsSection + u'/last directory', last_dir) + Settings().setValue(u'%s/%s files' % (self.settingsSection, self.settingsSection), self.getFileList()) if duplicates_found: critical_error_message_box(UiStrings().Duplicate, translate('OpenLP.MediaManagerItem', 'Duplicate files were found on import and were ignored.')) diff --git a/openlp/core/lib/treewidgetwithdnd.py b/openlp/core/lib/treewidgetwithdnd.py index df50bd54a..0eca98c01 100644 --- a/openlp/core/lib/treewidgetwithdnd.py +++ b/openlp/core/lib/treewidgetwithdnd.py @@ -46,7 +46,7 @@ class TreeWidgetWithDnD(QtGui.QTreeWidget): """ QtGui.QTreeWidget.__init__(self, parent) self.mimeDataText = name - self.allowInternalDnD = False + self.allow_internal_dnd = False self.header().close() self.defaultIndentation = self.indentation() self.setIndentation(0) @@ -63,9 +63,6 @@ class TreeWidgetWithDnD(QtGui.QTreeWidget): QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'%s_dnd_internal' % self.mimeDataText), self.parent().dnd_move_internal) - def doInternalDnD(self, accept): - self.allowInternalDnD = accept - def mouseMoveEvent(self, event): """ Drag and drop event does not care what data is selected @@ -87,16 +84,17 @@ class TreeWidgetWithDnD(QtGui.QTreeWidget): def dragEnterEvent(self, event): if event.mimeData().hasUrls(): event.accept() - elif self.allowInternalDnD: + elif self.allow_internal_dnd: event.accept() else: event.ignore() def dragMoveEvent(self, event): + QtGui.QTreeWidget.dragMoveEvent(self, event) if event.mimeData().hasUrls(): event.setDropAction(QtCore.Qt.CopyAction) event.accept() - elif self.allowInternalDnD: + elif self.allow_internal_dnd: event.setDropAction(QtCore.Qt.CopyAction) event.accept() else: @@ -122,7 +120,7 @@ class TreeWidgetWithDnD(QtGui.QTreeWidget): for file_name in listing: files.append(os.path.join(localFile, file_name)) Receiver.send_message(u'%s_dnd' % self.mimeDataText, {'files': files, 'target': self.itemAt(event.pos())}) - elif self.allowInternalDnD: + elif self.allow_internal_dnd: event.setDropAction(QtCore.Qt.CopyAction) event.accept() Receiver.send_message(u'%s_dnd_internal' % self.mimeDataText, self.itemAt(event.pos())) diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index af3c6a301..ea144d576 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -57,8 +57,8 @@ class ImageMediaItem(MediaManagerItem): self.manager = plugin.manager self.choosegroupform = ChooseGroupForm(self) self.addgroupform = AddGroupForm(self) - self.fill_groups_combo_box(self.choosegroupform.group_combobox) - self.fill_groups_combo_box(self.addgroupform.parent_group_combobox) + self.fill_groups_combobox(self.choosegroupform.group_combobox) + self.fill_groups_combobox(self.addgroupform.parent_group_combobox) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'live_theme_changed'), self.liveThemeChanged) # Allow DnD from the desktop self.listView.activateDnD() @@ -87,7 +87,7 @@ class ImageMediaItem(MediaManagerItem): self.listView.clear() self.listView.setIconSize(QtCore.QSize(88, 50)) self.listView.setIndentation(self.listView.defaultIndentation) - self.listView.doInternalDnD(True) + self.listView.allow_internal_dnd = True self.servicePath = os.path.join(AppLocation.get_section_data_path(self.settingsSection), u'thumbnails') check_directory_exists(self.servicePath) # Import old images list @@ -102,7 +102,8 @@ class ImageMediaItem(MediaManagerItem): Settings().remove(self.settingsSection + u'/images files') Settings().remove(self.settingsSection + u'/images count') # Load images from the database - self.loadFullList(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename), True) + self.loadFullList(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename), + initial_load=True) def addListViewToToolBar(self): MediaManagerItem.addListViewToToolBar(self) @@ -162,54 +163,70 @@ class ImageMediaItem(MediaManagerItem): self.listView.takeTopLevelItem(self.listView.indexOfTopLevelItem(row_item)) else: row_item.parent().removeChild(row_item) - self.fill_groups_combo_box(self.choosegroupform.group_combobox) - self.fill_groups_combo_box(self.addgroupform.parent_group_combobox) + self.fill_groups_combobox(self.choosegroupform.group_combobox) + self.fill_groups_combobox(self.addgroupform.parent_group_combobox) self.main_window.incrementProgressBar() self.main_window.finishedProgressBar() Receiver.send_message(u'cursor_normal') self.listView.blockSignals(False) - def addSubGroups(self, groupList, parentGroupId): + def add_sub_groups(self, group_list, parent_group_id): """ Recursively add subgroups to the given parent group """ - image_groups = self.manager.get_all_objects(ImageGroups, ImageGroups.parent_id == parentGroupId) + 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: group = QtGui.QTreeWidgetItem() group.setText(0, image_group.group_name) group.setData(0, QtCore.Qt.UserRole, image_group) - if parentGroupId is 0: + if parent_group_id is 0: self.listView.addTopLevelItem(group) else: - groupList[parentGroupId].addChild(group) - groupList[image_group.id] = group - self.addSubGroups(groupList, image_group.id) + 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_combo_box(self, comboBox, parentGroupId=0, prefix=''): + def fill_groups_combobox(self, combobox, parent_group_id=0, prefix=''): """ Recursively add groups to the combobox in the 'Add group' dialog """ - if parentGroupId is 0: - comboBox.clear() - comboBox.top_level_group_added = False - image_groups = self.manager.get_all_objects(ImageGroups, ImageGroups.parent_id == parentGroupId) + if parent_group_id is 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_combo_box(comboBox, image_group.id, prefix + ' ') + combobox.addItem(prefix + image_group.group_name, image_group.id) + self.fill_groups_combobox(combobox, image_group.id, prefix + ' ') - def loadFullList(self, images, initialLoad=False): + def expand_group(self, group_id, root_item=None): + 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. """ - if not initialLoad: + if not initial_load: 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) + 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 # characters. images.sort(cmp=locale_compare, key=lambda image_object: os.path.split(unicode(image_object.filename))[1]) @@ -238,13 +255,27 @@ class ImageMediaItem(MediaManagerItem): self.listView.insertTopLevelItem(0, imported_group) group_items[0] = imported_group group_items[imageFile.group_id].addChild(item_name) - if not initialLoad: + if not initial_load: self.main_window.incrementProgressBar() - if not initialLoad: + if not initial_load: self.main_window.finishedProgressBar() Receiver.send_message(u'cursor_normal') - def loadList(self, images, target_group=None, initialLoad=False): + 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`` + The files to be loaded. + """ + if target_group is None: + self.listView.clear() + 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. """ @@ -271,7 +302,7 @@ class ImageMediaItem(MediaManagerItem): imageFile.filename = unicode(filename) success = self.manager.save_object(imageFile) self.loadFullList(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename), - initialLoad) + initial_load=initial_load, open_group=parent_group) def dnd_move_internal(self, target): """ @@ -291,9 +322,11 @@ class ImageMediaItem(MediaManagerItem): if isinstance(item.data(0, QtCore.Qt.UserRole), ImageFilenames): item.parent().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) target_group.sortChildren(0, QtCore.Qt.AscendingOrder) # Update the group ID's of the images in the database self.manager.save_objects(items_to_save) @@ -396,8 +429,8 @@ class ImageMediaItem(MediaManagerItem): if self.manager.save_object(new_group): self.loadFullList(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename)) - self.fill_groups_combo_box(self.choosegroupform.group_combobox) - self.fill_groups_combo_box(self.addgroupform.parent_group_combobox) + self.fill_groups_combobox(self.choosegroupform.group_combobox) + self.fill_groups_combobox(self.addgroupform.parent_group_combobox) else: critical_error_message_box( message=translate('ImagePlugin.AddGroupForm', 'Could not add the new group.')) diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 3cce9ea03..07e4209f6 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -285,7 +285,6 @@ class MediaMediaItem(MediaManagerItem): item_name.setData(0, QtCore.Qt.UserRole, track) item_name.setToolTip(0, track) self.listView.addTopLevelItem(item_name) - Settings().setValue(self.settingsSection + u'/media files', self.getFileList()) def getList(self, type=MediaType.Audio): media = Settings().value(self.settingsSection + u'/media files') diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index 2dab04c47..5f7d117a2 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -208,7 +208,6 @@ class PresentationMediaItem(MediaManagerItem): item_name.setIcon(0, icon) item_name.setToolTip(0, file) self.listView.addTopLevelItem(item_name) - Settings().setValue(self.settingsSection + u'/presentations files', self.getFileList()) Receiver.send_message(u'cursor_normal') if not initialLoad: self.main_window.finishedProgressBar() From c72831df5df92c5d642c251b6342a4db40545cb8 Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Fri, 1 Feb 2013 14:08:53 +0100 Subject: [PATCH 17/39] Show warning when adding images and there is no group yet. Also show a message when trying to delete the special 'Imported' group. --- openlp/plugins/images/lib/mediaitem.py | 27 +++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 9dfb19d7b..ebb57d3f4 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -139,7 +139,7 @@ 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.')): + 'You must select an image or group to delete.')): item_list = self.listView.selectedItems() Receiver.send_message(u'cursor_busy') self.main_window.displayProgressBar(len(item_list)) @@ -165,6 +165,11 @@ class ImageMediaItem(MediaManagerItem): row_item.parent().removeChild(row_item) self.fill_groups_combobox(self.choosegroupform.group_combobox) self.fill_groups_combobox(self.addgroupform.parent_group_combobox) + elif item_data == u'Imported': + QtGui.QMessageBox.information(self, translate('ImagePlugin.MediaItem', 'Can\'t delete group'), + translate('ImagePlugin.MediaItem', 'The Imported group is a special group and can not be ' + 'deleted. It will disappear when it doesn\'t contain any images anymore.'), + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok)) self.main_window.incrementProgressBar() self.main_window.finishedProgressBar() Receiver.send_message(u'cursor_normal') @@ -252,6 +257,7 @@ class ImageMediaItem(MediaManagerItem): # configuration file imported_group = QtGui.QTreeWidgetItem() imported_group.setText(0, translate('ImagePlugin.MediaItem', 'Imported')) + imported_group.setData(0, QtCore.Qt.UserRole, u'Imported') self.listView.insertTopLevelItem(0, imported_group) group_items[0] = imported_group group_items[imageFile.group_id].addChild(item_name) @@ -279,6 +285,13 @@ class ImageMediaItem(MediaManagerItem): """ Add new images to the database. This method is called when adding images using the Add button or DnD. """ + if self.manager.get_object_count(ImageGroups) == 0: + QtGui.QMessageBox.warning(self, translate('ImagePlugin.MediaItem', 'No image groups'), + translate('ImagePlugin.MediaItem', 'No image groups exist yet. Please create one before adding images.'), + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok)) + self.loadFullList(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename), + initial_load=initial_load) + return if target_group is None: # Ask which group the images should be saved in if self.choosegroupform.exec_(): @@ -417,6 +430,18 @@ class ImageMediaItem(MediaManagerItem): groups = self.manager.get_all_objects(ImageGroups, ImageGroups.group_name == newGroup.group_name) return self.__checkObject(groups, newGroup, edit) + def onFileClick(self): + """ + Called when the user clicks the 'Load images' button. This method is overloaded from MediaManagerItem. + """ + if self.manager.get_object_count(ImageGroups) == 0: + QtGui.QMessageBox.warning(self, translate('ImagePlugin.MediaItem', 'No image groups'), + translate('ImagePlugin.MediaItem', 'No image groups exist yet. Please create one before adding images.'), + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok)) + else: + # At least one group exists, so we run the regular file adding code + MediaManagerItem.onFileClick(self) + def onAddGroupClick(self): """ Called to add a new group From 3717e7fa203b943dae01c4d3240ea74224395b51 Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Sun, 10 Feb 2013 17:11:12 +0100 Subject: [PATCH 18/39] - Enhanced 'choose group' dialog layout (thanks Raoul) - Revert back to old QListWidget and use QTreeWidget only for images plugin - Renamed dialogs to comply to coding standards - Added image group icon - Enabled animation on image groups - Removed special 'Imported' folder and allow toplevel images instead --- openlp/core/lib/__init__.py | 1 + openlp/core/lib/listwidgetwithdnd.py | 117 +++++++++++++ openlp/core/lib/mediamanageritem.py | 34 ++-- openlp/core/lib/treewidgetwithdnd.py | 11 ++ openlp/plugins/bibles/lib/mediaitem.py | 17 +- openlp/plugins/custom/lib/mediaitem.py | 9 +- .../plugins/images/forms/choosegroupdialog.py | 45 ++++- .../plugins/images/forms/choosegroupform.py | 2 - openlp/plugins/images/lib/mediaitem.py | 165 ++++++++++++------ openlp/plugins/media/lib/mediaitem.py | 33 ++-- openlp/plugins/presentations/lib/mediaitem.py | 24 ++- openlp/plugins/songs/lib/mediaitem.py | 25 ++- resources/images/image_group.png | Bin 0 -> 1562 bytes resources/images/openlp-2.qrc | 1 + 14 files changed, 345 insertions(+), 139 deletions(-) create mode 100644 openlp/core/lib/listwidgetwithdnd.py create mode 100644 resources/images/image_group.png diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index eca60a22b..28e56fff5 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -390,6 +390,7 @@ from uistrings import UiStrings from eventreceiver import Receiver from screen import ScreenList from settings import Settings +from listwidgetwithdnd import ListWidgetWithDnD from treewidgetwithdnd import TreeWidgetWithDnD from formattingtags import FormattingTags from spelltextedit import SpellTextEdit diff --git a/openlp/core/lib/listwidgetwithdnd.py b/openlp/core/lib/listwidgetwithdnd.py new file mode 100644 index 000000000..daff870ee --- /dev/null +++ b/openlp/core/lib/listwidgetwithdnd.py @@ -0,0 +1,117 @@ +# -*- 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 QListWidget to handle drag and drop functionality +""" +import os + +from PyQt4 import QtCore, QtGui + +from openlp.core.lib import Receiver + + +class ListWidgetWithDnD(QtGui.QListWidget): + """ + Provide a list widget to store objects and handle drag and drop events + """ + def __init__(self, parent=None, name=u''): + """ + Initialise the list widget + """ + QtGui.QListWidget.__init__(self, parent) + self.mimeDataText = name + assert(self.mimeDataText) + + def activateDnD(self): + """ + Activate DnD of widget + """ + self.setAcceptDrops(True) + self.setDragDropMode(QtGui.QAbstractItemView.DragDrop) + QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'%s_dnd' % self.mimeDataText), + self.parent().loadFile) + + 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 + """ + 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): + """ + When something is dragged into this object, check if you should be able to drop it in here. + """ + if event.mimeData().hasUrls(): + event.accept() + else: + event.ignore() + + def dragMoveEvent(self, event): + """ + Make an object droppable, and set it to copy the contents of the object, not move it. + """ + if event.mimeData().hasUrls(): + event.setDropAction(QtCore.Qt.CopyAction) + event.accept() + else: + event.ignore() + + def dropEvent(self, event): + """ + Receive drop event check if it is a file 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 in listing: + files.append(os.path.join(localFile, file)) + Receiver.send_message(u'%s_dnd' % self.mimeDataText, files) + else: + event.ignore() diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 94c891a51..0372e1d2a 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -35,7 +35,7 @@ import re from PyQt4 import QtCore, QtGui -from openlp.core.lib import OpenLPToolbar, ServiceItem, StringContent, Receiver, TreeWidgetWithDnD, \ +from openlp.core.lib import OpenLPToolbar, ServiceItem, StringContent, Receiver, ListWidgetWithDnD, \ ServiceItemContext, Settings, Registry, UiStrings, build_icon, translate from openlp.core.lib.searchedit import SearchEdit from openlp.core.lib.ui import create_widget_action, critical_error_message_box @@ -213,7 +213,8 @@ class MediaManagerItem(QtGui.QWidget): Creates the main widget for listing items the media item is tracking """ # Add the List widget - self.listView = TreeWidgetWithDnD(self, self.plugin.name) + self.listView = ListWidgetWithDnD(self, self.plugin.name) + self.listView.setSpacing(1) self.listView.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) self.listView.setAlternatingRowColors(True) self.listView.setObjectName(u'%sListView' % self.plugin.name) @@ -372,9 +373,9 @@ class MediaManagerItem(QtGui.QWidget): """ names = [] full_list = [] - for count in range(self.listView.topLevelItemCount()): - names.append(self.listView.topLevelItem(count).text(0)) - full_list.append(self.listView.topLevelItem(count).data(0, QtCore.Qt.UserRole)) + for count in range(self.listView.count()): + names.append(self.listView.item(count).text()) + full_list.append(self.listView.item(count).data(QtCore.Qt.UserRole)) duplicates_found = False files_added = False for file_path in files: @@ -413,9 +414,9 @@ class MediaManagerItem(QtGui.QWidget): """ count = 0 file_list = [] - while count < self.listView.topLevelItemCount(): - bitem = self.listView.topLevelItem(count) - filename = bitem.data(0, QtCore.Qt.UserRole) + while count < self.listView.count(): + bitem = self.listView.item(count) + filename = bitem.data(QtCore.Qt.UserRole) file_list.append(filename) count += 1 return file_list @@ -523,8 +524,8 @@ class MediaManagerItem(QtGui.QWidget): """ Create a media item from an item id. """ - item = QtGui.QTreeWidgetItem() - item.setData(0, QtCore.Qt.UserRole, item_id) + item = QtGui.QListWidgetItem() + item.setData(QtCore.Qt.UserRole, item_id) return item def onAddClick(self): @@ -597,16 +598,15 @@ class MediaManagerItem(QtGui.QWidget): """ Checks if the listView is empty and adds a "No Search Results" item. """ - if self.listView.topLevelItemCount(): + if self.listView.count(): return message = translate('OpenLP.MediaManagerItem', 'No Search Results') - item = QtGui.QTreeWidgetItem(message) - item.setText(0, message) + item = QtGui.QListWidgetItem(message) item.setFlags(QtCore.Qt.NoItemFlags) font = QtGui.QFont() font.setItalic(True) - item.setFont(0, font) - self.listView.addTopLevelItem(item) + item.setFont(font) + self.listView.addItem(item) def _getIdOfItemToGenerate(self, item, remoteItem): """ @@ -623,7 +623,7 @@ class MediaManagerItem(QtGui.QWidget): item = self.listView.currentItem() if item is None: return False - item_id = item.data(0, QtCore.Qt.UserRole) + item_id = item.data(QtCore.Qt.UserRole) else: item_id = remoteItem else: @@ -638,7 +638,7 @@ class MediaManagerItem(QtGui.QWidget): if self.autoSelectId == -1: item = self.listView.currentItem() if item: - self.autoSelectId = item.data(0, QtCore.Qt.UserRole) + self.autoSelectId = item.data(QtCore.Qt.UserRole) def search(self, string, showError=True): """ diff --git a/openlp/core/lib/treewidgetwithdnd.py b/openlp/core/lib/treewidgetwithdnd.py index 0eca98c01..09d96600e 100644 --- a/openlp/core/lib/treewidgetwithdnd.py +++ b/openlp/core/lib/treewidgetwithdnd.py @@ -50,6 +50,7 @@ class TreeWidgetWithDnD(QtGui.QTreeWidget): self.header().close() self.defaultIndentation = self.indentation() self.setIndentation(0) + self.setAnimated(True) assert(self.mimeDataText) def activateDnD(self): @@ -126,3 +127,13 @@ class TreeWidgetWithDnD(QtGui.QTreeWidget): Receiver.send_message(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) diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index afd2abb27..f9a49ebb1 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -76,7 +76,7 @@ class BibleMediaItem(MediaManagerItem): """ Check if the first item is a second bible item or not. """ - bitem = self.listView.topLevelItem(0) + bitem = self.listView.item(0) if not bitem.flags() & QtCore.Qt.ItemIsSelectable: # The item is the "No Search Results" item. self.listView.clear() @@ -95,7 +95,7 @@ class BibleMediaItem(MediaManagerItem): self.displayResults(bible, second_bible) def _decodeQtObject(self, bitem, key): - reference = bitem.data(0, QtCore.Qt.UserRole) + reference = bitem.data(QtCore.Qt.UserRole) obj = reference[unicode(key)] return unicode(obj).strip() @@ -634,7 +634,7 @@ class BibleMediaItem(MediaManagerItem): self.second_search_results = self.plugin.manager.get_verses(second_bible, versetext, book_ref_id) if not self.advancedLockButton.isChecked(): self.listView.clear() - if self.listView.topLevelItemCount() != 0: + if self.listView.count() != 0: self.__checkSecondBible(bible, second_bible) elif self.search_results: self.displayResults(bible, second_bible) @@ -691,7 +691,7 @@ class BibleMediaItem(MediaManagerItem): self.second_search_results = bibles[second_bible].get_verses(text) if not self.quickLockButton.isChecked(): self.listView.clear() - if self.listView.topLevelItemCount() != 0 and self.search_results: + if self.listView.count() != 0 and self.search_results: self.__checkSecondBible(bible, second_bible) elif self.search_results: self.displayResults(bible, second_bible) @@ -707,7 +707,7 @@ class BibleMediaItem(MediaManagerItem): """ items = self.buildDisplayResults(bible, second_bible, self.search_results) for bible_verse in items: - self.listView.addTopLevelItem(bible_verse) + self.listView.addItem(bible_verse) self.listView.selectAll() self.search_results = {} self.second_search_results = {} @@ -766,9 +766,8 @@ class BibleMediaItem(MediaManagerItem): second_version) else: bible_text = u'%s %d%s%d (%s)' % (book, verse.chapter, verse_separator, verse.verse, version) - bible_verse = QtGui.QTreeWidgetItem(bible_text) - bible_verse.setText(0, bible_text) - bible_verse.setData(0, QtCore.Qt.UserRole, data) + bible_verse = QtGui.QListWidgetItem(bible_text) + bible_verse.setData(QtCore.Qt.UserRole, data) items.append(bible_verse) return items @@ -972,7 +971,7 @@ class BibleMediaItem(MediaManagerItem): return [] def createItemFromId(self, item_id): - item = QtGui.QTreeWidgetItem() + item = QtGui.QListWidgetItem() bible = self.quickVersionComboBox.currentText() search_results = self.plugin.manager.get_verses(bible, item_id, False) items = self.buildDisplayResults(bible, u'', search_results) diff --git a/openlp/plugins/custom/lib/mediaitem.py b/openlp/plugins/custom/lib/mediaitem.py index 86cef59ed..7c604e322 100644 --- a/openlp/plugins/custom/lib/mediaitem.py +++ b/openlp/plugins/custom/lib/mediaitem.py @@ -103,10 +103,9 @@ class CustomMediaItem(MediaManagerItem): self.listView.clear() custom_slides.sort() for custom_slide in custom_slides: - custom_name = QtGui.QTreeWidgetItem(custom_slide.title) - custom_name.setText(0, custom_slide.title) - custom_name.setData(0, QtCore.Qt.UserRole, custom_slide.id) - self.listView.addTopLevelItem(custom_name) + custom_name = QtGui.QListWidgetItem(custom_slide.title) + custom_name.setData(QtCore.Qt.UserRole, custom_slide.id) + self.listView.addItem(custom_name) # Auto-select the custom. if custom_slide.id == self.autoSelectId: self.listView.setCurrentItem(custom_name) @@ -149,7 +148,7 @@ class CustomMediaItem(MediaManagerItem): """ if check_item_selected(self.listView, UiStrings().SelectEdit): item = self.listView.currentItem() - item_id = item.data(0, QtCore.Qt.UserRole) + item_id = item.data(QtCore.Qt.UserRole) self.edit_custom_form.loadCustom(item_id, False) self.edit_custom_form.exec_() self.autoSelectId = -1 diff --git a/openlp/plugins/images/forms/choosegroupdialog.py b/openlp/plugins/images/forms/choosegroupdialog.py index 0bbec6cc8..3ddfb6bb1 100644 --- a/openlp/plugins/images/forms/choosegroupdialog.py +++ b/openlp/plugins/images/forms/choosegroupdialog.py @@ -34,28 +34,61 @@ 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(440, 119) + 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(2, QtGui.QFormLayout.FieldRole, self.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(3, QtGui.QFormLayout.FieldRole, self.group_button_box) + 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): - choose_group_dialog.setWindowTitle(translate('ImagePlugin.ChooseGroupForm', 'Choose group')) - self.group_question_label.setText(translate('ImagePlugin.ChooseGroupForm', - 'To which group do you want these images to be added?')) + """ + 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')) diff --git a/openlp/plugins/images/forms/choosegroupform.py b/openlp/plugins/images/forms/choosegroupform.py index eed92e360..15a900c06 100644 --- a/openlp/plugins/images/forms/choosegroupform.py +++ b/openlp/plugins/images/forms/choosegroupform.py @@ -29,8 +29,6 @@ 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.choosegroupdialog import Ui_ChooseGroupDialog diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index ebb57d3f4..b668e9c34 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -34,8 +34,8 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import MediaManagerItem, build_icon, ItemCapabilities, SettingsManager, translate, \ check_item_selected, check_directory_exists, Receiver, create_thumb, validate_thumb, ServiceItemContext, Settings, \ - UiStrings -from openlp.core.lib.ui import critical_error_message_box + StringContent, TreeWidgetWithDnD, UiStrings +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.plugins.images.forms import AddGroupForm, ChooseGroupForm from openlp.plugins.images.lib.db import ImageFilenames, ImageGroups @@ -55,10 +55,10 @@ class ImageMediaItem(MediaManagerItem): self.quickPreviewAllowed = True self.hasSearch = True self.manager = plugin.manager - self.choosegroupform = ChooseGroupForm(self) - self.addgroupform = AddGroupForm(self) - self.fill_groups_combobox(self.choosegroupform.group_combobox) - self.fill_groups_combobox(self.addgroupform.parent_group_combobox) + 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) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'live_theme_changed'), self.liveThemeChanged) # Allow DnD from the desktop self.listView.activateDnD() @@ -106,7 +106,63 @@ class ImageMediaItem(MediaManagerItem): initial_load=True) 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()) + QtCore.QObject.connect(self.listView, QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), + self.onDoubleClicked) + QtCore.QObject.connect(self.listView, QtCore.SIGNAL(u'itemSelectionChanged()'), + self.onSelectionChange) + QtCore.QObject.connect(self.listView, QtCore.SIGNAL(u'customContextMenuRequested(QPoint)'), + self.contextMenu) self.listView.addAction(self.replaceAction) def addStartHeaderBar(self): @@ -148,7 +204,10 @@ class ImageMediaItem(MediaManagerItem): item_data = row_item.data(0, QtCore.Qt.UserRole) if isinstance(item_data, ImageFilenames): delete_file(os.path.join(self.servicePath, row_item.text(0))) - row_item.parent().removeChild(row_item) + 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(), @@ -163,13 +222,8 @@ class ImageMediaItem(MediaManagerItem): self.listView.takeTopLevelItem(self.listView.indexOfTopLevelItem(row_item)) else: row_item.parent().removeChild(row_item) - self.fill_groups_combobox(self.choosegroupform.group_combobox) - self.fill_groups_combobox(self.addgroupform.parent_group_combobox) - elif item_data == u'Imported': - QtGui.QMessageBox.information(self, translate('ImagePlugin.MediaItem', 'Can\'t delete group'), - translate('ImagePlugin.MediaItem', 'The Imported group is a special group and can not be ' - 'deleted. It will disappear when it doesn\'t contain any images anymore.'), - QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok)) + self.fill_groups_combobox(self.choose_group_form.group_combobox) + self.fill_groups_combobox(self.add_group_form.parent_group_combobox) self.main_window.incrementProgressBar() self.main_window.finishedProgressBar() Receiver.send_message(u'cursor_normal') @@ -177,14 +231,16 @@ class ImageMediaItem(MediaManagerItem): def add_sub_groups(self, group_list, parent_group_id): """ - Recursively add subgroups to the given parent group + Recursively add subgroups to the given parent group in a QTreeWidget """ 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 is 0: self.listView.addTopLevelItem(group) else: @@ -252,15 +308,9 @@ class ImageMediaItem(MediaManagerItem): item_name.setToolTip(0, imageFile.filename) item_name.setData(0, QtCore.Qt.UserRole, imageFile) 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')) - imported_group.setData(0, QtCore.Qt.UserRole, u'Imported') - self.listView.insertTopLevelItem(0, imported_group) - group_items[0] = imported_group - group_items[imageFile.group_id].addChild(item_name) + self.listView.addTopLevelItem(item_name) + else: + group_items[imageFile.group_id].addChild(item_name) if not initial_load: self.main_window.incrementProgressBar() if not initial_load: @@ -285,19 +335,36 @@ class ImageMediaItem(MediaManagerItem): """ Add new images to the database. This method is called when adding images using the Add button or DnD. """ - if self.manager.get_object_count(ImageGroups) == 0: - QtGui.QMessageBox.warning(self, translate('ImagePlugin.MediaItem', 'No image groups'), - translate('ImagePlugin.MediaItem', 'No image groups exist yet. Please create one before adding images.'), - QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok)) - self.loadFullList(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename), - initial_load=initial_load) - return if target_group is None: # Ask which group the images should be saved in - if self.choosegroupform.exec_(): - group_id = self.choosegroupform.group_combobox.itemData( - self.choosegroupform.group_combobox.currentIndex(), QtCore.Qt.UserRole) - parent_group = self.manager.get_object_filtered(ImageGroups, ImageGroups.id == group_id) + if self.manager.get_object_count(ImageGroups) == 0: + self.choose_group_form.nogroup_radio_button.setChecked(True) + self.choose_group_form.existing_radio_button.setChecked(False) + self.choose_group_form.new_radio_button.setChecked(False) + self.choose_group_form.existing_radio_button.setDisabled(True) + self.choose_group_form.group_combobox.setDisabled(True) + else: + self.choose_group_form.nogroup_radio_button.setChecked(True) + self.choose_group_form.existing_radio_button.setChecked(False) + self.choose_group_form.new_radio_button.setChecked(False) + self.choose_group_form.existing_radio_button.setDisabled(False) + self.choose_group_form.group_combobox.setDisabled(False) + if self.choose_group_form.exec_(): + 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): @@ -313,7 +380,7 @@ class ImageMediaItem(MediaManagerItem): imageFile = ImageFilenames() imageFile.group_id = parent_group.id imageFile.filename = unicode(filename) - success = self.manager.save_object(imageFile) + self.manager.save_object(imageFile) self.loadFullList(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename), initial_load=initial_load, open_group=parent_group) @@ -430,32 +497,20 @@ class ImageMediaItem(MediaManagerItem): groups = self.manager.get_all_objects(ImageGroups, ImageGroups.group_name == newGroup.group_name) return self.__checkObject(groups, newGroup, edit) - def onFileClick(self): - """ - Called when the user clicks the 'Load images' button. This method is overloaded from MediaManagerItem. - """ - if self.manager.get_object_count(ImageGroups) == 0: - QtGui.QMessageBox.warning(self, translate('ImagePlugin.MediaItem', 'No image groups'), - translate('ImagePlugin.MediaItem', 'No image groups exist yet. Please create one before adding images.'), - QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok)) - else: - # At least one group exists, so we run the regular file adding code - MediaManagerItem.onFileClick(self) - def onAddGroupClick(self): """ Called to add a new group """ - if self.addgroupform.exec_(show_top_level_group=True): - new_group = ImageGroups.populate(parent_id=self.addgroupform.parent_group_combobox.itemData( - self.addgroupform.parent_group_combobox.currentIndex(), QtCore.Qt.UserRole), - group_name=self.addgroupform.name_edit.text()) + if self.add_group_form.exec_(show_top_level_group=True): + 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 self.checkGroupName(new_group): if self.manager.save_object(new_group): self.loadFullList(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename)) - self.fill_groups_combobox(self.choosegroupform.group_combobox) - self.fill_groups_combobox(self.addgroupform.parent_group_combobox) + 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.')) diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index f0fb559b8..e4021a4ea 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -147,7 +147,7 @@ class MediaMediaItem(MediaManagerItem): if check_item_selected(self.listView, translate('MediaPlugin.MediaItem', 'You must select a media file to replace the background with.')): item = self.listView.currentItem() - filename = item.data(0, QtCore.Qt.UserRole) + filename = item.data(QtCore.Qt.UserRole) if os.path.exists(filename): service_item = ServiceItem() service_item.title = u'webkit' @@ -171,7 +171,7 @@ class MediaMediaItem(MediaManagerItem): item = self.listView.currentItem() if item is None: return False - filename = item.data(0, QtCore.Qt.UserRole) + filename = item.data(QtCore.Qt.UserRole) if not os.path.exists(filename): if not remote: # File is no longer present @@ -253,7 +253,7 @@ class MediaMediaItem(MediaManagerItem): row_list = [item.row() for item in self.listView.selectedIndexes()] row_list.sort(reverse=True) for row in row_list: - self.listView.takeTopLevelItem(row) + self.listView.takeItem(row) Settings().setValue(self.settingsSection + u'/media files', self.getFileList()) def loadList(self, media, target_group=None): @@ -264,27 +264,24 @@ class MediaMediaItem(MediaManagerItem): track_info = QtCore.QFileInfo(track) if not os.path.exists(track): filename = os.path.split(unicode(track))[1] - item_name = QtGui.QTreeWidgetItem(filename) - item_name.setText(0, filename) - item_name.setIcon(0, ERROR) - item_name.setData(0, QtCore.Qt.UserRole, track) + item_name = QtGui.QListWidgetItem(filename) + item_name.setIcon(ERROR) + item_name.setData(QtCore.Qt.UserRole, track) elif track_info.isFile(): filename = os.path.split(unicode(track))[1] - item_name = QtGui.QTreeWidgetItem(filename) - item_name.setText(0, filename) + item_name = QtGui.QListWidgetItem(filename) if u'*.%s' % (filename.split(u'.')[-1].lower()) in self.media_controller.audio_extensions_list: - item_name.setIcon(0, AUDIO) + item_name.setIcon(AUDIO) else: - item_name.setIcon(0, VIDEO) - item_name.setData(0, QtCore.Qt.UserRole, track) + item_name.setIcon(VIDEO) + item_name.setData(QtCore.Qt.UserRole, track) else: filename = os.path.split(unicode(track))[1] - item_name = QtGui.QTreeWidgetItem(filename) - item_name.setText(0, filename) - item_name.setIcon(0, build_icon(DVDICON)) - item_name.setData(0, QtCore.Qt.UserRole, track) - item_name.setToolTip(0, track) - self.listView.addTopLevelItem(item_name) + item_name = QtGui.QListWidgetItem(filename) + item_name.setIcon(build_icon(DVDICON)) + item_name.setData(QtCore.Qt.UserRole, track) + item_name.setToolTip(track) + self.listView.addItem(item_name) def getList(self, type=MediaType.Audio): media = Settings().value(self.settingsSection + u'/media files') diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index 2ba5f40a7..21d48a89e 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -164,12 +164,11 @@ class PresentationMediaItem(MediaManagerItem): continue filename = os.path.split(unicode(file))[1] if not os.path.exists(file): - item_name = QtGui.QTreeWidgetItem(filename) - item_name.setText(0, filename) - item_name.setIcon(0, build_icon(ERROR)) - item_name.setData(0, QtCore.Qt.UserRole, file) - item_name.setToolTip(0, file) - self.listView.addTopLevelItem(item_name) + item_name = QtGui.QListWidgetItem(filename) + item_name.setIcon(build_icon(ERROR)) + item_name.setData(QtCore.Qt.UserRole, file) + item_name.setToolTip(file) + self.listView.addItem(item_name) else: if titles.count(filename) > 0: if not initialLoad: @@ -202,12 +201,11 @@ class PresentationMediaItem(MediaManagerItem): critical_error_message_box(UiStrings().UnsupportedFile, translate('PresentationPlugin.MediaItem', 'This type of presentation is not supported.')) continue - item_name = QtGui.QTreeWidgetItem(filename) - item_name.setText(0, filename) - item_name.setData(0, QtCore.Qt.UserRole, file) - item_name.setIcon(0, icon) - item_name.setToolTip(0, file) - self.listView.addTopLevelItem(item_name) + item_name = QtGui.QListWidgetItem(filename) + item_name.setData(QtCore.Qt.UserRole, file) + item_name.setIcon(icon) + item_name.setToolTip(file) + self.listView.addItem(item_name) Receiver.send_message(u'cursor_normal') if not initialLoad: self.main_window.finishedProgressBar() @@ -233,7 +231,7 @@ class PresentationMediaItem(MediaManagerItem): self.main_window.finishedProgressBar() Receiver.send_message(u'cursor_normal') for row in row_list: - self.listView.takeTopLevelItem(row) + self.listView.takeItem(row) Settings().setValue(self.settingsSection + u'/presentations files', self.getFileList()) def generateSlideData(self, service_item, item=None, xmlVersion=False, diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 833d14013..de1ad22ff 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -226,10 +226,9 @@ class SongMediaItem(MediaManagerItem): author_list = [author.display_name for author in song.authors] song_title = unicode(song.title) song_detail = u'%s (%s)' % (song_title, create_separated_list(author_list)) - song_name = QtGui.QTreeWidgetItem(song_detail) - song_name.setText(0, song_detail) - song_name.setData(0, QtCore.Qt.UserRole, song.id) - self.listView.addTopLevelItem(song_name) + song_name = QtGui.QListWidgetItem(song_detail) + song_name.setData(QtCore.Qt.UserRole, song.id) + self.listView.addItem(song_name) # Auto-select the item if name has been set if song.id == self.autoSelectId: self.listView.setCurrentItem(song_name) @@ -244,10 +243,9 @@ class SongMediaItem(MediaManagerItem): if song.temporary: continue song_detail = u'%s (%s)' % (author.display_name, song.title) - song_name = QtGui.QTreeWidgetItem(song_detail) - song_name.setText(0, song_detail) - song_name.setData(0, QtCore.Qt.UserRole, song.id) - self.listView.addTopLevelItem(song_name) + song_name = QtGui.QListWidgetItem(song_detail) + song_name.setData(QtCore.Qt.UserRole, song.id) + self.listView.addItem(song_name) def displayResultsBook(self, searchresults, song_number=False): log.debug(u'display results Book') @@ -262,10 +260,9 @@ class SongMediaItem(MediaManagerItem): if song_number and not song_number in song.song_number: continue song_detail = u'%s - %s (%s)' % (book.name, song.song_number, song.title) - song_name = QtGui.QTreeWidgetItem(song_detail) - song_name.setText(0, song_detail) - song_name.setData(0, QtCore.Qt.UserRole, song.id) - self.listView.addTopLevelItem(song_name) + song_name = QtGui.QListWidgetItem(song_detail) + song_name.setData(QtCore.Qt.UserRole, song.id) + self.listView.addItem(song_name) def onClearTextButtonClick(self): """ @@ -344,7 +341,7 @@ class SongMediaItem(MediaManagerItem): log.debug(u'onEditClick') if check_item_selected(self.listView, UiStrings().SelectEdit): self.editItem = self.listView.currentItem() - item_id = self.editItem.data(0, QtCore.Qt.UserRole) + item_id = self.editItem.data(QtCore.Qt.UserRole) self.editSongForm.loadSong(item_id, False) self.editSongForm.exec_() self.autoSelectId = -1 @@ -393,7 +390,7 @@ class SongMediaItem(MediaManagerItem): log.debug(u'onCloneClick') if check_item_selected(self.listView, UiStrings().SelectEdit): self.editItem = self.listView.currentItem() - item_id = self.editItem.data(0, QtCore.Qt.UserRole) + item_id = self.editItem.data(QtCore.Qt.UserRole) old_song = self.plugin.manager.get_object(Song, item_id) song_xml = self.openLyrics.song_to_xml(old_song) new_song = self.openLyrics.xml_to_song(song_xml) diff --git a/resources/images/image_group.png b/resources/images/image_group.png new file mode 100644 index 0000000000000000000000000000000000000000..352c3b0e2cf95bee00f408e745f6734567b8148d GIT binary patch literal 1562 zcmV+#2IcvQP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01ejw01ejxLMWSf00007bV*G`2ipW5 z6$LIzCs;8600o*!L_t(o!|j(nsBKpf$A7cd+WXw=d)_2SA{u>&HVOtIDT7T4F<7K9 zq_Ge?8%v9Tjg5BJ76!BvENw(ku?PebC65?l5zWgpc^|&}?z?&So{#;pW-Mmb-RBVm z3nh&Q4tt+|ln4sc|8JGdV zNGa2If(QZ_VCFVr zU;-#;94g}H2#|mpPyZNZ!?VBs$#cIc0K;&fSKmD4pQl?&94_Jx4EXFPd%pa{SEOXY zFtWJAk_CX2ge)K;bjkN1LQaqb1b;UmC#aI_xdp|#>TgtI6Wktqr(fvG|A%x8lvP^<8%#~zYfH?Q>o1;A2fGi*Th0@mc` z_-0k+-8sj1-sAYz4aRYBI9s3w)v z&Rbv49M#knR2$^E7;;_%=nJNoOQx?vSuRm43|Fb{+?W<2e zY170)?|tm{*4Asd`d5WylwiJ?6D=D|K~4;+ES=H_h(XuRmdxqNZOYr%?tlK$3f8mI zMaDaWRU=enVFoI2n2p(lK7neqm%$iMLO)UKfvx(PH3FN03x<~>8>a|?YZ~$y&$qe<9Xm9|7 z2^iw=O*m^P%(1S)i#d`>Vlb;4slra(uxcFcoMRV(s`}qB7$ZYf5Bh**9rbfHY^%BB zs{Sk%lHYoMr`B|WU3Jo!KG-@I$PBt@{4VNoIl)Gk3r2y1SZiDv&C79^B2hC62Gi_2 zEH?I0jw#sN%CX<2DcF99m*Q*=9SF!Xj>LXfD{vsNn<1A#Rf3VNM%j4pL3s@U`(lU+l?V?j+T|xt@EfeFQxsVdI0aSeuY^zf&zI;S80+;>R%W4_e{!-p%=w8#B~Sqdb@ z-z{M#NKym^F@);X$1n^Lzekhk#cx3|-Ei{GFW~DR;XkI|song_book_edit.png + image_group.png image_new_group.png From bb5abea70455f28595e2f254377e8dc61e9a9a26 Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Mon, 11 Feb 2013 11:32:19 +0100 Subject: [PATCH 19/39] Preselect group when showing 'Add group' or 'Choose group' dialog --- openlp/plugins/images/forms/addgroupform.py | 6 ++- .../plugins/images/forms/choosegroupform.py | 7 +++ openlp/plugins/images/lib/mediaitem.py | 46 +++++++++++++++---- 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/openlp/plugins/images/forms/addgroupform.py b/openlp/plugins/images/forms/addgroupform.py index 535434e5b..95eceb678 100644 --- a/openlp/plugins/images/forms/addgroupform.py +++ b/openlp/plugins/images/forms/addgroupform.py @@ -45,13 +45,17 @@ class AddGroupForm(QtGui.QDialog, Ui_AddGroupDialog): QtGui.QDialog.__init__(self, parent) self.setupUi(self) - def exec_(self, clear=True, show_top_level_group=False): + def exec_(self, clear=True, show_top_level_group=False, selected_group=None): 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): diff --git a/openlp/plugins/images/forms/choosegroupform.py b/openlp/plugins/images/forms/choosegroupform.py index 15a900c06..5997d738d 100644 --- a/openlp/plugins/images/forms/choosegroupform.py +++ b/openlp/plugins/images/forms/choosegroupform.py @@ -42,3 +42,10 @@ class ChooseGroupForm(QtGui.QDialog, Ui_ChooseGroupDialog): """ QtGui.QDialog.__init__(self, parent) self.setupUi(self) + + def exec_(self, selected_group=None): + 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) diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 42a8c67d6..9f983da07 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -325,8 +325,6 @@ class ImageMediaItem(MediaManagerItem): ``files`` The files to be loaded. """ - if target_group is None: - self.listView.clear() self.loadList(files, target_group) last_dir = os.path.split(unicode(files[0]))[0] Settings().setValue(self.settingsSection + u'/last directory', last_dir) @@ -336,20 +334,33 @@ class ImageMediaItem(MediaManagerItem): Add new images to the database. This method is called when adding images using the Add button or DnD. """ if target_group is None: - # Ask which group the images should be saved in - if self.manager.get_object_count(ImageGroups) == 0: + # Find out if a group must be pre-selected + preselect_group = None + selected_items = self.listView.selectedItems() + if len(selected_items) > 0: + 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.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) + 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.nogroup_radio_button.setChecked(True) - self.choose_group_form.existing_radio_button.setChecked(False) - self.choose_group_form.new_radio_button.setChecked(False) self.choose_group_form.existing_radio_button.setDisabled(False) self.choose_group_form.group_combobox.setDisabled(False) - if self.choose_group_form.exec_(): + # 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() @@ -368,7 +379,11 @@ class ImageMediaItem(MediaManagerItem): else: parent_group = target_group.data(0, QtCore.Qt.UserRole) if isinstance(parent_group, ImageFilenames): - parent_group = target_group.parent().data(0, QtCore.Qt.UserRole) + 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 @@ -501,7 +516,18 @@ class ImageMediaItem(MediaManagerItem): """ Called to add a new group """ - if self.add_group_form.exec_(show_top_level_group=True): + # Find out if a group must be pre-selected + preselect_group = 0 + selected_items = self.listView.selectedItems() + if len(selected_items) > 0: + 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()) From c1ba98ee0f5d4f6c0f09d5b3bd573a3ac340d2d2 Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Sun, 17 Feb 2013 19:33:39 +0100 Subject: [PATCH 20/39] Use correct event handlers --- openlp/plugins/images/lib/mediaitem.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 0533e641a..d2cd48b97 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -157,12 +157,9 @@ class ImageMediaItem(MediaManagerItem): # Create the context menu and add all actions from the listView. self.menu = QtGui.QMenu() self.menu.addActions(self.listView.actions()) - QtCore.QObject.connect(self.listView, QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), - self.onDoubleClicked) - QtCore.QObject.connect(self.listView, QtCore.SIGNAL(u'itemSelectionChanged()'), - self.onSelectionChange) - QtCore.QObject.connect(self.listView, QtCore.SIGNAL(u'customContextMenuRequested(QPoint)'), - self.contextMenu) + self.listView.doubleClicked.connect(self.onDoubleClicked) + self.listView.itemSelectionChanged.connect(self.onSelectionChange) + self.listView.customContextMenuRequested.connect(self.contextMenu) self.listView.addAction(self.replaceAction) def addStartHeaderBar(self): From 25373944b2e355b4782b8a245ae8d2f07851ac3e Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 26 Feb 2013 12:23:57 +0100 Subject: [PATCH 21/39] started work on converting old image settings --- openlp/core/lib/plugin.py | 21 ++++--------------- openlp/core/lib/settings.py | 29 ++++++++++++++++++++++++++ openlp/core/ui/mainwindow.py | 28 +++++++++---------------- openlp/plugins/images/imageplugin.py | 1 - openlp/plugins/images/lib/mediaitem.py | 16 ++++++-------- 5 files changed, 49 insertions(+), 46 deletions(-) diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index 203f30e6e..58384c15d 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -301,23 +301,10 @@ class Plugin(QtCore.QObject): # This is needed to load the list of images/media/presentation from the config saved # before the settings rewrite. if self.mediaItemClass is not None: - # We need QSettings instead of Settings here to bypass our central settings dict. - # 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. - Settings().setValue(u'%s/%s files' % (self.settingsSection, self.name), loaded_list) - settings.endGroup() + loaded_list = Settings().get_files_from_config(self) + # Now save the list to the config using our Settings class. + Settings().setValue(u'%s/%s files' % (self.settingsSection, self.name), loaded_list) + # FIXME: make sure we do not do this for the images plugin. def usesTheme(self, theme): """ diff --git a/openlp/core/lib/settings.py b/openlp/core/lib/settings.py index 56821f1d1..94afa275c 100644 --- a/openlp/core/lib/settings.py +++ b/openlp/core/lib/settings.py @@ -364,3 +364,32 @@ class Settings(QtCore.QSettings): if isinstance(default_value, int): return int(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() + 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 diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index e86f068a8..07649a6b3 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -835,19 +835,11 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): return setting_sections = [] # Add main sections. - setting_sections.extend([self.generalSettingsSection]) - setting_sections.extend([self.advancedSettingsSection]) - setting_sections.extend([self.uiSettingsSection]) - setting_sections.extend([self.shortcutsSettingsSection]) - setting_sections.extend([self.serviceManagerSettingsSection]) - setting_sections.extend([self.themesSettingsSection]) - setting_sections.extend([self.playersSettingsSection]) - setting_sections.extend([self.displayTagsSection]) - setting_sections.extend([self.headerSection]) - setting_sections.extend([u'crashreport']) + setting_sections.extend([self.generalSettingsSection, self.advancedSettingsSection, self.uiSettingsSection, + self.shortcutsSettingsSection, self.serviceManagerSettingsSection, self.themesSettingsSection, + self.playersSettingsSection, self.displayTagsSection, self.headerSection, u'crashreport']) # Add plugin sections. - for plugin in self.plugin_manager.plugins: - setting_sections.extend([plugin.name]) + setting_sections.extend([plugin.name for plugin in self.plugin_manager.plugins]) # 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') check_directory_exists(temp_directory) @@ -857,9 +849,10 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): import_settings = Settings(temp_config, Settings.IniFormat) # Remove/rename old settings to prepare the import. import_settings.remove_obsolete_settings() - # Lets do a basic sanity check. If it contains this string we can - # assume it was created by OpenLP and so we'll load what we can - # from it, and just silently ignore anything we don't recognise + # FIXME: Convert image files + + # Lets do a basic sanity check. If it contains this string we can assume it was created by OpenLP and so we'll + # load what we can from it, and just silently ignore anything we don't recognise. if import_settings.value(u'SettingsImport/type') != u'OpenLP_settings_export': 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 ' @@ -894,9 +887,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): settings.setValue(u'file_date_imported', now.strftime("%Y-%m-%d %H:%M")) settings.endGroup() settings.sync() - # We must do an immediate restart or current configuration will - # overwrite what was just imported when application terminates - # normally. We need to exit without saving configuration. + # We must do an immediate restart or current configuration will overwrite what was just imported when + # application terminates normally. We need to exit without saving configuration. QtGui.QMessageBox.information(self, translate('OpenLP.MainWindow', 'Import settings'), translate('OpenLP.MainWindow', 'OpenLP will now close. Imported settings will ' 'be applied the next time you start OpenLP.'), diff --git a/openlp/plugins/images/imageplugin.py b/openlp/plugins/images/imageplugin.py index e474a088b..28f611863 100644 --- a/openlp/plugins/images/imageplugin.py +++ b/openlp/plugins/images/imageplugin.py @@ -41,7 +41,6 @@ log = logging.getLogger(__name__) __default_settings__ = { u'images/db type': u'sqlite', u'images/background color': u'#000000', - u'images/images files': [] } diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index d2cd48b97..3871fec5d 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -91,16 +91,12 @@ class ImageMediaItem(MediaManagerItem): self.servicePath = os.path.join(AppLocation.get_section_data_path(self.settingsSection), u'thumbnails') check_directory_exists(self.servicePath) # Import old images list - images_old = Settings().value(self.settingsSection + u'/images files') - if len(images_old) > 0: - for imageFile in images_old: - imagefilename = ImageFilenames() - imagefilename.group_id = 0 - imagefilename.filename = imageFile - success = self.manager.save_object(imagefilename) - Settings().setValue(self.settingsSection + u'/images files', []) - Settings().remove(self.settingsSection + u'/images files') - Settings().remove(self.settingsSection + u'/images count') + files_from_config = Settings().get_files_from_config(self.plugin) + for old_file in files_from_config: + imagefilename = ImageFilenames() + imagefilename.group_id = 0 + imagefilename.filename = old_file + success = self.manager.save_object(imagefilename) # Load images from the database self.loadFullList(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename), initial_load=True) From 78d7dacd4bf2c7ea0aec0667b94f871bcc9e3f07 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 26 Feb 2013 16:39:39 +0100 Subject: [PATCH 22/39] clean up --- openlp/plugins/images/lib/mediaitem.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 3871fec5d..61f2aa96b 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -96,10 +96,10 @@ class ImageMediaItem(MediaManagerItem): imagefilename = ImageFilenames() imagefilename.group_id = 0 imagefilename.filename = old_file - success = self.manager.save_object(imagefilename) + self.manager.save_object(imagefilename) # Load images from the database - self.loadFullList(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename), - initial_load=True) + self.loadFullList( + self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename), initial_load=True) def addListViewToToolBar(self): """ From 5a5845ccd1184b3c150b627f1a5eb6a8289e3f4f Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 26 Feb 2013 16:41:36 +0100 Subject: [PATCH 23/39] added commented line --- openlp/core/ui/mainwindow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 07649a6b3..92390cab0 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -850,7 +850,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): # Remove/rename old settings to prepare the import. import_settings.remove_obsolete_settings() # FIXME: Convert image files - + #settings.get_files_from_config() # Lets do a basic sanity check. If it contains this string we can assume it was created by OpenLP and so we'll # load what we can from it, and just silently ignore anything we don't recognise. if import_settings.value(u'SettingsImport/type') != u'OpenLP_settings_export': From 97184a9ea1a091a0ffe0e247fab0cd73971c4aa1 Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Sun, 3 Mar 2013 18:24:32 +0100 Subject: [PATCH 24/39] Moved conversion code. Doesn't work yet. --- openlp/core/lib/plugin.py | 7 ++- openlp/core/ui/mainwindow.py | 6 ++- openlp/plugins/images/converter/__init__.py | 30 +++++++++++ .../images/converter/imageslistsaver.py | 51 +++++++++++++++++++ openlp/plugins/images/lib/mediaitem.py | 7 --- 5 files changed, 90 insertions(+), 11 deletions(-) create mode 100644 openlp/plugins/images/converter/__init__.py create mode 100644 openlp/plugins/images/converter/imageslistsaver.py diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index 58384c15d..8482428a2 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -35,6 +35,7 @@ from PyQt4 import QtCore from openlp.core.lib import Settings, Registry, UiStrings from openlp.core.utils import get_application_version +from openlp.plugins.images.converter import ImagesListSaver log = logging.getLogger(__name__) @@ -303,8 +304,10 @@ class Plugin(QtCore.QObject): if self.mediaItemClass is not None: loaded_list = Settings().get_files_from_config(self) # Now save the list to the config using our Settings class. - Settings().setValue(u'%s/%s files' % (self.settingsSection, self.name), loaded_list) - # FIXME: make sure we do not do this for the images plugin. + if self.name == u'images': + ImagesListSaver.save_converted_images_list(loaded_list) + else: + Settings().setValue(u'%s/%s files' % (self.settingsSection, self.name), loaded_list) def usesTheme(self, theme): """ diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 4a4ad529a..27f298ed2 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -51,6 +51,7 @@ from openlp.core.utils import AppLocation, LanguageManager, add_actions, get_app get_filesystem_encoding from openlp.core.utils.actions import ActionList, CategoryOrder from openlp.core.ui.firsttimeform import FirstTimeForm +from openlp.plugins.images.converter import ImagesListSaver log = logging.getLogger(__name__) @@ -851,8 +852,9 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): import_settings = Settings(temp_config, Settings.IniFormat) # Remove/rename old settings to prepare the import. import_settings.remove_obsolete_settings() - # FIXME: Convert image files - #settings.get_files_from_config() + # Convert image files + loaded_list = import_settings.get_files_from_config(ImagePlugin) + ImagesListSaver.save_converted_images_list(loaded_list) # Lets do a basic sanity check. If it contains this string we can assume it was created by OpenLP and so we'll # load what we can from it, and just silently ignore anything we don't recognise. if import_settings.value(u'SettingsImport/type') != u'OpenLP_settings_export': diff --git a/openlp/plugins/images/converter/__init__.py b/openlp/plugins/images/converter/__init__.py new file mode 100644 index 000000000..ec3c1ac15 --- /dev/null +++ b/openlp/plugins/images/converter/__init__.py @@ -0,0 +1,30 @@ +# -*- 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 imageslistsaver import ImagesListSaver diff --git a/openlp/plugins/images/converter/imageslistsaver.py b/openlp/plugins/images/converter/imageslistsaver.py new file mode 100644 index 000000000..aa70f952c --- /dev/null +++ b/openlp/plugins/images/converter/imageslistsaver.py @@ -0,0 +1,51 @@ +# -*- 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 # +############################################################################### + +import logging + +from openlp.plugins.images.lib.db import ImageFilenames + +log = logging.getLogger(__name__) + + +class ImagesListSaver(object): + """ + This class is used to save a list of images into the database. + """ + @staticmethod + def save_converted_images_list(images_list): + """ + Save the list of images into the database. + """ + for filename in images_list: + log.debug(u'Adding new image: %s', filename) + imageFile = ImageFilenames() + imageFile.group_id = 0 + imageFile.filename = unicode(filename) + self.manager.save_object(imageFile) diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 242600af2..aa7ac7e69 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -90,13 +90,6 @@ class ImageMediaItem(MediaManagerItem): self.listView.allow_internal_dnd = True self.servicePath = os.path.join(AppLocation.get_section_data_path(self.settingsSection), u'thumbnails') check_directory_exists(self.servicePath) - # Import old images list - files_from_config = Settings().get_files_from_config(self.plugin) - for old_file in files_from_config: - imagefilename = ImageFilenames() - imagefilename.group_id = 0 - imagefilename.filename = old_file - self.manager.save_object(imagefilename) # Load images from the database self.loadFullList( self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename), initial_load=True) From a5e6cc358b68dd645151feaadabf56ee89cbfd08 Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Sun, 3 Mar 2013 20:35:11 +0100 Subject: [PATCH 25/39] Moved initial images list conversion to app_startup --- openlp/core/lib/plugin.py | 8 ++------ openlp/plugins/images/imageplugin.py | 19 ++++++++++++++++++- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index 8482428a2..a82651275 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -35,7 +35,6 @@ from PyQt4 import QtCore from openlp.core.lib import Settings, Registry, UiStrings from openlp.core.utils import get_application_version -from openlp.plugins.images.converter import ImagesListSaver log = logging.getLogger(__name__) @@ -301,13 +300,10 @@ class Plugin(QtCore.QObject): # FIXME: Remove after 2.2 release. # This is needed to load the list of images/media/presentation from the config saved # before the settings rewrite. - if self.mediaItemClass is not None: + if self.mediaItemClass is not None and self.name != u'images': loaded_list = Settings().get_files_from_config(self) # Now save the list to the config using our Settings class. - if self.name == u'images': - ImagesListSaver.save_converted_images_list(loaded_list) - else: - Settings().setValue(u'%s/%s files' % (self.settingsSection, self.name), loaded_list) + Settings().setValue(u'%s/%s files' % (self.settingsSection, self.name), loaded_list) def usesTheme(self, theme): """ diff --git a/openlp/plugins/images/imageplugin.py b/openlp/plugins/images/imageplugin.py index 646d1c33a..44146d4ae 100644 --- a/openlp/plugins/images/imageplugin.py +++ b/openlp/plugins/images/imageplugin.py @@ -34,7 +34,7 @@ import logging 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.db import init_schema +from openlp.plugins.images.lib.db import init_schema, ImageFilenames log = logging.getLogger(__name__) @@ -69,6 +69,23 @@ class ImagePlugin(Plugin): 'provided by the theme.') 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 len(files_from_config) > 0: + log.debug(u'Importing images list from old config: %s' % files_from_config) + for filename in files_from_config: + imageFile = ImageFilenames() + imageFile.group_id = 0 + imageFile.filename = unicode(filename) + self.manager.save_object(imageFile) + self.mediaItem.loadFullList(self.manager.get_all_objects(ImageFilenames, + order_by_ref=ImageFilenames.filename)) + def setPluginTextStrings(self): """ Called to define all translatable texts of the plugin From 2561f995ac0716940c064cd28a7756d153f82a0a Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Mon, 4 Mar 2013 14:37:19 +0100 Subject: [PATCH 26/39] Fixed manual import of configuration file with a list of images --- openlp/core/lib/plugin.py | 6 +++ openlp/core/lib/pluginmanager.py | 11 ++++ openlp/core/lib/settings.py | 2 +- openlp/core/ui/mainwindow.py | 7 ++- openlp/plugins/images/converter/__init__.py | 30 ----------- .../images/converter/imageslistsaver.py | 51 ------------------- openlp/plugins/images/imageplugin.py | 17 ++++--- openlp/plugins/images/lib/mediaitem.py | 13 +++-- 8 files changed, 40 insertions(+), 97 deletions(-) delete mode 100644 openlp/plugins/images/converter/__init__.py delete mode 100644 openlp/plugins/images/converter/imageslistsaver.py diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index a82651275..e8f14bbc5 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -216,6 +216,12 @@ class Plugin(QtCore.QObject): if self.mediaItemClass: self.mediaItem = self.mediaItemClass(self.main_window.mediaDockManager.media_dock, self, self.icon) + def upgrade_settings(self, settings): + """ + Upgrade the settings of this plugin. + """ + pass + def addImportMenuItem(self, importMenu): """ Create a menu item and add it to the "Import" menu. diff --git a/openlp/core/lib/pluginmanager.py b/openlp/core/lib/pluginmanager.py index 43410b4a7..b39eabdde 100644 --- a/openlp/core/lib/pluginmanager.py +++ b/openlp/core/lib/pluginmanager.py @@ -169,6 +169,17 @@ class PluginManager(object): if plugin.status is not PluginStatus.Disabled: plugin.addToolsMenuItem(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): """ Loop through all the plugins and give them an opportunity to diff --git a/openlp/core/lib/settings.py b/openlp/core/lib/settings.py index 5165cc0b5..42d4e7161 100644 --- a/openlp/core/lib/settings.py +++ b/openlp/core/lib/settings.py @@ -453,7 +453,7 @@ class Settings(QtCore.QSettings): files_list = [] # We need QSettings instead of Settings here to bypass our central settings dict. # Do NOT do this anywhere else! - settings = QtCore.QSettings() + settings = QtCore.QSettings(self.fileName(), Settings.IniFormat) settings.beginGroup(plugin.settingsSection) if settings.contains(u'%s count' % plugin.name): # Get the count. diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 27f298ed2..d21b9a198 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -51,7 +51,6 @@ from openlp.core.utils import AppLocation, LanguageManager, add_actions, get_app get_filesystem_encoding from openlp.core.utils.actions import ActionList, CategoryOrder from openlp.core.ui.firsttimeform import FirstTimeForm -from openlp.plugins.images.converter import ImagesListSaver log = logging.getLogger(__name__) @@ -850,11 +849,11 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): shutil.copyfile(import_file_name, temp_config) settings = Settings() 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. import_settings.remove_obsolete_settings() - # Convert image files - loaded_list = import_settings.get_files_from_config(ImagePlugin) - ImagesListSaver.save_converted_images_list(loaded_list) # Lets do a basic sanity check. If it contains this string we can assume it was created by OpenLP and so we'll # load what we can from it, and just silently ignore anything we don't recognise. if import_settings.value(u'SettingsImport/type') != u'OpenLP_settings_export': diff --git a/openlp/plugins/images/converter/__init__.py b/openlp/plugins/images/converter/__init__.py deleted file mode 100644 index ec3c1ac15..000000000 --- a/openlp/plugins/images/converter/__init__.py +++ /dev/null @@ -1,30 +0,0 @@ -# -*- 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 imageslistsaver import ImagesListSaver diff --git a/openlp/plugins/images/converter/imageslistsaver.py b/openlp/plugins/images/converter/imageslistsaver.py deleted file mode 100644 index aa70f952c..000000000 --- a/openlp/plugins/images/converter/imageslistsaver.py +++ /dev/null @@ -1,51 +0,0 @@ -# -*- 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 # -############################################################################### - -import logging - -from openlp.plugins.images.lib.db import ImageFilenames - -log = logging.getLogger(__name__) - - -class ImagesListSaver(object): - """ - This class is used to save a list of images into the database. - """ - @staticmethod - def save_converted_images_list(images_list): - """ - Save the list of images into the database. - """ - for filename in images_list: - log.debug(u'Adding new image: %s', filename) - imageFile = ImageFilenames() - imageFile.group_id = 0 - imageFile.filename = unicode(filename) - self.manager.save_object(imageFile) diff --git a/openlp/plugins/images/imageplugin.py b/openlp/plugins/images/imageplugin.py index 44146d4ae..250fd485a 100644 --- a/openlp/plugins/images/imageplugin.py +++ b/openlp/plugins/images/imageplugin.py @@ -78,13 +78,16 @@ class ImagePlugin(Plugin): files_from_config = Settings().get_files_from_config(self) if len(files_from_config) > 0: log.debug(u'Importing images list from old config: %s' % files_from_config) - for filename in files_from_config: - imageFile = ImageFilenames() - imageFile.group_id = 0 - imageFile.filename = unicode(filename) - self.manager.save_object(imageFile) - self.mediaItem.loadFullList(self.manager.get_all_objects(ImageFilenames, - order_by_ref=ImageFilenames.filename)) + self.mediaItem.save_new_images_list(files_from_config) + + def upgrade_settings(self, settings): + """ + Upgrade the settings of this plugin. + """ + files_from_config = settings.get_files_from_config(self) + if len(files_from_config) > 0: + log.debug(u'Importing images list from old config: %s' % files_from_config) + self.mediaItem.save_new_images_list(files_from_config) def setPluginTextStrings(self): """ diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index aa7ac7e69..977bc1ca8 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -374,16 +374,21 @@ class ImageMediaItem(MediaManagerItem): if not isinstance(parent_group, ImageGroups): return # Save the new images in the database - for filename in images: + 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) + + def save_new_images_list(self, images_list, group_id=0, reload_list=True): + 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 = parent_group.id + imageFile.group_id = group_id imageFile.filename = unicode(filename) self.manager.save_object(imageFile) - self.loadFullList(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename), - initial_load=initial_load, open_group=parent_group) + if reload_list: + self.loadFullList(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename)) def dnd_move_internal(self, target): """ From 94e231cf9398efb2acb1852539773dac7044975f Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Mon, 4 Mar 2013 15:29:31 +0100 Subject: [PATCH 27/39] Fixed internal_dnd error --- openlp/plugins/images/lib/mediaitem.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 977bc1ca8..7a094a670 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -397,16 +397,21 @@ class ImageMediaItem(MediaManagerItem): items_to_move = self.listView.selectedItems() # Determine group to move images to target_group = target - if isinstance(target_group.data(0, QtCore.Qt.UserRole), ImageFilenames): + if target_group is not None and isinstance(target_group.data(0, QtCore.Qt.UserRole), ImageFilenames): target_group = target.parent() - # Don't allow moving to the Imported group - if target_group.data(0, QtCore.Qt.UserRole) is None: - return + # 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): - item.parent().removeChild(item) + 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) From 68d034e877dd717de43d0c18bfc492ca75e0a79d Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Thu, 7 Mar 2013 10:03:02 +0100 Subject: [PATCH 28/39] Revert code shortening from lp:~googol/openlp/image_groups --- openlp/core/ui/mainwindow.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 2020103bf..550ffc156 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -807,9 +807,16 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): return setting_sections = [] # Add main sections. - setting_sections.extend([self.generalSettingsSection, self.advancedSettingsSection, self.uiSettingsSection, - self.shortcutsSettingsSection, self.serviceManagerSettingsSection, self.themesSettingsSection, - self.playersSettingsSection, self.displayTagsSection, self.headerSection, u'crashreport']) + setting_sections.extend([self.generalSettingsSection]) + setting_sections.extend([self.advancedSettingsSection]) + setting_sections.extend([self.uiSettingsSection]) + setting_sections.extend([self.shortcutsSettingsSection]) + setting_sections.extend([self.serviceManagerSettingsSection]) + setting_sections.extend([self.themesSettingsSection]) + setting_sections.extend([self.playersSettingsSection]) + setting_sections.extend([self.displayTagsSection]) + setting_sections.extend([self.headerSection]) + setting_sections.extend([u'crashreport']) # Add plugin sections. setting_sections.extend([plugin.name for plugin in self.plugin_manager.plugins]) # Copy the settings file to the tmp dir, because we do not want to change the original one. From 66a41ee8ba8f1f9494818d96d140a6054f55e8b6 Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Thu, 7 Mar 2013 11:23:47 +0100 Subject: [PATCH 29/39] Fix error when doubleclicking on empty groups --- openlp/plugins/images/lib/mediaitem.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 7a094a670..176abc2d2 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -451,6 +451,9 @@ class ImageMediaItem(MediaManagerItem): 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 From 9f1b09af6ed19ea5063429d683b4a38945c336fb Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Thu, 7 Mar 2013 11:37:26 +0100 Subject: [PATCH 30/39] - Added 'add group' and 'load images' to contextmenu - Fixed focus in the 'choose group' dialog - Fixed error when clicking 'replace live background' when a group is selected --- openlp/plugins/images/lib/mediaitem.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 176abc2d2..f42d042c7 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -151,6 +151,17 @@ class ImageMediaItem(MediaManagerItem): self.listView.customContextMenuRequested.connect(self.contextMenu) self.listView.addAction(self.replaceAction) + def addCustomContextActions(self): + 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): self.addGroupAction = self.toolbar.addToolbarAction(u'addGroupAction', icon=u':/images/image_new_group.png', triggers=self.onAddGroupClick) @@ -333,12 +344,14 @@ class ImageMediaItem(MediaManagerItem): # 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) @@ -564,6 +577,9 @@ class ImageMediaItem(MediaManagerItem): translate('ImagePlugin.MediaItem', 'You must select an image to replace the background with.')): background = QtGui.QColor(Settings().value(self.settingsSection + u'/background color')) bitem = self.listView.selectedItems()[0] + if not isinstance(bitem.data(0, QtCore.Qt.UserRole), ImageFilenames): + # Only continue when an image is selected + return filename = bitem.data(0, QtCore.Qt.UserRole).filename if os.path.exists(filename): if self.live_controller.display.directImage(filename, background): From acc9014f55a6fb79cb5995b6425fef1cc662dd58 Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Mon, 11 Mar 2013 16:09:11 +0100 Subject: [PATCH 31/39] - Fixed "jumping images" bug - Show progressbar when adding new images --- openlp/plugins/images/lib/mediaitem.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index fd06a1f9a..1544729fd 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -330,6 +330,8 @@ class ImageMediaItem(MediaManagerItem): """ Add new images to the database. This method is called when adding images using the Add button or DnD. """ + self.application.set_busy_cursor() + self.main_window.displayProgressBar(len(images)) if target_group is None: # Find out if a group must be pre-selected preselect_group = None @@ -400,6 +402,7 @@ class ImageMediaItem(MediaManagerItem): imageFile.group_id = group_id imageFile.filename = unicode(filename) self.manager.save_object(imageFile) + self.main_window.incrementProgressBar() if reload_list: self.loadFullList(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename)) @@ -431,9 +434,20 @@ class ImageMediaItem(MediaManagerItem): item_data.group_id = target_group.data(0, QtCore.Qt.UserRole).id items_to_save.append(item_data) target_group.setExpanded(True) - target_group.sortChildren(0, QtCore.Qt.AscendingOrder) # 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, remote=False, context=ServiceItemContext.Service): From c420a6fc67af7918ac1b4b8657fcf4405e5f8c46 Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Mon, 11 Mar 2013 16:26:41 +0100 Subject: [PATCH 32/39] Expand a newly added group immediately --- openlp/plugins/images/lib/mediaitem.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index e14e30020..bbf84e7fd 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -561,6 +561,7 @@ class ImageMediaItem(MediaManagerItem): 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: From 441296d808bf01237c85b8361a13f90af95d5db2 Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Thu, 14 Mar 2013 11:36:59 +0100 Subject: [PATCH 33/39] - Fixed cursor staying busy when adding images - Added documentation to methods - Fixed traceback when adding media files --- openlp/core/lib/mediamanageritem.py | 2 +- openlp/plugins/images/lib/mediaitem.py | 79 +++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index e063d0015..813348e01 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -359,7 +359,7 @@ class MediaManagerItem(QtGui.QWidget): duplicates_found = True else: files_added = True - full_list.append(file) + full_list.append(filename) if full_list and files_added: if target_group is None: self.listView.clear() diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index bbf84e7fd..7a50817f6 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -152,6 +152,9 @@ class ImageMediaItem(MediaManagerItem): 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, @@ -163,10 +166,16 @@ class ImageMediaItem(MediaManagerItem): 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): + """ + Add custom buttons to the end of the toolbar + """ self.replaceAction = self.toolbar.add_toolbar_action(u'replaceAction', icon=u':/slides/slide_blank.png', triggers=self.onReplaceClick) self.resetAction = self.toolbar.add_toolbar_action(u'resetAction', @@ -175,6 +184,9 @@ class ImageMediaItem(MediaManagerItem): 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: @@ -229,6 +241,12 @@ class ImageMediaItem(MediaManagerItem): 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) @@ -248,6 +266,15 @@ class ImageMediaItem(MediaManagerItem): 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 is 0: combobox.clear() @@ -259,6 +286,15 @@ class ImageMediaItem(MediaManagerItem): 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() @@ -275,6 +311,15 @@ class ImageMediaItem(MediaManagerItem): 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() @@ -316,11 +361,14 @@ class ImageMediaItem(MediaManagerItem): 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. + Process a list for files either from the File Dialog or from Drag and Drop. + This method is overloaded from MediaManagerItem. ``files`` - The files to be loaded. + 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.loadList(files, target_group) last_dir = os.path.split(unicode(files[0]))[0] @@ -329,6 +377,15 @@ class ImageMediaItem(MediaManagerItem): 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 """ self.application.set_busy_cursor() self.main_window.displayProgressBar(len(images)) @@ -392,8 +449,21 @@ class ImageMediaItem(MediaManagerItem): 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 @@ -409,6 +479,9 @@ class ImageMediaItem(MediaManagerItem): 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 From 5c15585413fe97e86169363840c7db3c61ff2009 Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Thu, 14 Mar 2013 12:32:58 +0100 Subject: [PATCH 34/39] Added more docstrings --- openlp/core/lib/mediamanageritem.py | 10 ++++++++-- openlp/core/lib/plugin.py | 3 +++ openlp/core/lib/treewidgetwithdnd.py | 20 +++++++++++++++++--- openlp/plugins/images/imageplugin.py | 3 +++ 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 813348e01..e0af60f05 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -315,8 +315,8 @@ class MediaManagerItem(QtGui.QWidget): """ Turn file from Drag and Drop into an array so the Validate code can run it. - ``files`` - The list of files to be loaded + ``data`` + A dictionary containing the list of files to be loaded and the target """ new_files = [] error_shown = False @@ -335,6 +335,9 @@ class MediaManagerItem(QtGui.QWidget): def dnd_move_internal(self, target): """ Handle internal moving of media manager items + + ``target`` + The target of the DnD action """ pass @@ -345,6 +348,9 @@ class MediaManagerItem(QtGui.QWidget): ``files`` The files to be loaded. + + ``target_group`` + The QTreeWidgetItem of the group that will be the parent of the added files """ names = [] full_list = [] diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index 2275cc112..dd2ec2255 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -219,6 +219,9 @@ class Plugin(QtCore.QObject): def upgrade_settings(self, settings): """ Upgrade the settings of this plugin. + + ``settings`` + The Settings object containing the old settings. """ pass diff --git a/openlp/core/lib/treewidgetwithdnd.py b/openlp/core/lib/treewidgetwithdnd.py index 40a7ea253..12ee0d12f 100644 --- a/openlp/core/lib/treewidgetwithdnd.py +++ b/openlp/core/lib/treewidgetwithdnd.py @@ -64,9 +64,11 @@ class TreeWidgetWithDnD(QtGui.QTreeWidget): 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 + 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() @@ -81,6 +83,12 @@ class TreeWidgetWithDnD(QtGui.QTreeWidget): 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: @@ -89,6 +97,12 @@ class TreeWidgetWithDnD(QtGui.QTreeWidget): 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) diff --git a/openlp/plugins/images/imageplugin.py b/openlp/plugins/images/imageplugin.py index 380d3823d..54f9eaf8f 100644 --- a/openlp/plugins/images/imageplugin.py +++ b/openlp/plugins/images/imageplugin.py @@ -83,6 +83,9 @@ class ImagePlugin(Plugin): 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 len(files_from_config) > 0: From 47c48618bc2acaa9400c5001d948e0af02b3957b Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Mon, 18 Mar 2013 14:30:31 +0100 Subject: [PATCH 35/39] Fixed a traceback that occurred when calling critical_error_message_box and friends while the splashscreen is disabled in the settings. --- openlp/core/ui/mainwindow.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 381a97f20..dba2cd452 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -681,7 +681,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): ``message`` The message to be displayed. """ - self.application.splash.close() + if hasattr(self.application, u'splash'): + self.application.splash.close() QtGui.QMessageBox.critical(self, title, message) def warning_message(self, title, message): @@ -694,7 +695,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): ``message`` The message to be displayed. """ - self.application.splash.close() + if hasattr(self.application, u'splash'): + self.application.splash.close() QtGui.QMessageBox.warning(self, title, message) def information_message(self, title, message): @@ -707,7 +709,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): ``message`` The message to be displayed. """ - self.application.splash.close() + if hasattr(self.application, u'splash'): + self.application.splash.close() QtGui.QMessageBox.information(self, title, message) def onHelpWebSiteClicked(self): From 50da75cfdbd6ac5d94df5150fb838f2d57841499 Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Mon, 18 Mar 2013 14:31:47 +0100 Subject: [PATCH 36/39] - Removed __checkObject method to clean up the code - Fixed busy cursor on 'choose group' dialog --- openlp/plugins/images/lib/mediaitem.py | 38 +++++++++----------------- 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 7a50817f6..27734e9a1 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -370,6 +370,7 @@ class ImageMediaItem(MediaManagerItem): ``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) @@ -387,8 +388,6 @@ class ImageMediaItem(MediaManagerItem): ``initial_load`` When set to False, the busy cursor and progressbar will be shown while loading images """ - self.application.set_busy_cursor() - self.main_window.displayProgressBar(len(images)) if target_group is None: # Find out if a group must be pre-selected preselect_group = None @@ -445,6 +444,9 @@ class ImageMediaItem(MediaManagerItem): # 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.displayProgressBar(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), @@ -584,32 +586,18 @@ class ImageMediaItem(MediaManagerItem): service_item.add_from_image(filename, name, background) return True - def __checkObject(self, objects, newObject, edit): + def check_group_exists(self, new_group): """ - Utility method to check for an existing object. + Returns *True* if the given Group already exists in the database, otherwise *False*. - ``edit`` - If we edit an item, this should be *True*. + ``new_group`` + The ImageGroups object that contains the name of the group that will be checked """ - 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: + groups = self.manager.get_all_objects(ImageGroups, ImageGroups.group_name == new_group.group_name) + if groups: 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) + else: + return False def onAddGroupClick(self): """ @@ -630,7 +618,7 @@ class ImageMediaItem(MediaManagerItem): 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 self.checkGroupName(new_group): + 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)) From 185754f9b5a0f4857f2dcfd84744ce48304938af Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Mon, 18 Mar 2013 14:43:45 +0100 Subject: [PATCH 37/39] Add more docstrings --- openlp/plugins/images/forms/addgroupform.py | 17 ++++++++++++++++- openlp/plugins/images/forms/choosegroupform.py | 8 +++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/images/forms/addgroupform.py b/openlp/plugins/images/forms/addgroupform.py index 95eceb678..7f7986499 100644 --- a/openlp/plugins/images/forms/addgroupform.py +++ b/openlp/plugins/images/forms/addgroupform.py @@ -36,7 +36,7 @@ from openlp.plugins.images.forms.addgroupdialog import Ui_AddGroupDialog class AddGroupForm(QtGui.QDialog, Ui_AddGroupDialog): """ - Class documentation goes here. + This class implements the 'Add group' form for the Images plugin. """ def __init__(self, parent=None): """ @@ -46,6 +46,18 @@ class AddGroupForm(QtGui.QDialog, Ui_AddGroupDialog): 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() @@ -59,6 +71,9 @@ class AddGroupForm(QtGui.QDialog, Ui_AddGroupDialog): 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.')) diff --git a/openlp/plugins/images/forms/choosegroupform.py b/openlp/plugins/images/forms/choosegroupform.py index 5997d738d..bbb57255c 100644 --- a/openlp/plugins/images/forms/choosegroupform.py +++ b/openlp/plugins/images/forms/choosegroupform.py @@ -34,7 +34,7 @@ from openlp.plugins.images.forms.choosegroupdialog import Ui_ChooseGroupDialog class ChooseGroupForm(QtGui.QDialog, Ui_ChooseGroupDialog): """ - Class documentation goes here. + This class implements the 'Choose group' form for the Images plugin. """ def __init__(self, parent=None): """ @@ -44,6 +44,12 @@ class ChooseGroupForm(QtGui.QDialog, Ui_ChooseGroupDialog): 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: From 24a51c0eec6d13f0cadfacdb2fb6af08f56fb443 Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Mon, 18 Mar 2013 15:35:19 +0100 Subject: [PATCH 38/39] Add tests for hook_upgrade_plugin_settings --- .../openlp_core_lib/test_pluginmanager.py | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/tests/functional/openlp_core_lib/test_pluginmanager.py b/tests/functional/openlp_core_lib/test_pluginmanager.py index 7b2a0ba31..5ae9341cb 100644 --- a/tests/functional/openlp_core_lib/test_pluginmanager.py +++ b/tests/functional/openlp_core_lib/test_pluginmanager.py @@ -6,7 +6,7 @@ from unittest import TestCase from mock import MagicMock 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): @@ -184,7 +184,7 @@ class TestPluginManager(TestCase): # WHEN: We run 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, \ u'The addExportMenuItem() method should not have been called.' @@ -204,6 +204,41 @@ class TestPluginManager(TestCase): # THEN: The addExportMenuItem() method should have been called 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): """ Test running the hook_tools_menu() method with a disabled plugin From 57651493ff9d08919dcf5e37e4a62d39d4d5e4f8 Mon Sep 17 00:00:00 2001 From: Arjan Schrijver Date: Mon, 18 Mar 2013 23:04:09 +0100 Subject: [PATCH 39/39] - Removed wrong len() use - Replaced wrong 'is' use with '==' - Added tests for ImageMediaItem.save_new_images_list() --- openlp/plugins/images/imageplugin.py | 4 +- openlp/plugins/images/lib/mediaitem.py | 14 +-- .../openlp_plugins/images/__init__.py | 0 .../openlp_plugins/images/test_lib.py | 112 ++++++++++++++++++ 4 files changed, 121 insertions(+), 9 deletions(-) create mode 100644 tests/functional/openlp_plugins/images/__init__.py create mode 100644 tests/functional/openlp_plugins/images/test_lib.py diff --git a/openlp/plugins/images/imageplugin.py b/openlp/plugins/images/imageplugin.py index 2dfb5e582..5b41f6761 100644 --- a/openlp/plugins/images/imageplugin.py +++ b/openlp/plugins/images/imageplugin.py @@ -75,7 +75,7 @@ class ImagePlugin(Plugin): Plugin.app_startup(self) # Convert old settings-based image list to the database files_from_config = Settings().get_files_from_config(self) - if len(files_from_config) > 0: + 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) @@ -87,7 +87,7 @@ class ImagePlugin(Plugin): The Settings object containing the old settings. """ files_from_config = settings.get_files_from_config(self) - if len(files_from_config) > 0: + 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) diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 604244c85..7a98a18d2 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -227,7 +227,7 @@ class ImageMediaItem(MediaManagerItem): 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 is 0: + if item_data.parent_id == 0: self.listView.takeTopLevelItem(self.listView.indexOfTopLevelItem(row_item)) else: row_item.parent().removeChild(row_item) @@ -256,7 +256,7 @@ class ImageMediaItem(MediaManagerItem): group.setText(0, image_group.group_name) group.setData(0, QtCore.Qt.UserRole, image_group) group.setIcon(0, folder_icon) - if parent_group_id is 0: + if parent_group_id == 0: self.listView.addTopLevelItem(group) else: group_list[parent_group_id].addChild(group) @@ -276,7 +276,7 @@ class ImageMediaItem(MediaManagerItem): ``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 is 0: + 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) @@ -349,7 +349,7 @@ class ImageMediaItem(MediaManagerItem): item_name.setIcon(0, icon) item_name.setToolTip(0, imageFile.filename) item_name.setData(0, QtCore.Qt.UserRole, imageFile) - if imageFile.group_id is 0: + if imageFile.group_id == 0: self.listView.addTopLevelItem(item_name) else: group_items[imageFile.group_id].addChild(item_name) @@ -392,7 +392,7 @@ class ImageMediaItem(MediaManagerItem): # Find out if a group must be pre-selected preselect_group = None selected_items = self.listView.selectedItems() - if len(selected_items) > 0: + if selected_items: selected_item = selected_items[0] if isinstance(selected_item.data(0, QtCore.Qt.UserRole), ImageFilenames): selected_item = selected_item.parent() @@ -475,7 +475,7 @@ class ImageMediaItem(MediaManagerItem): imageFile.filename = unicode(filename) self.manager.save_object(imageFile) self.main_window.increment_progress_bar() - if reload_list: + 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): @@ -606,7 +606,7 @@ class ImageMediaItem(MediaManagerItem): # Find out if a group must be pre-selected preselect_group = 0 selected_items = self.listView.selectedItems() - if len(selected_items) > 0: + if selected_items: selected_item = selected_items[0] if isinstance(selected_item.data(0, QtCore.Qt.UserRole), ImageFilenames): selected_item = selected_item.parent() diff --git a/tests/functional/openlp_plugins/images/__init__.py b/tests/functional/openlp_plugins/images/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/functional/openlp_plugins/images/test_lib.py b/tests/functional/openlp_plugins/images/test_lib.py new file mode 100644 index 000000000..eae30a113 --- /dev/null +++ b/tests/functional/openlp_plugins/images/test_lib.py @@ -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'