source: src/mlx/config.py@ 1080:e65385c0e41f

python3
Last change on this file since 1080:e65385c0e41f was 1080:e65385c0e41f, checked in by István Váradi <ivaradi@…>, 15 months ago

Removed the semi-automatic update URL updating

File size: 39.1 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
259 self._xplaneRemote = False
260 self._xplaneAddress = ""
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 xplaneRemote(self):
673 """Indicate if X-Plane should be accessed remotely."""
674 return self._xplaneRemote
675
676 @xplaneRemote.setter
677 def xplaneRemote(self, xplaneRemote):
678 """Set if X-Plane should be accessed remotely."""
679 if xplaneRemote!=self._xplaneRemote:
680 self._xplaneRemote = xplaneRemote
681 self._modified = True
682
683 @property
684 def xplaneAddress(self):
685 """Get the address of the machine running X-Plane"""
686 return self._xplaneAddress
687
688 @xplaneAddress.setter
689 def xplaneAddress(self, xplaneAddress):
690 """Set the address of the machine running X-Plane."""
691 if xplaneAddress!=self._xplaneAddress:
692 self._xplaneAddress = xplaneAddress
693 self._modified = True
694
695 def getChecklist(self, aircraftType):
696 """Get the checklist for the given aircraft type."""
697 return self._checklists[aircraftType]
698
699 def setChecklist(self, aircraftType, checklist):
700 """Set the checklist for the given aircraft type."""
701 if checklist!=self._checklists[aircraftType]:
702 self._checklists[aircraftType] = checklist.clone()
703 self._modified = True
704
705 def getApproachCallouts(self, aircraftType):
706 """Get the approach callouts for the given aircraft type."""
707 return self._approachCallouts[aircraftType]
708
709 def setApproachCallouts(self, aircraftType, approachCallouts):
710 """Set the approach callouts for the given aircraft type."""
711 if not approachCallouts==self._approachCallouts[aircraftType]:
712 self._approachCallouts[aircraftType] = approachCallouts.clone()
713 self._modified = True
714
715 def load(self):
716 """Load the configuration from its default location."""
717 try:
718 config = configparser.RawConfigParser()
719 config.read(configPath)
720 except:
721 traceback.print_exc()
722 return
723
724 self._pilotID = self._get(config, "login", "id", "")
725 self._password = self._get(config, "login", "password", "")
726 self._rememberPassword = self._getBoolean(config, "login",
727 "rememberPassword", False)
728
729 self._language = self._get(config, "general", "language", "")
730
731 self._hideMinimizedWindow = self._getBoolean(config, "general",
732 "hideMinimizedWindow",
733 True)
734 self._quitOnClose = self._getBoolean(config, "general",
735 "quitOnClose", False)
736
737 self._onlineGateSystem = self._getBoolean(config, "general",
738 "onlineGateSystem",
739 not secondaryInstallation)
740 self._onlineACARS = self._getBoolean(config, "general",
741 "onlineACARS",
742 not secondaryInstallation)
743 self._flareTimeFromFS = self._getBoolean(config, "general",
744 "flareTimeFromFS",
745 False)
746 self._syncFSTime = self._getBoolean(config, "general",
747 "syncFSTime",
748 False)
749 self._usingFS2Crew = self._getBoolean(config, "general",
750 "usingFS2Crew",
751 False)
752 self._iasSmoothingLength = int(self._get(config, "general",
753 "iasSmoothingLength",
754 -2))
755 self._vsSmoothingLength = int(self._get(config, "general",
756 "vsSmoothingLength",
757 -2))
758
759 self._useSimBrief = self._getBoolean(config, "simbrief",
760 "use", False)
761 self._simBriefUserName = self._get(config, "simbrief",
762 "username", "")
763 self._simBriefPassword = self._get(config, "simbrief",
764 "password", "")
765 self._rememberSimBriefPassword = self._getBoolean(config, "simbrief",
766 "rememberPassword",
767 False)
768
769 self._pirepDirectory = self._get(config, "general",
770 "pirepDirectory", None)
771
772 self._pirepAutoSave = self._getBoolean(config, "general",
773 "pirepAutoSave", False)
774 if self._pirepDirectory is None:
775 self._pirepAutoSave = False
776
777 self._messageTypeLevels = {}
778 for messageType in const.messageTypes:
779 self._messageTypeLevels[messageType] = \
780 self._getMessageTypeLevel(config, messageType)
781
782 self._enableSounds = self._getBoolean(config, "sounds", "enable",
783 not secondaryInstallation)
784 self._pilotControlsSounds = self._getBoolean(config, "sounds",
785 "pilotControls", True)
786 self._pilotHotkey.set(self._get(config, "sounds",
787 "pilotHotkey", "C0"))
788 self._enableApproachCallouts = \
789 self._getBoolean(config, "sounds", "enableApproachCallouts", False)
790 self._speedbrakeAtTD = self._getBoolean(config, "sounds",
791 "speedbrakeAtTD", True)
792
793 self._enableChecklists = self._getBoolean(config, "sounds",
794 "enableChecklists", False)
795 self._checklistHotkey.set(self._get(config, "sounds",
796 "checklistHotkey", "CS0"))
797
798 self._autoUpdate = self._getBoolean(config, "update", "auto", True)
799 self._updateURL = self._get(config, "update", "url",
800 Config.DEFAULT_UPDATE_URL)
801
802 for aircraftType in const.aircraftTypes:
803 self._checklists[aircraftType] = \
804 Checklist.fromConfig(config, aircraftType)
805 self._approachCallouts[aircraftType] = \
806 ApproachCallouts.fromConfig(config, aircraftType)
807
808 self._defaultMSFS = self._getBoolean(config, "general",
809 "defaultMSFS", os.name=="nt")
810
811 self._xplaneRemote = self._getBoolean(config, "general",
812 "xplaneRemote", False)
813 self._xplaneAddress = self._get(config, "general",
814 "xplaneAddress", "")
815
816 self._modified = False
817
818 def save(self):
819 """Save the configuration file if it has been modified."""
820 if not self._modified:
821 return
822
823 config = configparser.RawConfigParser()
824
825 config.add_section("login")
826 config.set("login", "id", self._pilotID)
827 config.set("login", "password", self._password)
828 config.set("login", "rememberPassword",
829 "yes" if self._rememberPassword else "no")
830
831 config.add_section("general")
832 if self._language:
833 config.set("general", "language", self._language)
834 config.set("general", "hideMinimizedWindow",
835 "yes" if self._hideMinimizedWindow else "no")
836 config.set("general", "quitOnClose",
837 "yes" if self._quitOnClose else "no")
838 config.set("general", "onlineGateSystem",
839 "yes" if self._onlineGateSystem else "no")
840 config.set("general", "onlineACARS",
841 "yes" if self._onlineACARS else "no")
842 config.set("general", "flareTimeFromFS",
843 "yes" if self._flareTimeFromFS else "no")
844 config.set("general", "syncFSTime",
845 "yes" if self._syncFSTime else "no")
846 config.set("general", "usingFS2Crew",
847 "yes" if self._usingFS2Crew else "no")
848 config.set("general", "iasSmoothingLength",
849 str(self._iasSmoothingLength))
850 config.set("general", "vsSmoothingLength",
851 str(self._vsSmoothingLength))
852
853 config.add_section("simbrief")
854 config.set("simbrief", "use",
855 "yes" if self._useSimBrief else "no")
856 config.set("simbrief", "username", self._simBriefUserName)
857 config.set("simbrief", "password", self._simBriefPassword)
858 config.set("simbrief", "rememberPassword",
859 "yes" if self._rememberSimBriefPassword else "no")
860
861 if self._pirepDirectory is not None:
862 config.set("general", "pirepDirectory", self._pirepDirectory)
863 config.set("general", "pirepAutoSave",
864 "yes" if self._pirepAutoSave else "no")
865
866 config.set("general", "defaultMSFS",
867 "yes" if self._defaultMSFS else "no")
868
869 config.set("general", "xplaneRemote",
870 "yes" if self._xplaneRemote else "no")
871 config.set("general", "xplaneAddress", self._xplaneAddress)
872
873 config.add_section(Config._messageTypesSection)
874 for messageType in const.messageTypes:
875 if messageType in self._messageTypeLevels:
876 option = self._getMessageTypeLevelOptionName(messageType)
877 level = self._messageTypeLevels[messageType]
878 config.set(Config._messageTypesSection, option,
879 const.messageLevel2string(level))
880
881 config.add_section("sounds")
882 config.set("sounds", "enable",
883 "yes" if self._enableSounds else "no")
884 config.set("sounds", "pilotControls",
885 "yes" if self._pilotControlsSounds else "no")
886 config.set("sounds", "pilotHotkey", str(self._pilotHotkey))
887 config.set("sounds", "enableApproachCallouts",
888 "yes" if self._enableApproachCallouts else "no")
889 config.set("sounds", "speedbrakeAtTD",
890 "yes" if self._speedbrakeAtTD else "no")
891
892 config.set("sounds", "enableChecklists",
893 "yes" if self._enableChecklists else "no")
894 config.set("sounds", "checklistHotkey",
895 str(self._checklistHotkey))
896
897 config.add_section("update")
898 config.set("update", "auto",
899 "yes" if self._autoUpdate else "no")
900 config.set("update", "url", self._updateURL)
901
902 config.add_section(Checklist.SECTION)
903 config.add_section(ApproachCallouts.SECTION)
904 for aircraftType in const.aircraftTypes:
905 self._checklists[aircraftType].toConfig(config, aircraftType)
906 self._approachCallouts[aircraftType].toConfig(config, aircraftType)
907
908 try:
909 fd = os.open(configPath, os.O_CREAT|os.O_TRUNC|os.O_WRONLY,
910 0o600)
911 with os.fdopen(fd, "wt") as f:
912 config.write(f)
913 self._modified = False
914
915 print("Configuration saved:")
916 self.log()
917
918 except Exception as e:
919 print("Failed to update config: " + \
920 utf2unicode(str(e)), file=sys.stderr)
921
922 def _getBoolean(self, config, section, option, default):
923 """Get the given option as a boolean, if found in the given config,
924 otherwise the default."""
925 return config.getboolean(section, option) \
926 if config.has_option(section, option) \
927 else default
928
929 def _get(self, config, section, option, default):
930 """Get the given option as a string, if found in the given config,
931 otherwise the default."""
932 return config.get(section, option) \
933 if config.has_option(section, option) \
934 else default
935
936 def _getMessageTypeLevel(self, config, messageType):
937 """Get the message type level for the given message type."""
938 option = self._getMessageTypeLevelOptionName(messageType)
939 if config.has_option(Config._messageTypesSection, option):
940 value = config.get(Config._messageTypesSection, option)
941 return const.string2messageLevel(value)
942 elif secondaryInstallation:
943 return const.MESSAGELEVEL_NONE
944 elif messageType in [const.MESSAGETYPE_LOGGER_ERROR,
945 const.MESSAGETYPE_FAULT,
946 const.MESSAGETYPE_NOGO,
947 const.MESSAGETYPE_GATE_SYSTEM,
948 const.MESSAGETYPE_HELP]:
949 return const.MESSAGELEVEL_BOTH
950 else:
951 return const.MESSAGELEVEL_FS
952
953 def _getMessageTypeLevelOptionName(self, messageType):
954 """Get the option name for the given message type level."""
955 return const.messageType2string(messageType)
956
957 def setupLocale(self):
958 """Setup the locale based on the language set.
959
960 Return True if a specific language was set, False otherwise."""
961 import locale
962 if self._language:
963 print("Setting up locale for", self._language)
964 os.environ["LANGUAGE"] = self._language
965 langAndEncoding = self._language + "." + locale.getpreferredencoding()
966 os.environ["LANG"] = langAndEncoding
967 os.environ["LC_MESSAGES"] = langAndEncoding
968 os.environ["LC_COLLATE"] = langAndEncoding
969 os.environ["LC_CTYPE"] = langAndEncoding
970 os.environ["LC_MONETARY"] = langAndEncoding
971 os.environ["LC_NUMERIC"] = langAndEncoding
972 os.environ["LC_TIME"] = langAndEncoding
973 return True
974 else:
975 return False
976
977 def getLanguage(self):
978 """Get the language to be used."""
979 import locale
980 if self._language:
981 if os.name=="nt":
982 if self._language in _languageMap:
983 locale.setlocale(locale.LC_ALL, _languageMap[self._language])
984 else:
985 locale.setlocale(locale.LC_ALL, "")
986 else:
987 locale.setlocale(locale.LC_ALL, (self._language,
988 locale.getpreferredencoding()))
989 return self._language
990 else:
991 locale.setlocale(locale.LC_ALL, "")
992 return locale.getdefaultlocale()[0]
993
994 def log(self):
995 """Log the configuration by printing the values"""
996 print(" pilot ID:", self._pilotID)
997 print(" rememberPassword:", self._rememberPassword)
998
999 print(" language:", self._language)
1000
1001 print(" hideMinimizedWindow:", self._hideMinimizedWindow)
1002 print(" quitOnClose:", self._quitOnClose)
1003
1004 print(" onlineGateSystem:", self._onlineGateSystem)
1005 print(" onlineACARS:", self._onlineACARS)
1006
1007 print(" flareTimeFromFS:", self._flareTimeFromFS)
1008 print(" syncFSTime:", self._syncFSTime)
1009 print(" usingFS2Crew:", self._usingFS2Crew)
1010
1011 print(" iasSmoothingLength:", self._iasSmoothingLength)
1012 print(" vsSmoothingLength:", self._vsSmoothingLength)
1013
1014 print(" useSimBrief:", self._useSimBrief)
1015 print(" simBriefUserName:", self._simBriefUserName)
1016 print(" rememberSimBriefPassword:", self._rememberSimBriefPassword)
1017
1018 print(" pirepDirectory:", self._pirepDirectory)
1019 print(" pirepAutoSave:", self._pirepAutoSave)
1020
1021 print(" defaultMSFS:", self._defaultMSFS)
1022 print(" xplaneRemote:", self._xplaneRemote)
1023 print(" xplaneAddress:", self._xplaneAddress)
1024
1025 print(" enableSounds:", self._enableSounds)
1026
1027 print(" pilotControlsSounds:", self._pilotControlsSounds)
1028 print(" pilotHotkey:", str(self._pilotHotkey))
1029
1030 print(" enableApproachCallouts:", self._enableApproachCallouts)
1031 print(" speedbrakeAtTD:", self._speedbrakeAtTD)
1032
1033 print(" enableChecklists:", self._enableChecklists)
1034 print(" checklistHotkey:", str(self._checklistHotkey))
1035
1036 print(" autoUpdate:", self._autoUpdate)
1037 print(" updateURL:", self._updateURL)
1038
1039 print(" messageTypeLevels:")
1040 for (type, level) in self._messageTypeLevels.items():
1041 print(" %s: %s" % (const.messageType2string(type),
1042 const.messageLevel2string(level)))
1043
1044 print(" checklists:")
1045 for (type, checklist) in self._checklists.items():
1046 print(" %s:" % (const.icaoCodes[type],))
1047 for path in checklist:
1048 print(" " + path)
1049
1050 print(" approachCallouts:")
1051 for (type, approachCallouts) in self._approachCallouts.items():
1052 print(" %s:" % (const.icaoCodes[type],))
1053 for (altitude, path) in approachCallouts:
1054 print(" %d: %s" % (altitude, path))
1055
1056#-------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.