source: src/mlx/config.py@ 908:fcf3c44650f1

Last change on this file since 908:fcf3c44650f1 was 897:673cfb8bd1d6, checked in by István Váradi <ivaradi@…>, 7 years ago

RPC-based communication is used always.

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