source: src/mlx/gui/common.py@ 954:ad190b3a88c7

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

Removed text2unicode (re #347).

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