1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
|
#-*- coding:utf-8 -*-
# Copyright © 2009, 2011-2017 B. Clausius <barcc@gmx.de>
#
# 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, either version 3 of the License, or
# (at your option) any later version.
#
# 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, see <http://www.gnu.org/licenses/>.
import re
import html
from . import config
from .settings import settings
from .ext import qt #FIXME: buildlib.utils imports dialogs and the purepython version of qt with PyQt5
from .theme import Theme
from .model import Model
from .application import Meta #FIXME: Circular import: works because dialogs is imported only inside functions
try:
_
except NameError:
_ = lambda t: t
def linkedtext_to_html(text):
html = qt.text_to_html(text)
html = re.sub(r'<((?:http:|https:|text:).*?)\|>', r'<a href="\1">', html)
html = re.sub(r'<\|>', r'</a>', html)
return re.sub(r'<((?:http:|https:).*?)>', r'<<a href="\1">\1</a>>', html)
def init_preferences(sample_buffers):
shader_names = {'lighting': _('Lighting'), 'simple': _('Simple'),
#TRANSLATORS: labels are the colored stickers on the cube
'label': _('Labels')}
facenames = [(_(name), key) for key, name in Model.cache_index['facenames']]
qt.preferences_dialog(shader_names, sample_buffers, facenames, Theme.textures.get_icons())
def get_langname_from_code_func():
try:
import icu
except ImportError:
print('PyICU is not installed')
try:
from .languages import languages
except ImportError:
print('No translations for language names')
return lambda code: None
else:
from locale import getlocale, getdefaultlocale
l = getlocale()[0]
if l is None:
l = getdefaultlocale()[0]
if l is None:
return lambda code: None
try:
languages = languages[l]
except KeyError:
try:
languages = languages[l.split('_')[0]]
except KeyError:
return lambda code: None
return lambda code: languages.get(code)
else:
def icu_langname_from_code(code):
iculocale = icu.Locale.createFromName(code)
lang = iculocale.getLanguage()
if icu.Locale.createFromName(lang).getDisplayName() == lang:
return None
return str(iculocale.getDisplayName()) or None
return icu_langname_from_code
class ModelSelection (metaclass=Meta):
def __init__(self):
self.stack = [self.get_modeldata_main]
self.pagedata = {}
def get_modeldata_main(self):
defaultindex = -1
defaultmtype = settings['game.type']
index = 0
modeldata_funcs = [None, self.get_modeldata_type1, self.get_modeldata_type2, self.get_modeldata_type3]
factory_get_modeldata = lambda mtype, func: func and (lambda: func(mtype))
for mtype in Model.cache_index['types']:
if mtype == defaultmtype:
defaultindex = index
size = Model.cache_index['type'][mtype]['defaultsize']
text = _(Model.cache_index['type'][mtype]['name'])
thumbnail = '{}/thumbnails/{}-{}.png'.format(config.UI_DIR, mtype, 'x'.join(str(i) for i in size))
yield text, thumbnail, mtype, size, factory_get_modeldata(mtype, modeldata_funcs[len(size)])
index += 1
yield defaultindex
yield _('Select Puzzle')
def get_modeldata_type1(self, mtype):
defaultindex = -1
defaultsize = settings['games',mtype,'size']
try:
defaultsize = Model.cache_index['normsize'][mtype][defaultsize]
except KeyError:
defaultsize = Model.cache_index['type'][mtype]['defaultsize']
index = 0
for size in sorted(set(Model.cache_index['normsize'][mtype].values())):
if size == defaultsize:
defaultindex = index
text = _(Model.cache_index['type'][mtype]['mformat']).format(*size)
thumbnail = '{}/thumbnails/{}-{}.png'.format(config.UI_DIR, mtype, 'x'.join(str(i) for i in size))
yield text, thumbnail, mtype, size, None
index += 1
yield defaultindex
yield _(Model.cache_index['type'][mtype]['name'])
def get_modeldata_type2(self, mtype):
defaultindex = -1
defaultsize = Model.cache_index['type'][mtype]['defaultsize']
index = 0
factory_get_modeldata = lambda mtype, height: lambda: self.get_modeldata_typeheight2(mtype, height)
for size in sorted(set(Model.cache_index['normsize'][mtype].values())):
if size[0] == defaultsize[0] and size[2:] == defaultsize[2:]:
if size == defaultsize:
defaultindex = index
#TRANSLATORS: 1 slice, 2 slices, 3 slices, ...
text = ngettext('{} slice','{} slices',size[1]).format(size[1])
thumbnail = '{}/thumbnails/{}-{}.png'.format(config.UI_DIR, mtype, 'x'.join(str(i) for i in size))
yield text, thumbnail, mtype, size, factory_get_modeldata(mtype, size[1])
index += 1
yield defaultindex
yield _(Model.cache_index['type'][mtype]['name'])
def get_modeldata_type3(self, mtype):
defaultindex = -1
defaultsize = Model.cache_index['type'][mtype]['defaultsize']
index = 0
factory_get_modeldata = lambda mtype, height: lambda: self.get_modeldata_typeheight3(mtype, height)
for size1 in range(1,11): #FIXME: hardcoded limit
try:
size = Model.cache_index['normsize'][mtype][(size1, defaultsize[0], defaultsize[2])]
except KeyError as e:
print('hm', e)
continue
if size == defaultsize:
defaultindex = index
text = ngettext('{} slice','{} slices',size1).format(size1)
thumbnail = '{}/thumbnails/{}-{}.png'.format(config.UI_DIR, mtype, 'x'.join(str(i) for i in size))
yield text, thumbnail, mtype, size, factory_get_modeldata(mtype, size1)
index += 1
yield defaultindex
yield _(Model.cache_index['type'][mtype]['name'])
def get_modeldata_typeheight2(self, mtype, height):
defaultindex = -1
defaultsize = settings['games',mtype,'size']
try:
defaultsize = Model.cache_index['normsize'][mtype][defaultsize]
except KeyError:
defaultsize = Model.cache_index['type'][mtype]['defaultsize']
if defaultsize[1] != height:
defaultsize = tuple((height if i==1 else s) for i,s in enumerate(defaultsize))
index = 0
for size in sorted(set(Model.cache_index['normsize'][mtype].values())):
if size[1] == height:
if size == defaultsize:
defaultindex = index
text = _(Model.cache_index['type'][mtype]['mformat']).format(*size)
thumbnail = '{}/thumbnails/{}-{}.png'.format(config.UI_DIR, mtype, 'x'.join(str(i) for i in size))
yield text, thumbnail, mtype, size, None
index += 1
yield defaultindex
yield _(Model.cache_index['type'][mtype]['name'])+' – '+ngettext('{} slice','{} slices',height).format(height)
def get_modeldata_typeheight3(self, mtype, height):
defaultindex = -1
defaultsize = Model.cache_index['type'][mtype]['defaultsize']
if defaultsize[1] != height:
defaultsize = tuple((height if i==1 else s) for i,s in enumerate(defaultsize))
index = 0
factory_get_modeldata = lambda mtype, height, width: lambda: self.get_modeldata_typeheightwidth3(mtype, height, width)
for width in range(1,11): #FIXME: hardcoded limit
try:
size = Model.cache_index['normsize'][mtype][(width, height, defaultsize[2])]
except KeyError:
continue
if size == defaultsize:
defaultindex = index
text = _('{}×{} slices').format(width, height)
thumbnail = '{}/thumbnails/{}-{}.png'.format(config.UI_DIR, mtype, 'x'.join(str(i) for i in size))
yield text, thumbnail, mtype, size, factory_get_modeldata(mtype, height, width)
index += 1
yield defaultindex
yield _(Model.cache_index['type'][mtype]['name'])+' – '+ngettext('{} slice','{} slices',height).format(height)
def get_modeldata_typeheightwidth3(self, mtype, height, width):
defaultindex = -1
defaultsize = settings['games',mtype,'size']
try:
defaultsize = Model.cache_index['normsize'][mtype][defaultsize]
except KeyError:
defaultsize = Model.cache_index['type'][mtype]['defaultsize']
if defaultsize[1] != height or defaultsize[0] != width:
defaultsize = (width, height, defaultsize[2])
index = 0
for depth in range(1,11): #FIXME: hardcoded limit
try:
size = Model.cache_index['normsize'][mtype][(width, height, depth)]
except KeyError:
continue
if size == defaultsize:
defaultindex = index
text = _(Model.cache_index['type'][mtype]['mformat']).format(*size)
thumbnail = '{}/thumbnails/{}-{}.png'.format(config.UI_DIR, mtype, 'x'.join(str(i) for i in size))
yield text, thumbnail, mtype, size, None
index += 1
yield defaultindex
yield _(Model.cache_index['type'][mtype]['name'])+' – '+_('{}×{} slices').format(width, height)
def on_model_get_modeldata(self, pageindex):
assert pageindex >= 1, (pageindex, self.stack)
func = self.stack[pageindex-1]
try:
items, defaultindex, title = self.pagedata[func]
except KeyError:
*items, defaultindex, title = func()
self.pagedata[func] = [items, defaultindex, title]
return items, defaultindex, title
def on_model_back(self):
if len(self.stack) > 1:
self.stack.pop()
return True
return False
def on_convert_to_mtype_size(self, pageindex, index):
func = self.stack[pageindex-1]
self.pagedata[func][1] = index
unused_text, unused_thumbnail, mtype, size, func = self.pagedata[func][0][index]
if func is None:
return mtype, size
else:
self.stack[pageindex:] = [func]
return None
class ProgressDialog:
def __init__(self):
self.canceled_ = False
self.value_max = 10
self.value = 0
def on_canceled(self):
self.canceled_ = True
#XXX: currently not used, so not translation needed
print(('Canceling operation, please wait'))
def tick(self, step, message=None, value_max=None):
if message is not None:
print(message)
if value_max is not None:
self.value_max = value_max
self.value = 0
if step < 0 or self.value > self.value_max:
print(' tick')
else:
print(' ', self.value, '/', self.value_max)
self.value += step
return self.canceled_
def done(self):
self.canceled_ = False
class DialogValues:
@classmethod
def get_text(self, string):
return getattr(self, string)()
helpstrings = [
#TRANSLATORS: The next strings form the text in the help dialog:
# Title of the 1st paragraph
_('Using the mouse to rotate the cube'),
#TRANSLATORS: Text of the 1st paragraph, followed by bullet list items
_('Position the mouse cursor over the puzzle and you will see an arrow '
'that gives you a hint in which direction the slice under the mouse cursor will be rotated.'),
_('The left mouse button rotates a single slice of the cube in the direction of the arrow.'),
_('The right mouse button rotates a single slice of the cube against the direction of the arrow.'),
_('To rotate the whole cube instead of a single slice press the Ctrl key together with the mouse button.'),
#TRANSLATORS: Title of the 2nd paragraph
_('Using the keyboard to rotate the cube'),
#TRANSLATORS: Text of the 2nd paragraph, followed by bullet list items
_('Make sure the keyboard focus is on the cube area (e.g. click on the background of the cube). '
'The keys can be configured in the preferences dialog, the default is:'),
#TRANSLATORS: before the item text is a list of keys
_('Moves the left, right, upper, down, front or back slice clockwise.'),
_('Moves a slice couterclockwise.'),
_('Moves the whole cube.'),
#TRANSLATORS: Title of the 3rd paragraph
_('Other keys and buttons'),
#TRANSLATORS: bullet list items of the 3rd paragraph
_('Mouse wheel – Zoom in/out'),
_('Arrow keys, Left mouse button on the background – Changes the direction of looking at the cube.'),
_('Moves keyboard focus to the sequence editor above the cube area '
'where you can edit the move sequence in the notation described below. '
'Hit enter when done.'),
#TRANSLATORS: Title of the 4th paragraph
_('Notation for moves'),
#TRANSLATORS: Text of the 4th paragraph, followed by bullet list items
_('All moves, however they are made, are displayed progressively above the cube area:'),
_('Moves the left, right, upper, down, front or back slice clockwise.'),
_('Moves a slice couterclockwise.'),
_('Moves the whole cube.'),
_('Moves the first, second or third slice from left clockwise. '
'The allowed numbers are in the range from 1 to the count of parallel slices. '
'"l1" is always the same as "l" '
'and for the classic 3×3×3-Cube "l2" is the same as "r2-" and "l3" is the same as "r-".'),
_('You can use a space to separate groups of moves.'),
]
helpformat = '''
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html>
<head>
<meta name="qrichtext" content="1" />
<style type="text/css">p, li {{ white-space: pre-wrap; }}</style>
</head>
<body>
<h3>{}</h3>
<p>{}</p>
<ul>
<li>{}</li>
<li>{}</li>
<li>{}</li>
</ul>
<h3>{}</h3>
<p>{}</p>
<ul>
<li>4,6,8,2,5,0 – {}</li>
<li>Shift+4, … – {}</li>
<li>Ctrl+4, … – {}</li>
</ul>
<h3>{}</h3>
<ul>
<li>{}</li>
<li>{}</li>
<li>Ctrl+L – {}</li>
</ul>
<h3>{}</h3>
<p>{}</p>
<ul>
<li>l, r, u, d, f, b – {}</li>
<li>l-, r-, u-, d-, f-, b- – {}</li>
<li>L, R, U, D, F, B – {}</li>
<li>l1, l2, l3 – {}</li>
<li>{}</li>
</ul>
</body></html>
'''
@classmethod
def help_text(self):
return self.helpformat.format(*[html.escape(s) for s in self.helpstrings])
@staticmethod
def about_website():
return '<a href="{}">{}</a>'.format(config.WEBSITE, _('Pybik project website'))
_about_translators = None
@classmethod
def about_translators(self):
if self._about_translators is not None:
return self._about_translators
html_template = '''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
p, li {{ white-space: pre-wrap; }}
</style></head>
<body>{}</body></html>'''
html_p_template = '<p align="center">{}</p>'
html_language_template1 = '<span style=" font-weight:600;">{}</span>'
html_language_template2 = '{}'
html_person_template = '<a href="{}">{}</a>'
html_p_items = []
from .translators import translators
langname_from_code = get_langname_from_code_func()
import locale
sortfunc = lambda v: locale.strxfrm(v[0])
def gentranslators():
for language, langcode, complete, persons in translators:
language = langname_from_code(langcode) or language
yield language, complete, persons
for language, complete, persons in sorted(gentranslators(), key=sortfunc):
language = html.escape(language)
html_items = [(html_language_template1 if complete else html_language_template2).format(language)]
for name, link in persons:
name = html.escape(name)
html_items.append(html_person_template.format(link, name))
html_p_items.append(html_p_template.format('<br/>'.join(html_items)))
self._about_translators = html_template.format(''.join(html_p_items))
return self._about_translators
@staticmethod
def about_contribute():
return linkedtext_to_html(config.get_filebug_text() +'\n\n\n'+ _(config.TRANSLATION_TEXT))
@staticmethod
def about_license_short():
text = '\n\n'.join((_(config.LICENSE_INFO), _(config.LICENSE_FURTHER)))
return linkedtext_to_html(text)
@staticmethod
def about_license_full():
try:
with open(config.LICENSE_FILE, 'rt', encoding='utf-8') as license_file:
text = license_file.read()
text = linkedtext_to_html(text)
except Exception as e:
print('Unable to find license text:', e)
text = ''
return text
@staticmethod
def about_license_notfound():
text = _(config.LICENSE_NOT_FOUND)
return linkedtext_to_html(text)
|