Project/ProjectTranslationsBrowser.py

changeset 0
de9c2efb9d02
child 12
1d8dd9706f46
equal deleted inserted replaced
-1:000000000000 0:de9c2efb9d02
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2002 - 2009 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a class used to display the translations part of the project.
8 """
9
10 import os
11 import sys
12 import shutil
13 import fnmatch
14
15 from PyQt4.QtCore import *
16 from PyQt4.QtGui import *
17
18 from E4Gui.E4Application import e4App
19
20 from ProjectBrowserModel import ProjectBrowserFileItem, \
21 ProjectBrowserSimpleDirectoryItem, ProjectBrowserDirectoryItem, \
22 ProjectBrowserTranslationType
23 from ProjectBaseBrowser import ProjectBaseBrowser
24
25 from UI.DeleteFilesConfirmationDialog import DeleteFilesConfirmationDialog
26 import UI.PixmapCache
27
28 import Preferences
29 import Utilities
30
31 class ProjectTranslationsBrowser(ProjectBaseBrowser):
32 """
33 A class used to display the translations part of the project.
34
35 @signal linguistFile(string) emitted to open a translation file with
36 Qt-Linguist
37 @signal appendStdout(string) emitted after something was received from
38 a QProcess on stdout
39 @signal appendStderr(string) emitted after something was received from
40 a QProcess on stderr
41 @signal sourceFile(string) emitted to open a translation file in an editor
42 @signal closeSourceWindow(string) emitted after a file has been removed/deleted
43 from the project
44 @signal trpreview(string list) emitted to preview translations in the
45 translations previewer
46 @signal showMenu(string, QMenu) emitted when a menu is about to be shown. The name
47 of the menu and a reference to the menu are given.
48 """
49 def __init__(self, project, parent=None):
50 """
51 Constructor
52
53 @param project reference to the project object
54 @param parent parent widget of this browser (QWidget)
55 """
56 ProjectBaseBrowser.__init__(self, project, ProjectBrowserTranslationType, parent)
57 self.isTranslationsBrowser = True
58
59 self.selectedItemsFilter = \
60 [ProjectBrowserFileItem, ProjectBrowserSimpleDirectoryItem]
61
62 self.setWindowTitle(self.trUtf8('Translations'))
63
64 self.setWhatsThis(self.trUtf8(
65 """<b>Project Translations Browser</b>"""
66 """<p>This allows to easily see all translations contained in the current"""
67 """ project. Several actions can be executed via the context menu.</p>"""
68 ))
69
70 self.lreleaseProc = None
71 self.lreleaseProcRunning = False
72 self.pylupdateProc = None
73 self.pylupdateProcRunning = False
74
75 def _createPopupMenus(self):
76 """
77 Protected overloaded method to generate the popup menu.
78 """
79 self.menuActions = []
80 self.multiMenuActions = []
81 self.dirMenuActions = []
82 self.dirMultiMenuActions = []
83
84 self.tsMenuActions = []
85 self.qmMenuActions = []
86 self.tsprocMenuActions = []
87 self.qmprocMenuActions = []
88
89 self.tsMultiMenuActions = []
90 self.qmMultiMenuActions = []
91 self.tsprocMultiMenuActions = []
92 self.qmprocMultiMenuActions = []
93
94 self.tsprocDirMenuActions = []
95 self.qmprocDirMenuActions = []
96
97 self.tsprocBackMenuActions = []
98 self.qmprocBackMenuActions = []
99
100 self.menu = QMenu(self)
101 if self.project.getProjectType() in \
102 ["Qt4", "Qt4C", "E4Plugin", "PySide", "PySideC"]:
103 act = self.menu.addAction(self.trUtf8('Generate translation'),
104 self.__generateSelected)
105 self.tsMenuActions.append(act)
106 self.tsprocMenuActions.append(act)
107 act = self.menu.addAction(\
108 self.trUtf8('Generate translation (with obsolete)'),
109 self.__generateObsoleteSelected)
110 self.tsMenuActions.append(act)
111 self.tsprocMenuActions.append(act)
112 act = self.menu.addAction(self.trUtf8('Generate all translations'),
113 self.__generateAll)
114 self.tsprocMenuActions.append(act)
115 act = self.menu.addAction(\
116 self.trUtf8('Generate all translations (with obsolete)'),
117 self.__generateObsoleteAll)
118 self.tsprocMenuActions.append(act)
119 self.menu.addSeparator()
120 act = self.menu.addAction(self.trUtf8('Open in Qt-Linguist'),
121 self._openItem)
122 self.tsMenuActions.append(act)
123 act = self.menu.addAction(self.trUtf8('Open in Editor'),
124 self.__openFileInEditor)
125 self.tsMenuActions.append(act)
126 self.menu.addSeparator()
127 act = self.menu.addAction(self.trUtf8('Release translation'),
128 self.__releaseSelected)
129 self.tsMenuActions.append(act)
130 self.qmprocMenuActions.append(act)
131 act = self.menu.addAction(self.trUtf8('Release all translations'),
132 self.__releaseAll)
133 self.qmprocMenuActions.append(act)
134 self.menu.addSeparator()
135 act = self.menu.addAction(self.trUtf8('Preview translation'),
136 self.__TRPreview)
137 self.qmMenuActions.append(act)
138 act = self.menu.addAction(self.trUtf8('Preview all translations'),
139 self.__TRPreviewAll)
140 self.menu.addSeparator()
141 else:
142 if self.hooks["extractMessages"] is not None:
143 act = self.menu.addAction(
144 self.hooksMenuEntries.get("extractMessages",
145 self.trUtf8('Extract messages')),
146 self.__extractMessages)
147 self.menuActions.append(act)
148 self.menu.addSeparator()
149 if self.hooks["generateSelected"] is not None:
150 act = self.menu.addAction(
151 self.hooksMenuEntries.get("generateSelected",
152 self.trUtf8('Generate translation')),
153 self.__generateSelected)
154 self.tsMenuActions.append(act)
155 self.tsprocMenuActions.append(act)
156 if self.hooks["generateSelectedWithObsolete"] is not None:
157 act = self.menu.addAction(
158 self.hooksMenuEntries.get("generateSelectedWithObsolete",
159 self.trUtf8('Generate translation (with obsolete)')),
160 self.__generateObsoleteSelected)
161 self.tsMenuActions.append(act)
162 self.tsprocMenuActions.append(act)
163 if self.hooks["generateAll"] is not None:
164 act = self.menu.addAction(
165 self.hooksMenuEntries.get("generateAll",
166 self.trUtf8('Generate all translations')),
167 self.__generateAll)
168 self.tsprocMenuActions.append(act)
169 if self.hooks["generateAllWithObsolete"] is not None:
170 act = self.menu.addAction(
171 self.hooksMenuEntries.get("generateAllWithObsolete",
172 self.trUtf8('Generate all translations (with obsolete)')),
173 self.__generateObsoleteAll)
174 self.tsprocMenuActions.append(act)
175 self.menu.addSeparator()
176 act = self.menu.addAction(self.trUtf8('Open in Editor'),
177 self.__openFileInEditor)
178 self.tsMenuActions.append(act)
179 self.menu.addSeparator()
180 if self.hooks["releaseSelected"] is not None:
181 act = self.menu.addAction(
182 self.hooksMenuEntries.get("releaseSelected",
183 self.trUtf8('Release translation')),
184 self.__releaseSelected)
185 self.tsMenuActions.append(act)
186 self.qmprocMenuActions.append(act)
187 if self.hooks["releaseAll"] is not None:
188 act = self.menu.addAction(
189 self.hooksMenuEntries.get("releaseAll",
190 self.trUtf8('Release all translations')),
191 self.__releaseAll)
192 self.qmprocMenuActions.append(act)
193 self.menu.addSeparator()
194 act = self.menu.addAction(self.trUtf8('Remove from project'),
195 self.__removeLanguageFile)
196 self.menuActions.append(act)
197 act = self.menu.addAction(self.trUtf8('Delete'), self.__deleteLanguageFile)
198 self.menuActions.append(act)
199 self.menu.addSeparator()
200 self.menu.addAction(self.trUtf8('Add translation...'), self.project.addLanguage)
201 self.menu.addAction(self.trUtf8('Add translation files...'),
202 self.__addTranslationFiles)
203 self.menu.addSeparator()
204 self.menu.addAction(self.trUtf8('Copy Path to Clipboard'),
205 self._copyToClipboard)
206 self.menu.addSeparator()
207 self.menu.addAction(self.trUtf8('Configure...'), self._configure)
208
209 self.backMenu = QMenu(self)
210 if self.project.getProjectType() in \
211 ["Qt4", "Qt4C", "E4Plugin", "PySide", "PySideC"]:
212 act = self.backMenu.addAction(self.trUtf8('Generate all translations'),
213 self.__generateAll)
214 self.tsprocBackMenuActions.append(act)
215 act = self.backMenu.addAction(\
216 self.trUtf8('Generate all translations (with obsolete)'),
217 self.__generateObsoleteAll)
218 self.tsprocBackMenuActions.append(act)
219 act = self.backMenu.addAction(self.trUtf8('Release all translations'),
220 self.__releaseAll)
221 self.qmprocBackMenuActions.append(act)
222 self.backMenu.addSeparator()
223 act = self.backMenu.addAction(self.trUtf8('Preview all translations'),
224 self.__TRPreview)
225 else:
226 if self.hooks["extractMessages"] is not None:
227 act = self.backMenu.addAction(
228 self.hooksMenuEntries.get("extractMessages",
229 self.trUtf8('Extract messages')),
230 self.__extractMessages)
231 self.backMenu.addSeparator()
232 if self.hooks["generateAll"] is not None:
233 act = self.backMenu.addAction(
234 self.hooksMenuEntries.get("generateAll",
235 self.trUtf8('Generate all translations')),
236 self.__generateAll)
237 self.tsprocBackMenuActions.append(act)
238 if self.hooks["generateAllWithObsolete"] is not None:
239 act = self.backMenu.addAction(
240 self.hooksMenuEntries.get("generateAllWithObsolete",
241 self.trUtf8('Generate all translations (with obsolete)')),
242 self.__generateObsoleteAll)
243 self.tsprocBackMenuActions.append(act)
244 if self.hooks["releaseAll"] is not None:
245 act = self.backMenu.addAction(
246 self.hooksMenuEntries.get("releaseAll",
247 self.trUtf8('Release all translations')),
248 self.__releaseAll)
249 self.qmprocBackMenuActions.append(act)
250 self.backMenu.addSeparator()
251 self.backMenu.addAction(self.trUtf8('Add translation...'),
252 self.project.addLanguage)
253 self.backMenu.addAction(self.trUtf8('Add translation files...'),
254 self.__addTranslationFiles)
255 self.backMenu.addSeparator()
256 self.backMenu.addAction(self.trUtf8('Configure...'), self._configure)
257 self.backMenu.setEnabled(False)
258
259 # create the menu for multiple selected files
260 self.multiMenu = QMenu(self)
261 if self.project.getProjectType() in \
262 ["Qt4", "Qt4C", "E4Plugin", "PySide", "PySideC"]:
263 act = self.multiMenu.addAction(self.trUtf8('Generate translations'),
264 self.__generateSelected)
265 self.tsMultiMenuActions.append(act)
266 self.tsprocMultiMenuActions.append(act)
267 act = self.multiMenu.addAction(\
268 self.trUtf8('Generate translations (with obsolete)'),
269 self.__generateObsoleteSelected)
270 self.tsMultiMenuActions.append(act)
271 self.tsprocMultiMenuActions.append(act)
272 self.multiMenu.addSeparator()
273 act = self.multiMenu.addAction(\
274 self.trUtf8('Open in Qt-Linguist'), self._openItem)
275 self.tsMultiMenuActions.append(act)
276 act = self.multiMenu.addAction(self.trUtf8('Open in Editor'),
277 self.__openFileInEditor)
278 self.tsMultiMenuActions.append(act)
279 self.multiMenu.addSeparator()
280 act = self.multiMenu.addAction(self.trUtf8('Release translations'),
281 self.__releaseSelected)
282 self.tsMultiMenuActions.append(act)
283 self.qmprocMultiMenuActions.append(act)
284 self.multiMenu.addSeparator()
285 act = self.multiMenu.addAction(self.trUtf8('Preview translations'),
286 self.__TRPreview)
287 self.qmMultiMenuActions.append(act)
288 else:
289 if self.hooks["extractMessages"] is not None:
290 act = self.multiMenu.addAction(
291 self.hooksMenuEntries.get("extractMessages",
292 self.trUtf8('Extract messages')),
293 self.__extractMessages)
294 self.multiMenuActions.append(act)
295 self.multiMenu.addSeparator()
296 if self.hooks["generateSelected"] is not None:
297 act = self.multiMenu.addAction(
298 self.hooksMenuEntries.get("generateSelected",
299 self.trUtf8('Generate translations')),
300 self.__generateSelected)
301 self.tsMultiMenuActions.append(act)
302 self.tsprocMultiMenuActions.append(act)
303 if self.hooks["generateSelectedWithObsolete"] is not None:
304 act = self.multiMenu.addAction(
305 self.hooksMenuEntries.get("generateSelectedWithObsolete",
306 self.trUtf8('Generate translations (with obsolete)')),
307 self.__generateObsoleteSelected)
308 self.tsMultiMenuActions.append(act)
309 self.tsprocMultiMenuActions.append(act)
310 self.multiMenu.addSeparator()
311 act = self.multiMenu.addAction(self.trUtf8('Open in Editor'),
312 self.__openFileInEditor)
313 self.tsMultiMenuActions.append(act)
314 self.multiMenu.addSeparator()
315 if self.hooks["releaseSelected"] is not None:
316 act = self.multiMenu.addAction(
317 self.hooksMenuEntries.get("releaseSelected",
318 self.trUtf8('Release translations')),
319 self.__releaseSelected)
320 self.tsMultiMenuActions.append(act)
321 self.qmprocMultiMenuActions.append(act)
322 self.multiMenu.addSeparator()
323 act = self.multiMenu.addAction(self.trUtf8('Remove from project'),
324 self.__removeLanguageFile)
325 self.multiMenuActions.append(act)
326 act = self.multiMenu.addAction(self.trUtf8('Delete'), self.__deleteLanguageFile)
327 self.multiMenuActions.append(act)
328 self.multiMenu.addSeparator()
329 self.multiMenu.addAction(self.trUtf8('Configure...'), self._configure)
330
331 self.dirMenu = QMenu(self)
332 if self.project.getProjectType() in \
333 ["Qt4", "Qt4C", "E4Plugin", "PySide", "PySideC"]:
334 act = self.dirMenu.addAction(self.trUtf8('Generate all translations'),
335 self.__generateAll)
336 self.tsprocDirMenuActions.append(act)
337 act = self.dirMenu.addAction(\
338 self.trUtf8('Generate all translations (with obsolete)'),
339 self.__generateObsoleteAll)
340 self.tsprocDirMenuActions.append(act)
341 act = self.dirMenu.addAction(self.trUtf8('Release all translations'),
342 self.__releaseAll)
343 self.qmprocDirMenuActions.append(act)
344 self.dirMenu.addSeparator()
345 act = self.dirMenu.addAction(self.trUtf8('Preview all translations'),
346 self.__TRPreview)
347 else:
348 if self.hooks["extractMessages"] is not None:
349 act = self.dirMenu.addAction(
350 self.hooksMenuEntries.get("extractMessages",
351 self.trUtf8('Extract messages')),
352 self.__extractMessages)
353 self.dirMenuActions.append(act)
354 self.dirMenu.addSeparator()
355 if self.hooks["generateAll"] is not None:
356 act = self.dirMenu.addAction(
357 self.hooksMenuEntries.get("generateAll",
358 self.trUtf8('Generate all translations')),
359 self.__generateAll)
360 self.tsprocDirMenuActions.append(act)
361 if self.hooks["generateAllWithObsolete"] is not None:
362 act = self.dirMenu.addAction(
363 self.hooksMenuEntries.get("generateAllWithObsolete",
364 self.trUtf8('Generate all translations (with obsolete)')),
365 self.__generateObsoleteAll)
366 self.tsprocDirMenuActions.append(act)
367 if self.hooks["releaseAll"] is not None:
368 act = self.dirMenu.addAction(
369 self.hooksMenuEntries.get("releaseAll",
370 self.trUtf8('Release all translations')),
371 self.__releaseAll)
372 self.qmprocDirMenuActions.append(act)
373 self.dirMenu.addSeparator()
374 self.dirMenu.addAction(self.trUtf8('Add translation...'),
375 self.project.addLanguage)
376 self.dirMenu.addAction(self.trUtf8('Add translation files...'),
377 self.__addTranslationFiles)
378 self.dirMenu.addSeparator()
379 self.dirMenu.addAction(self.trUtf8('Copy Path to Clipboard'),
380 self._copyToClipboard)
381 self.dirMenu.addSeparator()
382 self.dirMenu.addAction(self.trUtf8('Configure...'), self._configure)
383
384 self.dirMultiMenu = None
385
386 self.connect(self.menu, SIGNAL('aboutToShow()'),
387 self.__showContextMenu)
388 self.connect(self.multiMenu, SIGNAL('aboutToShow()'),
389 self.__showContextMenuMulti)
390 self.connect(self.dirMenu, SIGNAL('aboutToShow()'),
391 self.__showContextMenuDir)
392 self.connect(self.backMenu, SIGNAL('aboutToShow()'),
393 self.__showContextMenuBack)
394 self.mainMenu = self.menu
395
396 def _contextMenuRequested(self, coord):
397 """
398 Protected slot to show the context menu.
399
400 @param coord the position of the mouse pointer (QPoint)
401 """
402 if not self.project.isOpen():
403 return
404
405 try:
406 categories = self.getSelectedItemsCountCategorized(\
407 [ProjectBrowserFileItem, ProjectBrowserSimpleDirectoryItem])
408 cnt = categories["sum"]
409 if cnt <= 1:
410 index = self.indexAt(coord)
411 if index.isValid():
412 self._selectSingleItem(index)
413 categories = self.getSelectedItemsCountCategorized(\
414 [ProjectBrowserFileItem, ProjectBrowserSimpleDirectoryItem])
415 cnt = categories["sum"]
416
417 bfcnt = categories[unicode(ProjectBrowserFileItem)]
418 sdcnt = categories[unicode(ProjectBrowserSimpleDirectoryItem)]
419 if cnt > 1 and cnt == bfcnt:
420 self.multiMenu.popup(self.mapToGlobal(coord))
421 else:
422 index = self.indexAt(coord)
423 if cnt == 1 and index.isValid():
424 if bfcnt == 1:
425 self.menu.popup(self.mapToGlobal(coord))
426 elif sdcnt == 1:
427 self.dirMenu.popup(self.mapToGlobal(coord))
428 else:
429 self.backMenu.popup(self.mapToGlobal(coord))
430 else:
431 self.backMenu.popup(self.mapToGlobal(coord))
432 except:
433 pass
434
435 def __showContextMenu(self):
436 """
437 Private slot called by the menu aboutToShow signal.
438 """
439 if self.project.getProjectType() in \
440 ["Qt4", "Qt4C", "E4Plugin", "PySide", "PySideC"]:
441 tsFiles = 0
442 qmFiles = 0
443 itmList = self.getSelectedItems()
444 for itm in itmList[:]:
445 if itm.fileName().endswith('.ts'):
446 tsFiles += 1
447 elif itm.fileName().endswith('.qm'):
448 qmFiles += 1
449 if (tsFiles > 0 and qmFiles > 0) or \
450 (tsFiles == 0 and qmFiles == 0):
451 for act in self.tsMenuActions + self.qmMenuActions:
452 act.setEnabled(False)
453 elif tsFiles > 0:
454 for act in self.tsMenuActions:
455 act.setEnabled(True)
456 for act in self.qmMenuActions:
457 act.setEnabled(False)
458 elif qmFiles > 0:
459 for act in self.tsMenuActions:
460 act.setEnabled(False)
461 for act in self.qmMenuActions:
462 act.setEnabled(True)
463 if self.pylupdateProcRunning:
464 for act in self.tsprocMenuActions:
465 act.setEnabled(False)
466 if self.lreleaseProcRunning:
467 for act in self.qmprocMenuActions:
468 act.setEnabled(True)
469
470 ProjectBaseBrowser._showContextMenu(self, self.menu)
471
472 self.emit(SIGNAL("showMenu"), "Main", self.menu)
473
474 def __showContextMenuMulti(self):
475 """
476 Private slot called by the multiMenu aboutToShow signal.
477 """
478 if self.project.getProjectType() in \
479 ["Qt4", "Qt4C", "E4Plugin", "PySide", "PySideC"]:
480 tsFiles = 0
481 qmFiles = 0
482 itmList = self.getSelectedItems()
483 for itm in itmList[:]:
484 if itm.fileName().endswith('.ts'):
485 tsFiles += 1
486 elif itm.fileName().endswith('.qm'):
487 qmFiles += 1
488 if (tsFiles > 0 and qmFiles > 0) or \
489 (tsFiles == 0 and qmFiles == 0):
490 for act in self.tsMultiMenuActions + self.qmMultiMenuActions:
491 act.setEnabled(False)
492 elif tsFiles > 0:
493 for act in self.tsMultiMenuActions:
494 act.setEnabled(True)
495 for act in self.qmMultiMenuActions:
496 act.setEnabled(False)
497 elif qmFiles > 0:
498 for act in self.tsMultiMenuActions:
499 act.setEnabled(False)
500 for act in self.qmMultiMenuActions:
501 act.setEnabled(True)
502 if self.pylupdateProcRunning:
503 for act in self.tsprocMultiMenuActions:
504 act.setEnabled(False)
505 if self.lreleaseProcRunning:
506 for act in self.qmprocMultiMenuActions:
507 act.setEnabled(True)
508
509 ProjectBaseBrowser._showContextMenuMulti(self, self.multiMenu)
510
511 self.emit(SIGNAL("showMenu"), "MainMulti", self.multiMenu)
512
513 def __showContextMenuDir(self):
514 """
515 Private slot called by the dirMenu aboutToShow signal.
516 """
517 if self.project.getProjectType() in \
518 ["Qt4", "Qt4C", "E4Plugin", "PySide", "PySideC"]:
519 if self.pylupdateProcRunning:
520 for act in self.tsprocDirMenuActions:
521 act.setEnabled(False)
522 if self.lreleaseProcRunning:
523 for act in self.qmprocDirMenuActions:
524 act.setEnabled(True)
525
526 ProjectBaseBrowser._showContextMenuDir(self, self.dirMenu)
527
528 self.emit(SIGNAL("showMenu"), "MainDir", self.dirMenu)
529
530 def __showContextMenuBack(self):
531 """
532 Private slot called by the backMenu aboutToShow signal.
533 """
534 if self.project.getProjectType() in \
535 ["Qt4", "Qt4C", "E4Plugin", "PySide", "PySideC"]:
536 if self.pylupdateProcRunning:
537 for act in self.tsprocBackMenuActions:
538 act.setEnabled(False)
539 if self.lreleaseProcRunning:
540 for act in self.qmprocBackMenuActions:
541 act.setEnabled(True)
542
543 self.emit(SIGNAL("showMenu"), "MainBack", self.backMenu)
544
545 def __addTranslationFiles(self):
546 """
547 Private method to add translation files to the project.
548 """
549 itm = self.model().item(self.currentIndex())
550 if isinstance(itm, ProjectBrowserFileItem):
551 dn = os.path.dirname(itm.fileName())
552 elif isinstance(itm, ProjectBrowserSimpleDirectoryItem) or \
553 isinstance(itm, ProjectBrowserDirectoryItem):
554 dn = itm.dirName()
555 else:
556 dn = None
557 self.project.addFiles('translation', dn)
558
559 def _openItem(self):
560 """
561 Protected slot to handle the open popup menu entry.
562 """
563 itmList = self.getSelectedItems()
564 for itm in itmList:
565 if isinstance(itm, ProjectBrowserFileItem):
566 if itm.isLinguistFile():
567 if itm.fileExt() == '.ts':
568 self.emit(SIGNAL('linguistFile'), itm.fileName())
569 else:
570 self.emit(SIGNAL('trpreview'), [itm.fileName()])
571 else:
572 self.emit(SIGNAL('sourceFile'), itm.fileName())
573
574 def __openFileInEditor(self):
575 """
576 Private slot to handle the Open in Editor menu action.
577 """
578 itmList = self.getSelectedItems()
579 for itm in itmList[:]:
580 self.emit(SIGNAL('sourceFile'), itm.fileName())
581
582 def __removeLanguageFile(self):
583 """
584 Private method to remove a translation from the project.
585 """
586 itmList = self.getSelectedItems()
587
588 for itm in itmList[:]:
589 fn = itm.fileName()
590 self.emit(SIGNAL('closeSourceWindow'), fn)
591 self.project.removeLanguageFile(fn)
592
593 def __deleteLanguageFile(self):
594 """
595 Private method to delete a translation file from the project.
596 """
597 itmList = self.getSelectedItems()
598
599 translationFiles = [itm.fileName() for itm in itmList]
600
601 dlg = DeleteFilesConfirmationDialog(self.parent(),
602 self.trUtf8("Delete translation files"),
603 self.trUtf8("Do you really want to delete these translation files"
604 " from the project?"),
605 translationFiles)
606
607 if dlg.exec_() == QDialog.Accepted:
608 for fn in translationFiles:
609 self.emit(SIGNAL('closeSourceWindow'), fn)
610 self.project.deleteLanguageFile(fn)
611
612 def __TRPreview(self, previewAll = False):
613 """
614 Private slot to handle the Preview translations action.
615
616 @param previewAll flag indicating, that all translations
617 should be previewed (boolean)
618 """
619 fileNames = []
620 itmList = self.getSelectedItems()
621 if itmList and not previewAll:
622 for itm in itmList:
623 if isinstance(itm, ProjectBrowserSimpleDirectoryItem):
624 dname = itm.dirName().replace(self.project.ppath+os.sep, '')
625 trfiles = self.project.pdata["TRANSLATIONS"][:]
626 trfiles.sort()
627 for trfile in trfiles:
628 if trfile.startswith(dname):
629 if trfile not in fileNames:
630 fileNames.append(os.path.join(self.project.ppath, trfile))
631 else:
632 fn = itm.fileName()
633 if fn not in fileNames:
634 fileNames.append(os.path.join(self.project.ppath, fn))
635 else:
636 trfiles = self.project.pdata["TRANSLATIONS"][:]
637 trfiles.sort()
638 fileNames.extend([os.path.join(self.project.ppath, trfile) \
639 for trfile in trfiles \
640 if trfile.endswith('.qm')])
641 self.emit(SIGNAL('trpreview'), fileNames, True)
642
643 def __TRPreviewAll(self):
644 """
645 Private slot to handle the Preview all translations action.
646 """
647 self.__TRPreview(True)
648
649 ############################################################################
650 ## Methods to support the generation and release commands
651 ############################################################################
652
653 def __writeTempProjectFile(self, langs, filter):
654 """
655 Private method to write a temporary project file suitable for pylupdate and
656 lrelease.
657
658 @param langs list of languages to include in the process. An empty list (default)
659 means that all translations should be included.
660 (list of ProjectBrowserFileItem)
661 @param filter list of source file extension that should be considered
662 (list of strings)
663 @return flag indicating success
664 """
665 path, ext = os.path.splitext(self.project.pfile)
666 pfile = '%s_e4x.pro' % path
667
668 # only consider files satisfying the filter criteria
669 _sources = [s for s in self.project.pdata["SOURCES"] \
670 if os.path.splitext(s)[1] in filter]
671 sources = []
672 for s in _sources:
673 addIt = True
674 for transExcept in self.project.pdata["TRANSLATIONEXCEPTIONS"]:
675 if s.startswith(transExcept):
676 addIt = False
677 break
678 if addIt:
679 sources.append(s)
680 sections = [("SOURCES", sources)]
681
682 _forms = [f for f in self.project.pdata["FORMS"] if f.endswith('.ui')]
683 forms = []
684 for f in _forms:
685 addIt = True
686 for transExcept in self.project.pdata["TRANSLATIONEXCEPTIONS"]:
687 if f.startswith(transExcept):
688 addIt = False
689 break
690 if addIt:
691 forms.append(f)
692 sections.append(("FORMS", forms))
693
694 if langs:
695 l = [lang.fileName().replace(self.project.ppath + os.sep, '') \
696 for lang in langs if lang.fileName().endswith('.ts')]
697 else:
698 try:
699 pattern = self.project.pdata["TRANSLATIONPATTERN"][0]\
700 .replace("%language%", "*")
701 l = [lang for lang in self.project.pdata["TRANSLATIONS"] \
702 if fnmatch.fnmatch(lang, pattern)]
703 except IndexError:
704 l = []
705 if l:
706 sections.append(("TRANSLATIONS", l))
707 else:
708 QMessageBox.warning(None,
709 self.trUtf8("Write temporary project file"),
710 self.trUtf8("""No translation files (*.ts) selected."""),
711 QMessageBox.StandardButtons(\
712 QMessageBox.Abort))
713 return False
714
715 try:
716 pf = open(pfile, "wb")
717 for key, list in sections:
718 if len(list) > 0:
719 pf.write('%s = ' % key)
720 last = len(list) - 1
721 if last > 0:
722 pf.write('%s \\%s' % \
723 (list[0].replace(os.sep, '/'), os.linesep))
724 for i in range(1, last):
725 pf.write('\t%s \\%s' % \
726 (list[i].replace(os.sep, '/'), os.linesep))
727 pf.write('\t%s %s%s' % \
728 (list[last].replace(os.sep, '/'), os.linesep, os.linesep))
729 else:
730 pf.write('%s %s%s' % \
731 (list[0].replace(os.sep, '/'), os.linesep, os.linesep))
732
733 pf.close()
734 self.tmpProject = pfile
735 return True
736 except IOError:
737 QMessageBox.critical(None,
738 self.trUtf8("Write temporary project file"),
739 self.trUtf8("<p>The temporary project file <b>{0}</b> could not"
740 " be written.</p>").format(pfile),
741 QMessageBox.StandardButtons(\
742 QMessageBox.Abort))
743 self.tmpProject = None
744 return False
745
746 def __readStdoutLupdate(self):
747 """
748 Private slot to handle the readyReadStandardOutput signal of the
749 pylupdate process.
750 """
751 if self.pylupdateProc is not None:
752 self.__readStdout(self.pylupdateProc, '%s: ' % self.pylupdate)
753 else:
754 return
755
756 def __readStdoutLrelease(self):
757 """
758 Private slot to handle the readyReadStandardOutput signal of the
759 lrelease process.
760 """
761 if self.lreleaseProc is not None:
762 self.__readStdout(self.lreleaseProc, 'lrelease: ')
763 else:
764 return
765
766 def __readStdout(self, proc, ps):
767 """
768 Private method to read from a process' stdout channel.
769
770 @param proc process to read from (QProcess)
771 @param ps prompt string (string)
772 """
773 ioEncoding = str(Preferences.getSystem("IOEncoding"))
774
775 proc.setReadChannel(QProcess.StandardOutput)
776 while proc and proc.canReadLine():
777 s = ps
778 output = unicode(proc.readLine(), ioEncoding, 'replace')
779 s += output
780 self.emit(SIGNAL('appendStdout'), s)
781
782 def __readStderrLupdate(self):
783 """
784 Private slot to handle the readyReadStandardError signal of the
785 pylupdate process.
786 """
787 if self.pylupdateProc is not None:
788 self.__readStderr(self.pylupdateProc, '%s: ' % self.pylupdate)
789 else:
790 return
791
792 def __readStderrLrelease(self):
793 """
794 Private slot to handle the readyReadStandardError signal of the
795 lrelease process.
796 """
797 if self.lreleaseProc is not None:
798 self.__readStderr(self.lreleaseProc, 'lrelease: ')
799 else:
800 return
801
802 def __readStderr(self, proc, ps):
803 """
804 Private method to read from a process' stderr channel.
805
806 @param proc process to read from (QProcess)
807 @param ps propmt string (string)
808 """
809 ioEncoding = str(Preferences.getSystem("IOEncoding"))
810
811 proc.setReadChannel(QProcess.StandardError)
812 while proc and proc.canReadLine():
813 s = ps
814 error = unicode(proc.readLine(), ioEncoding, 'replace')
815 s += error
816 self.emit(SIGNAL('appendStderr'), s)
817
818 ############################################################################
819 ## Methods for the generation commands
820 ############################################################################
821
822 def __extractMessages(self):
823 """
824 Private slot to extract the messages to form a messages template file.
825 """
826 if self.hooks["extractMessages"] is not None:
827 self.hooks["extractMessages"]()
828
829 def __generateTSFileDone(self, exitCode, exitStatus):
830 """
831 Private slot to handle the finished signal of the pylupdate process.
832
833 @param exitCode exit code of the process (integer)
834 @param exitStatus exit status of the process (QProcess.ExitStatus)
835 """
836 self.pylupdateProcRunning = False
837 if exitStatus == QProcess.NormalExit and exitCode == 0:
838 QMessageBox.information(None,
839 self.trUtf8("Translation file generation"),
840 self.trUtf8("The generation of the translation files (*.ts)"
841 " was successful."))
842 else:
843 QMessageBox.critical(None,
844 self.trUtf8("Translation file generation"),
845 self.trUtf8("The generation of the translation files (*.ts) has failed."))
846 self.pylupdateProc = None
847 self.pylupdate = ""
848 try:
849 os.remove(self.tmpProject)
850 except EnvironmentError:
851 pass
852 self.tmpProject = None
853
854 def __generateTSFile(self, noobsolete = False, generateAll = True):
855 """
856 Private method used to run pylupdate/pylupdate4 to generate the .ts files.
857
858 @param noobsolete flag indicating whether obsolete entries should be
859 kept (boolean)
860 @param generateAll flag indicating whether all translations should be
861 generated (boolean)
862 """
863 if generateAll:
864 langs = []
865 else:
866 langs = self.getSelectedItems()
867
868 # Hook support
869 if generateAll:
870 if noobsolete:
871 if self.hooks["generateAll"] is not None:
872 self.hooks["generateAll"](self.project.pdata["TRANSLATIONS"])
873 return
874 else:
875 if self.hooks["generateAllWithObsolete"] is not None:
876 self.hooks["generateAllWithObsolete"](
877 self.project.pdata["TRANSLATIONS"])
878 return
879 else:
880 if noobsolete:
881 if self.hooks["generateSelected"] is not None:
882 l = [lang.fileName().replace(self.project.ppath + os.sep, '') \
883 for lang in langs]
884 self.hooks["generateSelected"](l)
885 return
886 else:
887 if self.hooks["generateSelectedWithObsolete"] is not None:
888 l = [lang.fileName().replace(self.project.ppath + os.sep, '') \
889 for lang in langs]
890 self.hooks["generateSelectedWithObsolete"](l)
891 return
892
893 # generate a minimal temporary projectfile suitable for pylupdate
894 if self.project.pdata["PROGLANGUAGE"][0] in ["Python", "Python3"]:
895 ok = self.__writeTempProjectFile(langs, [".py"])
896 else:
897 ok = False
898 if not ok:
899 return
900
901 self.pylupdateProc = QProcess()
902 args = []
903
904 if self.project.getProjectType() in ["Qt4", "Qt4C", "E4Plugin"]:
905 self.pylupdate = 'pylupdate4'
906 elif self.project.getProjectType() in ["PySide", "PySideC"]:
907 self.pylupdate = 'pyside-lupdate'
908 else:
909 return
910 if Utilities.isWindowsPlatform():
911 self.pylupdate = self.pylupdate + '.exe'
912
913 if noobsolete:
914 args.append('-noobsolete')
915
916 args.append('-verbose')
917 args.append(self.tmpProject)
918 self.pylupdateProc.setWorkingDirectory(self.project.ppath)
919 self.connect(self.pylupdateProc, SIGNAL('finished(int, QProcess::ExitStatus)'),
920 self.__generateTSFileDone)
921 self.connect(self.pylupdateProc, SIGNAL('readyReadStandardOutput()'),
922 self.__readStdoutLupdate)
923 self.connect(self.pylupdateProc, SIGNAL('readyReadStandardError()'),
924 self.__readStderrLupdate)
925
926 self.pylupdateProc.start(self.pylupdate, args)
927 procStarted = self.pylupdateProc.waitForStarted()
928 if procStarted:
929 self.pylupdateProcRunning = True
930 else:
931 QMessageBox.critical(self,
932 self.trUtf8('Process Generation Error'),
933 self.trUtf8(
934 'Could not start {0}.<br>'
935 'Ensure that it is in the search path.'
936 ).format(self.pylupdate))
937
938 def __generateAll(self):
939 """
940 Private method to generate all translation files (.ts) for Qt Linguist.
941
942 All obsolete strings are removed from the .ts file.
943 """
944 self.__generateTSFile(noobsolete = True, generateAll = True)
945
946 def __generateObsoleteAll(self):
947 """
948 Private method to generate all translation files (.ts) for Qt Linguist.
949
950 Obsolete strings are kept.
951 """
952 self.__generateTSFile(noobsolete = False, generateAll = True)
953
954 def __generateSelected(self):
955 """
956 Private method to generate selected translation files (.ts) for Qt Linguist.
957
958 All obsolete strings are removed from the .ts file.
959 """
960 self.__generateTSFile(noobsolete = True, generateAll = False)
961
962 def __generateObsoleteSelected(self):
963 """
964 Private method to generate selected translation files (.ts) for Qt Linguist.
965
966 Obsolete strings are kept.
967 """
968 self.__generateTSFile(noobsolete = False, generateAll = False)
969
970 ############################################################################
971 ## Methods for the release commands
972 ############################################################################
973
974 def __releaseTSFileDone(self, exitCode, exitStatus):
975 """
976 Private slot to handle the finished signal of the lrelease process.
977 """
978 self.lreleaseProcRunning = False
979 if exitStatus == QProcess.NormalExit and exitCode == 0:
980 QMessageBox.information(None,
981 self.trUtf8("Translation file release"),
982 self.trUtf8("The release of the translation files (*.qm)"
983 " was successful."))
984 if self.project.pdata["TRANSLATIONSBINPATH"]:
985 target = os.path.join(self.project.ppath,
986 self.project.pdata["TRANSLATIONSBINPATH"][0])
987 for langFile in self.project.pdata["TRANSLATIONS"][:]:
988 if langFile.endswith('.ts'):
989 qmFile = os.path.join(self.project.ppath,
990 langFile.replace('.ts', '.qm'))
991 if os.path.exists(qmFile):
992 shutil.move(qmFile, target)
993 else:
994 QMessageBox.critical(None,
995 self.trUtf8("Translation file release"),
996 self.trUtf8("The release of the translation files (*.qm) has failed."))
997 self.lreleaseProc = None
998 try:
999 os.remove(self.tmpProject)
1000 except EnvironmentError:
1001 pass
1002 self.tmpProject = None
1003 self.project.checkLanguageFiles()
1004
1005 def __releaseTSFile(self, generateAll = False):
1006 """
1007 Private method to run lrelease to release the translation files (.qm).
1008
1009 @param generateAll flag indicating whether all translations should be
1010 released (boolean)
1011 """
1012 if generateAll:
1013 langs = []
1014 else:
1015 langs = self.getSelectedItems()
1016
1017 # Hooks support
1018 if generateAll:
1019 if self.hooks["releaseAll"] is not None:
1020 self.hooks["releaseAll"](self.project.pdata["TRANSLATIONS"])
1021 return
1022 else:
1023 if self.hooks["releaseSelected"] is not None:
1024 l = [lang.fileName().replace(self.project.ppath+os.sep, '') \
1025 for lang in langs]
1026 self.hooks["releaseSelected"](l)
1027 return
1028
1029 # generate a minimal temporary projectfile suitable for lrelease
1030 if self.project.pdata["PROGLANGUAGE"][0] in ["Python", "Python3"]:
1031 ok = self.__writeTempProjectFile(langs, [".py"])
1032 else:
1033 ok = False
1034 if not ok:
1035 return
1036
1037 self.lreleaseProc = QProcess()
1038 args = []
1039
1040 if self.project.getProjectType() in \
1041 ["Qt4", "Qt4C", "E4Plugin", "PySide", "PySideC"]:
1042 lrelease = Utilities.generateQtToolName("lrelease")
1043 else:
1044 return
1045 if Utilities.isWindowsPlatform():
1046 lrelease = lrelease + '.exe'
1047
1048 args.append('-verbose')
1049 args.append(self.tmpProject)
1050 self.lreleaseProc.setWorkingDirectory(self.project.ppath)
1051 self.connect(self.lreleaseProc, SIGNAL('finished(int, QProcess::ExitStatus)'),
1052 self.__releaseTSFileDone)
1053 self.connect(self.lreleaseProc, SIGNAL('readyReadStandardOutput()'),
1054 self.__readStdoutLrelease)
1055 self.connect(self.lreleaseProc, SIGNAL('readyReadStandardError()'),
1056 self.__readStderrLrelease)
1057
1058 self.lreleaseProc.start(lrelease, args)
1059 procStarted = self.lreleaseProc.waitForStarted()
1060 if procStarted:
1061 self.lreleaseProcRunning = True
1062 else:
1063 QMessageBox.critical(self,
1064 self.trUtf8('Process Generation Error'),
1065 self.trUtf8(
1066 '<p>Could not start lrelease.<br>'
1067 'Ensure that it is available as <b>{0}</b>.</p>'
1068 ).format(lrelease))
1069
1070 def __releaseSelected(self):
1071 """
1072 Private method to release the translation files (.qm).
1073 """
1074 self.__releaseTSFile(generateAll = False)
1075
1076 def __releaseAll(self):
1077 """
1078 Private method to release the translation files (.qm).
1079 """
1080 self.__releaseTSFile(generateAll = True)
1081
1082 ############################################################################
1083 ## Support for hooks below
1084 ############################################################################
1085
1086 def _initHookMethods(self):
1087 """
1088 Protected method to initialize the hooks dictionary.
1089
1090 Supported hook methods are:
1091 <ul>
1092 <li>extractMessages: takes no parameters</li>
1093 <li>generateAll: takes list of filenames as parameter</li>
1094 <li>generateAllWithObsolete: takes list of filenames as parameter</li>
1095 <li>generateSelected: takes list of filenames as parameter</li>
1096 <li>generateSelectedWithObsolete: takes list of filenames as parameter</li>
1097 <li>releaseAll: takes list of filenames as parameter</li>
1098 <li>releaseSelected: takes list of filenames as parameter</li>
1099 </ul>
1100
1101 <b>Note</b>: Filenames are relative to the project directory.
1102 """
1103 self.hooks = {
1104 "extractMessages" : None,
1105 "generateAll" : None,
1106 "generateAllWithObsolete" : None,
1107 "generateSelected" : None,
1108 "generateSelectedWithObsolete" : None,
1109 "releaseAll" : None,
1110 "releaseSelected" : None,
1111 }

eric ide

mercurial