pylupdate4 kalam.pro
[bitcoin:spesmilo.git] / settings.py
1 # -*- coding: utf-8 -*-
2 # Spesmilo -- Python Bitcoin user interface
3 # Copyright © 2011 Luke Dashjr
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, version 3 only.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 from decimal import Decimal
18 import os
19 import re
20 from PySide.QtCore import *
21 from PySide.QtGui import *
22
23 _settings = QSettings('BitCoin', 'Spesmilo')
24
25 class SettingsTabCore(QWidget):
26     def __init__(self, parent, enableApply = None):
27         super(SettingsTabCore, self).__init__(parent)
28         
29         cblay = QFormLayout()
30         self.cbInternal = QCheckBox(self)
31         self.lblInternal = QLabel(self.tr('Use internal core'))
32         cblay.addRow(self.cbInternal, self.lblInternal)
33         self.cbInternal.stateChanged.connect(self.updateURIValidity)
34         
35         lelay = QFormLayout()
36         self.lblURI = QLabel(self.tr('URI:'))
37         self.leURI = QLineEdit()
38         lelay.addRow(self.lblURI, self.leURI)
39
40         mainlay = QVBoxLayout(self)
41         mainlay.addLayout(cblay)
42         mainlay.addLayout(lelay)
43         
44         if enableApply is not None:
45             self.cbInternal.stateChanged.connect(lambda: enableApply())
46             self.leURI.textChanged.connect(lambda: enableApply())
47     
48     def loadSettings(self, settings = None):
49         self.cbInternal.setChecked(settings.value('core/internal', 'True') != 'False')
50         self.leURI.setText(settings.value('core/uri', 'http://user:pass@localhost:8332'))
51     
52     def checkSettings(self):
53         if os.system('bitcoind --help'):
54             self.cbInternal.setChecked(False)
55             self.cbInternal.setEnabled(False)
56             self.lblInternal.setEnabled(False)
57     
58     def saveSettings(self, settings = None):
59         settings.setValue('core/internal', repr(self.cbInternal.isChecked()))
60         settings.setValue('core/uri', self.leURI.text())
61     
62     def updateURIValidity(self):
63         en = not self.cbInternal.isChecked()
64         self.lblURI.setEnabled(en)
65         self.leURI.setEnabled(en)
66
67 class SettingsTabLanguage(QWidget):
68     def __init__(self, parent, enableApply = None):
69         super(SettingsTabLanguage, self).__init__(parent)
70         
71         mainlay = QFormLayout(self)
72         
73         self.lang = QComboBox()
74         self.lang.addItem(self.tr('American'), 'en_US')
75         self.lang.addItem(self.tr('English'), 'en_GB')
76         self.lang.addItem(self.tr('Esperanto'), 'eo_EO')
77         mainlay.addRow(self.tr('Language:'), self.lang)
78         
79         nslay = QHBoxLayout()
80         self.strength = QComboBox()
81         self.strength.addItem(self.tr('Assume'), 'Assume')
82         self.strength.addItem(self.tr('Prefer'), 'Prefer')
83         self.strength.addItem(self.tr('Force'), 'Force')
84         nslay.addWidget(self.strength)
85         self.numsys = QComboBox(self)
86         self.numsys.addItem(self.tr('Decimal'), 'Decimal')
87         self.numsys.addItem(self.tr('Tonal'), 'Tonal')
88         nslay.addWidget(self.numsys)
89         mainlay.addRow(self.tr('Number system:'), nslay)
90         
91         self.hideTLA = QCheckBox(self.tr('Hide preferred unit name'))
92         mainlay.addRow(self.hideTLA)
93
94         if enableApply is not None:
95             self.lang.currentIndexChanged.connect(lambda: enableApply())
96             self.numsys.currentIndexChanged.connect(lambda: enableApply())
97             self.strength.currentIndexChanged.connect(lambda: enableApply())
98             self.hideTLA.stateChanged.connect(lambda: enableApply())
99     
100     def loadSettings(self, settings = None):
101         self.lang.setCurrentIndex(self.lang.findData(settings.value('language/language', 'en_GB')))
102         self.numsys.setCurrentIndex(self.numsys.findData(settings.value('units/numsys', 'Decimal')))
103         self.strength.setCurrentIndex(self.strength.findData(settings.value('units/strength', 'Assume')))
104         self.hideTLA.setChecked(settings.value('units/hideTLA', 'True') != 'False')
105     
106     def checkSettings(self):
107         pass
108     
109     def saveSettings(self, settings = None):
110         settings.setValue('language/language', self.lang.itemData(self.lang.currentIndex()))
111         SpesmiloSettings.loadTranslator()
112         settings.setValue('units/numsys', self.numsys.itemData(self.numsys.currentIndex()))
113         settings.setValue('units/strength', self.strength.itemData(self.strength.currentIndex()))
114         settings.setValue('units/hideTLA', repr(self.hideTLA.isChecked()))
115
116 class SettingsDialog(QDialog):
117     def __init__(self, parent):
118         super(SettingsDialog, self).__init__(parent)
119         
120         self.settings = _settings
121         
122         tabw = QTabWidget()
123         
124         self.tabs = []
125         self.tabs.append(('Core', SettingsTabCore(self, self.enableApply)))
126         self.tabs.append(('Language', SettingsTabLanguage(self, self.enableApply)))
127         
128         for name, widget in self.tabs:
129             tabw.addTab(widget, name)
130         
131         mainlay = QVBoxLayout(self)
132         mainlay.addWidget(tabw)
133         
134         actionlay = QHBoxLayout()
135         actionlay.addStretch()
136         
137         okbtn = QPushButton(self.tr('&OK'))
138         okbtn.clicked.connect(self.accept)
139         okbtn.setAutoDefault(True)
140         actionlay.addWidget(okbtn)
141         
142         applybtn = QPushButton(self.tr('&Apply'))
143         self.applybtn = applybtn
144         applybtn.clicked.connect(lambda: self.saveSettings())
145         actionlay.addWidget(applybtn)
146         
147         cancelbtn = QPushButton(self.tr('&Cancel'))
148         cancelbtn.clicked.connect(self.reject)
149         actionlay.addWidget(cancelbtn)
150
151         mainlay.addLayout(actionlay)
152         
153         self.loadSettings()
154         self.checkSettings()
155         
156         self.accepted.connect(lambda: self.saveSettings())
157         
158         if parent is not None:
159             self.setWindowIcon(parent.bitcoin_icon)
160         self.setWindowTitle(self.tr('Settings'))
161         self.show()
162     
163     def loadSettings(self):
164         settings = self.settings
165         for x, widget in self.tabs:
166             widget.loadSettings(settings)
167         self.applybtn.setEnabled(False)
168     
169     def checkSettings(self):
170         for x, widget in self.tabs:
171             widget.checkSettings()
172         
173     def saveSettings(self):
174         settings = self.settings
175         for x, widget in self.tabs:
176             widget.saveSettings(settings)
177         self.applybtn.setEnabled(False)
178
179     def enableApply(self):
180         self.applybtn.setEnabled(True)
181
182 class SpesmiloSettings:
183     def isConfigured(self):
184         NC = 'NOT CONFIGURED'
185         return _settings.value('core/internal', NC) != NC
186     
187     def useInternalCore(self):
188         return _settings.value('core/internal', 'True') != 'False'
189     
190     def getEffectiveURI(self):
191         if self.useInternalCore():
192             return 'http://user:pass@localhost:8332'
193         return _settings.value('core/uri', 'http://user:pass@localhost:8332')
194
195     def getNumberSystem(self):
196         return _settings.value('units/numsys', 'Decimal')
197
198     def getNumberSystemStrength(self):
199         return _settings.value('units/strength', 'Assume')
200
201     def getHideUnitTLA(self):
202         return _settings.value('units/hideTLA', 'True') != 'False'
203
204     def _commafy(self, s, groupsize):
205         n = ''
206         if s[0] == '-':
207             n = s[0]
208             s = s[1:]
209         firstcomma = len(s) % groupsize or groupsize
210         n += s[:firstcomma]
211         r = s[firstcomma:]
212         segments = (n,) + tuple(r[i:i+groupsize] for i in range(0, len(r), groupsize))
213         return ','.join(segments)
214
215     def format_decimal(self, n, addSign = False, wantDelimiters = False, centsHack = False):
216         if not type(n) is Decimal:
217             n = Decimal(str(n))
218         if centsHack and not n % Decimal('.1'):
219             n = n.quantize(Decimal('0.00'))
220             s = str(n)
221         else:
222             s = str(int(n)) + str(abs(n % 1) + 1)[1:]
223             if n < 0 and int(n) == 0:
224                 s = '-' + s
225         if wantDelimiters:
226             s = self._commafy(s, 3)
227         if addSign and n >= 0:
228                 s = "+" + s
229         return s
230
231     _toTonalDict = dict(((57, u'\ue9d9'), (65, u'\ue9da'), (66, u'\ue9db'), (67, u'\ue9dc'), (68, u'\ue9dd'), (69, u'\ue9de'), (70, u'\ue9df'), (97, u'\ue9da'), (98, u'\ue9db'), (99, u'\ue9dc'), (100, u'\ue9dd'), (101, u'\ue9de'), (102, u'\ue9df')))
232     def format_tonal(self, n, addSign = False, wantDelimiters = False):
233         s = "%x" % n
234         if wantDelimiters:
235             s = self._commafy(s, 4)
236         n = abs(n) % 1
237         if n:
238             s += '.'
239             while n:
240                 n *= 16
241                 s += "%x" % n
242                 n %= 1
243         s = unicode(s).translate(self._toTonalDict)
244         if addSign and n >= 0:
245                 s = "+" + s
246         return s
247
248     _fromTonalDict = dict(((0xe9d9, u'9'), (0xe9da, u'a'), (57, u'a'), (0xe9db, u'b'), (0xe9dc, u'c'), (0xe9dd, u'd'), (0xe9de, u'e'), (0xe9df, u'f')))
249     def parse_tonal(self, s, mult = 1):
250         s = unicode(s).translate(self._fromTonalDict)
251         s = ''.join(s.split(','))
252         try:
253             i = s.index('.')
254             s = s.rstrip('0')
255         except ValueError:
256             i = len(s)
257         n = 0
258         if i:
259             n = int(s[:i], 16)
260         n *= mult
261         if mult / (16 ** (len(s) - i - 1)) < 1:
262             mult = float(mult)
263         for j in range(1, len(s) - i):
264             d = int(s[i+j], 16)
265             n += (d * mult) / (16 ** j)
266         return n
267
268     def format_number(self, n, addSign = False, wantDelimiters = False):
269         ns = self.getNumberSystem()
270         if ns == 'Tonal':
271             ens = self.format_tonal
272         else:
273             ens = self.format_decimal
274         return ens(n, addSign=addSign, wantDelimiters=wantDelimiters)
275
276     def _toBTC(self, n, addSign = False, wantTLA = None, wantDelimiters = False):
277         n = Decimal(n) / 100000000
278         s = self.format_decimal(n, addSign=addSign, wantDelimiters=wantDelimiters, centsHack=True)
279         if wantTLA is None:
280             wantTLA = not self.getHideUnitTLA()
281         if wantTLA:
282             s += " BTC"
283         return s
284
285     def _fromBTC(self, s):
286         s = float(s)
287         s = int(s * 100000000)
288         return s
289
290     def _toTBC(self, n, addSign = False, wantTLA = None, wantDelimiters = False):
291         n /= float(0x10000)
292         s = self.format_tonal(n, addSign=addSign, wantDelimiters=wantDelimiters)
293         if wantTLA is None:
294             wantTLA = not self.getHideUnitTLA()
295         if wantTLA:
296             s += " TBC"
297         return s
298
299     def _fromTBC(self, s):
300         n = self.parse_tonal(s, mult=0x10000)
301         return n
302
303     def ChooseUnits(self, n, guess = None):
304         if float(n) != n:
305             raise ValueError()
306         ns = self.getNumberSystem()
307         nss = self.getNumberSystemStrength()
308         ens = None
309         if nss != 'Force' and n:
310             # If it's only valid as one, and not the other, choose it
311             ivD = 0 == n % 1000000
312             ivT = 0 == n % 0x100
313             if ivD and not ivT:
314                 ens = 'Decimal'
315             elif ivT and not ivD:
316                 ens = 'Tonal'
317             # If it could be either, pick the more likely one (only with 'Assume')
318             elif ivD and nss == 'Assume':
319                 if not guess is None:
320                     ens = guess
321                 dn = n / 1000000
322                 tn = n / 0x100
323                 while ens is None:
324                     dm = dn % 10 not in (0, 5)
325                     tm = tn % 0x10 not in (0, 8)
326                     if dm:
327                         if tm:
328                             break
329                         ens = 'Tonal'
330                     elif tm:
331                         ens = 'Decimal'
332                     dn /= 10.
333                     tn /= 16.
334         if ens is None: ens = ns
335         return ens
336
337     def humanAmount(self, n, addSign = False, wantTLA = None):
338         ns = self.getNumberSystem()
339         try:
340             ens = self.ChooseUnits(n)
341         except ValueError:
342             return n
343         if ens != ns:
344             wantTLA = True
345         if ens == 'Tonal':
346             ens = self._toTBC
347         else:
348             ens = self._toBTC
349         return ens(n, addSign, wantTLA)
350
351     def humanToAmount(self, s):
352         ens = self.getNumberSystem()
353         m = re.search('\s*\\b(BTC|TBC)\s*$', s, re.IGNORECASE)
354         if m:
355             if m.group(1) == 'TBC':
356                 ens = 'Tonal'
357             else:
358                 ens = 'Decimal'
359             s = s[:m.start()]
360         if ens == 'Tonal':
361             ens = self._fromTBC
362         else:
363             ens = self._fromBTC
364         return ens(s)
365
366     def loadTranslator(self):
367         lang = _settings.value('language/language', 'en_GB')
368         if not hasattr(self, 'translator'):
369             self.translator = QTranslator()
370         self.translator.load('i18n/%s' % (lang,))
371         app = QCoreApplication.instance()
372         app.installTranslator(self.translator)
373
374 SpesmiloSettings = SpesmiloSettings()
375 format_number = SpesmiloSettings.format_number
376 humanAmount = SpesmiloSettings.humanAmount
377 humanToAmount = SpesmiloSettings.humanToAmount
378
379 if __name__ == '__main__':
380     import sys
381     app = QApplication(sys.argv)
382     SpesmiloSettings.loadTranslator()
383     dlg = SettingsDialog(None)
384     sys.exit(app.exec_())