1# Copyright (C) 2016-2020 Damon Lynch <damonlynch@gmail.com>
2
3# This file is part of Rapid Photo Downloader.
4#
5# Rapid Photo Downloader is free software: you can redistribute it and/or
6# modify it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# Rapid Photo Downloader is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with Rapid Photo Downloader.  If not,
17# see <http://www.gnu.org/licenses/>.
18
19"""
20Display an About window
21"""
22
23__author__ = 'Damon Lynch'
24__copyright__ = "Copyright 2016-2020, Damon Lynch"
25
26import re
27
28from PyQt5.QtCore import Qt, pyqtSlot, QSize
29from PyQt5.QtGui import QPixmap, QFont
30
31from PyQt5.QtWidgets import (
32    QDialog, QLabel, QVBoxLayout, QDialogButtonBox, QSizePolicy, QHBoxLayout, QStackedWidget,
33    QWidget, QScrollArea, QPushButton
34)
35
36import raphodo.qrc_resources
37import raphodo.__about__ as __about__
38from raphodo.viewutils import translateDialogBoxButtons
39
40
41class AboutDialog(QDialog):
42    """
43    Display an About window
44    """
45
46    def __init__(self, parent=None) -> None:
47        super().__init__(parent)
48
49        self.setWindowFlags(Qt.Window | Qt.FramelessWindowHint)
50
51        self.setObjectName('AboutDialog')
52        self.setStyleSheet('QDialog#AboutDialog {background-image: url(:/splashscreen.png);}')
53        pixmap = QPixmap(':/splashscreen.png')
54        try:
55            ratio = pixmap.devicePixelRatioF()
56        except AttributeError:
57            ratio = pixmap.devicePixelRatio()
58
59        if ratio > 1.0:
60            size = QSize(pixmap.width() / ratio, pixmap.height() / ratio)
61        else:
62            size = pixmap.size()
63
64        self.setFixedSize(size)
65
66        # These values are derived from the splash screen image contents.
67        # If the image changes, so should these
68        white_box_height = 80
69        title_bottom = 45
70        left_margin = 16
71
72        transparency = "rgba(0, 0, 0, 130)"
73
74        # Standard About view
75
76        msg = """Copyright &copy; 2007-2020 Damon Lynch.<br><br>
77        <a href="http://www.damonlynch.net/rapid" %(link_style)s>
78        www.damonlynch.net/rapid</a><br><br>
79        This program comes with absolutely no warranty.<br>
80        See the <a href="http://www.gnu.org/copyleft/gpl.html" %(link_style)s>GNU General
81        Public License,
82        version 3 or later</a> for details.
83        """ % dict(link_style='style="color: white;"')
84
85        details = QLabel(msg)
86
87        details_style_sheet = """QLabel {
88        color: white;
89        background-color: %(transparency)s;
90        margin-left: 0px;
91        padding-left: %(left_margin)dpx;
92        padding-top: 6px;
93        padding-right: 6px;
94        padding-bottom: 6px;
95        }""" % dict(left_margin=left_margin, transparency=transparency)
96
97        details.setStyleSheet(details_style_sheet)
98        details.setOpenExternalLinks(True)
99        details.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
100        font = self.font()  # type: QFont
101        font_size = font.pointSize() - 2
102        font.setPointSize(font_size)
103        details.setFont(font)
104
105        aboutLayout = QVBoxLayout()
106        aboutLayout.setContentsMargins(0, 0, 0, 0)
107        aboutLayout.addSpacing(150)
108        detailsLayout = QHBoxLayout()
109        detailsLayout.setContentsMargins(0, 0, 0, 0)
110        detailsLayout.addWidget(details)
111        detailsLayout.addStretch(10)
112        aboutLayout.addLayout(detailsLayout)
113        aboutLayout.addStretch(10)
114
115        about = QWidget()
116        about.setLayout(aboutLayout)
117
118        # Credits view
119
120        credits_text = """
121        Copyright © 2007-2020 Damon Lynch.
122        Portions copyright © 2008-2015 Canonical Ltd.
123        Portions copyright © 2013 Bernard Baeyens.
124        Portions copyright © 2012-2015 Jim Easterbrook.
125        Portions copyright © 2012 Sven Marnach.
126        Portions copyright © 2015 Dmitry Shachnev.
127
128        Photo %(photolink)s copyright © 2014-2018 Damon Lynch, all rights reserved.
129        Camera icon courtesy %(artlink1)s.
130        Video camera icon courtesy %(artlink2)s.
131        Home icon courtesy %(artlink3)s.
132        Speech bubble courtesy %(artlink4)s.
133        Lightbulb icon courtesy %(artlink5)s.
134        Unlink icon courtesy %(artlink6)s.
135        Clock icon courtesy %(artlink7)s.
136        """
137
138        credits_text = credits_text.replace('\n', '<br>\n')
139
140        credits_text = credits_text % dict(
141            photolink="""<a href="https://500px.com/photo/246096445/afghan-men-pulling-heavy-load-
142            by-damon-lynch" style="color: white;">Afghan Men Pulling Heavy Load</a>""",
143            artlink1='<a href="http://www.webalys.com" style="color: white;">Vincent Le Moign</a>',
144            artlink2="""<a href="https://www.iconfinder.com/bluewolfski" style="color: white;">The
145                 Pictographers</a>""",
146            artlink3='<a href="https://www.iconfinder.com/Enesdal" style="color: white;">Enes'
147                     ' Dal</a>',
148            artlink4='<a href="http://www.iconsolid.com/" style="color: white;">Icons Solid</a>',
149            artlink5='<a href="https://sellfy.com/designcoon" style="color: white;">Icon Coon</a>',
150            artlink6='<a href="https://www.iconfinder.com/icons/1608708/unlink_icon" style="color: '
151                     'white;">Dave Gandy</a>',
152            artlink7='<a href="https://www.flaticon.com/authors/pixel-perfect" style="color: '
153                     'white;">Pixel perfect</a>'
154        )
155
156        label_style_sheet = """QLabel {
157        background-color: rgba(0, 0, 0, 0);
158        color: white;
159        padding-left: %(left_margin)dpx;
160        padding-top: 6px;
161        padding-right: 6px;
162        padding-bottom: 6px;
163        }""" % dict(left_margin=left_margin)
164
165        creditsLabel = QLabel(credits_text)
166        creditsLabel.setFont(font)
167        creditsLabel.setStyleSheet(label_style_sheet)
168        creditsLabel.setOpenExternalLinks(True)
169
170        credits = QScrollArea()
171        credits.setWidget(creditsLabel)
172        scroll_area_style_sheet = """QScrollArea {
173        background-color: %(transparency)s;
174        border: 0px;
175        }
176        """ % dict(transparency=transparency)
177        credits.setStyleSheet(scroll_area_style_sheet)
178
179        # Translators view
180
181        translators_text = """
182        <b>Algerian</b>
183        Algent Albrahimi <algent@protonmail.com>
184
185        <b>Belarusian</b>
186        Ilya Tsimokhin <ilya@tsimokhin.com>
187
188        <b>Brazilian Portuguese</b>
189        Ney Walens de Mesquita <walens@gmail.com>
190        Rubens Stuginski Jr <rubens.stuginski@gmail.com>
191
192        <b>Catalan</b>
193        Adolfo Jayme Barrientos <fitoschido@gmail.com>
194
195        <b>Czech</b>
196        Pavel Borecki <pavel.borecki@gmail.com>
197
198        <b>Danish</b>
199        Torben Gundtofte-Bruun <torben@g-b.dk>
200
201        <b>Dutch</b>
202        Alain J. Baudrez <a.baudrez@gmail.com>
203
204        <b>Estonian</b>
205        Tauno Erik <tauno.erik@gmail.com>
206
207        <b>Finnish</b>
208        Mikko Ruohola <mikko@ruohola.org>
209
210        <b>French</b>
211        Jean-Marc Lartigue <m.balthazar@posteo.net>
212
213        <b>Greek</b>
214        Dimitris Xenakis <dx@nkdx.gr>
215
216        <b>Hungarian</b>
217        László <csola48@gmail.com>
218        András Lőrincz <level.andrasnak@gmail.com>
219
220        <b>Italian</b>
221        Matteo Carotta <matteo.carotta@gmail.com>
222        Milo Casagrande <milo.casagrande@gmail.com>
223
224        <b>Japanese</b>
225        Koji Yokota <yokota6@gmail.com>
226
227        <b>Kabyle</b>
228        Mohammed Belkacem <belkacem77@gmail.com>
229
230        <b>Norwegian Bokmal</b>
231        Harlad H <haarektrans@gmail.com>
232        Rudolf Maurer <rudolf.maurer@googlemail.com>
233
234        <b>Norwegian Nynorsk</b>
235        Kevin Brubeck Unhammer <unhammer@fsfe.org>
236        Harlad H <haarektrans@gmail.com>
237
238        <b>Polish</b>
239        Michal Predotka <mpredotka@googlemail.com>
240
241        <b>Russian</b>
242        Evgeny Kozlov <evgeny.kozlov.mailbox@gmail.com>
243
244        <b>Serbian</b>
245        Мирослав Николић <miroslavnikolic@rocketmail.com>
246
247        <b>Slovak</b>
248        Robert Valik <robert@valik.sk>
249
250        <b>Spanish</b>
251        Adolfo Jayme Barrientos <fitoschido@gmail.com>
252        Jose Luis Tirado <joseluis.tirado@gmail.com>
253
254        <b>Swedish</b>
255        Joachim Johansson <joachim.j@gmail.com>
256
257        <b>Turkish</b>
258        Ilker Alp <ilkeryus@gmail.com>
259
260        <b>Previous translators</b>
261        Anton Alyab'ev <subeditor@dolgopa.org>
262        Michel Ange <michelange@wanadoo.fr>
263        Tobias Bannert <tobannert@gmail.com>
264        Bert <crinbert@yahoo.com>
265        Martin Dahl Moe
266        Marco de Freitas <marcodefreitas@gmail.com>
267        Martin Egger <martin.egger@gmx.net>
268        Sergiy Gavrylov <sergiovana@bigmir.net>
269        Emanuele Grande <caccolangrifata@gmail.com>
270        Toni Lähdekorpi <toni@lygon.net>
271        Miroslav Matejaš <silverspace@ubuntu-hr.org>
272        Erik M
273        Frederik Müller <spheniscus@freenet.de>
274        Jose Luis Navarro <jlnavarro111@gmail.com>
275        Tomas Novak <kuvaly@seznam.cz>
276        Abel O'Rian <abel.orian@gmail.com>
277        Balazs Oveges <ovegesb@freemail.hu>
278        Daniel Paessler <daniel@paessler.org>
279        Miloš Popović <gpopac@gmail.com>
280        Ye Qing <allen19920930@gmail.com>
281        Luca Reverberi <thereve@gmail.com>
282        Ahmed Shubbar <ahmed.shubbar@gmail.com>
283        Sergei Sedov <sedov@webmail.perm.ru>
284        Marco Solari <marcosolari@gmail.com>
285        Ulf Urdén <ulf.urden@purplescout.com>
286        Julien Valroff <julien@kirya.net>
287        Aron Xu <happyaron.xu@gmail.com>
288        Nicolás M. Zahlut <nzahlut@live.com>
289        梁其学 <yalongbay@gmail.com>
290        """
291
292        # Replace < and > in email addresses
293        translators_text = re.sub(
294            r'<(.+)@(.+)>', r'&lt;\1@\2&gt;', translators_text, flags=re.MULTILINE
295        )
296        translators_text = translators_text.replace('\n', '<br>\n')
297
298        translatorsLabel = QLabel(translators_text)
299        translatorsLabel.setFont(font)
300        translatorsLabel.setStyleSheet(label_style_sheet)
301
302        translators  = QScrollArea()
303        translators.setWidget(translatorsLabel)
304        translators.setStyleSheet(scroll_area_style_sheet)
305
306        mainLayout = QVBoxLayout()
307
308        self.stack = QStackedWidget()
309        self.stack.addWidget(about)
310        self.stack.addWidget(credits)
311        self.stack.addWidget(translators)
312        self.stack.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
313
314        buttonBox = QDialogButtonBox()
315        closeButton = buttonBox.addButton(QDialogButtonBox.Close)  # type: QPushButton
316        translateDialogBoxButtons(buttonBox)
317        self.creditsButton = buttonBox.addButton(
318            _('Credits'), QDialogButtonBox.HelpRole
319        )  # type: QPushButton
320        self.creditsButton.setDefault(False)
321        self.creditsButton.setCheckable(True)
322        self.translatorsButton = buttonBox.addButton(
323            _('Translators'), QDialogButtonBox.ResetRole
324        )  # type: QPushButton
325        self.translatorsButton.setDefault(False)
326        self.translatorsButton.setCheckable(True)
327        closeButton.setDefault(True)
328
329        buttonLayout = QVBoxLayout()
330        buttonLayout.addWidget(buttonBox)
331        buttonLayout.setContentsMargins(left_margin, left_margin, left_margin, left_margin)
332
333        mainLayout.setContentsMargins(0, 0, 0, 0)
334
335        version = QLabel(__about__.__version__)
336        version.setFixedHeight(white_box_height-title_bottom)
337
338        version_style_sheet = """QLabel {
339        padding-left: %(left_margin)dpx;
340        }""" % dict(left_margin=left_margin)
341
342        version.setStyleSheet(version_style_sheet)
343
344        mainLayout.addSpacing(title_bottom)
345        mainLayout.addWidget(version)
346        mainLayout.addWidget(self.stack)
347        mainLayout.addLayout(buttonLayout)
348
349        self.setLayout(mainLayout)
350
351        buttonBox.rejected.connect(self.reject)
352        self.creditsButton.clicked.connect(self.creditsButtonClicked)
353        self.translatorsButton.clicked.connect(self.translatorsButtonClicked)
354
355        closeButton.setFocus()
356
357    @pyqtSlot()
358    def creditsButtonClicked(self) -> None:
359        self.translatorsButton.setChecked(False)
360        self.showStackItem()
361
362    @pyqtSlot()
363    def translatorsButtonClicked(self) -> None:
364        self.creditsButton.setChecked(False)
365        self.showStackItem()
366
367    @pyqtSlot()
368    def showStackItem(self) -> None:
369        if self.creditsButton.isChecked():
370            self.stack.setCurrentIndex(1)
371        elif self.translatorsButton.isChecked():
372            self.stack.setCurrentIndex(2)
373            self.creditsButton.setChecked(False)
374        else:
375            self.stack.setCurrentIndex(0)
376
377
378