source: src/mlx/config.py@ 1084:4d1bce6fff23

python3
Last change on this file since 1084:4d1bce6fff23 was 1084:4d1bce6fff23, checked in by István Váradi <ivaradi@…>, 13 months ago

SimBrief handling via CEF is reinstated optionally

File size: 40.2 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 = "https://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._useInternalBrowserForSimBrief = False
237 self._simBriefUserName = ""
238 self._simBriefPassword = ""
239 self._rememberSimBriefPassword = False
240
241 self._pirepDirectory = None
242 self._pirepAutoSave = False
243
244 self._defaultMSFS = os.name=="nt"
245
246 self._enableSounds = not secondaryInstallation
247
248 self._pilotControlsSounds = True
249 self._pilotHotkey = Hotkey(ctrl = True, shift = False, key = "0")
250
251 self._enableApproachCallouts = False
252 self._speedbrakeAtTD = True
253
254 self._enableChecklists = False
255 self._checklistHotkey = Hotkey(ctrl = True, shift = True, key = "0")
256
257 self._autoUpdate = True
258 self._updateURL = Config.DEFAULT_UPDATE_URL
259
260 self._xplaneRemote = False
261 self._xplaneAddress = ""
262
263 self._messageTypeLevels = {}
264
265 self._checklists = {}
266 self._approachCallouts = {}
267 for aircraftType in const.aircraftTypes:
268 self._checklists[aircraftType] = Checklist()
269 self._approachCallouts[aircraftType] = ApproachCallouts()
270
271 self._modified = False
272
273 @property
274 def pilotID(self):
275 """Get the pilot ID."""
276 return self._pilotID
277
278 @pilotID.setter
279 def pilotID(self, pilotID):
280 """Set the pilot ID."""
281 if pilotID!=self._pilotID:
282 self._pilotID = pilotID
283 self._modified = True
284
285 @property
286 def password(self):
287 """Get the password."""
288 return self._password
289
290 @password.setter
291 def password(self, password):
292 """Set the password."""
293 if password!=self._password:
294 self._password = password
295 self._modified = True
296
297 @property
298 def rememberPassword(self):
299 """Get if we should remember the password."""
300 return self._rememberPassword
301
302 @rememberPassword.setter
303 def rememberPassword(self, rememberPassword):
304 """Set if we should remember the password."""
305 if rememberPassword!=self._rememberPassword:
306 self._rememberPassword = rememberPassword
307 self._modified = True
308
309 @property
310 def language(self):
311 """Get the language to use."""
312 return self._language
313
314 @language.setter
315 def language(self, language):
316 """Set the language to use."""
317 if language!=self._language:
318 self._language = language
319 self._modified = True
320
321 @property
322 def hideMinimizedWindow(self):
323 """Get whether a minimized window should be hidden."""
324 return self._hideMinimizedWindow
325
326 @hideMinimizedWindow.setter
327 def hideMinimizedWindow(self, hideMinimizedWindow):
328 """Set whether a minimized window should be hidden."""
329 if hideMinimizedWindow!=self._hideMinimizedWindow:
330 self._hideMinimizedWindow = hideMinimizedWindow
331 self._modified = True
332
333 @property
334 def quitOnClose(self):
335 """Get whether the application should quit when the close button is
336 clicked."""
337 return self._quitOnClose
338
339 @quitOnClose.setter
340 def quitOnClose(self, quitOnClose):
341 """Set whether the application should quit when the close button is
342 clicked."""
343 if quitOnClose!=self._quitOnClose:
344 self._quitOnClose = quitOnClose
345 self._modified = True
346
347 @property
348 def onlineGateSystem(self):
349 """Get whether the online gate system should be used."""
350 return self._onlineGateSystem
351
352 @onlineGateSystem.setter
353 def onlineGateSystem(self, onlineGateSystem):
354 """Set whether the online gate system should be used."""
355 if onlineGateSystem!=self._onlineGateSystem:
356 self._onlineGateSystem = onlineGateSystem
357 self._modified = True
358
359 @property
360 def onlineACARS(self):
361 """Get whether the online ACARS system should be used."""
362 return self._onlineACARS
363
364 @onlineACARS.setter
365 def onlineACARS(self, onlineACARS):
366 """Set whether the online ACARS system should be used."""
367 if onlineACARS!=self._onlineACARS:
368 self._onlineACARS = onlineACARS
369 self._modified = True
370
371 @property
372 def flareTimeFromFS(self):
373 """Get whether the flare time should be calculated from the time values
374 returned by the simulator."""
375 return self._flareTimeFromFS
376
377 @flareTimeFromFS.setter
378 def flareTimeFromFS(self, flareTimeFromFS):
379 """Set whether the flare time should be calculated from the time values
380 returned by the simulator."""
381 if flareTimeFromFS!=self._flareTimeFromFS:
382 self._flareTimeFromFS = flareTimeFromFS
383 self._modified = True
384
385 @property
386 def syncFSTime(self):
387 """Get whether the simulator's time should be synchronized with the
388 machine's clock."""
389 return self._syncFSTime
390
391 @syncFSTime.setter
392 def syncFSTime(self, syncFSTime):
393 """Set whether the simulator's time should be synchronized with the
394 machine's clock."""
395 if syncFSTime!=self._syncFSTime:
396 self._syncFSTime = syncFSTime
397 self._modified = True
398
399 @property
400 def usingFS2Crew(self):
401 """Get whether the FS2Crew addon is being used."""
402 return self._usingFS2Crew
403
404 @usingFS2Crew.setter
405 def usingFS2Crew(self, usingFS2Crew):
406 """Set whether the FS2Crew addon is being used."""
407 if usingFS2Crew!=self._usingFS2Crew:
408 self._usingFS2Crew = usingFS2Crew
409 self._modified = True
410
411 @property
412 def iasSmoothingLength(self):
413 """Get the number of samples over which the IAS is averaged for the
414 smoothed IAS calculation. It may be negative, in which case smoothing
415 is disabled, but we nevertheless store the number of seconds in case it
416 may become useful later."""
417 return self._iasSmoothingLength
418
419 @property
420 def realIASSmoothingLength(self):
421 """Get the real smoothing length of IAS."""
422 return max(self._iasSmoothingLength, 1)
423
424 @iasSmoothingLength.setter
425 def iasSmoothingLength(self, iasSmoothingLength):
426 """Set the number of samples over which the IAS is averaged for the
427 smoothed IAS calculation."""
428 if iasSmoothingLength!=self._iasSmoothingLength:
429 self._iasSmoothingLength = iasSmoothingLength
430 self._modified = True
431
432 @property
433 def vsSmoothingLength(self):
434 """Get the number of samples over which the VS is averaged for the
435 smoothed VS calculation. It may be negative, in which case smoothing
436 is disabled, but we nevertheless store the number of seconds in case it
437 may become useful later."""
438 return self._vsSmoothingLength
439
440 @property
441 def realVSSmoothingLength(self):
442 """Get the real smoothing length of VS."""
443 return max(self._vsSmoothingLength, 1)
444
445 @vsSmoothingLength.setter
446 def vsSmoothingLength(self, vsSmoothingLength):
447 """Set the number of samples over which the VS is averaged for the
448 smoothed VS calculation."""
449 if vsSmoothingLength!=self._vsSmoothingLength:
450 self._vsSmoothingLength = vsSmoothingLength
451 self._modified = True
452
453 @property
454 def useSimBrief(self):
455 """Check if SimBrief should be used."""
456 return self._useSimBrief
457
458 @useSimBrief.setter
459 def useSimBrief(self, useSimBrief):
460 """Check if SimBrief should be used."""
461 if self._useSimBrief != useSimBrief:
462 self._useSimBrief = useSimBrief
463 self._modified = True
464
465 @property
466 def useInternalBrowserForSimBrief(self):
467 """Get if we should use the internal browser to handle SimBrief."""
468 return self._useInternalBrowserForSimBrief
469
470 @useInternalBrowserForSimBrief.setter
471 def useInternalBrowserForSimBrief(self, useInternalBrowserForSimBrief):
472 """Set if we should use the internal browser to handle SimBrief."""
473 if useInternalBrowserForSimBrief!=self._useInternalBrowserForSimBrief:
474 self._useInternalBrowserForSimBrief = useInternalBrowserForSimBrief
475 self._modified = True
476
477 @property
478 def simBriefUserName(self):
479 """Get the SimBrief user name last used"""
480 return self._simBriefUserName
481
482 @simBriefUserName.setter
483 def simBriefUserName(self, simBriefUserName):
484 """Set the SimBrief user name to be used next."""
485 if self._simBriefUserName != simBriefUserName:
486 self._simBriefUserName = simBriefUserName
487 self._modified = True
488
489 @property
490 def simBriefPassword(self):
491 """Get the SimBrief password last used"""
492 return self._simBriefPassword
493
494 @simBriefPassword.setter
495 def simBriefPassword(self, simBriefPassword):
496 """Set the SimBrief password to be used next."""
497 if self._simBriefPassword != simBriefPassword:
498 self._simBriefPassword = simBriefPassword
499 self._modified = True
500
501 @property
502 def rememberSimBriefPassword(self):
503 """Get if we should remember the SimBrief password."""
504 return self._rememberSimBriefPassword
505
506 @rememberSimBriefPassword.setter
507 def rememberSimBriefPassword(self, rememberSimBriefPassword):
508 """Set if we should remember the SimBrief password."""
509 if rememberSimBriefPassword!=self._rememberSimBriefPassword:
510 self._rememberSimBriefPassword = rememberSimBriefPassword
511 self._modified = True
512
513 @property
514 def pirepDirectory(self):
515 """Get the directory offered by default when saving a PIREP."""
516 return self._pirepDirectory
517
518 @pirepDirectory.setter
519 def pirepDirectory(self, pirepDirectory):
520 """Get the directory offered by default when saving a PIREP."""
521 if pirepDirectory!=self._pirepDirectory and \
522 (pirepDirectory!="" or self._pirepDirectory is not None):
523 self._pirepDirectory = None if pirepDirectory=="" \
524 else pirepDirectory
525 if self._pirepDirectory is None:
526 self._pirepAutoSave = False
527 self._modified = True
528
529 @property
530 def pirepAutoSave(self):
531 """Get whether the PIREP should be saved automatically when it becomes
532 saveable."""
533 return self._pirepAutoSave
534
535 @pirepAutoSave.setter
536 def pirepAutoSave(self, pirepAutoSave):
537 """Set whether the PIREP should be saved automatically when it becomes
538 saveable."""
539 pirepAutoSave = pirepAutoSave and self._pirepDirectory is not None
540 if pirepAutoSave!=self._pirepAutoSave:
541 self._pirepAutoSave = pirepAutoSave
542 self._modified = True
543
544 @property
545 def defaultMSFS(self):
546 """Get if the default simulator type is MS FS."""
547 return self._defaultMSFS
548
549 @defaultMSFS.setter
550 def defaultMSFS(self, defaultMSFS):
551 """Set if the default simulator type is MS FS."""
552 if defaultMSFS!=self._defaultMSFS:
553 self._defaultMSFS = defaultMSFS
554 self._modified = True
555
556 def getMessageTypeLevel(self, messageType):
557 """Get the level for the given message type."""
558 return self._messageTypeLevels[messageType] \
559 if messageType in self._messageTypeLevels \
560 else const.MESSAGELEVEL_NONE
561
562 def isMessageTypeFS(self, messageType):
563 """Determine if the given message type is displayed in the
564 simulator."""
565 level = self.getMessageTypeLevel(messageType)
566 return level==const.MESSAGELEVEL_FS or \
567 level==const.MESSAGELEVEL_BOTH
568
569 def setMessageTypeLevel(self, messageType, level):
570 """Set the level of the given message type."""
571 if messageType not in self._messageTypeLevels or \
572 self._messageTypeLevels[messageType]!=level:
573 self._messageTypeLevels[messageType] = level
574 self._modified = True
575
576 @property
577 def enableSounds(self):
578 """Get whether background sounds are enabled."""
579 return self._enableSounds
580
581 @enableSounds.setter
582 def enableSounds(self, enableSounds):
583 """Set whether background sounds are enabled."""
584 if enableSounds!=self._enableSounds:
585 self._enableSounds = enableSounds
586 self._modified = True
587
588 @property
589 def pilotControlsSounds(self):
590 """Get whether the pilot controls the background sounds."""
591 return self._pilotControlsSounds
592
593 @pilotControlsSounds.setter
594 def pilotControlsSounds(self, pilotControlsSounds):
595 """Set whether the pilot controls the background sounds."""
596 if pilotControlsSounds!=self._pilotControlsSounds:
597 self._pilotControlsSounds = pilotControlsSounds
598 self._modified = True
599
600 @property
601 def pilotHotkey(self):
602 """Get the pilot's hotkey."""
603 return self._pilotHotkey
604
605 @pilotHotkey.setter
606 def pilotHotkey(self, pilotHotkey):
607 """Set the pilot's hotkey."""
608 if pilotHotkey!=self._pilotHotkey:
609 self._pilotHotkey = pilotHotkey
610 self._modified = True
611
612 @property
613 def enableApproachCallouts(self):
614 """Get whether the approach callouts should be played."""
615 return self._enableApproachCallouts
616
617 @enableApproachCallouts.setter
618 def enableApproachCallouts(self, enableApproachCallouts):
619 """Set whether the approach callouts should be played."""
620 if enableApproachCallouts!=self._enableApproachCallouts:
621 self._enableApproachCallouts = enableApproachCallouts
622 self._modified = True
623
624 @property
625 def speedbrakeAtTD(self):
626 """Get whether the speedbrake sounds should be played at touchdown."""
627 return self._speedbrakeAtTD
628
629 @speedbrakeAtTD.setter
630 def speedbrakeAtTD(self, speedbrakeAtTD):
631 """Set whether the speedbrake sounds should be played at touchdown."""
632 if speedbrakeAtTD!=self._speedbrakeAtTD:
633 self._speedbrakeAtTD = speedbrakeAtTD
634 self._modified = True
635
636 @property
637 def enableChecklists(self):
638 """Get whether aircraft-specific checklists should be played."""
639 return self._enableChecklists
640
641 @enableChecklists.setter
642 def enableChecklists(self, enableChecklists):
643 """Get whether aircraft-specific checklists should be played."""
644 if enableChecklists!=self._enableChecklists:
645 self._enableChecklists = enableChecklists
646 self._modified = True
647
648 @property
649 def checklistHotkey(self):
650 """Get the checklist hotkey."""
651 return self._checklistHotkey
652
653 @checklistHotkey.setter
654 def checklistHotkey(self, checklistHotkey):
655 """Set the checklist hotkey."""
656 if checklistHotkey!=self._checklistHotkey:
657 self._checklistHotkey = checklistHotkey
658 self._modified = True
659
660 @property
661 def autoUpdate(self):
662 """Get if an automatic update is needed."""
663 return self._autoUpdate
664
665 @autoUpdate.setter
666 def autoUpdate(self, autoUpdate):
667 """Set if an automatic update is needed."""
668 if autoUpdate!=self._autoUpdate:
669 self._autoUpdate = autoUpdate
670 self._modified = True
671
672 @property
673 def updateURL(self):
674 """Get the update URL."""
675 return self._updateURL
676
677 @updateURL.setter
678 def updateURL(self, updateURL):
679 """Set the update URL."""
680 if updateURL!=self._updateURL:
681 self._updateURL = updateURL
682 self._modified = True
683
684 @property
685 def xplaneRemote(self):
686 """Indicate if X-Plane should be accessed remotely."""
687 return self._xplaneRemote
688
689 @xplaneRemote.setter
690 def xplaneRemote(self, xplaneRemote):
691 """Set if X-Plane should be accessed remotely."""
692 if xplaneRemote!=self._xplaneRemote:
693 self._xplaneRemote = xplaneRemote
694 self._modified = True
695
696 @property
697 def xplaneAddress(self):
698 """Get the address of the machine running X-Plane"""
699 return self._xplaneAddress
700
701 @xplaneAddress.setter
702 def xplaneAddress(self, xplaneAddress):
703 """Set the address of the machine running X-Plane."""
704 if xplaneAddress!=self._xplaneAddress:
705 self._xplaneAddress = xplaneAddress
706 self._modified = True
707
708 def getChecklist(self, aircraftType):
709 """Get the checklist for the given aircraft type."""
710 return self._checklists[aircraftType]
711
712 def setChecklist(self, aircraftType, checklist):
713 """Set the checklist for the given aircraft type."""
714 if checklist!=self._checklists[aircraftType]:
715 self._checklists[aircraftType] = checklist.clone()
716 self._modified = True
717
718 def getApproachCallouts(self, aircraftType):
719 """Get the approach callouts for the given aircraft type."""
720 return self._approachCallouts[aircraftType]
721
722 def setApproachCallouts(self, aircraftType, approachCallouts):
723 """Set the approach callouts for the given aircraft type."""
724 if not approachCallouts==self._approachCallouts[aircraftType]:
725 self._approachCallouts[aircraftType] = approachCallouts.clone()
726 self._modified = True
727
728 def load(self):
729 """Load the configuration from its default location."""
730 try:
731 config = configparser.RawConfigParser()
732 config.read(configPath)
733 except:
734 traceback.print_exc()
735 return
736
737 self._pilotID = self._get(config, "login", "id", "")
738 self._password = self._get(config, "login", "password", "")
739 self._rememberPassword = self._getBoolean(config, "login",
740 "rememberPassword", False)
741
742 self._language = self._get(config, "general", "language", "")
743
744 self._hideMinimizedWindow = self._getBoolean(config, "general",
745 "hideMinimizedWindow",
746 True)
747 self._quitOnClose = self._getBoolean(config, "general",
748 "quitOnClose", False)
749
750 self._onlineGateSystem = self._getBoolean(config, "general",
751 "onlineGateSystem",
752 not secondaryInstallation)
753 self._onlineACARS = self._getBoolean(config, "general",
754 "onlineACARS",
755 not secondaryInstallation)
756 self._flareTimeFromFS = self._getBoolean(config, "general",
757 "flareTimeFromFS",
758 False)
759 self._syncFSTime = self._getBoolean(config, "general",
760 "syncFSTime",
761 False)
762 self._usingFS2Crew = self._getBoolean(config, "general",
763 "usingFS2Crew",
764 False)
765 self._iasSmoothingLength = int(self._get(config, "general",
766 "iasSmoothingLength",
767 -2))
768 self._vsSmoothingLength = int(self._get(config, "general",
769 "vsSmoothingLength",
770 -2))
771
772 self._useSimBrief = self._getBoolean(config, "simbrief",
773 "use", False)
774 self._useInternalBrowserForSimBrief = self._getBoolean(config, "simbrief",
775 "useInternalBrowser",
776 False)
777 self._simBriefUserName = self._get(config, "simbrief",
778 "username", "")
779 self._simBriefPassword = self._get(config, "simbrief",
780 "password", "")
781 self._rememberSimBriefPassword = self._getBoolean(config, "simbrief",
782 "rememberPassword",
783 False)
784
785 self._pirepDirectory = self._get(config, "general",
786 "pirepDirectory", None)
787
788 self._pirepAutoSave = self._getBoolean(config, "general",
789 "pirepAutoSave", False)
790 if self._pirepDirectory is None:
791 self._pirepAutoSave = False
792
793 self._messageTypeLevels = {}
794 for messageType in const.messageTypes:
795 self._messageTypeLevels[messageType] = \
796 self._getMessageTypeLevel(config, messageType)
797
798 self._enableSounds = self._getBoolean(config, "sounds", "enable",
799 not secondaryInstallation)
800 self._pilotControlsSounds = self._getBoolean(config, "sounds",
801 "pilotControls", True)
802 self._pilotHotkey.set(self._get(config, "sounds",
803 "pilotHotkey", "C0"))
804 self._enableApproachCallouts = \
805 self._getBoolean(config, "sounds", "enableApproachCallouts", False)
806 self._speedbrakeAtTD = self._getBoolean(config, "sounds",
807 "speedbrakeAtTD", True)
808
809 self._enableChecklists = self._getBoolean(config, "sounds",
810 "enableChecklists", False)
811 self._checklistHotkey.set(self._get(config, "sounds",
812 "checklistHotkey", "CS0"))
813
814 self._autoUpdate = self._getBoolean(config, "update", "auto", True)
815 self._updateURL = self._get(config, "update", "url",
816 Config.DEFAULT_UPDATE_URL)
817
818 for aircraftType in const.aircraftTypes:
819 self._checklists[aircraftType] = \
820 Checklist.fromConfig(config, aircraftType)
821 self._approachCallouts[aircraftType] = \
822 ApproachCallouts.fromConfig(config, aircraftType)
823
824 self._defaultMSFS = self._getBoolean(config, "general",
825 "defaultMSFS", os.name=="nt")
826
827 self._xplaneRemote = self._getBoolean(config, "general",
828 "xplaneRemote", False)
829 self._xplaneAddress = self._get(config, "general",
830 "xplaneAddress", "")
831
832 self._modified = False
833
834 def save(self):
835 """Save the configuration file if it has been modified."""
836 if not self._modified:
837 return
838
839 config = configparser.RawConfigParser()
840
841 config.add_section("login")
842 config.set("login", "id", self._pilotID)
843 config.set("login", "password", self._password)
844 config.set("login", "rememberPassword",
845 "yes" if self._rememberPassword else "no")
846
847 config.add_section("general")
848 if self._language:
849 config.set("general", "language", self._language)
850 config.set("general", "hideMinimizedWindow",
851 "yes" if self._hideMinimizedWindow else "no")
852 config.set("general", "quitOnClose",
853 "yes" if self._quitOnClose else "no")
854 config.set("general", "onlineGateSystem",
855 "yes" if self._onlineGateSystem else "no")
856 config.set("general", "onlineACARS",
857 "yes" if self._onlineACARS else "no")
858 config.set("general", "flareTimeFromFS",
859 "yes" if self._flareTimeFromFS else "no")
860 config.set("general", "syncFSTime",
861 "yes" if self._syncFSTime else "no")
862 config.set("general", "usingFS2Crew",
863 "yes" if self._usingFS2Crew else "no")
864 config.set("general", "iasSmoothingLength",
865 str(self._iasSmoothingLength))
866 config.set("general", "vsSmoothingLength",
867 str(self._vsSmoothingLength))
868
869 config.add_section("simbrief")
870 config.set("simbrief", "use",
871 "yes" if self._useSimBrief else "no")
872 config.set("simbrief", "useInternalBrowser",
873 "yes" if self._useInternalBrowserForSimBrief else "no")
874 config.set("simbrief", "username", self._simBriefUserName)
875 config.set("simbrief", "password", self._simBriefPassword)
876 config.set("simbrief", "rememberPassword",
877 "yes" if self._rememberSimBriefPassword else "no")
878
879 if self._pirepDirectory is not None:
880 config.set("general", "pirepDirectory", self._pirepDirectory)
881 config.set("general", "pirepAutoSave",
882 "yes" if self._pirepAutoSave else "no")
883
884 config.set("general", "defaultMSFS",
885 "yes" if self._defaultMSFS else "no")
886
887 config.set("general", "xplaneRemote",
888 "yes" if self._xplaneRemote else "no")
889 config.set("general", "xplaneAddress", self._xplaneAddress)
890
891 config.add_section(Config._messageTypesSection)
892 for messageType in const.messageTypes:
893 if messageType in self._messageTypeLevels:
894 option = self._getMessageTypeLevelOptionName(messageType)
895 level = self._messageTypeLevels[messageType]
896 config.set(Config._messageTypesSection, option,
897 const.messageLevel2string(level))
898
899 config.add_section("sounds")
900 config.set("sounds", "enable",
901 "yes" if self._enableSounds else "no")
902 config.set("sounds", "pilotControls",
903 "yes" if self._pilotControlsSounds else "no")
904 config.set("sounds", "pilotHotkey", str(self._pilotHotkey))
905 config.set("sounds", "enableApproachCallouts",
906 "yes" if self._enableApproachCallouts else "no")
907 config.set("sounds", "speedbrakeAtTD",
908 "yes" if self._speedbrakeAtTD else "no")
909
910 config.set("sounds", "enableChecklists",
911 "yes" if self._enableChecklists else "no")
912 config.set("sounds", "checklistHotkey",
913 str(self._checklistHotkey))
914
915 config.add_section("update")
916 config.set("update", "auto",
917 "yes" if self._autoUpdate else "no")
918 config.set("update", "url", self._updateURL)
919
920 config.add_section(Checklist.SECTION)
921 config.add_section(ApproachCallouts.SECTION)
922 for aircraftType in const.aircraftTypes:
923 self._checklists[aircraftType].toConfig(config, aircraftType)
924 self._approachCallouts[aircraftType].toConfig(config, aircraftType)
925
926 try:
927 fd = os.open(configPath, os.O_CREAT|os.O_TRUNC|os.O_WRONLY,
928 0o600)
929 with os.fdopen(fd, "wt") as f:
930 config.write(f)
931 self._modified = False
932
933 print("Configuration saved:")
934 self.log()
935
936 except Exception as e:
937 print("Failed to update config: " + \
938 utf2unicode(str(e)), file=sys.stderr)
939
940 def _getBoolean(self, config, section, option, default):
941 """Get the given option as a boolean, if found in the given config,
942 otherwise the default."""
943 return config.getboolean(section, option) \
944 if config.has_option(section, option) \
945 else default
946
947 def _get(self, config, section, option, default):
948 """Get the given option as a string, if found in the given config,
949 otherwise the default."""
950 return config.get(section, option) \
951 if config.has_option(section, option) \
952 else default
953
954 def _getMessageTypeLevel(self, config, messageType):
955 """Get the message type level for the given message type."""
956 option = self._getMessageTypeLevelOptionName(messageType)
957 if config.has_option(Config._messageTypesSection, option):
958 value = config.get(Config._messageTypesSection, option)
959 return const.string2messageLevel(value)
960 elif secondaryInstallation:
961 return const.MESSAGELEVEL_NONE
962 elif messageType in [const.MESSAGETYPE_LOGGER_ERROR,
963 const.MESSAGETYPE_FAULT,
964 const.MESSAGETYPE_NOGO,
965 const.MESSAGETYPE_GATE_SYSTEM,
966 const.MESSAGETYPE_HELP]:
967 return const.MESSAGELEVEL_BOTH
968 else:
969 return const.MESSAGELEVEL_FS
970
971 def _getMessageTypeLevelOptionName(self, messageType):
972 """Get the option name for the given message type level."""
973 return const.messageType2string(messageType)
974
975 def setupLocale(self):
976 """Setup the locale based on the language set.
977
978 Return True if a specific language was set, False otherwise."""
979 import locale
980 if self._language:
981 print("Setting up locale for", self._language)
982 os.environ["LANGUAGE"] = self._language
983 langAndEncoding = self._language + "." + locale.getpreferredencoding()
984 os.environ["LANG"] = langAndEncoding
985 os.environ["LC_MESSAGES"] = langAndEncoding
986 os.environ["LC_COLLATE"] = langAndEncoding
987 os.environ["LC_CTYPE"] = langAndEncoding
988 os.environ["LC_MONETARY"] = langAndEncoding
989 os.environ["LC_NUMERIC"] = langAndEncoding
990 os.environ["LC_TIME"] = langAndEncoding
991 return True
992 else:
993 return False
994
995 def getLanguage(self):
996 """Get the language to be used."""
997 import locale
998 if self._language:
999 if os.name=="nt":
1000 if self._language in _languageMap:
1001 locale.setlocale(locale.LC_ALL, _languageMap[self._language])
1002 else:
1003 locale.setlocale(locale.LC_ALL, "")
1004 else:
1005 locale.setlocale(locale.LC_ALL, (self._language,
1006 locale.getpreferredencoding()))
1007 return self._language
1008 else:
1009 locale.setlocale(locale.LC_ALL, "")
1010 return locale.getdefaultlocale()[0]
1011
1012 def log(self):
1013 """Log the configuration by printing the values"""
1014 print(" pilot ID:", self._pilotID)
1015 print(" rememberPassword:", self._rememberPassword)
1016
1017 print(" language:", self._language)
1018
1019 print(" hideMinimizedWindow:", self._hideMinimizedWindow)
1020 print(" quitOnClose:", self._quitOnClose)
1021
1022 print(" onlineGateSystem:", self._onlineGateSystem)
1023 print(" onlineACARS:", self._onlineACARS)
1024
1025 print(" flareTimeFromFS:", self._flareTimeFromFS)
1026 print(" syncFSTime:", self._syncFSTime)
1027 print(" usingFS2Crew:", self._usingFS2Crew)
1028
1029 print(" iasSmoothingLength:", self._iasSmoothingLength)
1030 print(" vsSmoothingLength:", self._vsSmoothingLength)
1031
1032 print(" useSimBrief:", self._useSimBrief)
1033 print(" useInternalBrowserForSimBrief:", self._useInternalBrowserForSimBrief)
1034 print(" simBriefUserName:", self._simBriefUserName)
1035 print(" rememberSimBriefPassword:", self._rememberSimBriefPassword)
1036
1037 print(" pirepDirectory:", self._pirepDirectory)
1038 print(" pirepAutoSave:", self._pirepAutoSave)
1039
1040 print(" defaultMSFS:", self._defaultMSFS)
1041 print(" xplaneRemote:", self._xplaneRemote)
1042 print(" xplaneAddress:", self._xplaneAddress)
1043
1044 print(" enableSounds:", self._enableSounds)
1045
1046 print(" pilotControlsSounds:", self._pilotControlsSounds)
1047 print(" pilotHotkey:", str(self._pilotHotkey))
1048
1049 print(" enableApproachCallouts:", self._enableApproachCallouts)
1050 print(" speedbrakeAtTD:", self._speedbrakeAtTD)
1051
1052 print(" enableChecklists:", self._enableChecklists)
1053 print(" checklistHotkey:", str(self._checklistHotkey))
1054
1055 print(" autoUpdate:", self._autoUpdate)
1056 print(" updateURL:", self._updateURL)
1057
1058 print(" messageTypeLevels:")
1059 for (type, level) in self._messageTypeLevels.items():
1060 print(" %s: %s" % (const.messageType2string(type),
1061 const.messageLevel2string(level)))
1062
1063 print(" checklists:")
1064 for (type, checklist) in self._checklists.items():
1065 print(" %s:" % (const.icaoCodes[type],))
1066 for path in checklist:
1067 print(" " + path)
1068
1069 print(" approachCallouts:")
1070 for (type, approachCallouts) in self._approachCallouts.items():
1071 print(" %s:" % (const.icaoCodes[type],))
1072 for (altitude, path) in approachCallouts:
1073 print(" %d: %s" % (altitude, path))
1074
1075#-------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.