source: src/mlx/config.py@ 261:aad834a04851

Last change on this file since 261:aad834a04851 was 249:d055e454a7ea, checked in by István Váradi <ivaradi@…>, 12 years ago

Implemented option to control whether to quit or hide when the window close button is pressed

File size: 26.2 KB
Line 
1# Configuration and related stuff
2# -*- encoding: utf-8 -*-
3
4#-------------------------------------------------------------------------------
5
6import const
7
8import os
9import sys
10import ConfigParser
11
12#-------------------------------------------------------------------------------
13
14configPath = os.path.join(os.path.expanduser("~"),
15 "mlx.config" if os.name=="nt" else ".mlxrc")
16
17#-------------------------------------------------------------------------------
18
19if os.name=="nt":
20 _languageMap = { "en_GB" : "eng",
21 "hu_HU" : "hun" }
22
23#-------------------------------------------------------------------------------
24
25class Hotkey(object):
26 """A hotkey."""
27 def __init__(self, ctrl = False, shift = False, key = "0"):
28 """Construct the hotkey."""
29 self.ctrl = ctrl
30 self.shift = shift
31 self.key = key
32
33 def set(self, s):
34 """Set the hotkey from the given string."""
35 self.ctrl = "C" in s[:-1]
36 self.shift = "S" in s[:-1]
37 self.key = s[-1]
38
39 def __eq__(self, other):
40 """Check if the given hotkey is equal to the other one."""
41 return self.ctrl == other.ctrl and self.shift == other.shift and \
42 self.key == other.key
43
44 def __str__(self):
45 """Construct the hotkey to a string."""
46 s = ""
47 if self.ctrl: s += "C"
48 if self.shift: s += "S"
49 s += self.key
50 return s
51
52#-------------------------------------------------------------------------------
53
54class Checklist(object):
55 """A checklist for a certain aircraft type."""
56 # The name of the section of the checklists
57 SECTION="checklists"
58
59 @staticmethod
60 def fromConfig(config, aircraftType):
61 """Create a checklist for the given aircraft type from the given
62 config."""
63 baseName = "checklist." + const.icaoCodes[aircraftType] + "."
64 fileList = []
65 while True:
66 option = baseName + str(len(fileList))
67 if config.has_option(Checklist.SECTION, option):
68 fileList.append(config.get(Checklist.SECTION, option))
69 else:
70 break
71
72 return Checklist(fileList)
73
74 def __init__(self, fileList = None):
75 """Construct the check list with the given file list."""
76 self._fileList = [] if fileList is None else fileList[:]
77
78 def clone(self):
79 """Clone the checklist."""
80 return Checklist(self._fileList)
81
82 def toConfig(self, config, aircraftType):
83 """Add this checklist to the given config."""
84 baseName = "checklist." + const.icaoCodes[aircraftType] + "."
85 for index in range(0, len(self._fileList)):
86 option = baseName + str(index)
87 config.set(Checklist.SECTION, option,
88 self._fileList[index])
89
90 def __eq__(self, other):
91 """Determine if the checklist is equal to the given other one."""
92 return self._fileList == other._fileList
93
94 def __len__(self):
95 """Get the length of the file list."""
96 return len(self._fileList)
97
98 def __getitem__(self, index):
99 """Get the file with the given index."""
100 return self._fileList[index]
101
102 def __iter__(self):
103 """Iterate over the files."""
104 return iter(self._fileList)
105
106#-------------------------------------------------------------------------------
107
108class Config(object):
109 """Our configuration."""
110 DEFAULT_UPDATE_URL = "http://mlx.varadiistvan.hu/update"
111
112 _messageTypesSection = "messageTypes"
113
114 def __init__(self):
115 """Construct the configuration with default values."""
116
117 self._pilotID = ""
118 self._password = ""
119 self._rememberPassword = False
120
121 self._language = ""
122 self._hideMinimizedWindow = True
123 self._quitOnClose = False
124 self._onlineGateSystem = True
125 self._onlineACARS = True
126 self._flareTimeFromFS = False
127 self._syncFSTime = False
128 self._usingFS2Crew = False
129 self._iasSmoothingLength = -2
130 self._vsSmoothingLength = -2
131
132 self._pirepDirectory = None
133
134 self._enableSounds = True
135
136 self._pilotControlsSounds = True
137 self._pilotHotkey = Hotkey(ctrl = True, shift = False, key = "0")
138
139 #self._approachCallOuts = False
140 self._speedbrakeAtTD = True
141
142 self._enableChecklists = False
143 self._checklistHotkey = Hotkey(ctrl = True, shift = True, key = "0")
144
145 self._autoUpdate = True
146 self._updateURL = Config.DEFAULT_UPDATE_URL
147
148 self._messageTypeLevels = {}
149
150 self._checklists = {}
151 for aircraftType in const.aircraftTypes:
152 self._checklists[aircraftType] = Checklist()
153
154 self._modified = False
155
156 @property
157 def pilotID(self):
158 """Get the pilot ID."""
159 return self._pilotID
160
161 @pilotID.setter
162 def pilotID(self, pilotID):
163 """Set the pilot ID."""
164 if pilotID!=self._pilotID:
165 self._pilotID = pilotID
166 self._modified = True
167
168 @property
169 def password(self):
170 """Get the password."""
171 return self._password
172
173 @password.setter
174 def password(self, password):
175 """Set the password."""
176 if password!=self._password:
177 self._password = password
178 self._modified = True
179
180 @property
181 def rememberPassword(self):
182 """Get if we should remember the password."""
183 return self._rememberPassword
184
185 @rememberPassword.setter
186 def rememberPassword(self, rememberPassword):
187 """Set if we should remember the password."""
188 if rememberPassword!=self._rememberPassword:
189 self._rememberPassword = rememberPassword
190 self._modified = True
191
192 @property
193 def language(self):
194 """Get the language to use."""
195 return self._language
196
197 @language.setter
198 def language(self, language):
199 """Set the language to use."""
200 if language!=self._language:
201 self._language = language
202 self._modified = True
203
204 @property
205 def hideMinimizedWindow(self):
206 """Get whether a minimized window should be hidden."""
207 return self._hideMinimizedWindow
208
209 @hideMinimizedWindow.setter
210 def hideMinimizedWindow(self, hideMinimizedWindow):
211 """Set whether a minimized window should be hidden."""
212 if hideMinimizedWindow!=self._hideMinimizedWindow:
213 self._hideMinimizedWindow = hideMinimizedWindow
214 self._modified = True
215
216 @property
217 def quitOnClose(self):
218 """Get whether the application should quit when the close button is
219 clicked."""
220 return self._quitOnClose
221
222 @quitOnClose.setter
223 def quitOnClose(self, quitOnClose):
224 """Set whether the application should quit when the close button is
225 clicked."""
226 if quitOnClose!=self._quitOnClose:
227 self._quitOnClose = quitOnClose
228 self._modified = True
229
230 @property
231 def onlineGateSystem(self):
232 """Get whether the online gate system should be used."""
233 return self._onlineGateSystem
234
235 @onlineGateSystem.setter
236 def onlineGateSystem(self, onlineGateSystem):
237 """Set whether the online gate system should be used."""
238 if onlineGateSystem!=self._onlineGateSystem:
239 self._onlineGateSystem = onlineGateSystem
240 self._modified = True
241
242 @property
243 def onlineACARS(self):
244 """Get whether the online ACARS system should be used."""
245 return self._onlineACARS
246
247 @onlineACARS.setter
248 def onlineACARS(self, onlineACARS):
249 """Set whether the online ACARS system should be used."""
250 if onlineACARS!=self._onlineACARS:
251 self._onlineACARS = onlineACARS
252 self._modified = True
253
254 @property
255 def flareTimeFromFS(self):
256 """Get whether the flare time should be calculated from the time values
257 returned by the simulator."""
258 return self._flareTimeFromFS
259
260 @flareTimeFromFS.setter
261 def flareTimeFromFS(self, flareTimeFromFS):
262 """Set whether the flare time should be calculated from the time values
263 returned by the simulator."""
264 if flareTimeFromFS!=self._flareTimeFromFS:
265 self._flareTimeFromFS = flareTimeFromFS
266 self._modified = True
267
268 @property
269 def syncFSTime(self):
270 """Get whether the simulator's time should be synchronized with the
271 machine's clock."""
272 return self._syncFSTime
273
274 @syncFSTime.setter
275 def syncFSTime(self, syncFSTime):
276 """Set whether the simulator's time should be synchronized with the
277 machine's clock."""
278 if syncFSTime!=self._syncFSTime:
279 self._syncFSTime = syncFSTime
280 self._modified = True
281
282 @property
283 def usingFS2Crew(self):
284 """Get whether the FS2Crew addon is being used."""
285 return self._usingFS2Crew
286
287 @usingFS2Crew.setter
288 def usingFS2Crew(self, usingFS2Crew):
289 """Set whether the FS2Crew addon is being used."""
290 if usingFS2Crew!=self._usingFS2Crew:
291 self._usingFS2Crew = usingFS2Crew
292 self._modified = True
293
294 @property
295 def iasSmoothingLength(self):
296 """Get the number of samples over which the IAS is averaged for the
297 smoothed IAS calculation. It may be negative, in which case smoothing
298 is disabled, but we nevertheless store the number of seconds in case it
299 may become useful later."""
300 return self._iasSmoothingLength
301
302 @property
303 def realIASSmoothingLength(self):
304 """Get the real smoothing length of IAS."""
305 return max(self._iasSmoothingLength, 1)
306
307 @iasSmoothingLength.setter
308 def iasSmoothingLength(self, iasSmoothingLength):
309 """Set the number of samples over which the IAS is averaged for the
310 smoothed IAS calculation."""
311 if iasSmoothingLength!=self._iasSmoothingLength:
312 self._iasSmoothingLength = iasSmoothingLength
313 self._modified = True
314
315 @property
316 def vsSmoothingLength(self):
317 """Get the number of samples over which the VS is averaged for the
318 smoothed VS calculation. It may be negative, in which case smoothing
319 is disabled, but we nevertheless store the number of seconds in case it
320 may become useful later."""
321 return self._vsSmoothingLength
322
323 @property
324 def realVSSmoothingLength(self):
325 """Get the real smoothing length of VS."""
326 return max(self._vsSmoothingLength, 1)
327
328 @vsSmoothingLength.setter
329 def vsSmoothingLength(self, vsSmoothingLength):
330 """Set the number of samples over which the VS is averaged for the
331 smoothed VS calculation."""
332 if vsSmoothingLength!=self._vsSmoothingLength:
333 self._vsSmoothingLength = vsSmoothingLength
334 self._modified = True
335
336 @property
337 def pirepDirectory(self):
338 """Get the directory offered by default when saving a PIREP."""
339 return self._pirepDirectory
340
341 @pirepDirectory.setter
342 def pirepDirectory(self, pirepDirectory):
343 """Get the directory offered by default when saving a PIREP."""
344 if pirepDirectory!=self._pirepDirectory and \
345 (pirepDirectory!="" or self._pirepDirectory is not None):
346 self._pirepDirectory = None if pirepDirectory=="" \
347 else pirepDirectory
348 self._modified = True
349
350 def getMessageTypeLevel(self, messageType):
351 """Get the level for the given message type."""
352 return self._messageTypeLevels[messageType] \
353 if messageType in self._messageTypeLevels \
354 else const.MESSAGELEVEL_NONE
355
356 def isMessageTypeFS(self, messageType):
357 """Determine if the given message type is displayed in the
358 simulator."""
359 level = self.getMessageTypeLevel(messageType)
360 return level==const.MESSAGELEVEL_FS or \
361 level==const.MESSAGELEVEL_BOTH
362
363 def setMessageTypeLevel(self, messageType, level):
364 """Set the level of the given message type."""
365 if messageType not in self._messageTypeLevels or \
366 self._messageTypeLevels[messageType]!=level:
367 self._messageTypeLevels[messageType] = level
368 self._modified = True
369
370 @property
371 def enableSounds(self):
372 """Get whether background sounds are enabled."""
373 return self._enableSounds
374
375 @enableSounds.setter
376 def enableSounds(self, enableSounds):
377 """Set whether background sounds are enabled."""
378 if enableSounds!=self._enableSounds:
379 self._enableSounds = enableSounds
380 self._modified = True
381
382 @property
383 def pilotControlsSounds(self):
384 """Get whether the pilot controls the background sounds."""
385 return self._pilotControlsSounds
386
387 @pilotControlsSounds.setter
388 def pilotControlsSounds(self, pilotControlsSounds):
389 """Set whether the pilot controls the background sounds."""
390 if pilotControlsSounds!=self._pilotControlsSounds:
391 self._pilotControlsSounds = pilotControlsSounds
392 self._modified = True
393
394 @property
395 def pilotHotkey(self):
396 """Get the pilot's hotkey."""
397 return self._pilotHotkey
398
399 @pilotHotkey.setter
400 def pilotHotkey(self, pilotHotkey):
401 """Set the pilot's hotkey."""
402 if pilotHotkey!=self._pilotHotkey:
403 self._pilotHotkey = pilotHotkey
404 self._modified = True
405
406 # @property
407 # def approachCallOuts(self):
408 # """Get whether the approach callouts should be played."""
409 # return self._approachCallOuts
410
411 # @approachCallOuts.setter
412 # def approachCallOuts(self, approachCallOuts):
413 # """Set whether the approach callouts should be played."""
414 # if approachCallOuts!=self._approachCallOuts:
415 # self._approachCallOuts = approachCallOuts
416 # self._modified = True
417
418 @property
419 def speedbrakeAtTD(self):
420 """Get whether the speedbrake sounds should be played at touchdown."""
421 return self._speedbrakeAtTD
422
423 @speedbrakeAtTD.setter
424 def speedbrakeAtTD(self, speedbrakeAtTD):
425 """Set whether the speedbrake sounds should be played at touchdown."""
426 if speedbrakeAtTD!=self._speedbrakeAtTD:
427 self._speedbrakeAtTD = speedbrakeAtTD
428 self._modified = True
429
430 @property
431 def enableChecklists(self):
432 """Get whether aircraft-specific checklists should be played."""
433 return self._enableChecklists
434
435 @enableChecklists.setter
436 def enableChecklists(self, enableChecklists):
437 """Get whether aircraft-specific checklists should be played."""
438 if enableChecklists!=self._enableChecklists:
439 self._enableChecklists = enableChecklists
440 self._modified = True
441
442 @property
443 def checklistHotkey(self):
444 """Get the checklist hotkey."""
445 return self._checklistHotkey
446
447 @checklistHotkey.setter
448 def checklistHotkey(self, checklistHotkey):
449 """Set the checklist hotkey."""
450 if checklistHotkey!=self._checklistHotkey:
451 self._checklistHotkey = checklistHotkey
452 self._modified = True
453
454 @property
455 def autoUpdate(self):
456 """Get if an automatic update is needed."""
457 return self._autoUpdate
458
459 @autoUpdate.setter
460 def autoUpdate(self, autoUpdate):
461 """Set if an automatic update is needed."""
462 if autoUpdate!=self._autoUpdate:
463 self._autoUpdate = autoUpdate
464 self._modified = True
465
466 @property
467 def updateURL(self):
468 """Get the update URL."""
469 return self._updateURL
470
471 @updateURL.setter
472 def updateURL(self, updateURL):
473 """Set the update URL."""
474 if updateURL!=self._updateURL:
475 self._updateURL = updateURL
476 self._modified = True
477
478 def getChecklist(self, aircraftType):
479 """Get the checklist for the given aircraft type."""
480 return self._checklists[aircraftType]
481
482 def setChecklist(self, aircraftType, checklist):
483 """Set the checklist for the given aircraft type."""
484 if checklist!=self._checklists[aircraftType]:
485 self._checklists[aircraftType] = checklist.clone()
486 self._modified = True
487
488 def load(self):
489 """Load the configuration from its default location."""
490 config = ConfigParser.RawConfigParser()
491 config.read(configPath)
492
493 self._pilotID = self._get(config, "login", "id", "")
494 self._password = self._get(config, "login", "password", "")
495 self._rememberPassword = self._getBoolean(config, "login",
496 "rememberPassword", False)
497
498 self._language = self._get(config, "general", "language", "")
499
500 self._hideMinimizedWindow = self._getBoolean(config, "general",
501 "hideMinimizedWindow",
502 True)
503 self._quitOnClose = self._getBoolean(config, "general",
504 "quitOnClose", False)
505
506 self._onlineGateSystem = self._getBoolean(config, "general",
507 "onlineGateSystem",
508 True)
509 self._onlineACARS = self._getBoolean(config, "general",
510 "onlineACARS", True)
511 self._flareTimeFromFS = self._getBoolean(config, "general",
512 "flareTimeFromFS",
513 False)
514 self._syncFSTime = self._getBoolean(config, "general",
515 "syncFSTime",
516 False)
517 self._usingFS2Crew = self._getBoolean(config, "general",
518 "usingFS2Crew",
519 False)
520 self._iasSmoothingLength = int(self._get(config, "general",
521 "iasSmoothingLength",
522 -2))
523 self._vsSmoothingLength = int(self._get(config, "general",
524 "vsSmoothingLength",
525 -2))
526 self._pirepDirectory = self._get(config, "general",
527 "pirepDirectory", None)
528
529 self._messageTypeLevels = {}
530 for messageType in const.messageTypes:
531 self._messageTypeLevels[messageType] = \
532 self._getMessageTypeLevel(config, messageType)
533
534 self._enableSounds = self._getBoolean(config, "sounds",
535 "enable", True)
536 self._pilotControlsSounds = self._getBoolean(config, "sounds",
537 "pilotControls", True)
538 self._pilotHotkey.set(self._get(config, "sounds",
539 "pilotHotkey", "C0"))
540 #self._approachCallOuts = self._getBoolean(config, "sounds",
541 # "approachCallOuts", False)
542 self._speedbrakeAtTD = self._getBoolean(config, "sounds",
543 "speedbrakeAtTD", True)
544
545 self._enableChecklists = self._getBoolean(config, "sounds",
546 "enableChecklists", False)
547 self._checklistHotkey.set(self._get(config, "sounds",
548 "checklistHotkey", "CS0"))
549
550 self._autoUpdate = self._getBoolean(config, "update", "auto", True)
551 self._updateURL = self._get(config, "update", "url",
552 Config.DEFAULT_UPDATE_URL)
553
554 for aircraftType in const.aircraftTypes:
555 self._checklists[aircraftType] = \
556 Checklist.fromConfig(config, aircraftType)
557
558 self._modified = False
559
560 def save(self):
561 """Save the configuration file if it has been modified."""
562 if not self._modified:
563 return
564
565 config = ConfigParser.RawConfigParser()
566
567 config.add_section("login")
568 config.set("login", "id", self._pilotID)
569 config.set("login", "password", self._password)
570 config.set("login", "rememberPassword",
571 "yes" if self._rememberPassword else "no")
572
573 config.add_section("general")
574 if self._language:
575 config.set("general", "language", self._language)
576 config.set("general", "hideMinimizedWindow",
577 "yes" if self._hideMinimizedWindow else "no")
578 config.set("general", "quitOnClose",
579 "yes" if self._quitOnClose else "no")
580 config.set("general", "onlineGateSystem",
581 "yes" if self._onlineGateSystem else "no")
582 config.set("general", "onlineACARS",
583 "yes" if self._onlineACARS else "no")
584 config.set("general", "flareTimeFromFS",
585 "yes" if self._flareTimeFromFS else "no")
586 config.set("general", "syncFSTime",
587 "yes" if self._syncFSTime else "no")
588 config.set("general", "usingFS2Crew",
589 "yes" if self._usingFS2Crew else "no")
590 config.set("general", "iasSmoothingLength",
591 str(self._iasSmoothingLength))
592 config.set("general", "vsSmoothingLength",
593 str(self._vsSmoothingLength))
594
595 if self._pirepDirectory is not None:
596 config.set("general", "pirepDirectory", self._pirepDirectory)
597
598 config.add_section(Config._messageTypesSection)
599 for messageType in const.messageTypes:
600 if messageType in self._messageTypeLevels:
601 option = self._getMessageTypeLevelOptionName(messageType)
602 level = self._messageTypeLevels[messageType]
603 config.set(Config._messageTypesSection, option,
604 const.messageLevel2string(level))
605
606 config.add_section("sounds")
607 config.set("sounds", "enable",
608 "yes" if self._enableSounds else "no")
609 config.set("sounds", "pilotControls",
610 "yes" if self._pilotControlsSounds else "no")
611 config.set("sounds", "pilotHotkey", str(self._pilotHotkey))
612 #config.set("sounds", "approachCallOuts",
613 # "yes" if self._approachCallOuts else "no")
614 config.set("sounds", "speedbrakeAtTD",
615 "yes" if self._speedbrakeAtTD else "no")
616
617 config.set("sounds", "enableChecklists",
618 "yes" if self._enableChecklists else "no")
619 config.set("sounds", "checklistHotkey",
620 str(self._checklistHotkey))
621
622 config.add_section("update")
623 config.set("update", "auto",
624 "yes" if self._autoUpdate else "no")
625 config.set("update", "url", self._updateURL)
626
627 config.add_section(Checklist.SECTION)
628 for aircraftType in const.aircraftTypes:
629 self._checklists[aircraftType].toConfig(config, aircraftType)
630
631 try:
632 fd = os.open(configPath, os.O_CREAT|os.O_TRUNC|os.O_WRONLY,
633 0600)
634 with os.fdopen(fd, "wt") as f:
635 config.write(f)
636 self._modified = False
637 except Exception, e:
638 print >> sys.stderr, "Failed to update config: " + str(e)
639
640 def _getBoolean(self, config, section, option, default):
641 """Get the given option as a boolean, if found in the given config,
642 otherwise the default."""
643 return config.getboolean(section, option) \
644 if config.has_option(section, option) \
645 else default
646
647 def _get(self, config, section, option, default):
648 """Get the given option as a string, if found in the given config,
649 otherwise the default."""
650 return config.get(section, option) \
651 if config.has_option(section, option) \
652 else default
653
654 def _getMessageTypeLevel(self, config, messageType):
655 """Get the message type level for the given message type."""
656 option = self._getMessageTypeLevelOptionName(messageType)
657 if config.has_option(Config._messageTypesSection, option):
658 value = config.get(Config._messageTypesSection, option)
659 return const.string2messageLevel(value)
660 elif messageType in [const.MESSAGETYPE_LOGGER_ERROR,
661 const.MESSAGETYPE_FAULT,
662 const.MESSAGETYPE_NOGO,
663 const.MESSAGETYPE_GATE_SYSTEM,
664 const.MESSAGETYPE_HELP]:
665 return const.MESSAGELEVEL_BOTH
666 else:
667 return const.MESSAGELEVEL_FS
668
669 def _getMessageTypeLevelOptionName(self, messageType):
670 """Get the option name for the given message type level."""
671 return const.messageType2string(messageType)
672
673 def setupLocale(self):
674 """Setup the locale based on the language set.
675
676 Return True if a specific language was set, False otherwise."""
677 import locale
678 if self._language:
679 print "Setting up locale for", self._language
680 os.environ["LANGUAGE"] = self._language
681 langAndEncoding = self._language + "." + locale.getpreferredencoding()
682 os.environ["LANG"] = langAndEncoding
683 os.environ["LC_MESSAGES"] = langAndEncoding
684 os.environ["LC_COLLATE"] = langAndEncoding
685 os.environ["LC_CTYPE"] = langAndEncoding
686 os.environ["LC_MONETARY"] = langAndEncoding
687 os.environ["LC_NUMERIC"] = langAndEncoding
688 os.environ["LC_TIME"] = langAndEncoding
689 return True
690 else:
691 return False
692
693 def getLanguage(self):
694 """Get the language to be used."""
695 import locale
696 if self._language:
697 if os.name=="nt":
698 if self._language in _languageMap:
699 locale.setlocale(locale.LC_ALL, _languageMap[self._language])
700 else:
701 locale.setlocale(locale.LC_ALL, "")
702 else:
703 locale.setlocale(locale.LC_ALL, (self._language,
704 locale.getpreferredencoding()))
705 return self._language
706 else:
707 locale.setlocale(locale.LC_ALL, "")
708 return locale.getdefaultlocale()[0]
709
710#-------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.