Changeset 266:b80521b41013


Ignore:
Timestamp:
07/01/12 17:13:55 (12 years ago)
Author:
István Váradi <ivaradi@…>
Branch:
default
Phase:
public
Message:

The approach callout editor works

Location:
src/mlx
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • src/mlx/config.py

    r264 r266  
    584584        """Set the approach callouts for the given aircraft type."""
    585585        if not approachCallouts==self._approachCallouts[aircraftType]:
    586             print "Updating approach callouts"
    587586            self._approachCallouts[aircraftType] = approachCallouts.clone()
    588587            self._modified = True
  • src/mlx/gui/callouts.py

    r264 r266  
    1010
    1111import os
     12import re
    1213
    1314#------------------------------------------------------------------------------
     
    1516class ApproachCalloutsEditor(gtk.Dialog):
    1617    """The dialog to edit the approach callouts."""
     18    integerRE = re.compile("[0-9]+")
     19
     20    # A list of "usual" altitudes for callouts
     21    _usualAltitudes = [10, 20, 30, 40, 50, 100, 200, 300, 400, 500, 1000,
     22                       1500, 2000, 2500, 3000, 3500, 4000, 4500, 5000]
     23
     24    @staticmethod
     25    def _getNextUsualAltitude(altitude, descending):
     26        """Get the next altitude coming after the given one in the
     27        given direction."""
     28        if descending:
     29            previous = None
     30            for alt in ApproachCalloutsEditor._usualAltitudes:
     31                if alt>=altitude: return previous
     32                previous = alt
     33        else:
     34            for alt in ApproachCalloutsEditor._usualAltitudes:
     35                if alt>altitude: return alt               
     36                   
     37        return None
     38
    1739    def __init__(self, gui):
    1840        super(ApproachCalloutsEditor, self).__init__(WINDOW_TITLE_BASE + " - " +
     
    2749        self._approachCallouts = {}
    2850        self._currentAircraftType = const.aircraftTypes[0]
     51        self._fileOpenDialog = None
    2952
    3053        contentArea = self.get_content_area()
     
    6083
    6184        contentArea.pack_start(typeBoxAlignment, False, False, 12)
    62         # Until here, but not that some texts are different
     85        # FIXME: common code until here, but note that some texts are different
    6386
    6487        fileBox = gtk.HBox()       
    65 
    66         # self._fileChooser = gtk.FileChooserWidget()
    67         # self._fileChooser.set_select_multiple(True)
    68        
    69         # filter = gtk.FileFilter()
    70         # filter.set_name(xstr("chklst_filter_audio"))
    71         # filter.add_pattern("*.wav")
    72         # filter.add_pattern("*.mp3")
    73         # self._fileChooser.add_filter(filter)
    74            
    75         # filter = gtk.FileFilter()
    76         # filter.set_name(xstr("file_filter_all"))
    77         # filter.add_pattern("*.*")
    78         # self._fileChooser.add_filter(filter)
    79 
    80         # self._fileChooser.connect("selection-changed",
    81         #                           self._fileChooserSelectionChanged)
    82 
    83         # fileBox.pack_start(self._fileChooser, True, True, 4)
    8488
    8589        controlBox = gtk.VBox()
     
    97101        addAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
    98102                                     xscale = 0.0, yscale = 0.0)
    99         addAlignment.set_padding(padding_top = 64, padding_bottom = 0,
     103        addAlignment.set_padding(padding_top = 24, padding_bottom = 0,
    100104                                 padding_left = 0, padding_right = 0)
    101105        addAlignment.add(self._addButton)
     
    110114        removeAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
    111115                                        xscale = 0.0, yscale = 0.0)
    112         removeAlignment.set_padding(padding_top = 64, padding_bottom = 0,
     116        removeAlignment.set_padding(padding_top = 24, padding_bottom = 0,
    113117                                    padding_left = 0, padding_right = 0)
    114118        removeAlignment.add(self._removeButton)
    115119        controlBox.pack_start(removeAlignment, False, False, 0)
    116120
    117         # self._moveUpButton = gtk.Button(xstr("chklst_moveUp"))
    118         # self._moveUpButton.set_use_underline(True)
    119         # self._moveUpButton.set_tooltip_text(xstr("chklst_moveUp_tooltip"))
    120         # self._moveUpButton.set_sensitive(False)
    121         # self._moveUpButton.connect("clicked", self._moveUpButtonClicked)
    122 
    123         # moveUpAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
    124         #                                 xscale = 0.0, yscale = 0.0)
    125         # moveUpAlignment.set_padding(padding_top = 16, padding_bottom = 0,
    126         #                             padding_left = 0, padding_right = 0)
    127         # moveUpAlignment.add(self._moveUpButton)
    128         # controlBox.pack_start(moveUpAlignment, False, False, 0)
    129 
    130         # self._moveDownButton = gtk.Button(xstr("chklst_moveDown"))
    131         # self._moveDownButton.set_use_underline(True)
    132         # self._moveDownButton.set_tooltip_text(xstr("chklst_moveDown_tooltip"))
    133         # self._moveDownButton.set_sensitive(False)
    134         # self._moveDownButton.connect("clicked", self._moveDownButtonClicked)
    135 
    136         # moveDownAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
    137         #                                 xscale = 0.0, yscale = 0.0)
    138         # moveDownAlignment.set_padding(padding_top = 4, padding_bottom = 0,
    139         #                             padding_left = 0, padding_right = 0)
    140         # moveDownAlignment.add(self._moveDownButton)
    141         # controlBox.pack_start(moveDownAlignment, False, False, 0)
    142 
    143121        self._fileListModel = gtk.ListStore(int, str, str)
    144122        self._fileListModel.set_sort_column_id(0, SORT_DESCENDING)
     123
     124        self._addingFile = False
     125        self._fileListModel.connect("row-inserted", self._fileAdded)
     126       
    145127        self._fileList = gtk.TreeView(model = self._fileListModel)
    146128
     
    173155        self._fileList.set_tooltip_column(2)
    174156        self._fileList.set_reorderable(False)
    175         self._fileList.set_size_request(400, -1)
     157        self._fileList.set_size_request(300, -1)
    176158        selection = self._fileList.get_selection()
    177159        selection.set_mode(SELECTION_MULTIPLE)
     
    182164        contentArea.pack_start(fileBox, True, True, 4)
    183165
    184         self.set_size_request(500, 500)
     166        self.set_size_request(400, 300)
    185167
    186168    def run(self):
     
    205187        self._displayCurrentApproachCallouts()
    206188       
    207     # def _fileChooserSelectionChanged(self, fileChooser):
    208     #     """Called when the selection of the given file chooser is changed."""
    209     #     numFiles = 0
    210     #     numSelected = 0
    211     #     for path in fileChooser.get_filenames():
    212     #         path = text2unicode(path)
    213     #         numSelected += 1
    214     #         if os.path.isfile(path): numFiles += 1
    215 
    216     #     self._addButton.set_sensitive(numFiles>0 and numFiles==numSelected)
    217 
    218189    def _addButtonClicked(self, button):
    219190        """Called when the Add button is clicked."""
    220         selection = self._fileList.get_selection()
    221         (model, paths) = selection.get_selected_rows()
    222         iters = [model.get_iter(path) for path in paths]
    223         print paths, iters
    224         self._fileListModel.append([1300, "1300.mp3", "1300.mp3"])
    225        
     191        dialog = self._getFileOpenDialog()
     192       
     193        dialog.show_all()
     194        result = dialog.run()
     195        dialog.hide()
     196
     197        if result==RESPONSETYPE_OK:
     198            filePath = dialog.get_filename()
     199            baseName = os.path.basename(filePath)
     200            altitude = self._getNewAltitude(baseName)
     201            self._addingFile = True
     202            self._fileListModel.append([altitude, baseName, filePath])
     203            self._addingFile = False
     204
     205    def _fileAdded(self, model, path, iter):
     206        """Called when a file is added to the list of callouts.
     207
     208        Makes the treeview to edit the altitude in the given row."""
     209        if self._addingFile:
     210            gobject.idle_add(self._fileList.set_cursor,
     211                             model.get_path(iter),
     212                             self._fileList.get_column(0), True)
     213            self._fileList.grab_focus()
     214            self.grab_focus()
     215                             
    226216    def _removeButtonClicked(self, button):
    227217        """Called when the Remove button is clicked."""
     
    235225                model.remove(i)
    236226       
    237     # def _moveUpButtonClicked(self, button):
    238     #     """Called when the move up button is clicked."""
    239     #     self._moveSelected(True)
    240        
    241     # def _moveDownButtonClicked(self, button):
    242     #     """Called when the move down button is clicked."""
    243     #     self._moveSelected(False)
    244 
    245     # def _moveSelected(self, up):
    246     #     """Move the selected files up or down."""
    247     #     selection = self._fileList.get_selection()
    248     #     (model, paths) = selection.get_selected_rows()
    249     #     indexes = [(path.get_indices() if pygobject else path)[0]
    250     #                for path in paths]       
    251     #     indexes.sort()
    252     #     if not up:
    253     #         indexes.reverse()
    254 
    255     #     for index in indexes:
    256     #         fromIter = model.iter_nth_child(None, index)
    257     #         toIter = model.iter_nth_child(None, index-1 if up else index + 1)
    258     #         if up:
    259     #             model.move_before(fromIter, toIter)
    260     #         else:
    261     #             model.move_after(fromIter, toIter)
    262        
    263     #     self._moveUpButton.set_sensitive(indexes[0]>1 if up else True)
    264     #     numRows = model.iter_n_children(None)
    265     #     self._moveDownButton.set_sensitive(True if up else
    266     #                                        indexes[0]<(numRows-2))
    267 
    268227    def _fileListSelectionChanged(self, selection):
    269228        """Called when the selection in the file list changes."""
     
    284243        editedIter = model.get_iter_from_string(path)
    285244        editedPath = model.get_path(editedIter)
    286         iter = model.get_iter_first()
    287         while iter is not None and newAltitude is not None:
    288             path = model.get_path(iter)
    289             if editedPath!=path and newAltitude==model[path][0]:
    290                 dialog = gtk.MessageDialog(parent = self,
    291                                            type = MESSAGETYPE_QUESTION,
    292                                            message_format =
    293                                            xstr("callouts_altitude_clash"))
    294                 dialog.format_secondary_markup(xstr("callouts_altitude_clash_sec"))
    295                 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
    296                 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
    297                 dialog.set_title(WINDOW_TITLE_BASE)
    298 
    299                 result = dialog.run()
    300                 dialog.hide()
    301 
    302                 if result==RESPONSETYPE_NO:
    303                     newAltitude = None
    304             iter = model.iter_next(iter)
     245        if self._hasAltitude(newAltitude, ignorePath = editedPath):
     246            dialog = gtk.MessageDialog(parent = self,
     247                                       type = MESSAGETYPE_QUESTION,
     248                                       message_format =
     249                                       xstr("callouts_altitude_clash"))
     250            dialog.format_secondary_markup(xstr("callouts_altitude_clash_sec"))
     251            dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
     252            dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
     253            dialog.set_title(WINDOW_TITLE_BASE)
     254
     255            result = dialog.run()
     256            dialog.hide()
     257
     258            if result==RESPONSETYPE_NO:
     259                newAltitude = None
    305260               
    306261        if newAltitude is not None:
    307             self._fileListModel[editedPath][0] = newAltitude
     262            model[editedPath][0] = newAltitude
    308263
    309264    def _saveApproachCallouts(self):
     
    335290        for (altitude, path) in approachCallouts:
    336291            self._fileListModel.append([altitude, os.path.basename(path), path])       
     292
     293    def _getFileOpenDialog(self):
     294        """Get the dialog to open a file.
     295
     296        If it does not exist yet, it will be created."""
     297        if self._fileOpenDialog is None:
     298            dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
     299                                           xstr("callouts_open_title"),
     300                                           action = FILE_CHOOSER_ACTION_OPEN,
     301                                           buttons = (gtk.STOCK_CANCEL,
     302                                                      RESPONSETYPE_CANCEL,
     303                                                      gtk.STOCK_OK, RESPONSETYPE_OK),
     304                                           parent = self)
     305            dialog.set_modal(True)           
     306            dialog.set_do_overwrite_confirmation(True)
     307     
     308            # FIXME: create the filters in one location and use them
     309            # from there
     310            filter = gtk.FileFilter()
     311            filter.set_name(xstr("file_filter_audio"))
     312            filter.add_pattern("*.wav")
     313            filter.add_pattern("*.mp3")
     314            dialog.add_filter(filter)
     315
     316            filter = gtk.FileFilter()
     317            filter.set_name(xstr("file_filter_all"))
     318            filter.add_pattern("*.*")
     319            dialog.add_filter(filter)
     320           
     321            self._fileOpenDialog = dialog
     322
     323        return self._fileOpenDialog
     324   
     325    def _getNewAltitude(self, baseName):
     326        """Get a new, unique altitude for the audio file with the given
     327        base name.
     328
     329        First the given file name is searched for suitable
     330        numbers. Otherwise the smallest altitude in the model is
     331        considered, and, depending on the actual ordering of the
     332        table, a suitable smaller or greater value is found. It is
     333        ensured that the number is unique, unless all numbers are
     334        taken.
     335       
     336        If there is no entry in the table yet, 2500 is returned if the
     337        table is sorted descending, 10 otherwise."""
     338        altitude = self._getNewAltitudeFromFileName(baseName)
     339        if altitude is not None: return altitude
     340
     341        descending = self._fileList.get_column(0).get_sort_order()==SORT_DESCENDING
     342        model = self._fileListModel
     343        numEntries = model.iter_n_children(None)
     344        if numEntries==0:
     345            return 2500 if descending else 10
     346        else:
     347            lastIter = model.iter_nth_child(None, numEntries-1)
     348            lastValue = model.get_value(lastIter, 0)
     349
     350            altitude = self._getNextValidUsualAltitude(lastValue, descending)
     351            if altitude is None:
     352                altitude = self._getNextValidUsualAltitude(lastValue,
     353                                                           not descending)
     354
     355            if altitude is None:
     356                for altitude in range(0 if descending else 4999,
     357                                      4999 if descending else 0,
     358                                      1 if descending else -1):
     359                    if not self._hasAltitude(altitude): break
     360                                   
     361            return altitude
     362
     363    def _getNewAltitudeFromFileName(self, baseName):
     364        """Get a new altitude value from the given file name.
     365
     366        The name is traversed for numbers. If a number is less than
     367        5000 and there is no such altitude yet in the table, it is
     368        checked if it is divisible by 100 or 1000, and if so, it gets
     369        a score of 2. If it is divisible by 10, the score will be 1,
     370        otherwise 0. The first highest scoring number is returned, if
     371        there are any at all, otherwise None."""
     372        candidateAltitude = None
     373        candidateScore = None
     374       
     375        (baseName, _) = os.path.splitext(baseName)
     376        numbers = ApproachCalloutsEditor.integerRE.findall(baseName)
     377        for number in numbers:
     378            value = int(number)
     379            if value<5000 and not self._hasAltitude(value):
     380                score = 2 if (value%100)==0 or (value%1000)==0 \
     381                    else 1 if (value%10)==0 else 0
     382                if candidateAltitude is None or score>candidateScore:
     383                    candidateAltitude = value
     384                    candidateScore = score
     385
     386        return candidateAltitude       
     387       
     388    def _hasAltitude(self, altitude, ignorePath = None):
     389        """Determine if the model already contains the given altitude
     390        or not.
     391       
     392        ignorePath is a path in the model to ignore."""       
     393        model = self._fileListModel
     394        iter = model.get_iter_first()
     395        while iter is not None:
     396            path = model.get_path(iter)
     397            if path!=ignorePath and altitude==model[path][0]:
     398                return True
     399            iter = model.iter_next(iter)
     400
     401        return False
     402
     403    def _getNextValidUsualAltitude(self, startValue, descending):
     404        """Get the next valid usual altitude."""
     405        value = startValue
     406        while value is not None and self._hasAltitude(value):
     407            value = \
     408                ApproachCalloutsEditor._getNextUsualAltitude(value,
     409                                                             descending)       
     410
     411        return value
    337412       
    338413#------------------------------------------------------------------------------
  • src/mlx/gui/checklist.py

    r264 r266  
    6666       
    6767        filter = gtk.FileFilter()
    68         filter.set_name(xstr("chklst_filter_audio"))
     68        filter.set_name(xstr("file_filter_audio"))
    6969        filter.add_pattern("*.wav")
    7070        filter.add_pattern("*.mp3")
  • src/mlx/i18n.py

    r264 r266  
    163163        self.add("file_filter_all", "All files")
    164164        self.add("file_filter_pireps", "PIREP files")
     165        self.add("file_filter_audio", "Audio files")
    165166
    166167        self.add("button_ok", "_OK")
     
    778779        self.add("chklst_moveDown_tooltip",
    779780                 "Move down the selected file(s) in the checklist.")
    780         self.add("chklst_filter_audio", "Audio files")
    781781        self.add("chklst_header", "Checklist files")
    782782
     
    10341034        self.add("file_filter_all", "Összes fájl")
    10351035        self.add("file_filter_pireps", "PIREP fájlok")
     1036        self.add("file_filter_audio", "Audio fájlok")
    10361037
    10371038        self.add("button_ok", "_OK")
     
    15731574        self.add("chklst_moveDown_tooltip",
    15741575                 "Az ellenőrzőlistából kijelölt fájl(ok) eggyel lejjebb mozgatása.")
    1575         self.add("chklst_filter_audio", "Audio fájlok")
    15761576        self.add("chklst_header", "Ellenőrzőlista fájljai")
    15771577
Note: See TracChangeset for help on using the changeset viewer.