Merge pull request #1209 from living180/tmp_filename
[git-cola.git] / cola / widgets / about.py
blob044514b0f29e4bb7daa13f3220d1128b9730e6ec
1 # encoding: utf-8
2 from __future__ import absolute_import, division, print_function, unicode_literals
3 import platform
4 import webbrowser
5 import sys
7 import qtpy
8 from qtpy.QtCore import Qt
9 from qtpy import QtGui
10 from qtpy import QtWidgets
12 from ..i18n import N_
13 from .. import core
14 from .. import resources
15 from .. import hotkeys
16 from .. import icons
17 from .. import qtutils
18 from .. import version
19 from . import defs
22 def about_dialog(context):
23 """Launches the Help -> About dialog"""
24 view = AboutView(context, qtutils.active_window())
25 view.show()
26 return view
29 class ExpandingTabBar(QtWidgets.QTabBar):
30 """A TabBar with tabs that expand to fill the empty space
32 The setExpanding(True) method does not work in practice because
33 it respects the OS style. We override the style by implementing
34 tabSizeHint() so that we can specify the size explicitly.
36 """
38 def tabSizeHint(self, tab_index):
39 width = self.parent().width() // max(2, self.count()) - 1
40 size = super(ExpandingTabBar, self).tabSizeHint(tab_index)
41 size.setWidth(width)
42 return size
45 class ExpandingTabWidget(QtWidgets.QTabWidget):
46 def __init__(self, parent=None):
47 super(ExpandingTabWidget, self).__init__(parent)
48 self.setTabBar(ExpandingTabBar(self))
50 def resizeEvent(self, event):
51 """Forward resize events to the ExpandingTabBar"""
52 # Qt does not resize the tab bar when the dialog is resized
53 # so manually forward resize events to the tab bar.
54 width = event.size().width()
55 height = self.tabBar().height()
56 self.tabBar().resize(width, height)
57 return super(ExpandingTabWidget, self).resizeEvent(event)
60 class AboutView(QtWidgets.QDialog):
61 """Provides the git-cola 'About' dialog"""
63 def __init__(self, context, parent=None):
64 QtWidgets.QDialog.__init__(self, parent)
66 self.context = context
67 self.setWindowTitle(N_('About git-cola'))
68 self.setWindowModality(Qt.WindowModal)
70 # Top-most large icon
71 logo_pixmap = icons.cola().pixmap(defs.huge_icon, defs.large_icon)
73 self.logo_label = QtWidgets.QLabel()
74 self.logo_label.setPixmap(logo_pixmap)
75 self.logo_label.setAlignment(Qt.AlignCenter)
77 self.logo_text_label = QtWidgets.QLabel()
78 self.logo_text_label.setText('Git Cola')
79 self.logo_text_label.setAlignment(Qt.AlignLeft | Qt.AlignCenter)
81 font = self.logo_text_label.font()
82 font.setPointSize(defs.logo_text)
83 self.logo_text_label.setFont(font)
85 self.text = qtutils.textbrowser(text=copyright_text())
86 self.version = qtutils.textbrowser(text=version_text(context))
87 self.authors = qtutils.textbrowser(text=authors_text())
88 self.translators = qtutils.textbrowser(text=translators_text())
90 self.tabs = ExpandingTabWidget()
91 self.tabs.addTab(self.text, N_('About'))
92 self.tabs.addTab(self.version, N_('Version'))
93 self.tabs.addTab(self.authors, N_('Authors'))
94 self.tabs.addTab(self.translators, N_('Translators'))
96 self.close_button = qtutils.close_button()
97 self.close_button.setDefault(True)
99 self.logo_layout = qtutils.hbox(
100 defs.no_margin,
101 defs.button_spacing,
102 self.logo_label,
103 self.logo_text_label,
104 qtutils.STRETCH,
107 self.button_layout = qtutils.hbox(
108 defs.spacing, defs.margin, qtutils.STRETCH, self.close_button
111 self.main_layout = qtutils.vbox(
112 defs.no_margin,
113 defs.spacing,
114 self.logo_layout,
115 self.tabs,
116 self.button_layout,
118 self.setLayout(self.main_layout)
120 qtutils.connect_button(self.close_button, self.accept)
122 self.resize(defs.scale(600), defs.scale(720))
125 def copyright_text():
126 return """
127 Git Cola: The highly caffeinated Git GUI
129 Copyright (C) 2007-2020 David Aguilar and contributors
131 This program is free software: you can redistribute it and/or
132 modify it under the terms of the GNU General Public License
133 version 2 as published by the Free Software Foundation.
135 This program is distributed in the hope that it will
136 be useful, but WITHOUT ANY WARRANTY; without even the
137 implied warranty of MERCHANTABILITY or
138 FITNESS FOR A PARTICULAR PURPOSE.
140 See the GNU General Public License for more details.
142 You should have received a copy of the
143 GNU General Public License along with this program.
144 If not, see http://www.gnu.org/licenses/.
149 def version_text(context):
150 git_version = version.git_version(context)
151 cola_version = version.version()
152 build_version = version.build_version()
153 python_path = sys.executable
154 python_version = sys.version
155 qt_version = qtpy.QT_VERSION
156 qtpy_version = qtpy.__version__
157 pyqt_api_name = qtpy.API_NAME
158 if qtpy.PYQT5 or qtpy.PYQT4:
159 pyqt_api_version = qtpy.PYQT_VERSION
160 elif qtpy.PYSIDE:
161 pyqt_api_version = qtpy.PYSIDE_VERSION
162 else:
163 pyqt_api_version = 'unknown'
165 platform_version = platform.platform()
167 # Only show the build version if _build_version.py exists
168 if build_version:
169 build_version = '(%s)' % build_version
170 else:
171 build_version = ''
173 scope = dict(
174 cola_version=cola_version,
175 build_version=build_version,
176 git_version=git_version,
177 platform_version=platform_version,
178 pyqt_api_name=pyqt_api_name,
179 pyqt_api_version=pyqt_api_version,
180 python_path=python_path,
181 python_version=python_version,
182 qt_version=qt_version,
183 qtpy_version=qtpy_version,
186 return (
189 <br>
190 Git Cola version %(cola_version)s %(build_version)s
191 <ul>
192 <li> %(platform_version)s
193 <li> Python (%(python_path)s) %(python_version)s
194 <li> Git %(git_version)s
195 <li> Qt %(qt_version)s
196 <li> QtPy %(qtpy_version)s
197 <li> %(pyqt_api_name)s %(pyqt_api_version)s
198 </ul>
201 % scope
205 def link(url, text, palette=None):
206 if palette is None:
207 palette = QtGui.QPalette()
209 color = palette.color(QtGui.QPalette.Foreground)
210 rgb = 'rgb(%s, %s, %s)' % (color.red(), color.green(), color.blue())
211 scope = dict(rgb=rgb, text=text, url=url)
213 return (
215 <a style="font-style: italic; text-decoration: none; color: %(rgb)s;"
216 href="%(url)s">
217 %(text)s
218 </a>
220 % scope
224 def mailto(email, text, palette):
225 return link('mailto:%s' % email, text, palette) + '<br>'
228 def render_authors(authors):
229 """Render a list of author details into richtext html"""
230 for x in authors:
231 x.setdefault('email', '')
233 entries = [
237 <strong>%(name)s</strong><br>
238 <em>%(title)s</em><br>
239 %(email)s
240 </p>
242 % author
244 for author in authors
247 return ''.join(entries)
250 def contributors_text(authors, prelude='', epilogue=''):
251 author_text = render_authors(authors)
252 scope = dict(author_text=author_text, epilogue=epilogue, prelude=prelude)
254 return (
256 %(prelude)s
257 %(author_text)s
258 %(epilogue)s
260 % scope
264 def authors_text():
265 palette = QtGui.QPalette()
266 contact = N_('Email contributor')
267 authors = (
268 # The names listed here are listed in the same order as
269 # `git shortlog --summary --numbered --no-merges`
270 # Please submit a pull request if you would like to include your
271 # email address in the about screen.
272 # See the `generate-about` script in the "todo" branch.
273 # vim :read! ./Meta/generate-about
274 dict(
275 name='David Aguilar',
276 title=N_('Maintainer (since 2007) and developer'),
277 email=mailto('davvid@gmail.com', contact, palette),
279 dict(name='Daniel Harding', title=N_('Developer')),
280 dict(
281 name='V字龍(Vdragon)',
282 title=N_('Developer'),
283 email=mailto('Vdragon.Taiwan@gmail.com', contact, palette),
285 dict(name='Efimov Vasily', title=N_('Developer')),
286 dict(name='Guillaume de Bure', title=N_('Developer')),
287 dict(name='Uri Okrent', title=N_('Developer')),
288 dict(name='Javier Rodriguez Cuevas', title=N_('Developer')),
289 dict(name='Alex Chernetz', title=N_('Developer')),
290 dict(name='xhl', title=N_('Developer')),
291 dict(name='Andreas Sommer', title=N_('Developer')),
292 dict(name='Thomas Kluyver', title=N_('Developer')),
293 dict(name='Minarto Margoliono', title=N_('Developer')),
294 dict(name='Ville Skyttä', title=N_('Developer')),
295 dict(name='Szymon Judasz', title=N_('Developer')),
296 dict(name='jm4R', title=N_('Developer')),
297 dict(name='Stanislaw Halik', title=N_('Developer')),
298 dict(name='Igor Galarraga', title=N_('Developer')),
299 dict(name='Virgil Dupras', title=N_('Developer')),
300 dict(name='wsdfhjxc', title=N_('Developer')),
301 dict(name='Barry Roberts', title=N_('Developer')),
302 dict(name='林博仁(Buo-ren Lin)', title=N_('Developer')),
303 dict(name='Guo Yunhe', title=N_('Developer')),
304 dict(name='cclauss', title=N_('Developer')),
305 dict(name='Stefan Naewe', title=N_('Developer')),
306 dict(name='Victor Nepveu', title=N_('Developer')),
307 dict(name='Pavel Rehak', title=N_('Developer')),
308 dict(name='Benedict Lee', title=N_('Developer')),
309 dict(name='Tim Brown', title=N_('Developer')),
310 dict(name='Steffen Prohaska', title=N_('Developer')),
311 dict(name='Filip Danilović', title=N_('Developer')),
312 dict(name='NotSqrt', title=N_('Developer')),
313 dict(name='Michael Geddes', title=N_('Developer')),
314 dict(name='Rustam Safin', title=N_('Developer')),
315 dict(name='Justin Lecher', title=N_('Developer')),
316 dict(name='Alex Gulyás', title=N_('Developer')),
317 dict(name='David Martínez Martí', title=N_('Developer')),
318 dict(name='Hualiang Xie', title=N_('Developer')),
319 dict(name='Kai Krakow', title=N_('Developer')),
320 dict(name='Karl Bielefeldt', title=N_('Developer')),
321 dict(name='Marco Costalba', title=N_('Developer')),
322 dict(name='Michael Homer', title=N_('Developer')),
323 dict(name='Sebastian Schuberth', title=N_('Developer')),
324 dict(name='Sven Claussner', title=N_('Developer')),
325 dict(name='Victor Gambier', title=N_('Developer')),
326 dict(name='bsomers', title=N_('Developer')),
327 dict(name='real', title=N_('Developer')),
328 dict(name='v.paritskiy', title=N_('Developer')),
329 dict(name='vanderkoort', title=N_('Developer')),
330 dict(name='wm4', title=N_('Developer')),
331 dict(name='Audrius Karabanovas', title=N_('Developer')),
332 dict(name='Matthew E. Levine', title=N_('Developer')),
333 dict(name='Matthias Mailänder', title=N_('Developer')),
334 dict(name='Md. Mahbub Alam', title=N_('Developer')),
335 dict(name='Jakub Szymański', title=N_('Developer')),
336 dict(name='ochristi', title=N_('Developer')),
337 dict(name='Miguel Boekhold', title=N_('Developer')),
338 dict(name='MiguelBoekhold', title=N_('Developer')),
339 dict(name='Mikhail Terekhov', title=N_('Developer')),
340 dict(name='Jake Biesinger', title=N_('Developer')),
341 dict(name='Iulian Udrea', title=N_('Developer')),
342 dict(name='Paul Hildebrandt', title=N_('Developer')),
343 dict(name='Paul Weingardt', title=N_('Developer')),
344 dict(name='Paulo Fidalgo', title=N_('Developer')),
345 dict(name='Ilya Tumaykin', title=N_('Developer')),
346 dict(name='Petr Gladkikh', title=N_('Developer')),
347 dict(name='Philip Stark', title=N_('Developer')),
348 dict(name='Radek Postołowicz', title=N_('Developer')),
349 dict(name='Rainer Müller', title=N_('Developer')),
350 dict(name='Ricardo J. Barberis', title=N_('Developer')),
351 dict(name='Rolando Espinoza', title=N_('Developer')),
352 dict(name='George Vasilakos', title=N_('Developer')),
353 dict(name="Samsul Ma'arif", title=N_('Developer')),
354 dict(name='Sebastian Brass', title=N_('Developer')),
355 dict(name='Arthur Coelho', title=N_('Developer')),
356 dict(name='Simon Peeters', title=N_('Developer')),
357 dict(name='Felipe Morales', title=N_('Developer')),
358 dict(name='David Zumbrunnen', title=N_('Developer')),
359 dict(name='David Schwörer', title=N_('Developer')),
360 dict(name='Stephen', title=N_('Developer')),
361 dict(name='Andrej', title=N_('Developer')),
362 dict(name='Daniel Pavel', title=N_('Developer')),
363 dict(name='Daniel King', title=N_('Developer')),
364 dict(name='Daniel Haskin', title=N_('Developer')),
365 dict(name='Clément Pit--Claudel', title=N_('Developer')),
366 dict(name='Vaibhav Sagar', title=N_('Developer')),
367 dict(name='Ved Vyas', title=N_('Developer')),
368 dict(name='Adrien be', title=N_('Developer')),
369 dict(name='Charles', title=N_('Developer')),
370 dict(name='Boris W', title=N_('Developer')),
371 dict(name='Ben Boeckel', title=N_('Developer')),
372 dict(name='Voicu Hodrea', title=N_('Developer')),
373 dict(name='Wesley Wong', title=N_('Developer')),
374 dict(name='Wolfgang Ocker', title=N_('Developer')),
375 dict(name='Zhang Han', title=N_('Developer')),
376 dict(name='beauxq', title=N_('Developer')),
377 dict(name='Jamie Pate', title=N_('Developer')),
378 dict(name='Jean-Francois Dagenais', title=N_('Developer')),
379 dict(name='Joachim Lusiardi', title=N_('Developer')),
380 dict(name='0xflotus', title=N_('Developer')),
381 dict(name='AJ Bagwell', title=N_('Developer')),
382 dict(name='Barrett Lowe', title=N_('Developer')),
383 dict(name='Karthik Manamcheri', title=N_('Developer')),
384 dict(name='Kelvie Wong', title=N_('Developer')),
385 dict(name='Kyle', title=N_('Developer')),
386 dict(name='Maciej Filipiak', title=N_('Developer')),
387 dict(name='Maicon D. Filippsen', title=N_('Developer')),
388 dict(name='Markus Heidelberg', title=N_('Developer')),
390 bug_url = 'https://github.com/git-cola/git-cola/issues'
391 bug_link = link(bug_url, bug_url)
392 scope = dict(bug_link=bug_link)
393 prelude = (
396 <br>
397 Please use %(bug_link)s to report issues.
398 <br>
401 % scope
404 return contributors_text(authors, prelude=prelude)
407 def translators_text():
408 palette = QtGui.QPalette()
409 contact = N_('Email contributor')
411 translators = (
412 # See the `generate-about` script in the "todo" branch.
413 # vim :read! ./Meta/generate-about --translators
414 dict(
415 name='V字龍(Vdragon)',
416 title=N_('Traditional Chinese (Taiwan) translation'),
417 email=mailto('Vdragon.Taiwan@gmail.com', contact, palette),
419 dict(name='Pavel Rehak', title=N_('Czech translation')),
420 dict(name='Zhang Han', title=N_('Simplified Chinese translation')),
421 dict(name='Victorhck', title=N_('Spanish translation')),
422 dict(name='Vitor Lobo', title=N_('Brazilian translation')),
423 dict(name='Igor Kopach', title=N_('Ukranian translation')),
424 dict(name='Łukasz Wojniłowicz', title=N_('Polish translation')),
425 dict(name='Rafael Nascimento', title=N_('Brazilian translation')),
426 dict(name='Barış ÇELİK', title=N_('Turkish translation')),
427 dict(name='Minarto Margoliono', title=N_('Indonesian translation')),
428 dict(name='Sven Claussner', title=N_('German translation')),
429 dict(name='Shun Sakai', title=N_('Japanese translation')),
430 dict(name='Vaiz', title=N_('Russian translation')),
431 dict(name='adlgrbz', title=N_('Turkish translation')),
432 dict(name='fu7mu4', title=N_('Japanese translation')),
433 dict(name='Guo Yunhe', title=N_('Simplified Chinese translation')),
434 dict(name="Samsul Ma'arif", title=N_('Indonesian translation')),
435 dict(name='Gyuris Gellért', title=N_('Hungarian translation')),
436 dict(name='Joachim Lusiardi', title=N_('German translation')),
437 dict(name='Kai Krakow', title=N_('German translation')),
438 dict(name='Louis Rousseau', title=N_('French translation')),
439 dict(name='Mickael Albertus', title=N_('French translation')),
440 dict(
441 name='Peter Dave Hello',
442 title=N_('Traditional Chinese (Taiwan) translation'),
444 dict(name='Pilar Molina Lopez', title=N_('Spanish translation')),
445 dict(name='Rafael Reuber', title=N_('Brazilian translation')),
446 dict(name='Sabri Ünal', title=N_('Turkish translation')),
447 dict(name='Balázs Meskó', title=N_('Translation')),
448 dict(name='Zeioth', title=N_('Spanish translation')),
449 dict(name='balping', title=N_('Hungarian translation')),
450 dict(name='p-bo', title=N_('Czech translation')),
451 dict(
452 name='林博仁(Buo-ren Lin)',
453 title=N_('Traditional Chinese (Taiwan) translation'),
457 bug_url = 'https://github.com/git-cola/git-cola/issues'
458 bug_link = link(bug_url, bug_url)
459 scope = dict(bug_link=bug_link)
461 prelude = (
464 <br>
465 Git Cola has been translated into different languages thanks
466 to the help of the individuals listed below.
468 <br>
470 Translation is approximate. If you find a mistake,
471 please let us know by opening an issue on Github:
472 </p>
475 %(bug_link)s
476 </p>
478 <br>
480 We invite you to participate in translation by adding or updating
481 a translation and opening a pull request.
482 </p>
484 <br>
488 % scope
490 return contributors_text(translators, prelude=prelude)
493 def show_shortcuts():
494 hotkeys_html = resources.doc(N_('hotkeys.html'))
495 try:
496 from qtpy import QtWebEngineWidgets # pylint: disable=all
497 except (ImportError, qtpy.PythonQtError):
498 # redhat disabled QtWebKit in their qt build but don't punish the users
499 webbrowser.open_new_tab('file://' + hotkeys_html)
500 return
502 html = core.read(hotkeys_html)
504 parent = qtutils.active_window()
505 widget = QtWidgets.QDialog(parent)
506 widget.setWindowModality(Qt.WindowModal)
507 widget.setWindowTitle(N_('Shortcuts'))
509 web = QtWebEngineWidgets.QWebEngineView()
510 web.setHtml(html)
512 layout = qtutils.hbox(defs.no_margin, defs.spacing, web)
513 widget.setLayout(layout)
514 widget.resize(800, min(parent.height(), 600))
515 qtutils.add_action(
516 widget, N_('Close'), widget.accept, hotkeys.QUESTION, *hotkeys.ACCEPT
518 widget.show()
519 widget.exec_()