Merge lp:~trb143/openlp/dnd into lp:openlp

Proposed by Tim Bentley
Status: Merged
Approved by: Andreas Preikschat
Approved revision: 1701
Merged at revision: 1701
Proposed branch: lp:~trb143/openlp/dnd
Merge into: lp:openlp
Diff against target: 367 lines (+153/-34)
9 files modified
openlp.pyw (+2/-0)
openlp/core/lib/__init__.py (+1/-1)
openlp/core/lib/listwidgetwithdnd.py (+50/-0)
openlp/core/lib/mediamanageritem.py (+52/-14)
openlp/core/ui/servicemanager.py (+29/-9)
openlp/plugins/images/lib/mediaitem.py (+3/-0)
openlp/plugins/media/lib/mediaitem.py (+12/-9)
openlp/plugins/presentations/lib/mediaitem.py (+3/-0)
openlp/plugins/songusage/songusageplugin.py (+1/-1)
To merge this branch: bzr merge lp:~trb143/openlp/dnd
Reviewer Review Type Date Requested Status
Andreas Preikschat (community) Approve
Raoul Snyman Approve
Review via email: mp+70399@code.launchpad.net

This proposal supersedes a proposal from 2011-08-03.

Description of the change

Move all the Drag and Drop changes from the B1 tree which is stuck at present.
It is no possible to Dnd Images, Presentations and Media into the plugins as wee as dragging a otz file into the service manager.

Can import Folders now.
Remove duplicate popups now.

Servicefile load now checks for unsaved file and asks to save.

To post a comment you must log in.
Revision history for this message
Andreas Preikschat (googol-deactivatedaccount) wrote : Posted in a previous version of this proposal

A few things:

1) line 74: hasUrls should be hasUrls()
2) line 143: spelling (runn)
3) line 146-147, 165-166: wrong indents
4) Dropping a few files (e. g. images) makes the mainwindow process bar going from 0 to 100 (in one step) as many times as many files I drop (instead of increasing the bar after processing each file)
5) when I DnD a service file to the service manager without asking me to save an already opened service
6) I thought you were going to add support for folders as well (e. g. image library)
7) line 220-222, ...: I do not like this. All ListWidgets which allow dnd need to connect to the signal, thus it should be taken care of when activating the dnd. I am not sure if we should call/have an activate method. I was thinking about specifying this in the constructor or to have an attribute (like hasDnD) in the media manager.
8) When I DnD two files (e. g. "Copy of Foo" and "Foo") a dialog pops up and says that "Copy of Foo" is already in the list. I guess this will be/is fixed in your b1 branch?
9) The "duplicate error message" does not pop up once, instead it pop ups x times which can block the program (as you have to close x dialogs)

review: Needs Fixing
Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

line 74: hasUrls should be hasUrls()

review: Needs Fixing
Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

You have a conflict :-(

review: Needs Fixing
Revision history for this message
Raoul Snyman (raoul-snyman) :
review: Approve
Revision history for this message
Andreas Preikschat (googol-deactivatedaccount) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'openlp.pyw'
2--- openlp.pyw 2011-07-23 22:52:34 +0000
3+++ openlp.pyw 2011-08-04 04:56:04 +0000
4@@ -127,6 +127,8 @@
5 # now kill the splashscreen
6 self.splash.finish(self.mainWindow)
7 log.debug(u'Splashscreen closed')
8+ # make sure Qt really display the splash screen
9+ self.processEvents()
10 self.mainWindow.repaint()
11 self.processEvents()
12 if not has_run_wizard:
13
14=== modified file 'openlp/core/lib/__init__.py'
15--- openlp/core/lib/__init__.py 2011-07-30 07:34:37 +0000
16+++ openlp/core/lib/__init__.py 2011-08-04 04:56:04 +0000
17@@ -233,9 +233,9 @@
18 except IOError:
19 pass
20
21+from eventreceiver import Receiver
22 from listwidgetwithdnd import ListWidgetWithDnD
23 from formattingtags import FormattingTags
24-from eventreceiver import Receiver
25 from spelltextedit import SpellTextEdit
26 from settingsmanager import SettingsManager
27 from plugin import PluginStatus, StringContent, Plugin
28
29=== modified file 'openlp/core/lib/listwidgetwithdnd.py'
30--- openlp/core/lib/listwidgetwithdnd.py 2011-06-12 16:02:52 +0000
31+++ openlp/core/lib/listwidgetwithdnd.py 2011-08-04 04:56:04 +0000
32@@ -27,8 +27,12 @@
33 """
34 Extend QListWidget to handle drag and drop functionality
35 """
36+import os.path
37+
38 from PyQt4 import QtCore, QtGui
39
40+from openlp.core.lib import Receiver
41+
42 class ListWidgetWithDnD(QtGui.QListWidget):
43 """
44 Provide a list widget to store objects and handle drag and drop events
45@@ -41,6 +45,16 @@
46 self.mimeDataText = name
47 assert(self.mimeDataText)
48
49+ def activateDnD(self):
50+ """
51+ Activate DnD of widget
52+ """
53+ self.setAcceptDrops(True)
54+ self.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
55+ QtCore.QObject.connect(Receiver.get_receiver(),
56+ QtCore.SIGNAL(u'%s_dnd' % self.mimeDataText),
57+ self.parent().loadFile)
58+
59 def mouseMoveEvent(self, event):
60 """
61 Drag and drop event does not care what data is selected
62@@ -58,3 +72,39 @@
63 drag.setMimeData(mimeData)
64 mimeData.setText(self.mimeDataText)
65 drag.start(QtCore.Qt.CopyAction)
66+
67+ def dragEnterEvent(self, event):
68+ if event.mimeData().hasUrls():
69+ event.accept()
70+ else:
71+ event.ignore()
72+
73+ def dragMoveEvent(self, event):
74+ if event.mimeData().hasUrls():
75+ event.setDropAction(QtCore.Qt.CopyAction)
76+ event.accept()
77+ else:
78+ event.ignore()
79+
80+ def dropEvent(self, event):
81+ """
82+ Receive drop event check if it is a file and process it if it is.
83+
84+ ``event``
85+ Handle of the event pint passed
86+ """
87+ if event.mimeData().hasUrls():
88+ event.setDropAction(QtCore.Qt.CopyAction)
89+ event.accept()
90+ files = []
91+ for url in event.mimeData().urls():
92+ localFile = unicode(url.toLocalFile())
93+ if os.path.isfile(localFile):
94+ files.append(localFile)
95+ elif os.path.isdir(localFile):
96+ listing = os.listdir(localFile)
97+ for file in listing:
98+ files.append(os.path.join(localFile,file))
99+ Receiver.send_message(u'%s_dnd' % self.mimeDataText,files)
100+ else:
101+ event.ignore()
102
103=== modified file 'openlp/core/lib/mediamanageritem.py'
104--- openlp/core/lib/mediamanageritem.py 2011-07-23 21:29:24 +0000
105+++ openlp/core/lib/mediamanageritem.py 2011-08-04 04:56:04 +0000
106@@ -252,7 +252,6 @@
107 self.listView.setSelectionMode(
108 QtGui.QAbstractItemView.ExtendedSelection)
109 self.listView.setAlternatingRowColors(True)
110- self.listView.setDragEnabled(True)
111 self.listView.setObjectName(u'%sListView' % self.plugin.name)
112 # Add to pageLayout
113 self.pageLayout.addWidget(self.listView)
114@@ -339,26 +338,65 @@
115 log.info(u'New files(s) %s', unicode(files))
116 if files:
117 Receiver.send_message(u'cursor_busy')
118- names = []
119- for count in range(0, self.listView.count()):
120- names.append(self.listView.item(count).text())
121- newFiles = []
122- for file in files:
123- filename = os.path.split(unicode(file))[1]
124- if filename in names:
125+ self.validateAndLoad(files)
126+ Receiver.send_message(u'cursor_normal')
127+
128+ def loadFile(self, files):
129+ """
130+ Turn file from Drag and Drop into an array so the Validate code
131+ can run it.
132+
133+ ``files``
134+ The list of files to be loaded
135+ """
136+ newFiles = []
137+ errorShown = False
138+ for file in files:
139+ type = file.split(u'.')[-1]
140+ if type.lower() not in self.onNewFileMasks:
141+ if not errorShown:
142 critical_error_message_box(
143- UiStrings().Duplicate,
144+ translate('OpenLP.MediaManagerItem',
145+ 'Invalid File Type'),
146 unicode(translate('OpenLP.MediaManagerItem',
147- 'Duplicate filename %s.\nThis filename is already in '
148- 'the list')) % filename)
149- else:
150- newFiles.append(file)
151+ 'Invalid File %s.\nSuffix not supported'))
152+ % file)
153+ errorShown = True
154+ else:
155+ newFiles.append(file)
156+ if file:
157+ self.validateAndLoad(newFiles)
158+
159+ def validateAndLoad(self, files):
160+ """
161+ Process a list for files either from the File Dialog or from Drag and
162+ Drop
163+
164+ ``files``
165+ The files to be loaded
166+ """
167+ names = []
168+ for count in range(0, self.listView.count()):
169+ names.append(self.listView.item(count).text())
170+ newFiles = []
171+ duplicatesFound = False
172+ for file in files:
173+ filename = os.path.split(unicode(file))[1]
174+ if filename in names:
175+ duplicatesFound = True
176+ else:
177+ newFiles.append(file)
178+ if newFiles:
179 self.loadList(newFiles)
180 lastDir = os.path.split(unicode(files[0]))[0]
181 SettingsManager.set_last_dir(self.settingsSection, lastDir)
182 SettingsManager.set_list(self.settingsSection,
183 self.settingsSection, self.getFileList())
184- Receiver.send_message(u'cursor_normal')
185+ if duplicatesFound:
186+ critical_error_message_box(
187+ UiStrings().Duplicate,
188+ unicode(translate('OpenLP.MediaManagerItem',
189+ 'Duplicate files found on import and ignored.')))
190
191 def contextMenu(self, point):
192 item = self.listView.itemAt(point)
193
194=== modified file 'openlp/core/ui/servicemanager.py'
195--- openlp/core/ui/servicemanager.py 2011-07-24 17:52:53 +0000
196+++ openlp/core/ui/servicemanager.py 2011-08-04 04:56:04 +0000
197@@ -408,20 +408,33 @@
198 return False
199 self.newFile()
200
201- def onLoadServiceClicked(self):
202+ def onLoadServiceClicked(self, loadFile=None):
203+ """
204+ Loads the service file and saves the existing one it there is one
205+ unchanged
206+
207+ ``loadFile``
208+ The service file to the loaded. Will be None is from menu so
209+ selection will be required.
210+ """
211 if self.isModified():
212 result = self.saveModifiedService()
213 if result == QtGui.QMessageBox.Cancel:
214 return False
215 elif result == QtGui.QMessageBox.Save:
216 self.saveFile()
217- fileName = unicode(QtGui.QFileDialog.getOpenFileName(self.mainwindow,
218- translate('OpenLP.ServiceManager', 'Open File'),
219- SettingsManager.get_last_dir(
220- self.mainwindow.serviceSettingsSection),
221- translate('OpenLP.ServiceManager', 'OpenLP Service Files (*.osz)')))
222- if not fileName:
223- return False
224+ if not loadFile:
225+ fileName = unicode(QtGui.QFileDialog.getOpenFileName(
226+ self.mainwindow,
227+ translate('OpenLP.ServiceManager', 'Open File'),
228+ SettingsManager.get_last_dir(
229+ self.mainwindow.serviceSettingsSection),
230+ translate('OpenLP.ServiceManager',
231+ 'OpenLP Service Files (*.osz)')))
232+ if not fileName:
233+ return False
234+ else:
235+ fileName = loadFile
236 SettingsManager.set_last_dir(self.mainwindow.serviceSettingsSection,
237 split_filename(fileName)[0])
238 self.loadFile(fileName)
239@@ -1239,7 +1252,14 @@
240 Handle of the event pint passed
241 """
242 link = event.mimeData()
243- if link.hasText():
244+ if event.mimeData().hasUrls():
245+ event.setDropAction(QtCore.Qt.CopyAction)
246+ event.accept()
247+ for url in event.mimeData().urls():
248+ filename = unicode(url.toLocalFile())
249+ if filename.endswith(u'.osz'):
250+ self.onLoadServiceClicked(filename)
251+ elif event.mimeData().hasText():
252 plugin = unicode(event.mimeData().text())
253 item = self.serviceManagerList.itemAt(event.pos())
254 # ServiceManager started the drag and drop
255
256=== modified file 'openlp/plugins/images/lib/mediaitem.py'
257--- openlp/plugins/images/lib/mediaitem.py 2011-07-03 08:13:48 +0000
258+++ openlp/plugins/images/lib/mediaitem.py 2011-08-04 04:56:04 +0000
259@@ -52,6 +52,8 @@
260 self.hasSearch = True
261 QtCore.QObject.connect(Receiver.get_receiver(),
262 QtCore.SIGNAL(u'live_theme_changed'), self.liveThemeChanged)
263+ # Allow DnD from the desktop
264+ self.listView.activateDnD()
265
266 def retranslateUi(self):
267 self.onNewPrompt = translate('ImagePlugin.MediaItem',
268@@ -131,6 +133,7 @@
269 icon = self.iconFromFile(imageFile, thumb)
270 item_name = QtGui.QListWidgetItem(filename)
271 item_name.setIcon(icon)
272+ item_name.setToolTip(imageFile)
273 item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(imageFile))
274 self.listView.addItem(item_name)
275 if not initialLoad:
276
277=== modified file 'openlp/plugins/media/lib/mediaitem.py'
278--- openlp/plugins/media/lib/mediaitem.py 2011-07-03 08:13:48 +0000
279+++ openlp/plugins/media/lib/mediaitem.py 2011-08-04 04:56:04 +0000
280@@ -39,6 +39,8 @@
281
282 log = logging.getLogger(__name__)
283
284+CLAPPERBOARD = QtGui.QPixmap(u':/media/media_video.png').toImage()
285+
286 class MediaMediaItem(MediaManagerItem):
287 """
288 This is the custom media manager item for Media Slides.
289@@ -48,8 +50,7 @@
290 def __init__(self, parent, plugin, icon):
291 self.IconPath = u'images/image'
292 self.background = False
293- self.PreviewFunction = QtGui.QPixmap(
294- u':/media/media_video.png').toImage()
295+ self.PreviewFunction = CLAPPERBOARD
296 MediaManagerItem.__init__(self, parent, plugin, icon)
297 self.singleServiceItem = False
298 self.hasSearch = True
299@@ -60,6 +61,8 @@
300 QtCore.QObject.connect(Receiver.get_receiver(),
301 QtCore.SIGNAL(u'openlp_phonon_creation'),
302 self.createPhonon)
303+ # Allow DnD from the desktop
304+ self.listView.activateDnD()
305
306 def retranslateUi(self):
307 self.onNewPrompt = translate('MediaPlugin.MediaItem', 'Select Media')
308@@ -201,17 +204,17 @@
309 SettingsManager.set_list(self.settingsSection,
310 u'media', self.getFileList())
311
312- def loadList(self, files):
313+ def loadList(self, media):
314 # Sort the themes by its filename considering language specific
315 # characters. lower() is needed for windows!
316- files.sort(cmp=locale.strcoll,
317+ media.sort(cmp=locale.strcoll,
318 key=lambda filename: os.path.split(unicode(filename))[1].lower())
319- for file in files:
320- filename = os.path.split(unicode(file))[1]
321+ for track in media:
322+ filename = os.path.split(unicode(track))[1]
323 item_name = QtGui.QListWidgetItem(filename)
324- img = QtGui.QPixmap(u':/media/media_video.png').toImage()
325- item_name.setIcon(build_icon(img))
326- item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file))
327+ item_name.setIcon(build_icon(CLAPPERBOARD))
328+ item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(track))
329+ item_name.setToolTip(track)
330 self.listView.addItem(item_name)
331
332 def createPhonon(self):
333
334=== modified file 'openlp/plugins/presentations/lib/mediaitem.py'
335--- openlp/plugins/presentations/lib/mediaitem.py 2011-06-12 17:56:11 +0000
336+++ openlp/plugins/presentations/lib/mediaitem.py 2011-08-04 04:56:04 +0000
337@@ -58,6 +58,8 @@
338 self.hasSearch = True
339 QtCore.QObject.connect(Receiver.get_receiver(),
340 QtCore.SIGNAL(u'mediaitem_presentation_rebuild'), self.rebuild)
341+ # Allow DnD from the desktop
342+ self.listView.activateDnD()
343
344 def retranslateUi(self):
345 """
346@@ -205,6 +207,7 @@
347 item_name = QtGui.QListWidgetItem(filename)
348 item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file))
349 item_name.setIcon(icon)
350+ item_name.setToolTip(file)
351 self.listView.addItem(item_name)
352 Receiver.send_message(u'cursor_normal')
353 if not initialLoad:
354
355=== modified file 'openlp/plugins/songusage/songusageplugin.py'
356--- openlp/plugins/songusage/songusageplugin.py 2011-07-23 21:29:24 +0000
357+++ openlp/plugins/songusage/songusageplugin.py 2011-08-04 04:56:04 +0000
358@@ -91,8 +91,8 @@
359 self.toolsMenu.addAction(self.songUsageMenu.menuAction())
360 self.songUsageMenu.addAction(self.songUsageStatus)
361 self.songUsageMenu.addSeparator()
362+ self.songUsageMenu.addAction(self.songUsageReport)
363 self.songUsageMenu.addAction(self.songUsageDelete)
364- self.songUsageMenu.addAction(self.songUsageReport)
365 self.songUsageActiveButton = QtGui.QToolButton(
366 self.formparent.statusBar)
367 self.songUsageActiveButton.setCheckable(True)