source: src/mlx/config.py@ 1040:1c0a2408634b

python3
Last change on this file since 1040:1c0a2408634b was 1040:1c0a2408634b, checked in by István Váradi <ivaradi@…>, 2 years ago

New default update URL (re #357)

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/new"
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._updateURLUpdated = self._getBoolean(config, "update",
791 "urlUpdated", False)
792 if self._updateURLUpdated:
793 self._updateURL = self._get(config, "update", "url",
794 Config.DEFAULT_UPDATE_URL) # +
795 #("/exp" if secondaryInstallation else ""))
796 if self._updateURL.startswith("http://") and not self._updateURLUpdated:
797 self._updateURL = "https://" + self._updateURL[7:]
798 self._updateURLUpdated = True
799
800 self._useRPC = self._getBoolean(config, "general", "useRPC", True)
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._modified = False
812
813 def save(self):
814 """Save the configuration file if it has been modified."""
815 if not self._modified:
816 return
817
818 config = configparser.RawConfigParser()
819
820 config.add_section("login")
821 config.set("login", "id", self._pilotID)
822 config.set("login", "password", self._password)
823 config.set("login", "rememberPassword",
824 "yes" if self._rememberPassword else "no")
825
826 config.add_section("general")
827 if self._language:
828 config.set("general", "language", self._language)
829 config.set("general", "hideMinimizedWindow",
830 "yes" if self._hideMinimizedWindow else "no")
831 config.set("general", "quitOnClose",
832 "yes" if self._quitOnClose else "no")
833 config.set("general", "onlineGateSystem",
834 "yes" if self._onlineGateSystem else "no")
835 config.set("general", "onlineACARS",
836 "yes" if self._onlineACARS else "no")
837 config.set("general", "flareTimeFromFS",
838 "yes" if self._flareTimeFromFS else "no")
839 config.set("general", "syncFSTime",
840 "yes" if self._syncFSTime else "no")
841 config.set("general", "usingFS2Crew",
842 "yes" if self._usingFS2Crew else "no")
843 config.set("general", "iasSmoothingLength",
844 str(self._iasSmoothingLength))
845 config.set("general", "vsSmoothingLength",
846 str(self._vsSmoothingLength))
847
848 config.add_section("simbrief")
849 config.set("simbrief", "use",
850 "yes" if self._useSimBrief else "no")
851 config.set("simbrief", "username", self._simBriefUserName)
852 config.set("simbrief", "password", self._simBriefPassword)
853 config.set("simbrief", "rememberPassword",
854 "yes" if self._rememberSimBriefPassword else "no")
855
856 if self._pirepDirectory is not None:
857 config.set("general", "pirepDirectory", self._pirepDirectory)
858 config.set("general", "pirepAutoSave",
859 "yes" if self._pirepAutoSave else "no")
860
861 config.set("general", "defaultMSFS",
862 "yes" if self._defaultMSFS else "no")
863
864 config.add_section(Config._messageTypesSection)
865 for messageType in const.messageTypes:
866 if messageType in self._messageTypeLevels:
867 option = self._getMessageTypeLevelOptionName(messageType)
868 level = self._messageTypeLevels[messageType]
869 config.set(Config._messageTypesSection, option,
870 const.messageLevel2string(level))
871
872 config.add_section("sounds")
873 config.set("sounds", "enable",
874 "yes" if self._enableSounds else "no")
875 config.set("sounds", "pilotControls",
876 "yes" if self._pilotControlsSounds else "no")
877 config.set("sounds", "pilotHotkey", str(self._pilotHotkey))
878 config.set("sounds", "enableApproachCallouts",
879 "yes" if self._enableApproachCallouts else "no")
880 config.set("sounds", "speedbrakeAtTD",
881 "yes" if self._speedbrakeAtTD else "no")
882
883 config.set("sounds", "enableChecklists",
884 "yes" if self._enableChecklists else "no")
885 config.set("sounds", "checklistHotkey",
886 str(self._checklistHotkey))
887
888 config.add_section("update")
889 config.set("update", "auto",
890 "yes" if self._autoUpdate else "no")
891 config.set("update", "url", self._updateURL)
892 config.set("update", "urlUpdated", self._updateURLUpdated)
893
894 config.set("general", "useRPC",
895 "yes" if self._useRPC else "no")
896
897 config.add_section(Checklist.SECTION)
898 config.add_section(ApproachCallouts.SECTION)
899 for aircraftType in const.aircraftTypes:
900 self._checklists[aircraftType].toConfig(config, aircraftType)
901 self._approachCallouts[aircraftType].toConfig(config, aircraftType)
902
903 try:
904 fd = os.open(configPath, os.O_CREAT|os.O_TRUNC|os.O_WRONLY,
905 0o600)
906 with os.fdopen(fd, "wt") as f:
907 config.write(f)
908 self._modified = False
909
910 print("Configuration saved:")
911 self.log()
912
913 except Exception as e:
914 print("Failed to update config: " + \
915 utf2unicode(str(e)), file=sys.stderr)
916
917 def _getBoolean(self, config, section, option, default):
918 """Get the given option as a boolean, if found in the given config,
919 otherwise the default."""
920 return config.getboolean(section, option) \
921 if config.has_option(section, option) \
922 else default
923
924 def _get(self, config, section, option, default):
925 """Get the given option as a string, if found in the given config,
926 otherwise the default."""
927 return config.get(section, option) \
928 if config.has_option(section, option) \
929 else default
930
931 def _getMessageTypeLevel(self, config, messageType):
932 """Get the message type level for the given message type."""
933 option = self._getMessageTypeLevelOptionName(messageType)
934 if config.has_option(Config._messageTypesSection, option):
935 value = config.get(Config._messageTypesSection, option)
936 return const.string2messageLevel(value)
937 elif secondaryInstallation:
938 return const.MESSAGELEVEL_NONE
939 elif messageType in [const.MESSAGETYPE_LOGGER_ERROR,
940 const.MESSAGETYPE_FAULT,
941 const.MESSAGETYPE_NOGO,
942 const.MESSAGETYPE_GATE_SYSTEM,
943 const.MESSAGETYPE_HELP]:
944 return const.MESSAGELEVEL_BOTH
945 else:
946 return const.MESSAGELEVEL_FS
947
948 def _getMessageTypeLevelOptionName(self, messageType):
949 """Get the option name for the given message type level."""
950 return const.messageType2string(messageType)
951
952 def setupLocale(self):
953 """Setup the locale based on the language set.
954
955 Return True if a specific language was set, False otherwise."""
956 import locale
957 if self._language:
958 print("Setting up locale for", self._language)
959 os.environ["LANGUAGE"] = self._language
960 langAndEncoding = self._language + "." + locale.getpreferredencoding()
961 os.environ["LANG"] = langAndEncoding
962 os.environ["LC_MESSAGES"] = langAndEncoding
963 os.environ["LC_COLLATE"] = langAndEncoding
964 os.environ["LC_CTYPE"] = langAndEncoding
965 os.environ["LC_MONETARY"] = langAndEncoding
966 os.environ["LC_NUMERIC"] = langAndEncoding
967 os.environ["LC_TIME"] = langAndEncoding
968 return True
969 else:
970 return False
971
972 def getLanguage(self):
973 """Get the language to be used."""
974 import locale
975 if self._language:
976 if os.name=="nt":
977 if self._language in _languageMap:
978 locale.setlocale(locale.LC_ALL, _languageMap[self._language])
979 else:
980 locale.setlocale(locale.LC_ALL, "")
981 else:
982 locale.setlocale(locale.LC_ALL, (self._language,
983 locale.getpreferredencoding()))
984 return self._language
985 else:
986 locale.setlocale(locale.LC_ALL, "")
987 return locale.getdefaultlocale()[0]
988
989 def log(self):
990 """Log the configuration by printing the values"""
991 print(" pilot ID:", self._pilotID)
992 print(" rememberPassword:", self._rememberPassword)
993
994 print(" language:", self._language)
995
996 print(" hideMinimizedWindow:", self._hideMinimizedWindow)
997 print(" quitOnClose:", self._quitOnClose)
998
999 print(" onlineGateSystem:", self._onlineGateSystem)
1000 print(" onlineACARS:", self._onlineACARS)
1001
1002 print(" flareTimeFromFS:", self._flareTimeFromFS)
1003 print(" syncFSTime:", self._syncFSTime)
1004 print(" usingFS2Crew:", self._usingFS2Crew)
1005
1006 print(" iasSmoothingLength:", self._iasSmoothingLength)
1007 print(" vsSmoothingLength:", self._vsSmoothingLength)
1008
1009 print(" useSimBrief:", self._useSimBrief)
1010 print(" simBriefUserName:", self._simBriefUserName)
1011 print(" rememberSimBriefPassword:", self._rememberSimBriefPassword)
1012
1013 print(" pirepDirectory:", self._pirepDirectory)
1014 print(" pirepAutoSave:", self._pirepAutoSave)
1015
1016 print(" defaultMSFS:", self._defaultMSFS)
1017
1018 print(" enableSounds:", self._enableSounds)
1019
1020 print(" pilotControlsSounds:", self._pilotControlsSounds)
1021 print(" pilotHotkey:", str(self._pilotHotkey))
1022
1023 print(" enableApproachCallouts:", self._enableApproachCallouts)
1024 print(" speedbrakeAtTD:", self._speedbrakeAtTD)
1025
1026 print(" enableChecklists:", self._enableChecklists)
1027 print(" checklistHotkey:", str(self._checklistHotkey))
1028
1029 print(" autoUpdate:", self._autoUpdate)
1030 print(" updateURL:", self._updateURL)
1031 print(" updateURLUpdated:", self._updateURLUpdated)
1032 print(" useRPC:", self._useRPC)
1033
1034 print(" messageTypeLevels:")
1035 for (type, level) in self._messageTypeLevels.items():
1036 print(" %s: %s" % (const.messageType2string(type),
1037 const.messageLevel2string(level)))
1038
1039 print(" checklists:")
1040 for (type, checklist) in self._checklists.items():
1041 print(" %s:" % (const.icaoCodes[type],))
1042 for path in checklist:
1043 print(" " + path)
1044
1045 print(" approachCallouts:")
1046 for (type, approachCallouts) in self._approachCallouts.items():
1047 print(" %s:" % (const.icaoCodes[type],))
1048 for (altitude, path) in approachCallouts:
1049 print(" %d: %s" % (altitude, path))
1050
1051#-------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.