source: src/mlx/config.py@ 984:499ea82e9b0d

python3
Last change on this file since 984:499ea82e9b0d was 919:2ce8ca39525b, checked in by István Váradi <ivaradi@…>, 6 years ago

Ran 2to3

File size: 38.5 KB
Line 
1# -*- encoding: utf-8 -*-
2
3from . import const
4from .util import secondaryInstallation, utf2unicode
5
6import os
7import sys
8import traceback
9import configparser
10
11## @package mlx.config
12#
13# The handling of the configuration.
14#
15# The \ref Config class contains the main configuration and is capable of
16# loading and saving the configuration. It contains getters and setters for the
17# configuration options.
18#
19# Some parts of the configuration are not simple data items, like strings or
20# booleans, but more complicated data. These have their own class, like \ref
21# ApproachCallouts or \ref Checklist.
22
23#-------------------------------------------------------------------------------
24
25configPath = os.path.join(os.path.expanduser("~"),
26 "mlx.config" if os.name=="nt" else ".mlxrc") + \
27 ("-secondary" if secondaryInstallation else "")
28
29#-------------------------------------------------------------------------------
30
31if os.name=="nt":
32 _languageMap = { "en_GB" : "eng",
33 "hu_HU" : "hun" }
34
35#-------------------------------------------------------------------------------
36
37class Hotkey(object):
38 """A hotkey."""
39 def __init__(self, ctrl = False, shift = False, key = "0"):
40 """Construct the hotkey."""
41 self.ctrl = ctrl
42 self.shift = shift
43 self.key = key
44
45 def set(self, s):
46 """Set the hotkey from the given string."""
47 self.ctrl = "C" in s[:-1]
48 self.shift = "S" in s[:-1]
49 self.key = s[-1]
50
51 def __eq__(self, other):
52 """Check if the given hotkey is equal to the other one."""
53 return self.ctrl == other.ctrl and self.shift == other.shift and \
54 self.key == other.key
55
56 def __ne__(self, other):
57 """Check if the given hotkey is not equal to the other one."""
58 return not self==other
59
60 def __str__(self):
61 """Construct the hotkey to a string."""
62 s = ""
63 if self.ctrl: s += "C"
64 if self.shift: s += "S"
65 s += self.key
66 return s
67
68#-------------------------------------------------------------------------------
69
70class Checklist(object):
71 """A checklist for a certain aircraft type."""
72 # The name of the section of the checklists
73 SECTION="checklists"
74
75 @staticmethod
76 def fromConfig(config, aircraftType):
77 """Create a checklist for the given aircraft type from the given
78 config."""
79 baseName = "checklist." + const.icaoCodes[aircraftType] + "."
80 fileList = []
81 while True:
82 option = baseName + str(len(fileList))
83 if config.has_option(Checklist.SECTION, option):
84 fileList.append(config.get(Checklist.SECTION, option))
85 else:
86 break
87
88 return Checklist(fileList)
89
90 def __init__(self, fileList = None):
91 """Construct the check list with the given file list."""
92 self._fileList = [] if fileList is None else fileList[:]
93
94 def clone(self):
95 """Clone the checklist."""
96 return Checklist(self._fileList)
97
98 def toConfig(self, config, aircraftType):
99 """Add this checklist to the given config."""
100 baseName = "checklist." + const.icaoCodes[aircraftType] + "."
101 for index in range(0, len(self._fileList)):
102 option = baseName + str(index)
103 config.set(Checklist.SECTION, option,
104 self._fileList[index])
105
106 def __eq__(self, other):
107 """Determine if the checklist is equal to the given other one."""
108 return self._fileList == other._fileList
109
110 def __ne__(self, other):
111 """Determine if the checklist is not equal to the given other one."""
112 return not self==other
113
114 def __len__(self):
115 """Get the length of the file list."""
116 return len(self._fileList)
117
118 def __getitem__(self, index):
119 """Get the file with the given index."""
120 return self._fileList[index]
121
122 def __iter__(self):
123 """Iterate over the files."""
124 return iter(self._fileList)
125
126#-------------------------------------------------------------------------------
127
128class ApproachCallouts(object):
129 """The approach callouts for a certain aircraft type."""
130 # The name of the section of the approach callouts
131 SECTION="callouts"
132
133 @staticmethod
134 def fromConfig(config, aircraftType):
135 """Create a checklist for the given aircraft type from the given
136 config."""
137 baseName = "callouts." + const.icaoCodes[aircraftType] + "."
138 mapping = {}
139 while True:
140 option = baseName + str(len(mapping))
141 if config.has_option(ApproachCallouts.SECTION, option):
142 value = config.get(ApproachCallouts.SECTION, option)
143 (altitude, path) = value.split(",")
144 altitude = int(altitude.strip())
145 path = path.strip()
146 mapping[altitude] = path
147 else:
148 break
149
150 return ApproachCallouts(mapping)
151
152 def __init__(self, mapping = None):
153 """Construct the check list with the given mapping of altitudes to
154 files."""
155 self._mapping = {} if mapping is None else mapping.copy()
156
157 def clone(self):
158 """Clone the callout information."""
159 return ApproachCallouts(self._mapping)
160
161 def toConfig(self, config, aircraftType):
162 """Add this checklist to the given config."""
163 baseName = "callouts." + const.icaoCodes[aircraftType] + "."
164 index = 0
165 for (altitude, path) in self._mapping.items():
166 option = baseName + str(index)
167 config.set(ApproachCallouts.SECTION, option,
168 "%d, %s" % (altitude, path))
169 index += 1
170
171 def getAltitudes(self, descending = True):
172 """Get the altitudes in decreasing order by default."""
173 altitudes = list(self._mapping.keys())
174 altitudes.sort(reverse = descending)
175 return altitudes
176
177 def __bool__(self):
178 """Return if there is anything in the mapping."""
179 return not not self._mapping
180
181 def __eq__(self, other):
182 """Determine if the approach callout mapping is equal to the given
183 other one."""
184 return self._mapping == other._mapping
185
186 def __ne__(self, other):
187 """Determine if the approach callout mapping is not equal to the given
188 other one."""
189 return not self==other
190
191 def __len__(self):
192 """Get the number of elements in the mapping."""
193 return len(self._mapping)
194
195 def __getitem__(self, altitude):
196 """Get the file that is associated with the given altitude.
197
198 If no such file found, return None."""
199 return self._mapping[altitude] if altitude in self._mapping else None
200
201 def __iter__(self):
202 """Iterate over the pairs of altitudes and paths in decreasing order of
203 the altitude."""
204 altitudes = self.getAltitudes()
205
206 for altitude in altitudes:
207 yield (altitude, self._mapping[altitude])
208
209#-------------------------------------------------------------------------------
210
211class Config(object):
212 """Our configuration."""
213 DEFAULT_UPDATE_URL = "http://mlx.varadiistvan.hu/update"
214
215 _messageTypesSection = "messageTypes"
216
217 def __init__(self):
218 """Construct the configuration with default values."""
219
220 self._pilotID = ""
221 self._password = ""
222 self._rememberPassword = False
223
224 self._language = ""
225 self._hideMinimizedWindow = True
226 self._quitOnClose = False
227 self._onlineGateSystem = not secondaryInstallation
228 self._onlineACARS = not secondaryInstallation
229 self._flareTimeFromFS = False
230 self._syncFSTime = False
231 self._usingFS2Crew = False
232 self._iasSmoothingLength = -2
233 self._vsSmoothingLength = -2
234
235 self._useSimBrief = False
236 self._simBriefUserName = ""
237 self._simBriefPassword = ""
238 self._rememberSimBriefPassword = False
239
240 self._pirepDirectory = None
241 self._pirepAutoSave = False
242
243 self._defaultMSFS = os.name=="nt"
244
245 self._enableSounds = not secondaryInstallation
246
247 self._pilotControlsSounds = True
248 self._pilotHotkey = Hotkey(ctrl = True, shift = False, key = "0")
249
250 self._enableApproachCallouts = False
251 self._speedbrakeAtTD = True
252
253 self._enableChecklists = False
254 self._checklistHotkey = Hotkey(ctrl = True, shift = True, key = "0")
255
256 self._autoUpdate = True
257 self._updateURL = Config.DEFAULT_UPDATE_URL
258 if secondaryInstallation:
259 self._updateURL += "/exp"
260 self._useRPC = True
261
262 self._messageTypeLevels = {}
263
264 self._checklists = {}
265 self._approachCallouts = {}
266 for aircraftType in const.aircraftTypes:
267 self._checklists[aircraftType] = Checklist()
268 self._approachCallouts[aircraftType] = ApproachCallouts()
269
270 self._modified = False
271
272 @property
273 def pilotID(self):
274 """Get the pilot ID."""
275 return self._pilotID
276
277 @pilotID.setter
278 def pilotID(self, pilotID):
279 """Set the pilot ID."""
280 if pilotID!=self._pilotID:
281 self._pilotID = pilotID
282 self._modified = True
283
284 @property
285 def password(self):
286 """Get the password."""
287 return self._password
288
289 @password.setter
290 def password(self, password):
291 """Set the password."""
292 if password!=self._password:
293 self._password = password
294 self._modified = True
295
296 @property
297 def rememberPassword(self):
298 """Get if we should remember the password."""
299 return self._rememberPassword
300
301 @rememberPassword.setter
302 def rememberPassword(self, rememberPassword):
303 """Set if we should remember the password."""
304 if rememberPassword!=self._rememberPassword:
305 self._rememberPassword = rememberPassword
306 self._modified = True
307
308 @property
309 def language(self):
310 """Get the language to use."""
311 return self._language
312
313 @language.setter
314 def language(self, language):
315 """Set the language to use."""
316 if language!=self._language:
317 self._language = language
318 self._modified = True
319
320 @property
321 def hideMinimizedWindow(self):
322 """Get whether a minimized window should be hidden."""
323 return self._hideMinimizedWindow
324
325 @hideMinimizedWindow.setter
326 def hideMinimizedWindow(self, hideMinimizedWindow):
327 """Set whether a minimized window should be hidden."""
328 if hideMinimizedWindow!=self._hideMinimizedWindow:
329 self._hideMinimizedWindow = hideMinimizedWindow
330 self._modified = True
331
332 @property
333 def quitOnClose(self):
334 """Get whether the application should quit when the close button is
335 clicked."""
336 return self._quitOnClose
337
338 @quitOnClose.setter
339 def quitOnClose(self, quitOnClose):
340 """Set whether the application should quit when the close button is
341 clicked."""
342 if quitOnClose!=self._quitOnClose:
343 self._quitOnClose = quitOnClose
344 self._modified = True
345
346 @property
347 def onlineGateSystem(self):
348 """Get whether the online gate system should be used."""
349 return self._onlineGateSystem
350
351 @onlineGateSystem.setter
352 def onlineGateSystem(self, onlineGateSystem):
353 """Set whether the online gate system should be used."""
354 if onlineGateSystem!=self._onlineGateSystem:
355 self._onlineGateSystem = onlineGateSystem
356 self._modified = True
357
358 @property
359 def onlineACARS(self):
360 """Get whether the online ACARS system should be used."""
361 return self._onlineACARS
362
363 @onlineACARS.setter
364 def onlineACARS(self, onlineACARS):
365 """Set whether the online ACARS system should be used."""
366 if onlineACARS!=self._onlineACARS:
367 self._onlineACARS = onlineACARS
368 self._modified = True
369
370 @property
371 def flareTimeFromFS(self):
372 """Get whether the flare time should be calculated from the time values
373 returned by the simulator."""
374 return self._flareTimeFromFS
375
376 @flareTimeFromFS.setter
377 def flareTimeFromFS(self, flareTimeFromFS):
378 """Set whether the flare time should be calculated from the time values
379 returned by the simulator."""
380 if flareTimeFromFS!=self._flareTimeFromFS:
381 self._flareTimeFromFS = flareTimeFromFS
382 self._modified = True
383
384 @property
385 def syncFSTime(self):
386 """Get whether the simulator's time should be synchronized with the
387 machine's clock."""
388 return self._syncFSTime
389
390 @syncFSTime.setter
391 def syncFSTime(self, syncFSTime):
392 """Set whether the simulator's time should be synchronized with the
393 machine's clock."""
394 if syncFSTime!=self._syncFSTime:
395 self._syncFSTime = syncFSTime
396 self._modified = True
397
398 @property
399 def usingFS2Crew(self):
400 """Get whether the FS2Crew addon is being used."""
401 return self._usingFS2Crew
402
403 @usingFS2Crew.setter
404 def usingFS2Crew(self, usingFS2Crew):
405 """Set whether the FS2Crew addon is being used."""
406 if usingFS2Crew!=self._usingFS2Crew:
407 self._usingFS2Crew = usingFS2Crew
408 self._modified = True
409
410 @property
411 def iasSmoothingLength(self):
412 """Get the number of samples over which the IAS is averaged for the
413 smoothed IAS calculation. It may be negative, in which case smoothing
414 is disabled, but we nevertheless store the number of seconds in case it
415 may become useful later."""
416 return self._iasSmoothingLength
417
418 @property
419 def realIASSmoothingLength(self):
420 """Get the real smoothing length of IAS."""
421 return max(self._iasSmoothingLength, 1)
422
423 @iasSmoothingLength.setter
424 def iasSmoothingLength(self, iasSmoothingLength):
425 """Set the number of samples over which the IAS is averaged for the
426 smoothed IAS calculation."""
427 if iasSmoothingLength!=self._iasSmoothingLength:
428 self._iasSmoothingLength = iasSmoothingLength
429 self._modified = True
430
431 @property
432 def vsSmoothingLength(self):
433 """Get the number of samples over which the VS is averaged for the
434 smoothed VS calculation. It may be negative, in which case smoothing
435 is disabled, but we nevertheless store the number of seconds in case it
436 may become useful later."""
437 return self._vsSmoothingLength
438
439 @property
440 def realVSSmoothingLength(self):
441 """Get the real smoothing length of VS."""
442 return max(self._vsSmoothingLength, 1)
443
444 @vsSmoothingLength.setter
445 def vsSmoothingLength(self, vsSmoothingLength):
446 """Set the number of samples over which the VS is averaged for the
447 smoothed VS calculation."""
448 if vsSmoothingLength!=self._vsSmoothingLength:
449 self._vsSmoothingLength = vsSmoothingLength
450 self._modified = True
451
452 @property
453 def useSimBrief(self):
454 """Check if SimBrief should be used."""
455 return self._useSimBrief
456
457 @useSimBrief.setter
458 def useSimBrief(self, useSimBrief):
459 """Check if SimBrief should be used."""
460 if self._useSimBrief != useSimBrief:
461 self._useSimBrief = useSimBrief
462 self._modified = True
463
464 @property
465 def simBriefUserName(self):
466 """Get the SimBrief user name last used"""
467 return self._simBriefUserName
468
469 @simBriefUserName.setter
470 def simBriefUserName(self, simBriefUserName):
471 """Set the SimBrief user name to be used next."""
472 if self._simBriefUserName != simBriefUserName:
473 self._simBriefUserName = simBriefUserName
474 self._modified = True
475
476 @property
477 def simBriefPassword(self):
478 """Get the SimBrief password last used"""
479 return self._simBriefPassword
480
481 @simBriefPassword.setter
482 def simBriefPassword(self, simBriefPassword):
483 """Set the SimBrief password to be used next."""
484 if self._simBriefPassword != simBriefPassword:
485 self._simBriefPassword = simBriefPassword
486 self._modified = True
487
488 @property
489 def rememberSimBriefPassword(self):
490 """Get if we should remember the SimBrief password."""
491 return self._rememberSimBriefPassword
492
493 @rememberSimBriefPassword.setter
494 def rememberSimBriefPassword(self, rememberSimBriefPassword):
495 """Set if we should remember the SimBrief password."""
496 if rememberSimBriefPassword!=self._rememberSimBriefPassword:
497 self._rememberSimBriefPassword = rememberSimBriefPassword
498 self._modified = True
499
500 @property
501 def pirepDirectory(self):
502 """Get the directory offered by default when saving a PIREP."""
503 return self._pirepDirectory
504
505 @pirepDirectory.setter
506 def pirepDirectory(self, pirepDirectory):
507 """Get the directory offered by default when saving a PIREP."""
508 if pirepDirectory!=self._pirepDirectory and \
509 (pirepDirectory!="" or self._pirepDirectory is not None):
510 self._pirepDirectory = None if pirepDirectory=="" \
511 else pirepDirectory
512 if self._pirepDirectory is None:
513 self._pirepAutoSave = False
514 self._modified = True
515
516 @property
517 def pirepAutoSave(self):
518 """Get whether the PIREP should be saved automatically when it becomes
519 saveable."""
520 return self._pirepAutoSave
521
522 @pirepAutoSave.setter
523 def pirepAutoSave(self, pirepAutoSave):
524 """Set whether the PIREP should be saved automatically when it becomes
525 saveable."""
526 pirepAutoSave = pirepAutoSave and self._pirepDirectory is not None
527 if pirepAutoSave!=self._pirepAutoSave:
528 self._pirepAutoSave = pirepAutoSave
529 self._modified = True
530
531 @property
532 def defaultMSFS(self):
533 """Get if the default simulator type is MS FS."""
534 return self._defaultMSFS
535
536 @defaultMSFS.setter
537 def defaultMSFS(self, defaultMSFS):
538 """Set if the default simulator type is MS FS."""
539 if defaultMSFS!=self._defaultMSFS:
540 self._defaultMSFS = defaultMSFS
541 self._modified = True
542
543 def getMessageTypeLevel(self, messageType):
544 """Get the level for the given message type."""
545 return self._messageTypeLevels[messageType] \
546 if messageType in self._messageTypeLevels \
547 else const.MESSAGELEVEL_NONE
548
549 def isMessageTypeFS(self, messageType):
550 """Determine if the given message type is displayed in the
551 simulator."""
552 level = self.getMessageTypeLevel(messageType)
553 return level==const.MESSAGELEVEL_FS or \
554 level==const.MESSAGELEVEL_BOTH
555
556 def setMessageTypeLevel(self, messageType, level):
557 """Set the level of the given message type."""
558 if messageType not in self._messageTypeLevels or \
559 self._messageTypeLevels[messageType]!=level:
560 self._messageTypeLevels[messageType] = level
561 self._modified = True
562
563 @property
564 def enableSounds(self):
565 """Get whether background sounds are enabled."""
566 return self._enableSounds
567
568 @enableSounds.setter
569 def enableSounds(self, enableSounds):
570 """Set whether background sounds are enabled."""
571 if enableSounds!=self._enableSounds:
572 self._enableSounds = enableSounds
573 self._modified = True
574
575 @property
576 def pilotControlsSounds(self):
577 """Get whether the pilot controls the background sounds."""
578 return self._pilotControlsSounds
579
580 @pilotControlsSounds.setter
581 def pilotControlsSounds(self, pilotControlsSounds):
582 """Set whether the pilot controls the background sounds."""
583 if pilotControlsSounds!=self._pilotControlsSounds:
584 self._pilotControlsSounds = pilotControlsSounds
585 self._modified = True
586
587 @property
588 def pilotHotkey(self):
589 """Get the pilot's hotkey."""
590 return self._pilotHotkey
591
592 @pilotHotkey.setter
593 def pilotHotkey(self, pilotHotkey):
594 """Set the pilot's hotkey."""
595 if pilotHotkey!=self._pilotHotkey:
596 self._pilotHotkey = pilotHotkey
597 self._modified = True
598
599 @property
600 def enableApproachCallouts(self):
601 """Get whether the approach callouts should be played."""
602 return self._enableApproachCallouts
603
604 @enableApproachCallouts.setter
605 def enableApproachCallouts(self, enableApproachCallouts):
606 """Set whether the approach callouts should be played."""
607 if enableApproachCallouts!=self._enableApproachCallouts:
608 self._enableApproachCallouts = enableApproachCallouts
609 self._modified = True
610
611 @property
612 def speedbrakeAtTD(self):
613 """Get whether the speedbrake sounds should be played at touchdown."""
614 return self._speedbrakeAtTD
615
616 @speedbrakeAtTD.setter
617 def speedbrakeAtTD(self, speedbrakeAtTD):
618 """Set whether the speedbrake sounds should be played at touchdown."""
619 if speedbrakeAtTD!=self._speedbrakeAtTD:
620 self._speedbrakeAtTD = speedbrakeAtTD
621 self._modified = True
622
623 @property
624 def enableChecklists(self):
625 """Get whether aircraft-specific checklists should be played."""
626 return self._enableChecklists
627
628 @enableChecklists.setter
629 def enableChecklists(self, enableChecklists):
630 """Get whether aircraft-specific checklists should be played."""
631 if enableChecklists!=self._enableChecklists:
632 self._enableChecklists = enableChecklists
633 self._modified = True
634
635 @property
636 def checklistHotkey(self):
637 """Get the checklist hotkey."""
638 return self._checklistHotkey
639
640 @checklistHotkey.setter
641 def checklistHotkey(self, checklistHotkey):
642 """Set the checklist hotkey."""
643 if checklistHotkey!=self._checklistHotkey:
644 self._checklistHotkey = checklistHotkey
645 self._modified = True
646
647 @property
648 def autoUpdate(self):
649 """Get if an automatic update is needed."""
650 return self._autoUpdate
651
652 @autoUpdate.setter
653 def autoUpdate(self, autoUpdate):
654 """Set if an automatic update is needed."""
655 if autoUpdate!=self._autoUpdate:
656 self._autoUpdate = autoUpdate
657 self._modified = True
658
659 @property
660 def updateURL(self):
661 """Get the update URL."""
662 return self._updateURL
663
664 @updateURL.setter
665 def updateURL(self, updateURL):
666 """Set the update URL."""
667 if updateURL!=self._updateURL:
668 self._updateURL = updateURL
669 self._modified = True
670
671 @property
672 def useRPC(self):
673 """Determine if RPC calls should be used."""
674 return self._useRPC
675
676 @useRPC.setter
677 def useRPC(self, useRPC):
678 """Set whether RPC calls should be used."""
679 if useRPC!=self._useRPC:
680 self._useRPC = useRPC
681 self._modified = True
682
683 def getChecklist(self, aircraftType):
684 """Get the checklist for the given aircraft type."""
685 return self._checklists[aircraftType]
686
687 def setChecklist(self, aircraftType, checklist):
688 """Set the checklist for the given aircraft type."""
689 if checklist!=self._checklists[aircraftType]:
690 self._checklists[aircraftType] = checklist.clone()
691 self._modified = True
692
693 def getApproachCallouts(self, aircraftType):
694 """Get the approach callouts for the given aircraft type."""
695 return self._approachCallouts[aircraftType]
696
697 def setApproachCallouts(self, aircraftType, approachCallouts):
698 """Set the approach callouts for the given aircraft type."""
699 if not approachCallouts==self._approachCallouts[aircraftType]:
700 self._approachCallouts[aircraftType] = approachCallouts.clone()
701 self._modified = True
702
703 def load(self):
704 """Load the configuration from its default location."""
705 try:
706 config = configparser.RawConfigParser()
707 config.read(configPath)
708 except:
709 traceback.print_exc()
710 return
711
712 self._pilotID = self._get(config, "login", "id", "")
713 self._password = self._get(config, "login", "password", "")
714 self._rememberPassword = self._getBoolean(config, "login",
715 "rememberPassword", False)
716
717 self._language = self._get(config, "general", "language", "")
718
719 self._hideMinimizedWindow = self._getBoolean(config, "general",
720 "hideMinimizedWindow",
721 True)
722 self._quitOnClose = self._getBoolean(config, "general",
723 "quitOnClose", False)
724
725 self._onlineGateSystem = self._getBoolean(config, "general",
726 "onlineGateSystem",
727 not secondaryInstallation)
728 self._onlineACARS = self._getBoolean(config, "general",
729 "onlineACARS",
730 not secondaryInstallation)
731 self._flareTimeFromFS = self._getBoolean(config, "general",
732 "flareTimeFromFS",
733 False)
734 self._syncFSTime = self._getBoolean(config, "general",
735 "syncFSTime",
736 False)
737 self._usingFS2Crew = self._getBoolean(config, "general",
738 "usingFS2Crew",
739 False)
740 self._iasSmoothingLength = int(self._get(config, "general",
741 "iasSmoothingLength",
742 -2))
743 self._vsSmoothingLength = int(self._get(config, "general",
744 "vsSmoothingLength",
745 -2))
746
747 self._useSimBrief = self._getBoolean(config, "simbrief",
748 "use", False)
749 self._simBriefUserName = self._get(config, "simbrief",
750 "username", "")
751 self._simBriefPassword = self._get(config, "simbrief",
752 "password", "")
753 self._rememberSimBriefPassword = self._getBoolean(config, "simbrief",
754 "rememberPassword",
755 False)
756
757 self._pirepDirectory = self._get(config, "general",
758 "pirepDirectory", None)
759
760 self._pirepAutoSave = self._getBoolean(config, "general",
761 "pirepAutoSave", False)
762 if self._pirepDirectory is None:
763 self._pirepAutoSave = False
764
765 self._messageTypeLevels = {}
766 for messageType in const.messageTypes:
767 self._messageTypeLevels[messageType] = \
768 self._getMessageTypeLevel(config, messageType)
769
770 self._enableSounds = self._getBoolean(config, "sounds", "enable",
771 not secondaryInstallation)
772 self._pilotControlsSounds = self._getBoolean(config, "sounds",
773 "pilotControls", True)
774 self._pilotHotkey.set(self._get(config, "sounds",
775 "pilotHotkey", "C0"))
776 self._enableApproachCallouts = \
777 self._getBoolean(config, "sounds", "enableApproachCallouts", False)
778 self._speedbrakeAtTD = self._getBoolean(config, "sounds",
779 "speedbrakeAtTD", True)
780
781 self._enableChecklists = self._getBoolean(config, "sounds",
782 "enableChecklists", False)
783 self._checklistHotkey.set(self._get(config, "sounds",
784 "checklistHotkey", "CS0"))
785
786 self._autoUpdate = self._getBoolean(config, "update", "auto", True)
787 self._updateURL = self._get(config, "update", "url",
788 Config.DEFAULT_UPDATE_URL +
789 ("/exp" if secondaryInstallation else ""))
790 self._useRPC = self._getBoolean(config, "general", "useRPC", True)
791
792 for aircraftType in const.aircraftTypes:
793 self._checklists[aircraftType] = \
794 Checklist.fromConfig(config, aircraftType)
795 self._approachCallouts[aircraftType] = \
796 ApproachCallouts.fromConfig(config, aircraftType)
797
798 self._defaultMSFS = self._getBoolean(config, "general",
799 "defaultMSFS", os.name=="nt")
800
801 self._modified = False
802
803 def save(self):
804 """Save the configuration file if it has been modified."""
805 if not self._modified:
806 return
807
808 config = configparser.RawConfigParser()
809
810 config.add_section("login")
811 config.set("login", "id", self._pilotID)
812 config.set("login", "password", self._password)
813 config.set("login", "rememberPassword",
814 "yes" if self._rememberPassword else "no")
815
816 config.add_section("general")
817 if self._language:
818 config.set("general", "language", self._language)
819 config.set("general", "hideMinimizedWindow",
820 "yes" if self._hideMinimizedWindow else "no")
821 config.set("general", "quitOnClose",
822 "yes" if self._quitOnClose else "no")
823 config.set("general", "onlineGateSystem",
824 "yes" if self._onlineGateSystem else "no")
825 config.set("general", "onlineACARS",
826 "yes" if self._onlineACARS else "no")
827 config.set("general", "flareTimeFromFS",
828 "yes" if self._flareTimeFromFS else "no")
829 config.set("general", "syncFSTime",
830 "yes" if self._syncFSTime else "no")
831 config.set("general", "usingFS2Crew",
832 "yes" if self._usingFS2Crew else "no")
833 config.set("general", "iasSmoothingLength",
834 str(self._iasSmoothingLength))
835 config.set("general", "vsSmoothingLength",
836 str(self._vsSmoothingLength))
837
838 config.add_section("simbrief")
839 config.set("simbrief", "use",
840 "yes" if self._useSimBrief else "no")
841 config.set("simbrief", "username", self._simBriefUserName)
842 config.set("simbrief", "password", self._simBriefPassword)
843 config.set("simbrief", "rememberPassword",
844 "yes" if self._rememberSimBriefPassword else "no")
845
846 if self._pirepDirectory is not None:
847 config.set("general", "pirepDirectory", self._pirepDirectory)
848 config.set("general", "pirepAutoSave",
849 "yes" if self._pirepAutoSave else "no")
850
851 config.set("general", "defaultMSFS",
852 "yes" if self._defaultMSFS else "no")
853
854 config.add_section(Config._messageTypesSection)
855 for messageType in const.messageTypes:
856 if messageType in self._messageTypeLevels:
857 option = self._getMessageTypeLevelOptionName(messageType)
858 level = self._messageTypeLevels[messageType]
859 config.set(Config._messageTypesSection, option,
860 const.messageLevel2string(level))
861
862 config.add_section("sounds")
863 config.set("sounds", "enable",
864 "yes" if self._enableSounds else "no")
865 config.set("sounds", "pilotControls",
866 "yes" if self._pilotControlsSounds else "no")
867 config.set("sounds", "pilotHotkey", str(self._pilotHotkey))
868 config.set("sounds", "enableApproachCallouts",
869 "yes" if self._enableApproachCallouts else "no")
870 config.set("sounds", "speedbrakeAtTD",
871 "yes" if self._speedbrakeAtTD else "no")
872
873 config.set("sounds", "enableChecklists",
874 "yes" if self._enableChecklists else "no")
875 config.set("sounds", "checklistHotkey",
876 str(self._checklistHotkey))
877
878 config.add_section("update")
879 config.set("update", "auto",
880 "yes" if self._autoUpdate else "no")
881 config.set("update", "url", self._updateURL)
882
883 config.set("general", "useRPC",
884 "yes" if self._useRPC else "no")
885
886 config.add_section(Checklist.SECTION)
887 config.add_section(ApproachCallouts.SECTION)
888 for aircraftType in const.aircraftTypes:
889 self._checklists[aircraftType].toConfig(config, aircraftType)
890 self._approachCallouts[aircraftType].toConfig(config, aircraftType)
891
892 try:
893 fd = os.open(configPath, os.O_CREAT|os.O_TRUNC|os.O_WRONLY,
894 0o600)
895 with os.fdopen(fd, "wt") as f:
896 config.write(f)
897 self._modified = False
898
899 print("Configuration saved:")
900 self.log()
901
902 except Exception as e:
903 print("Failed to update config: " + \
904 utf2unicode(str(e)), file=sys.stderr)
905
906 def _getBoolean(self, config, section, option, default):
907 """Get the given option as a boolean, if found in the given config,
908 otherwise the default."""
909 return config.getboolean(section, option) \
910 if config.has_option(section, option) \
911 else default
912
913 def _get(self, config, section, option, default):
914 """Get the given option as a string, if found in the given config,
915 otherwise the default."""
916 return config.get(section, option) \
917 if config.has_option(section, option) \
918 else default
919
920 def _getMessageTypeLevel(self, config, messageType):
921 """Get the message type level for the given message type."""
922 option = self._getMessageTypeLevelOptionName(messageType)
923 if config.has_option(Config._messageTypesSection, option):
924 value = config.get(Config._messageTypesSection, option)
925 return const.string2messageLevel(value)
926 elif secondaryInstallation:
927 return const.MESSAGELEVEL_NONE
928 elif messageType in [const.MESSAGETYPE_LOGGER_ERROR,
929 const.MESSAGETYPE_FAULT,
930 const.MESSAGETYPE_NOGO,
931 const.MESSAGETYPE_GATE_SYSTEM,
932 const.MESSAGETYPE_HELP]:
933 return const.MESSAGELEVEL_BOTH
934 else:
935 return const.MESSAGELEVEL_FS
936
937 def _getMessageTypeLevelOptionName(self, messageType):
938 """Get the option name for the given message type level."""
939 return const.messageType2string(messageType)
940
941 def setupLocale(self):
942 """Setup the locale based on the language set.
943
944 Return True if a specific language was set, False otherwise."""
945 import locale
946 if self._language:
947 print("Setting up locale for", self._language)
948 os.environ["LANGUAGE"] = self._language
949 langAndEncoding = self._language + "." + locale.getpreferredencoding()
950 os.environ["LANG"] = langAndEncoding
951 os.environ["LC_MESSAGES"] = langAndEncoding
952 os.environ["LC_COLLATE"] = langAndEncoding
953 os.environ["LC_CTYPE"] = langAndEncoding
954 os.environ["LC_MONETARY"] = langAndEncoding
955 os.environ["LC_NUMERIC"] = langAndEncoding
956 os.environ["LC_TIME"] = langAndEncoding
957 return True
958 else:
959 return False
960
961 def getLanguage(self):
962 """Get the language to be used."""
963 import locale
964 if self._language:
965 if os.name=="nt":
966 if self._language in _languageMap:
967 locale.setlocale(locale.LC_ALL, _languageMap[self._language])
968 else:
969 locale.setlocale(locale.LC_ALL, "")
970 else:
971 locale.setlocale(locale.LC_ALL, (self._language,
972 locale.getpreferredencoding()))
973 return self._language
974 else:
975 locale.setlocale(locale.LC_ALL, "")
976 return locale.getdefaultlocale()[0]
977
978 def log(self):
979 """Log the configuration by printing the values"""
980 print(" pilot ID:", self._pilotID)
981 print(" rememberPassword:", self._rememberPassword)
982
983 print(" language:", self._language)
984
985 print(" hideMinimizedWindow:", self._hideMinimizedWindow)
986 print(" quitOnClose:", self._quitOnClose)
987
988 print(" onlineGateSystem:", self._onlineGateSystem)
989 print(" onlineACARS:", self._onlineACARS)
990
991 print(" flareTimeFromFS:", self._flareTimeFromFS)
992 print(" syncFSTime:", self._syncFSTime)
993 print(" usingFS2Crew:", self._usingFS2Crew)
994
995 print(" iasSmoothingLength:", self._iasSmoothingLength)
996 print(" vsSmoothingLength:", self._vsSmoothingLength)
997
998 print(" useSimBrief:", self._useSimBrief)
999 print(" simBriefUserName:", self._simBriefUserName)
1000 print(" rememberSimBriefPassword:", self._rememberSimBriefPassword)
1001
1002 print(" pirepDirectory:", self._pirepDirectory)
1003 print(" pirepAutoSave:", self._pirepAutoSave)
1004
1005 print(" defaultMSFS:", self._defaultMSFS)
1006
1007 print(" enableSounds:", self._enableSounds)
1008
1009 print(" pilotControlsSounds:", self._pilotControlsSounds)
1010 print(" pilotHotkey:", str(self._pilotHotkey))
1011
1012 print(" enableApproachCallouts:", self._enableApproachCallouts)
1013 print(" speedbrakeAtTD:", self._speedbrakeAtTD)
1014
1015 print(" enableChecklists:", self._enableChecklists)
1016 print(" checklistHotkey:", str(self._checklistHotkey))
1017
1018 print(" autoUpdate:", self._autoUpdate)
1019 print(" updateURL:", self._updateURL)
1020 print(" useRPC:", self._useRPC)
1021
1022 print(" messageTypeLevels:")
1023 for (type, level) in self._messageTypeLevels.items():
1024 print(" %s: %s" % (const.messageType2string(type),
1025 const.messageLevel2string(level)))
1026
1027 print(" checklists:")
1028 for (type, checklist) in self._checklists.items():
1029 print(" %s:" % (const.icaoCodes[type],))
1030 for path in checklist:
1031 print(" " + path)
1032
1033 print(" approachCallouts:")
1034 for (type, approachCallouts) in self._approachCallouts.items():
1035 print(" %s:" % (const.icaoCodes[type],))
1036 for (altitude, path) in approachCallouts:
1037 print(" %d: %s" % (altitude, path))
1038
1039#-------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.