source: src/mlx/gui/common.py@ 926:4cfa5825f5ab

python3
Last change on this file since 926:4cfa5825f5ab was 926:4cfa5825f5ab, checked in by István Váradi <ivaradi@…>, 5 years ago

If the AppIndicator module is not available, it is ignored properly (re #347).

File size: 23.2 KB
RevLine 
[575]1
2from mlx.common import *
[28]3
[105]4import mlx.const as _const
[175]5from mlx.i18n import xstr
[105]6
[373]7from mlx.util import secondaryInstallation
8
[28]9import os
[837]10import time
[851]11import calendar
[28]12
[300]13#-----------------------------------------------------------------------------
14
15## @package mlx.gui.common
16#
17# Common definitions and utilities for the GUI
18#
19# The main purpose of this module is to provide common definitions for things
20# that are named differently in Gtk+ 2 and 3. This way the other parts of the
[474]21# GUI have to check the version in use very rarely. The variable \c pygobject
[300]22# tells which version is being used. If it is \c True, Gtk+ 3 is used via the
23# PyGObject interface. Otherwise Gtk+ 2 is used, which is the default on
24# Windows or when the \c FORCE_PYGTK environment variable is set.
25#
[302]26# Besides this there are some common utility classes and functions.
[300]27
28#-----------------------------------------------------------------------------
29
[28]30appIndicator = False
31
[575]32if not pygobject:
[919]33 print("Using PyGTK")
[28]34 pygobject = False
35 import pygtk
[682]36 pygtk.require("2.0")
[29]37 import gtk.gdk as gdk
[28]38 import gtk
[44]39 import pango
[575]40
[28]41 try:
42 import appindicator
43 appIndicator = True
[919]44 except Exception as e:
[28]45 pass
[42]46
47 MESSAGETYPE_ERROR = gtk.MESSAGE_ERROR
[76]48 MESSAGETYPE_QUESTION = gtk.MESSAGE_QUESTION
[97]49 MESSAGETYPE_INFO = gtk.MESSAGE_INFO
[175]50
[850]51 RESPONSETYPE_NONE = gtk.RESPONSE_NONE
[124]52 RESPONSETYPE_OK = gtk.RESPONSE_OK
[76]53 RESPONSETYPE_YES = gtk.RESPONSE_YES
[124]54 RESPONSETYPE_NO = gtk.RESPONSE_NO
[123]55 RESPONSETYPE_ACCEPT = gtk.RESPONSE_ACCEPT
56 RESPONSETYPE_REJECT = gtk.RESPONSE_REJECT
[149]57 RESPONSETYPE_CANCEL = gtk.RESPONSE_CANCEL
[175]58
[93]59 ACCEL_VISIBLE = gtk.ACCEL_VISIBLE
60 CONTROL_MASK = gdk.CONTROL_MASK
[123]61 DIALOG_MODAL = gtk.DIALOG_MODAL
[127]62 WRAP_WORD = gtk.WRAP_WORD
[604]63
[132]64 JUSTIFY_CENTER = gtk.JUSTIFY_CENTER
[604]65 JUSTIFY_LEFT = gtk.JUSTIFY_LEFT
[97]66
[144]67 CONTROL_MASK = gdk.CONTROL_MASK
68 SHIFT_MASK = gdk.SHIFT_MASK
69 BUTTON1_MASK = gdk.BUTTON1_MASK
70
71 SCROLL_UP = gdk.SCROLL_UP
72 SCROLL_DOWN = gdk.SCROLL_DOWN
73
74 SPIN_USER_DEFINED = gtk.SPIN_USER_DEFINED
75
[149]76 FILE_CHOOSER_ACTION_SELECT_FOLDER = gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER
[151]77 FILE_CHOOSER_ACTION_OPEN = gtk.FILE_CHOOSER_ACTION_OPEN
78 FILE_CHOOSER_ACTION_SAVE = gtk.FILE_CHOOSER_ACTION_SAVE
[149]79
[175]80 SELECTION_MULTIPLE = gtk.SELECTION_MULTIPLE
81
[220]82 SHADOW_IN = gtk.SHADOW_IN
[433]83 SHADOW_NONE = gtk.SHADOW_NONE
[220]84
85 POLICY_AUTOMATIC = gtk.POLICY_AUTOMATIC
[604]86 POLICY_NEVER = gtk.POLICY_NEVER
87 POLICY_ALWAYS = gtk.POLICY_ALWAYS
[220]88
[362]89 WEIGHT_NORMAL = pango.WEIGHT_NORMAL
[226]90 WEIGHT_BOLD = pango.WEIGHT_BOLD
91
[246]92 WINDOW_STATE_ICONIFIED = gdk.WINDOW_STATE_ICONIFIED
93 WINDOW_STATE_WITHDRAWN = gdk.WINDOW_STATE_WITHDRAWN
94
[264]95 SORT_ASCENDING = gtk.SORT_ASCENDING
96 SORT_DESCENDING = gtk.SORT_DESCENDING
97
[277]98 EVENT_BUTTON_PRESS = gdk.BUTTON_PRESS
99
[433]100 TREE_VIEW_COLUMN_FIXED = gtk.TREE_VIEW_COLUMN_FIXED
101
[438]102 FILL = gtk.FILL
103 EXPAND = gtk.EXPAND
104
[554]105 UPDATE_IF_VALID = gtk.UPDATE_IF_VALID
106
[823]107 SELECTION_MULTIPLE = gtk.SELECTION_MULTIPLE
108
[858]109 WINDOW_POPUP = gtk.WINDOW_POPUP
110
[227]111 pixbuf_new_from_file = gdk.pixbuf_new_from_file
112
[97]113 def text2unicode(text):
114 """Convert the given text, returned by a Gtk widget, to Unicode."""
[919]115 return str(text)
[276]116
117 def text2str(text):
118 """Convert the given text, returned by xstr to a string."""
119 return str(text)
[401]120
[575]121else: # pygobject
[925]122 import gi
123 gi.require_version("Gdk", "3.0")
[29]124 from gi.repository import Gdk as gdk
[227]125 from gi.repository import GdkPixbuf as gdkPixbuf
[925]126 gi.require_version("Gtk", "3.0")
[28]127 from gi.repository import Gtk as gtk
[926]128 try:
129 gi.require_version("AppIndicator3", "0.1")
130 from gi.repository import AppIndicator3 as appindicator
131 appIndicator = True
132 except:
133 pass
[44]134 from gi.repository import Pango as pango
[575]135
[401]136
[575]137
[42]138 MESSAGETYPE_ERROR = gtk.MessageType.ERROR
[76]139 MESSAGETYPE_QUESTION = gtk.MessageType.QUESTION
[97]140 MESSAGETYPE_INFO = gtk.MessageType.INFO
[850]141 RESPONSETYPE_NONE = gtk.ResponseType.NONE
[124]142 RESPONSETYPE_OK = gtk.ResponseType.OK
[76]143 RESPONSETYPE_YES = gtk.ResponseType.YES
[124]144 RESPONSETYPE_NO = gtk.ResponseType.NO
[123]145 RESPONSETYPE_ACCEPT = gtk.ResponseType.ACCEPT
146 RESPONSETYPE_REJECT = gtk.ResponseType.REJECT
[149]147 RESPONSETYPE_CANCEL = gtk.ResponseType.CANCEL
[93]148 ACCEL_VISIBLE = gtk.AccelFlags.VISIBLE
149 CONTROL_MASK = gdk.ModifierType.CONTROL_MASK
[123]150 DIALOG_MODAL = gtk.DialogFlags.MODAL
[127]151 WRAP_WORD = gtk.WrapMode.WORD
[132]152 JUSTIFY_CENTER = gtk.Justification.CENTER
[604]153 JUSTIFY_LEFT = gtk.Justification.LEFT
[42]154
[144]155 CONTROL_MASK = gdk.ModifierType.CONTROL_MASK
156 SHIFT_MASK = gdk.ModifierType.SHIFT_MASK
157 BUTTON1_MASK = gdk.ModifierType.BUTTON1_MASK
158
159 SCROLL_UP = gdk.ScrollDirection.UP
160 SCROLL_DOWN = gdk.ScrollDirection.DOWN
161
162 SPIN_USER_DEFINED = gtk.SpinType.USER_DEFINED
163
[149]164 FILE_CHOOSER_ACTION_SELECT_FOLDER = gtk.FileChooserAction.SELECT_FOLDER
[151]165 FILE_CHOOSER_ACTION_OPEN = gtk.FileChooserAction.OPEN
166 FILE_CHOOSER_ACTION_SAVE = gtk.FileChooserAction.SAVE
[149]167
[175]168 SELECTION_MULTIPLE = gtk.SelectionMode.MULTIPLE
169
[220]170 SHADOW_IN = gtk.ShadowType.IN
[433]171 SHADOW_NONE = gtk.ShadowType.NONE
[220]172
173 POLICY_AUTOMATIC = gtk.PolicyType.AUTOMATIC
[604]174 POLICY_NEVER = gtk.PolicyType.NEVER
175 POLICY_ALWAYS = gtk.PolicyType.ALWAYS
[220]176
[362]177 WEIGHT_NORMAL = pango.Weight.NORMAL
[226]178 WEIGHT_BOLD = pango.Weight.BOLD
179
[246]180 WINDOW_STATE_ICONIFIED = gdk.WindowState.ICONIFIED
181 WINDOW_STATE_WITHDRAWN = gdk.WindowState.WITHDRAWN
182
[264]183 SORT_ASCENDING = gtk.SortType.ASCENDING
184 SORT_DESCENDING = gtk.SortType.DESCENDING
185
[277]186 EVENT_BUTTON_PRESS = gdk.EventType.BUTTON_PRESS
187
[433]188 TREE_VIEW_COLUMN_FIXED = gtk.TreeViewColumnSizing.FIXED
189
[438]190 FILL = gtk.AttachOptions.FILL
191 EXPAND = gtk.AttachOptions.EXPAND
192
[554]193 UPDATE_IF_VALID = gtk.SpinButtonUpdatePolicy.IF_VALID
194
[823]195 SELECTION_MULTIPLE = gtk.SelectionMode.MULTIPLE
196
[227]197 pixbuf_new_from_file = gdkPixbuf.Pixbuf.new_from_file
198
[97]199 import codecs
200 _utf8Decoder = codecs.getdecoder("utf-8")
[401]201
[97]202 def text2unicode(str):
203 """Convert the given text, returned by a Gtk widget, to Unicode."""
204 return _utf8Decoder(str)[0]
205
[276]206 def text2str(text):
207 """Convert the given text, returned by xstr to a string."""
208 return _utf8Decoder(text)[0]
209
[28]210import cairo
211
[32]212#------------------------------------------------------------------------------
213
214class FlightStatusHandler(object):
215 """Base class for objects that handle the flight status in some way."""
216 def __init__(self):
217 self._stage = None
218 self._rating = 100
219 self._noGoReason = None
220
221 def resetFlightStatus(self):
222 """Reset the flight status."""
223 self._stage = None
224 self._rating = 100
225 self._noGoReason = None
226 self._updateFlightStatus()
[401]227
[32]228 def setStage(self, stage):
229 """Set the stage of the flight."""
230 if stage!=self._stage:
231 self._stage = stage
232 self._updateFlightStatus()
233
234 def setRating(self, rating):
235 """Set the rating to the given value."""
236 if rating!=self._rating:
237 self._rating = rating
238 if self._noGoReason is None:
239 self._updateFlightStatus()
240
241 def setNoGo(self, reason):
242 """Set a No-Go condition with the given reason."""
243 if self._noGoReason is None:
244 self._noGoReason = reason
245 self._updateFlightStatus()
246
247#------------------------------------------------------------------------------
[84]248
249class IntegerEntry(gtk.Entry):
250 """An entry that allows only either an empty value, or an integer."""
251 def __init__(self, defaultValue = None):
252 """Construct the entry."""
253 gtk.Entry.__init__(self)
254
[86]255 self.set_alignment(1.0)
256
[84]257 self._defaultValue = defaultValue
258 self._currentInteger = defaultValue
259 self._selfSetting = False
260 self._set_text()
261
262 self.connect("changed", self._handle_changed)
263
264 def get_int(self):
265 """Get the integer."""
266 return self._currentInteger
267
[241]268 def reset(self):
269 """Reset the integer."""
270 self.set_int(None)
271
[84]272 def set_int(self, value):
273 """Set the integer."""
274 if value!=self._currentInteger:
275 self._currentInteger = value
276 self.emit("integer-changed", self._currentInteger)
277 self._set_text()
[401]278
[84]279 def _handle_changed(self, widget):
280 """Handle the changed signal."""
281 if self._selfSetting:
282 return
283 text = self.get_text()
284 if text=="":
285 self.set_int(self._defaultValue)
286 else:
287 try:
288 self.set_int(int(text))
289 except:
290 self._set_text()
291
292 def _set_text(self):
293 """Set the text value from the current integer."""
294 self._selfSetting = True
295 self.set_text("" if self._currentInteger is None
296 else str(self._currentInteger))
297 self._selfSetting = False
[401]298
[84]299#------------------------------------------------------------------------------
300
[837]301class TimeEntry(gtk.Entry):
302 """Widget to display and edit a time value in HH:MM format."""
303 def __init__(self):
304 """Construct the entry"""
[926]305 super(TimeEntry, self).__init__()
306 self.set_max_width_chars(5)
[837]307
308 self.connect("insert-text", self._insertText)
309 self.connect("delete-text", self._deleteText)
310 self.connect("focus-out-event", self._focusOutEvent)
311
312 @property
313 def hour(self):
314 """Get the hour from the current text"""
315 text = self.get_text()
316 if not text or text==":":
317 return 0
318
319 words = text.split(":")
320 if len(words)==1:
321 return 0
322 elif len(words)>=2:
323 return 0 if len(words[0])==0 else int(words[0])
324 else:
325 return 0
326
327 @property
328 def minute(self):
329 """Get the hour from the current text"""
330 text = self.get_text()
331 if not text or text==":":
332 return 0
333
334 words = text.split(":")
335 if len(words)==1:
336 return 0 if len(words[0])==0 else int(words[0])
337 elif len(words)>=2:
338 return 0 if len(words[1])==0 else int(words[1])
339 else:
340 return 0
341
[846]342 @property
343 def minutes(self):
344 """Get the time in minutes, i.e. hour*60+minute."""
345 return self.hour * 60 + self.minute
346
[837]347 def setTimestamp(self, timestamp):
348 """Set the hour and minute from the given timestamp in UTC."""
349 tm = time.gmtime(timestamp)
350 self.set_text("%02d:%02d" % (tm.tm_hour, tm.tm_min))
351
[851]352 def getTimestampFrom(self, timestamp):
353 """Get the timestamp by replacing the hour and minute from the given
354 timestamp with what is set in this widget."""
355 tm = time.gmtime(timestamp)
356 ts = calendar.timegm((tm.tm_year, tm.tm_mon, tm.tm_mday,
357 self.hour, self.minute, 0,
358 tm.tm_wday, tm.tm_yday, tm.tm_isdst))
359
360 if ts > (timestamp + (16*60*60)):
361 ts -= 24*60*60
362 elif (ts + 16*60*60) < timestamp:
363 ts += 24*60*60
364
365 return ts
366
[837]367 def _focusOutEvent(self, widget, event):
368 """Reformat the text to match pattern HH:MM"""
369 text = "%02d:%02d" % (self.hour, self.minute)
370 if text!=self.get_text():
371 self.set_text(text)
372
373 def _insertText(self, entry, text, length, position):
374 """Called when some text is inserted into the entry."""
375 text=text[:length]
376 currentText = self.get_text()
377 position = self.get_position()
378 newText = currentText[:position] + text + currentText[position:]
379 self._checkText(newText, "insert-text")
380
381 def _deleteText(self, entry, start, end):
382 """Called when some text is erased from the entry."""
383 currentText = self.get_text()
384 newText = currentText[:start] + currentText[end:]
385 self._checkText(newText, "delete-text")
386
387 def _checkText(self, newText, signal):
388 """Check the given text.
389
390 If it is not suitable, stop the emission of the signal to prevent the
391 change from appearing."""
392 if not newText or newText==":":
393 return
394
395 words = newText.split(":")
396 if (len(words)==1 and
397 len(words[0])<=2 and (len(words[0])==0 or
398 (words[0].isdigit() and int(words[0])<60))) or \
399 (len(words)==2 and
400 len(words[0])<=2 and (len(words[0])==0 or
401 (words[0].isdigit() and int(words[0])<24)) and
402 len(words[1])<=2 and (len(words[1])==0 or
403 (words[1].isdigit() and int(words[1])<60))):
404 pass
405 else:
406 gtk.gdk.display_get_default().beep()
407 self.stop_emission(signal)
408
409#------------------------------------------------------------------------------
410
[741]411class CredentialsDialog(gtk.Dialog):
412 """A dialog window to ask for a user name and a password."""
413 def __init__(self, gui, userName, password,
414 titleLabel, cancelButtonLabel, okButtonLabel,
415 userNameLabel, userNameTooltip,
416 passwordLabel, passwordTooltip,
417 infoText = None,
418 rememberPassword = None,
419 rememberLabel = None, rememberTooltip = None):
420 """Construct the dialog."""
421 super(CredentialsDialog, self).__init__(WINDOW_TITLE_BASE + " - " +
422 titleLabel,
423 gui.mainWindow,
424 DIALOG_MODAL)
425 self.add_button(cancelButtonLabel, RESPONSETYPE_CANCEL)
426 self.add_button(okButtonLabel, RESPONSETYPE_OK)
427
428 contentArea = self.get_content_area()
429
430 contentAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
431 xscale = 0.0, yscale = 0.0)
432 contentAlignment.set_padding(padding_top = 4, padding_bottom = 16,
433 padding_left = 8, padding_right = 8)
434
435 contentArea.pack_start(contentAlignment, False, False, 0)
436
437 contentVBox = gtk.VBox()
438 contentAlignment.add(contentVBox)
439
440 if infoText is not None:
441 label = gtk.Label(infoText)
442 label.set_alignment(0.0, 0.0)
443
444 contentVBox.pack_start(label, False, False, 0)
445
446 tableAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
447 xscale = 0.0, yscale = 0.0)
448 tableAlignment.set_padding(padding_top = 24, padding_bottom = 0,
449 padding_left = 0, padding_right = 0)
450
451 table = gtk.Table(3, 2)
452 table.set_row_spacings(4)
453 table.set_col_spacings(16)
454 table.set_homogeneous(False)
455
456 tableAlignment.add(table)
457 contentVBox.pack_start(tableAlignment, True, True, 0)
458
459 label = gtk.Label(userNameLabel)
460 label.set_use_underline(True)
461 label.set_alignment(0.0, 0.5)
462 table.attach(label, 0, 1, 0, 1)
463
464 self._userName = gtk.Entry()
465 self._userName.set_width_chars(16)
466 # FIXME: enabled the OK button only when there is something in thr
467 # user name and password fields
468 #self._userName.connect("changed",
469 # lambda button: self._updateForwardButton())
470 self._userName.set_tooltip_text(userNameTooltip)
471 self._userName.set_text(userName)
472 table.attach(self._userName, 1, 2, 0, 1)
473 label.set_mnemonic_widget(self._userName)
474
475 label = gtk.Label(passwordLabel)
476 label.set_use_underline(True)
477 label.set_alignment(0.0, 0.5)
478 table.attach(label, 0, 1, 1, 2)
479
480 self._password = gtk.Entry()
481 self._password.set_visibility(False)
482 #self._password.connect("changed",
483 # lambda button: self._updateForwardButton())
484 self._password.set_tooltip_text(passwordTooltip)
485 self._password.set_text(password)
486 table.attach(self._password, 1, 2, 1, 2)
487 label.set_mnemonic_widget(self._password)
488
489 if rememberPassword is not None:
490 self._rememberButton = gtk.CheckButton(rememberLabel)
491 self._rememberButton.set_use_underline(True)
492 self._rememberButton.set_tooltip_text(rememberTooltip)
493 self._rememberButton.set_active(rememberPassword)
494 table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
495 else:
496 self._rememberButton = None
497
498 @property
499 def userName(self):
500 """Get the user name entered."""
501 return self._userName.get_text()
502
503 @property
504 def password(self):
505 """Get the password entered."""
506 return self._password.get_text()
507
508 @property
509 def rememberPassword(self):
510 """Get whether the password is to be remembered."""
511 return None if self._rememberButton is None \
512 else self._rememberButton.get_active()
513
514 def run(self):
515 """Run the dialog."""
516 self.show_all()
517
518 response = super(CredentialsDialog, self).run()
519
520 self.hide()
521
522 return response
523
524#------------------------------------------------------------------------------
525
[84]526gobject.signal_new("integer-changed", IntegerEntry, gobject.SIGNAL_RUN_FIRST,
527 None, (object,))
528
529#------------------------------------------------------------------------------
[105]530
[227]531PROGRAM_NAME = "MAVA Logger X"
532
[732]533WINDOW_TITLE_BASE = PROGRAM_NAME + " " + _const.VERSION
[373]534if secondaryInstallation:
535 WINDOW_TITLE_BASE += " (" + xstr("secondary") + ")"
[105]536
537#------------------------------------------------------------------------------
[175]538
539# A mapping of aircraft types to their screen names
[191]540aircraftNames = { _const.AIRCRAFT_B736 : xstr("aircraft_b736"),
541 _const.AIRCRAFT_B737 : xstr("aircraft_b737"),
542 _const.AIRCRAFT_B738 : xstr("aircraft_b738"),
543 _const.AIRCRAFT_B738C : xstr("aircraft_b738c"),
[790]544 _const.AIRCRAFT_B732 : xstr("aircraft_b732"),
[191]545 _const.AIRCRAFT_B733 : xstr("aircraft_b733"),
546 _const.AIRCRAFT_B734 : xstr("aircraft_b734"),
547 _const.AIRCRAFT_B735 : xstr("aircraft_b735"),
548 _const.AIRCRAFT_DH8D : xstr("aircraft_dh8d"),
549 _const.AIRCRAFT_B762 : xstr("aircraft_b762"),
550 _const.AIRCRAFT_B763 : xstr("aircraft_b763"),
551 _const.AIRCRAFT_CRJ2 : xstr("aircraft_crj2"),
552 _const.AIRCRAFT_F70 : xstr("aircraft_f70"),
553 _const.AIRCRAFT_DC3 : xstr("aircraft_dc3"),
554 _const.AIRCRAFT_T134 : xstr("aircraft_t134"),
555 _const.AIRCRAFT_T154 : xstr("aircraft_t154"),
[443]556 _const.AIRCRAFT_YK40 : xstr("aircraft_yk40"),
557 _const.AIRCRAFT_B462 : xstr("aircraft_b462") }
[175]558
559#------------------------------------------------------------------------------
[221]560
[858]561aircraftFamilyNames = {
562 _const.AIRCRAFT_FAMILY_B737NG: xstr("aircraft_family_b737ng"),
563
564 _const.AIRCRAFT_FAMILY_B737CL: xstr("aircraft_family_b737cl"),
565
566 _const.AIRCRAFT_FAMILY_DH8D: xstr("aircraft_family_dh8d"),
567
568 _const.AIRCRAFT_FAMILY_B767: xstr("aircraft_family_b767"),
569
570 _const.AIRCRAFT_FAMILY_CRJ2: xstr("aircraft_family_crj2"),
571
572 _const.AIRCRAFT_FAMILY_F70: xstr("aircraft_family_f70"),
573
574 _const.AIRCRAFT_FAMILY_DC3: xstr("aircraft_family_dc3"),
575
576 _const.AIRCRAFT_FAMILY_T134: xstr("aircraft_family_t134"),
577
578 _const.AIRCRAFT_FAMILY_T154: xstr("aircraft_family_t154"),
579
580 _const.AIRCRAFT_FAMILY_YK40: xstr("aircraft_family_yk40"),
581
582 _const.AIRCRAFT_FAMILY_B462: xstr("aircraft_family_b462")
583}
584
585#------------------------------------------------------------------------------
586
[226]587def formatFlightLogLine(timeStr, line):
[221]588 """Format the given flight log line."""
589 """Format the given line for flight logging."""
590 if timeStr is not None:
591 line = timeStr + ": " + line
592 return line + "\n"
593
594#------------------------------------------------------------------------------
[226]595
596def addFaultTag(buffer):
597 """Add a tag named 'fault' to the given buffer."""
[362]598 buffer.create_tag("fault", foreground="red", weight=WEIGHT_BOLD)
[226]599
600#------------------------------------------------------------------------------
601
602def appendTextBuffer(buffer, text, isFault = False):
603 """Append the given line at the end of the given text buffer.
604
605 If isFault is set, use the tag named 'fault'."""
[345]606 insertTextBuffer(buffer, buffer.get_end_iter(), text, isFault)
[226]607
608#------------------------------------------------------------------------------
[345]609
610def insertTextBuffer(buffer, iter, text, isFault = False):
611 """Insert the given line into the given text buffer at the given iterator.
612
[362]613 If isFault is set, use the tag named 'fault' else use the tag named
614 'normal'."""
615 line = iter.get_line()
616
617 buffer.insert(iter, text)
618
619 iter0 = buffer.get_iter_at_line(line)
620 iter1 = buffer.get_iter_at_line(line+1)
[345]621 if isFault:
[362]622 buffer.apply_tag_by_name("fault", iter0, iter1)
[345]623 else:
[362]624 buffer.remove_all_tags(iter0, iter1)
[345]625
626#------------------------------------------------------------------------------
[826]627
628def askYesNo(question, parent = None, title = WINDOW_TITLE_BASE):
629 """Ask a Yes/No question.
630
631 Return a boolean indicating the answer."""
632 dialog = gtk.MessageDialog(parent = parent,
633 type = MESSAGETYPE_QUESTION,
634 message_format = question)
635
636 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
637 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
638
639 dialog.set_title(title)
640 result = dialog.run()
641 dialog.hide()
642
643 return result==RESPONSETYPE_YES
644
645#------------------------------------------------------------------------------
[828]646
[863]647def errorDialog(message, parent = None, secondary = None,
[828]648 title = WINDOW_TITLE_BASE):
649 """Display an error dialog box with the given message."""
650 dialog = gtk.MessageDialog(parent = parent,
651 type = MESSAGETYPE_ERROR,
652 message_format = message)
653 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
654 dialog.set_title(title)
655 if secondary is not None:
656 dialog.format_secondary_markup(secondary)
657
658 dialog.run()
659 dialog.hide()
660
661#------------------------------------------------------------------------------
662
663def communicationErrorDialog(parent = None, title = WINDOW_TITLE_BASE):
664 """Display a communication error dialog."""
[863]665 errorDialog(xstr("error_communication"), parent = parent,
[828]666 secondary = xstr("error_communication_secondary"),
667 title = title)
668
669#------------------------------------------------------------------------------
[836]670
671def createFlightTypeComboBox():
672 flightTypeModel = gtk.ListStore(str, int)
673 for type in _const.flightTypes:
674 name = "flighttype_" + _const.flightType2string(type)
675 flightTypeModel.append([xstr(name), type])
676
677 flightType = gtk.ComboBox(model = flightTypeModel)
678 renderer = gtk.CellRendererText()
679 flightType.pack_start(renderer, True)
680 flightType.add_attribute(renderer, "text", 0)
681
682 return flightType
683
684#------------------------------------------------------------------------------
[852]685
686def getTextViewText(textView):
687 """Get the text from the given text view."""
688 buffer = textView.get_buffer()
689 return buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True)
690
691#------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.