source: src/mlx/config.py@ 1019:d45b3d2eeb1d

python3
Last change on this file since 1019:d45b3d2eeb1d was 1011:6a2730357409, checked in by István Váradi <ivaradi@…>, 5 years ago

Semi-automatic upgrate of the update URL to HTTPS

File size: 39.0 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._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._updateURLUpdated = True
261 self._useRPC = True
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 simBriefUserName(self):
467 """Get the SimBrief user name last used"""
468 return self._simBriefUserName
469
470 @simBriefUserName.setter
471 def simBriefUserName(self, simBriefUserName):
472 """Set the SimBrief user name to be used next."""
473 if self._simBriefUserName != simBriefUserName:
474 self._simBriefUserName = simBriefUserName
475 self._modified = True
476
477 @property
478 def simBriefPassword(self):
479 """Get the SimBrief password last used"""
480 return self._simBriefPassword
481
482 @simBriefPassword.setter
483 def simBriefPassword(self, simBriefPassword):
484 """Set the SimBrief password to be used next."""
485 if self._simBriefPassword != simBriefPassword:
486 self._simBriefPassword = simBriefPassword
487 self._modified = True
488
489 @property
490 def rememberSimBriefPassword(self):
491 """Get if we should remember the SimBrief password."""
492 return self._rememberSimBriefPassword
493
494 @rememberSimBriefPassword.setter
495 def rememberSimBriefPassword(self, rememberSimBriefPassword):
496 """Set if we should remember the SimBrief password."""
497 if rememberSimBriefPassword!=self._rememberSimBriefPassword:
498 self._rememberSimBriefPassword = rememberSimBriefPassword
499 self._modified = True
500
501 @property
502 def pirepDirectory(self):
503 """Get the directory offered by default when saving a PIREP."""
504 return self._pirepDirectory
505
506 @pirepDirectory.setter
507 def pirepDirectory(self, pirepDirectory):
508 """Get the directory offered by default when saving a PIREP."""
509 if pirepDirectory!=self._pirepDirectory and \
510 (pirepDirectory!="" or self._pirepDirectory is not None):
511 self._pirepDirectory = None if pirepDirectory=="" \
512 else pirepDirectory
513 if self._pirepDirectory is None:
514 self._pirepAutoSave = False
515 self._modified = True
516
517 @property
518 def pirepAutoSave(self):
519 """Get whether the PIREP should be saved automatically when it becomes
520 saveable."""
521 return self._pirepAutoSave
522
523 @pirepAutoSave.setter
524 def pirepAutoSave(self, pirepAutoSave):
525 """Set whether the PIREP should be saved automatically when it becomes
526 saveable."""
527 pirepAutoSave = pirepAutoSave and self._pirepDirectory is not None
528 if pirepAutoSave!=self._pirepAutoSave:
529 self._pirepAutoSave = pirepAutoSave
530 self._modified = True
531
532 @property
533 def defaultMSFS(self):
534 """Get if the default simulator type is MS FS."""
535 return self._defaultMSFS
536
537 @defaultMSFS.setter
538 def defaultMSFS(self, defaultMSFS):
539 """Set if the default simulator type is MS FS."""
540 if defaultMSFS!=self._defaultMSFS:
541 self._defaultMSFS = defaultMSFS
542 self._modified = True
543
544 def getMessageTypeLevel(self, messageType):
545 """Get the level for the given message type."""
546 return self._messageTypeLevels[messageType] \
547 if messageType in self._messageTypeLevels \
548 else const.MESSAGELEVEL_NONE
549
550 def isMessageTypeFS(self, messageType):
551 """Determine if the given message type is displayed in the
552 simulator."""
553 level = self.getMessageTypeLevel(messageType)
554 return level==const.MESSAGELEVEL_FS or \
555 level==const.MESSAGELEVEL_BOTH
556
557 def setMessageTypeLevel(self, messageType, level):
558 """Set the level of the given message type."""
559 if messageType not in self._messageTypeLevels or \
560 self._messageTypeLevels[messageType]!=level:
561 self._messageTypeLevels[messageType] = level
562 self._modified = True
563
564 @property
565 def enableSounds(self):
566 """Get whether background sounds are enabled."""
567 return self._enableSounds
568
569 @enableSounds.setter
570 def enableSounds(self, enableSounds):
571 """Set whether background sounds are enabled."""
572 if enableSounds!=self._enableSounds:
573 self._enableSounds = enableSounds
574 self._modified = True
575
576 @property
577 def pilotControlsSounds(self):
578 """Get whether the pilot controls the background sounds."""
579 return self._pilotControlsSounds
580
581 @pilotControlsSounds.setter
582 def pilotControlsSounds(self, pilotControlsSounds):
583 """Set whether the pilot controls the background sounds."""
584 if pilotControlsSounds!=self._pilotControlsSounds:
585 self._pilotControlsSounds = pilotControlsSounds
586 self._modified = True
587
588 @property
589 def pilotHotkey(self):
590 """Get the pilot's hotkey."""
591 return self._pilotHotkey
592
593 @pilotHotkey.setter
594 def pilotHotkey(self, pilotHotkey):
595 """Set the pilot's hotkey."""
596 if pilotHotkey!=self._pilotHotkey:
597 self._pilotHotkey = pilotHotkey
598 self._modified = True
599
600 @property
601 def enableApproachCallouts(self):
602 """Get whether the approach callouts should be played."""
603 return self._enableApproachCallouts
604
605 @enableApproachCallouts.setter
606 def enableApproachCallouts(self, enableApproachCallouts):
607 """Set whether the approach callouts should be played."""
608 if enableApproachCallouts!=self._enableApproachCallouts:
609 self._enableApproachCallouts = enableApproachCallouts
610 self._modified = True
611
612 @property
613 def speedbrakeAtTD(self):
614 """Get whether the speedbrake sounds should be played at touchdown."""
615 return self._speedbrakeAtTD
616
617 @speedbrakeAtTD.setter
618 def speedbrakeAtTD(self, speedbrakeAtTD):
619 """Set whether the speedbrake sounds should be played at touchdown."""
620 if speedbrakeAtTD!=self._speedbrakeAtTD:
621 self._speedbrakeAtTD = speedbrakeAtTD
622 self._modified = True
623
624 @property
625 def enableChecklists(self):
626 """Get whether aircraft-specific checklists should be played."""
627 return self._enableChecklists
628
629 @enableChecklists.setter
630 def enableChecklists(self, enableChecklists):
631 """Get whether aircraft-specific checklists should be played."""
632 if enableChecklists!=self._enableChecklists:
633 self._enableChecklists = enableChecklists
634 self._modified = True
635
636 @property
637 def checklistHotkey(self):
638 """Get the checklist hotkey."""
639 return self._checklistHotkey
640
641 @checklistHotkey.setter
642 def checklistHotkey(self, checklistHotkey):
643 """Set the checklist hotkey."""
644 if checklistHotkey!=self._checklistHotkey:
645 self._checklistHotkey = checklistHotkey
646 self._modified = True
647
648 @property
649 def autoUpdate(self):
650 """Get if an automatic update is needed."""
651 return self._autoUpdate
652
653 @autoUpdate.setter
654 def autoUpdate(self, autoUpdate):
655 """Set if an automatic update is needed."""
656 if autoUpdate!=self._autoUpdate:
657 self._autoUpdate = autoUpdate
658 self._modified = True
659
660 @property
661 def updateURL(self):
662 """Get the update URL."""
663 return self._updateURL
664
665 @updateURL.setter
666 def updateURL(self, updateURL):
667 """Set the update URL."""
668 if updateURL!=self._updateURL:
669 self._updateURL = updateURL
670 self._modified = True
671
672 @property
673 def useRPC(self):
674 """Determine if RPC calls should be used."""
675 return self._useRPC
676
677 @useRPC.setter
678 def useRPC(self, useRPC):
679 """Set whether RPC calls should be used."""
680 if useRPC!=self._useRPC:
681 self._useRPC = useRPC
682 self._modified = True
683
684 def getChecklist(self, aircraftType):
685 """Get the checklist for the given aircraft type."""
686 return self._checklists[aircraftType]
687
688 def setChecklist(self, aircraftType, checklist):
689 """Set the checklist for the given aircraft type."""
690 if checklist!=self._checklists[aircraftType]:
691 self._checklists[aircraftType] = checklist.clone()
692 self._modified = True
693
694 def getApproachCallouts(self, aircraftType):
695 """Get the approach callouts for the given aircraft type."""
696 return self._approachCallouts[aircraftType]
697
698 def setApproachCallouts(self, aircraftType, approachCallouts):
699 """Set the approach callouts for the given aircraft type."""
700 if not approachCallouts==self._approachCallouts[aircraftType]:
701 self._approachCallouts[aircraftType] = approachCallouts.clone()
702 self._modified = True
703
704 def load(self):
705 """Load the configuration from its default location."""
706 self._updateURLUpdated = False
707
708 try:
709 config = configparser.RawConfigParser()
710 config.read(configPath)
711 except:
712 traceback.print_exc()
713 return
714
715 self._pilotID = self._get(config, "login", "id", "")
716 self._password = self._get(config, "login", "password", "")
717 self._rememberPassword = self._getBoolean(config, "login",
718 "rememberPassword", False)
719
720 self._language = self._get(config, "general", "language", "")
721
722 self._hideMinimizedWindow = self._getBoolean(config, "general",
723 "hideMinimizedWindow",
724 True)
725 self._quitOnClose = self._getBoolean(config, "general",
726 "quitOnClose", False)
727
728 self._onlineGateSystem = self._getBoolean(config, "general",
729 "onlineGateSystem",
730 not secondaryInstallation)
731 self._onlineACARS = self._getBoolean(config, "general",
732 "onlineACARS",
733 not secondaryInstallation)
734 self._flareTimeFromFS = self._getBoolean(config, "general",
735 "flareTimeFromFS",
736 False)
737 self._syncFSTime = self._getBoolean(config, "general",
738 "syncFSTime",
739 False)
740 self._usingFS2Crew = self._getBoolean(config, "general",
741 "usingFS2Crew",
742 False)
743 self._iasSmoothingLength = int(self._get(config, "general",
744 "iasSmoothingLength",
745 -2))
746 self._vsSmoothingLength = int(self._get(config, "general",
747 "vsSmoothingLength",
748 -2))
749
750 self._useSimBrief = self._getBoolean(config, "simbrief",
751 "use", False)
752 self._simBriefUserName = self._get(config, "simbrief",
753 "username", "")
754 self._simBriefPassword = self._get(config, "simbrief",
755 "password", "")
756 self._rememberSimBriefPassword = self._getBoolean(config, "simbrief",
757 "rememberPassword",
758 False)
759
760 self._pirepDirectory = self._get(config, "general",
761 "pirepDirectory", None)
762
763 self._pirepAutoSave = self._getBoolean(config, "general",
764 "pirepAutoSave", False)
765 if self._pirepDirectory is None:
766 self._pirepAutoSave = False
767
768 self._messageTypeLevels = {}
769 for messageType in const.messageTypes:
770 self._messageTypeLevels[messageType] = \
771 self._getMessageTypeLevel(config, messageType)
772
773 self._enableSounds = self._getBoolean(config, "sounds", "enable",
774 not secondaryInstallation)
775 self._pilotControlsSounds = self._getBoolean(config, "sounds",
776 "pilotControls", True)
777 self._pilotHotkey.set(self._get(config, "sounds",
778 "pilotHotkey", "C0"))
779 self._enableApproachCallouts = \
780 self._getBoolean(config, "sounds", "enableApproachCallouts", False)
781 self._speedbrakeAtTD = self._getBoolean(config, "sounds",
782 "speedbrakeAtTD", True)
783
784 self._enableChecklists = self._getBoolean(config, "sounds",
785 "enableChecklists", False)
786 self._checklistHotkey.set(self._get(config, "sounds",
787 "checklistHotkey", "CS0"))
788
789 self._autoUpdate = self._getBoolean(config, "update", "auto", True)
790 self._updateURL = self._get(config, "update", "url",
791 Config.DEFAULT_UPDATE_URL +
792 ("/exp" if secondaryInstallation else ""))
793 self._updateURLUpdated = self._getBoolean(config, "update",
794 "urlUpdated", False)
795 if self._updateURL.startswith("http://") and not self._updateURLUpdated:
796 self._updateURL = "https://" + self._updateURL[7:]
797 self._updateURLUpdated = True
798
799 self._useRPC = self._getBoolean(config, "general", "useRPC", True)
800
801 for aircraftType in const.aircraftTypes:
802 self._checklists[aircraftType] = \
803 Checklist.fromConfig(config, aircraftType)
804 self._approachCallouts[aircraftType] = \
805 ApproachCallouts.fromConfig(config, aircraftType)
806
807 self._defaultMSFS = self._getBoolean(config, "general",
808 "defaultMSFS", os.name=="nt")
809
810 self._modified = False
811
812 def save(self):
813 """Save the configuration file if it has been modified."""
814 if not self._modified:
815 return
816
817 config = configparser.RawConfigParser()
818
819 config.add_section("login")
820 config.set("login", "id", self._pilotID)
821 config.set("login", "password", self._password)
822 config.set("login", "rememberPassword",
823 "yes" if self._rememberPassword else "no")
824
825 config.add_section("general")
826 if self._language:
827 config.set("general", "language", self._language)
828 config.set("general", "hideMinimizedWindow",
829 "yes" if self._hideMinimizedWindow else "no")
830 config.set("general", "quitOnClose",
831 "yes" if self._quitOnClose else "no")
832 config.set("general", "onlineGateSystem",
833 "yes" if self._onlineGateSystem else "no")
834 config.set("general", "onlineACARS",
835 "yes" if self._onlineACARS else "no")
836 config.set("general", "flareTimeFromFS",
837 "yes" if self._flareTimeFromFS else "no")
838 config.set("general", "syncFSTime",
839 "yes" if self._syncFSTime else "no")
840 config.set("general", "usingFS2Crew",
841 "yes" if self._usingFS2Crew else "no")
842 config.set("general", "iasSmoothingLength",
843 str(self._iasSmoothingLength))
844 config.set("general", "vsSmoothingLength",
845 str(self._vsSmoothingLength))
846
847 config.add_section("simbrief")
848 config.set("simbrief", "use",
849 "yes" if self._useSimBrief else "no")
850 config.set("simbrief", "username", self._simBriefUserName)
851 config.set("simbrief", "password", self._simBriefPassword)
852 config.set("simbrief", "rememberPassword",
853 "yes" if self._rememberSimBriefPassword else "no")
854
855 if self._pirepDirectory is not None:
856 config.set("general", "pirepDirectory", self._pirepDirectory)
857 config.set("general", "pirepAutoSave",
858 "yes" if self._pirepAutoSave else "no")
859
860 config.set("general", "defaultMSFS",
861 "yes" if self._defaultMSFS else "no")
862
863 config.add_section(Config._messageTypesSection)
864 for messageType in const.messageTypes:
865 if messageType in self._messageTypeLevels:
866 option = self._getMessageTypeLevelOptionName(messageType)
867 level = self._messageTypeLevels[messageType]
868 config.set(Config._messageTypesSection, option,
869 const.messageLevel2string(level))
870
871 config.add_section("sounds")
872 config.set("sounds", "enable",
873 "yes" if self._enableSounds else "no")
874 config.set("sounds", "pilotControls",
875 "yes" if self._pilotControlsSounds else "no")
876 config.set("sounds", "pilotHotkey", str(self._pilotHotkey))
877 config.set("sounds", "enableApproachCallouts",
878 "yes" if self._enableApproachCallouts else "no")
879 config.set("sounds", "speedbrakeAtTD",
880 "yes" if self._speedbrakeAtTD else "no")
881
882 config.set("sounds", "enableChecklists",
883 "yes" if self._enableChecklists else "no")
884 config.set("sounds", "checklistHotkey",
885 str(self._checklistHotkey))
886
887 config.add_section("update")
888 config.set("update", "auto",
889 "yes" if self._autoUpdate else "no")
890 config.set("update", "url", self._updateURL)
891 config.set("update", "urlUpdated", self._updateURLUpdated)
892
893 config.set("general", "useRPC",
894 "yes" if self._useRPC else "no")
895
896 config.add_section(Checklist.SECTION)
897 config.add_section(ApproachCallouts.SECTION)
898 for aircraftType in const.aircraftTypes:
899 self._checklists[aircraftType].toConfig(config, aircraftType)
900 self._approachCallouts[aircraftType].toConfig(config, aircraftType)
901
902 try:
903 fd = os.open(configPath, os.O_CREAT|os.O_TRUNC|os.O_WRONLY,
904 0o600)
905 with os.fdopen(fd, "wt") as f:
906 config.write(f)
907 self._modified = False
908
909 print("Configuration saved:")
910 self.log()
911
912 except Exception as e:
913 print("Failed to update config: " + \
914 utf2unicode(str(e)), file=sys.stderr)
915
916 def _getBoolean(self, config, section, option, default):
917 """Get the given option as a boolean, if found in the given config,
918 otherwise the default."""
919 return config.getboolean(section, option) \
920 if config.has_option(section, option) \
921 else default
922
923 def _get(self, config, section, option, default):
924 """Get the given option as a string, if found in the given config,
925 otherwise the default."""
926 return config.get(section, option) \
927 if config.has_option(section, option) \
928 else default
929
930 def _getMessageTypeLevel(self, config, messageType):
931 """Get the message type level for the given message type."""
932 option = self._getMessageTypeLevelOptionName(messageType)
933 if config.has_option(Config._messageTypesSection, option):
934 value = config.get(Config._messageTypesSection, option)
935 return const.string2messageLevel(value)
936 elif secondaryInstallation:
937 return const.MESSAGELEVEL_NONE
938 elif messageType in [const.MESSAGETYPE_LOGGER_ERROR,
939 const.MESSAGETYPE_FAULT,
940 const.MESSAGETYPE_NOGO,
941 const.MESSAGETYPE_GATE_SYSTEM,
942 const.MESSAGETYPE_HELP]:
943 return const.MESSAGELEVEL_BOTH
944 else:
945 return const.MESSAGELEVEL_FS
946
947 def _getMessageTypeLevelOptionName(self, messageType):
948 """Get the option name for the given message type level."""
949 return const.messageType2string(messageType)
950
951 def setupLocale(self):
952 """Setup the locale based on the language set.
953
954 Return True if a specific language was set, False otherwise."""
955 import locale
956 if self._language:
957 print("Setting up locale for", self._language)
958 os.environ["LANGUAGE"] = self._language
959 langAndEncoding = self._language + "." + locale.getpreferredencoding()
960 os.environ["LANG"] = langAndEncoding
961 os.environ["LC_MESSAGES"] = langAndEncoding
962 os.environ["LC_COLLATE"] = langAndEncoding
963 os.environ["LC_CTYPE"] = langAndEncoding
964 os.environ["LC_MONETARY"] = langAndEncoding
965 os.environ["LC_NUMERIC"] = langAndEncoding
966 os.environ["LC_TIME"] = langAndEncoding
967 return True
968 else:
969 return False
970
971 def getLanguage(self):
972 """Get the language to be used."""
973 import locale
974 if self._language:
975 if os.name=="nt":
976 if self._language in _languageMap:
977 locale.setlocale(locale.LC_ALL, _languageMap[self._language])
978 else:
979 locale.setlocale(locale.LC_ALL, "")
980 else:
981 locale.setlocale(locale.LC_ALL, (self._language,
982 locale.getpreferredencoding()))
983 return self._language
984 else:
985 locale.setlocale(locale.LC_ALL, "")
986 return locale.getdefaultlocale()[0]
987
988 def log(self):
989 """Log the configuration by printing the values"""
990 print(" pilot ID:", self._pilotID)
991 print(" rememberPassword:", self._rememberPassword)
992
993 print(" language:", self._language)
994
995 print(" hideMinimizedWindow:", self._hideMinimizedWindow)
996 print(" quitOnClose:", self._quitOnClose)
997
998 print(" onlineGateSystem:", self._onlineGateSystem)
999 print(" onlineACARS:", self._onlineACARS)
1000
1001 print(" flareTimeFromFS:", self._flareTimeFromFS)
1002 print(" syncFSTime:", self._syncFSTime)
1003 print(" usingFS2Crew:", self._usingFS2Crew)
1004
1005 print(" iasSmoothingLength:", self._iasSmoothingLength)
1006 print(" vsSmoothingLength:", self._vsSmoothingLength)
1007
1008 print(" useSimBrief:", self._useSimBrief)
1009 print(" simBriefUserName:", self._simBriefUserName)
1010 print(" rememberSimBriefPassword:", self._rememberSimBriefPassword)
1011
1012 print(" pirepDirectory:", self._pirepDirectory)
1013 print(" pirepAutoSave:", self._pirepAutoSave)
1014
1015 print(" defaultMSFS:", self._defaultMSFS)
1016
1017 print(" enableSounds:", self._enableSounds)
1018
1019 print(" pilotControlsSounds:", self._pilotControlsSounds)
1020 print(" pilotHotkey:", str(self._pilotHotkey))
1021
1022 print(" enableApproachCallouts:", self._enableApproachCallouts)
1023 print(" speedbrakeAtTD:", self._speedbrakeAtTD)
1024
1025 print(" enableChecklists:", self._enableChecklists)
1026 print(" checklistHotkey:", str(self._checklistHotkey))
1027
1028 print(" autoUpdate:", self._autoUpdate)
1029 print(" updateURL:", self._updateURL)
1030 print(" updateURLUpdated:", self._updateURLUpdated)
1031 print(" useRPC:", self._useRPC)
1032
1033 print(" messageTypeLevels:")
1034 for (type, level) in self._messageTypeLevels.items():
1035 print(" %s: %s" % (const.messageType2string(type),
1036 const.messageLevel2string(level)))
1037
1038 print(" checklists:")
1039 for (type, checklist) in self._checklists.items():
1040 print(" %s:" % (const.icaoCodes[type],))
1041 for path in checklist:
1042 print(" " + path)
1043
1044 print(" approachCallouts:")
1045 for (type, approachCallouts) in self._approachCallouts.items():
1046 print(" %s:" % (const.icaoCodes[type],))
1047 for (altitude, path) in approachCallouts:
1048 print(" %d: %s" % (altitude, path))
1049
1050#-------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.