project.py

Go to the documentation of this file.
00001 # Copyright 2005, 2006  Timo Savola
00002 #
00003 # This program is free software; you can redistribute it and/or modify
00004 # it under the terms of the GNU Lesser General Public License as published
00005 # by the Free Software Foundation; either version 2.1 of the License, or
00006 # (at your option) any later version.
00007 
00008 import os
00009 import stat
00010 import re
00011 import glob
00012 import shutil
00013 import gobject
00014 import gtk
00015 import gnomevfs
00016 from gettext import gettext
00017 
00018 import environment
00019 import view
00020 import config
00021 import execution
00022 import extension
00023 
00024 if 'frozenset' not in __builtins__:
00025         import sets
00026         frozenset = sets.ImmutableSet
00027 
00028 # os.path.expanduser() is not used to implement tilde_to_home() because
00029 # it must be symmetic with home_to_tilde() and expanduser() also translates
00030 # ~user which home_to_tilde() does not
00031 
00032 bloated = os.environ['HOME'] + os.sep
00033 compact = '~' + os.sep
00034 
00035 def tilde_to_home(path):
00036         if path.startswith(compact):
00037                 return path.replace(compact, bloated, 1)
00038         else:
00039                 return path
00040 
00041 def home_to_tilde(path):
00042         if path.startswith(bloated):
00043                 return path.replace(bloated, compact, 1)
00044         else:
00045                 return path
00046 
00047 def copy_hierarchy(old, new):
00048         if os.path.isdir(old):
00049                 shutil.copytree(old, new)
00050         else:
00051                 shutil.copy(old, new)
00052 
00053 def construct_components(store, iter):
00054         components = []
00055 
00056         while iter:
00057                 file = store.get_value(iter, 0)
00058                 components.insert(0, file)
00059 
00060                 iter = store.iter_parent(iter)
00061 
00062         return components
00063 
00064 def construct_path(store, iter):
00065         components = construct_components(store, iter)
00066 
00067         path = ''
00068         for c in components:
00069                 path = os.path.join(path, c)
00070 
00071         return tilde_to_home(path)
00072 
00073 def construct_project_name(store, iter):
00074         components = construct_components(store, iter)
00075 
00076         project = components[0]
00077 
00078         name = ''
00079         for c in components[1:]:
00080                 name = os.path.join(name, c)
00081 
00082         return tilde_to_home(project), name
00083 
00084 class TreeData (gobject.GObject):
00085         def __init__(self):
00086                 gobject.GObject.__init__(self)
00087 
00088                 self.monitor = None
00089                 self.options = None
00090                 self.controller = None
00091 
00092         def cleanup(self):
00093                 if self.monitor is not None:
00094                         gnomevfs.monitor_cancel(self.monitor)
00095                         self.monitor = None
00096 
00097                 if self.options:
00098                         self.options.destroy()
00099                         self.options = None
00100 
00101                 if self.controller:
00102                         self.controller.destroy()
00103                         self.controller = None
00104 
00105 gobject.type_register(TreeData)
00106 
00107 PLACEHOLDER = ('', False, None)
00108 
00109 datadir = None
00110 
00111 def init_extension(d):
00112         global datadir
00113         datadir = d
00114 
00115 class View (view.AbstractView):
00116         def __init__(self):
00117                 view.AbstractView.__init__(self)
00118 
00119                 self.last_run_batch = None
00120 
00121                 # Glade
00122 
00123                 path = os.path.join(datadir, 'encode', 'project.glade')
00124                 self.glade = gtk.glade.XML(path)
00125                 self.glade.signal_autoconnect(self)
00126 
00127                 # UI
00128 
00129                 group = gtk.ActionGroup('encode-project')
00130 
00131                 action = gtk.Action('project', gettext('Project'), None, None)
00132                 group.add_action(action)
00133 
00134                 action = gtk.Action('add-tree', gettext('_Add directories'), None, gtk.STOCK_ADD)
00135                 action.connect_after('activate', self.on_tree_add_activate)
00136                 group.add_action(action)
00137 
00138                 action = gtk.Action('build-selected', gettext('_Build selected'), None, gtk.STOCK_EXECUTE)
00139                 action.connect_after('activate', self.on_tree_build_activate)
00140                 group.add_action_with_accel(action, 'F7')
00141 
00142                 action = gtk.Action('clean-selected', gettext('_Clean selected'), None, gtk.STOCK_CLEAR)
00143                 action.connect_after('activate', self.on_tree_clean_activate)
00144                 group.add_action_with_accel(action, 'F6')
00145 
00146                 action = gtk.Action('run-last', gettext('_Run last'), None, gtk.STOCK_MEDIA_PLAY)
00147                 action.connect_after('activate', self.on_last_run_activate)
00148                 group.add_action_with_accel(action, 'F8')
00149 
00150                 action = gtk.Action('build-tree', gettext('_Build'), None, gtk.STOCK_EXECUTE)
00151                 action.connect_after('activate', self.on_tree_build_activate)
00152                 group.add_action(action)
00153 
00154                 action = gtk.Action('clean-tree', gettext('_Clean'), None, gtk.STOCK_CLEAR)
00155                 action.connect_after('activate', self.on_tree_clean_activate)
00156                 group.add_action(action)
00157 
00158                 action = gtk.Action('remove-tree', gettext('_Remove from Project'), None, gtk.STOCK_REMOVE)
00159                 action.connect_after('activate', self.on_tree_remove_activate)
00160                 group.add_action(action)
00161 
00162                 action = gtk.Action('build-dir', gettext('_Build'), None, gtk.STOCK_EXECUTE)
00163                 action.connect_after('activate', self.on_dir_build_activate)
00164                 group.add_action(action)
00165 
00166                 action = gtk.Action('clean-dir', gettext('_Clean'), None, gtk.STOCK_CLEAR)
00167                 action.connect_after('activate', self.on_dir_clean_activate)
00168                 group.add_action(action)
00169 
00170                 action = gtk.Action('control-dir', gettext('Control'), None, gtk.STOCK_ADD)
00171                 action.connect_after('activate', self.on_control_activate)
00172                 group.add_action(action)
00173 
00174                 action = gtk.Action('control-recursive', gettext('Control All'), None, gtk.STOCK_ADD)
00175                 action.connect_after('activate', self.on_control_recursive_activate)
00176                 group.add_action(action)
00177 
00178                 action = gtk.Action('release-dir', gettext('Release All'), None, gtk.STOCK_REMOVE)
00179                 action.connect_after('activate', self.on_release_activate)
00180                 group.add_action(action)
00181 
00182                 action = gtk.Action('run-file', gettext('_Run'), None, gtk.STOCK_MEDIA_PLAY)
00183                 action.connect_after('activate', self.on_file_run_activate)
00184                 group.add_action(action)
00185 
00186                 action = gtk.Action('compare-file', gettext('_Compare with Latest'), None, gtk.STOCK_DIALOG_INFO)
00187                 action.connect_after('activate', self.on_compare_activate)
00188                 group.add_action(action)
00189 
00190                 action = gtk.Action('control-file', gettext('Control'), None, gtk.STOCK_ADD)
00191                 action.connect_after('activate', self.on_control_activate)
00192                 group.add_action(action)
00193 
00194                 action = gtk.Action('release-file', gettext('Release'), None, gtk.STOCK_REMOVE)
00195                 action.connect_after('activate', self.on_release_activate)
00196                 group.add_action(action)
00197 
00198                 path = os.path.join(datadir, 'encode', 'project.ui')
00199                 self.ui = environment.add_ui(group, path, self.get_widget())
00200 
00201                 # Actions
00202 
00203                 item = self.ui.get_widget("/menubar/project/run-last")
00204                 item.set_sensitive(False)
00205 
00206                 # Popup hooks
00207 
00208                 menu = self.ui.get_widget('/dir-popup')
00209                 menu.connect('show', self.on_dir_popup_show)
00210 
00211                 menu = self.ui.get_widget('/file-popup')
00212                 menu.connect('show', self.on_file_popup_show)
00213 
00214                 # Handler
00215 
00216                 handlers_by_label = []
00217 
00218                 for handler in extension.get_singleton_children(environment.HandlerExtension):
00219                         action = handler.get_action()
00220                         label = action.get_property('label')
00221                         handlers_by_label.append((label, handler))
00222 
00223                 handlers_by_label.sort()
00224 
00225                 menu = self.ui.get_widget('/file-popup')
00226                 self.handlers = []
00227 
00228                 for label, handler in handlers_by_label:
00229                         action = handler.get_action()
00230                         action.connect_after('activate', self.on_handler_action_activate, handler)
00231 
00232                         item = action.create_menu_item()
00233                         menu.append(item)
00234 
00235                         self.handlers.append((handler, item))
00236 
00237                 # Tree
00238 
00239                 treeview = self.glade.get_widget('treeview')
00240 
00241                 treestore = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_BOOLEAN, gobject.TYPE_OBJECT)
00242                 treeview.set_model(treestore)
00243 
00244                 render = gtk.CellRendererText()
00245                 render.set_property('ypad', 0)
00246                 render.set_property('background-gdk', treeview.style.bg[gtk.STATE_PRELIGHT])
00247                 column = gtk.TreeViewColumn(None, render)
00248                 column.add_attribute(render, 'text', 0)
00249                 column.add_attribute(render, 'background-set', 1)
00250                 treeview.append_column(column)
00251 
00252                 # Drag-and-drop
00253 
00254                 targets = (
00255                         ('_ENCODE_PROJECT_MOVE', gtk.TARGET_SAME_WIDGET, 0),
00256                         ('text/uri-list', 0, 10),
00257                         ('_NETSCAPE_URL', 0, 11),
00258                 )
00259                 treeview.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, targets, gtk.gdk.ACTION_COPY)
00260                 treeview.enable_model_drag_dest(targets, gtk.gdk.ACTION_COPY)
00261 
00262                 treeview.connect('drag-data-get', self.on_treeview_get)
00263                 treeview.connect('drag-data-received', self.on_treeview_received)
00264 
00265                 # Configuration
00266 
00267                 self.conf = config.Directory('encode/project')
00268 
00269                 self.conf.add_string('font', lambda value: render.set_property('font', value))
00270                 self.conf.add_strings('contents', self._set_contents)
00271 
00272                 hidden = self.conf.get_string('hidden', '')
00273                 self.hidden = re.compile(hidden)
00274 
00275         # Configuration
00276 
00277         def _store_contents(self, paths):
00278                 self.conf.set_strings('contents', paths)
00279 
00280         # Content
00281 
00282         def _set_contents(self, paths):
00283                 store = self._get_store()
00284 
00285                 root_iter = store.get_iter_root()
00286                 while root_iter:
00287                         root_name = store.get_value(root_iter, 0)
00288 
00289                         prev_iter = root_iter
00290                         root_iter = store.iter_next(root_iter)
00291 
00292                         if root_name not in paths:
00293                                 store.remove(prev_iter)
00294 
00295                 for path in paths:
00296                         name = home_to_tilde(path)
00297 
00298                         exists = False
00299 
00300                         root_iter = store.get_iter_root()
00301                         while root_iter:
00302                                 root_name = store.get_value(root_iter, 0)
00303 
00304                                 if root_name == name:
00305                                         exists = True
00306                                         break
00307 
00308                                 if root_name > name:
00309                                         break
00310 
00311                                 root_iter = store.iter_next(root_iter)
00312 
00313                         if not exists:
00314                                 iter = store.insert_before(None, root_iter, (name, True, None))
00315                                 store.append(iter, PLACEHOLDER)
00316 
00317         def _get_contents(self):
00318                 paths = []
00319 
00320                 store = self._get_store()
00321 
00322                 iter = store.get_iter_root()
00323                 while iter:
00324                         name = store.get_value(iter, 0)
00325                         paths.append(name)
00326 
00327                         iter = store.iter_next(iter)
00328 
00329                 return paths
00330 
00331         def _open_directory(self, store, iter):
00332                 data = store.get_value(iter, 2)
00333                 if not data:
00334                         data = TreeData()
00335                         store.set_value(iter, 2, data)
00336 
00337                         path = construct_path(store, iter)
00338 
00339                         treepath = store.get_path(iter)
00340                         ref = gtk.TreeRowReference(store, treepath)
00341 
00342                         uri = gnomevfs.get_uri_from_local_path(path)
00343                         data.monitor = gnomevfs.monitor_add(uri, gnomevfs.MONITOR_DIRECTORY, self.on_directory_changed, ref)
00344 
00345                         for file in os.listdir(path):
00346                                 self._update_directory(path, ref, file, None)
00347 
00348                         if not store.iter_parent(iter):
00349                                 data.options = self._create_options(path)
00350                                 data.controller = self._create_controller(path, data.options)
00351 
00352         def _close_directory(self, store, dir_iter):
00353                 if dir_iter:
00354                         data = store.get_value(dir_iter, 2)
00355                         if data:
00356                                 data.cleanup()
00357                                 store.set_value(dir_iter, 2, None)
00358 
00359                 iter = store.iter_children(dir_iter)
00360                 while iter:
00361                         is_dir = store.get_value(iter, 1)
00362                         if is_dir:
00363                                 self._close_directory(store, iter)
00364 
00365                         next = store.iter_next(iter)
00366                         store.remove(iter)
00367                         iter = next
00368 
00369         # Tree utilities
00370 
00371         def _get_selection(self):
00372                 store, iter, root_iter = self._get_selection_iters()
00373                 root_path = construct_path(store, root_iter)
00374                 root_path = tilde_to_home(root_path)
00375 
00376                 return store, iter, root_path
00377 
00378         def _get_selection_iters(self):
00379                 treeview = self.glade.get_widget('treeview')
00380                 treepath, column = treeview.get_cursor()
00381 
00382                 store = treeview.get_model()
00383                 iter = store.get_iter(treepath)
00384                 root_iter = store.get_iter(treepath[:1])
00385 
00386                 return store, iter, root_iter
00387 
00388         def _get_store(self):
00389                 treeview = self.glade.get_widget('treeview')
00390                 return treeview.get_model()
00391 
00392         def _get_selected(self):
00393                 treeview = self.glade.get_widget('treeview')
00394                 store, iter = treeview.get_selection().get_selected()
00395 
00396                 path = construct_path(store, iter)
00397 
00398                 treepath = store.get_path(iter)
00399                 root_treepath = treepath[:1]
00400                 root_iter = store.get_iter(root_treepath)
00401 
00402                 root_path = construct_path(store, root_iter)
00403 
00404                 return root_path, path
00405 
00406         # Resources
00407 
00408         def _create_options(self, root):
00409                 pair = self._find_project_extension(root, OptionsExtension, 'options_rules')
00410                 if pair:
00411                         eclass, match = pair
00412                 else:
00413                         classname = self.conf.get_string('default_options')
00414                         eclass = extension.get_class(classname)
00415                         match = None
00416 
00417                 return eclass(root, match)
00418 
00419         def _create_controller(self, root, options):
00420                 classname = options.get_controller_name()
00421                 if classname:
00422                         eclass = extension.get_class(classname)
00423                 else:
00424                         pair = self._find_project_extension(root, ControllerExtension)
00425                         if pair:
00426                                 eclass = pair[0]
00427                         else:
00428                                 classname = self.conf.get_string('default_controller')
00429                                 eclass = extension.get_class(classname)
00430 
00431                 return eclass(root)
00432 
00433         def _find_project_extension(self, root, etype, rulekey=None):
00434                 dirname = os.path.basename(root)
00435 
00436                 def check(rule):
00437                         name = rule.replace('$(DIRNAME)', dirname)
00438                         pattern = os.path.join(root, name)
00439                         paths = glob.glob(pattern)
00440                         if paths:
00441                                 return paths[0]
00442                         else:
00443                                 return None
00444 
00445                 if rulekey:
00446                         entries = self.conf.get_strings(rulekey)
00447                         if entries:
00448                                 for entry in entries:
00449                                         classname, rules = entry.split('=')
00450                                         for rule in rules.split(':'):
00451                                                 path = check(rule)
00452                                                 if path:
00453                                                         eclass = extension.get_class(classname)
00454                                                         return eclass, path
00455 
00456                 for eclass in extension.get_children(etype):
00457                         if eclass.rules:
00458                                 for rule in eclass.rules:
00459                                         path = check(rule)
00460                                         if path:
00461                                                 return eclass, path
00462 
00463                 return None
00464 
00465         def _get_options(self, store, iter):
00466                 treepath = store.get_path(iter)
00467                 data = self._get_tree_data(store, treepath)
00468                 if data:
00469                         return data.options
00470                 else:
00471                         return None
00472 
00473         def _get_controller(self, store, treepath):
00474                 data = self._get_tree_data(store, treepath)
00475                 if data:
00476                         return data.controller
00477                 else:
00478                         return None
00479 
00480         def _get_tree_data(self, store, treepath):
00481                 root_treepath = treepath[:1]
00482                 root_iter = store.get_iter(root_treepath)
00483                 return store.get_value(root_iter, 2)
00484 
00485         # Filesystem
00486 
00487         def on_directory_changed(self, dir_uri, file_uri, event, dir_ref):
00488                 dir_path = gnomevfs.get_local_path_from_uri(dir_uri)
00489 
00490                 file_path = gnomevfs.get_local_path_from_uri(file_uri)
00491                 file_name = os.path.basename(file_path)
00492 
00493                 self._update_directory(dir_path, dir_ref, file_name, event)
00494 
00495         def _update_directory(self, dir_path, dir_ref, file, event):
00496                 if self.hidden.match(file) != None:
00497                         return
00498 
00499                 store = self._get_store()
00500 
00501                 dir_treepath = dir_ref.get_path()
00502                 dir_iter = store.get_iter(dir_treepath)
00503 
00504                 if event is None or event == gnomevfs.MONITOR_EVENT_CREATED:
00505                         path = os.path.join(dir_path, file)
00506                         if not os.path.exists(path):
00507                                 return
00508 
00509                         is_dir = os.path.isdir(path)
00510 
00511                         sib_iter = store.iter_children(dir_iter)
00512                         while sib_iter:
00513                                 sib_file = store.get_value(sib_iter, 0)
00514 
00515                                 if sib_file == file:
00516                                         sib_is_dir = store.get_value(sib_iter, 1)
00517                                         if sib_is_dir != is_dir:
00518                                                 store.set_value(sib_iter, 1, is_dir)
00519                                                 if is_dir:
00520                                                         store.append(sib_iter, PLACEHOLDER)
00521                                                 else:
00522                                                         self._close_directory(store, sib_iter)
00523                                         return
00524 
00525                                 if sib_file > file:
00526                                         break
00527 
00528                                 sib_iter = store.iter_next(sib_iter)
00529 
00530                         iter = store.insert_before(dir_iter, sib_iter, (file, is_dir, None))
00531                         if is_dir:
00532                                 store.append(iter, PLACEHOLDER)
00533 
00534                         child_count = store.iter_n_children(dir_iter)
00535                         if child_count == 2:
00536                                 iter = store.iter_children(dir_iter)
00537                                 tmp_name = store.get_value(iter, 0)
00538                                 if tmp_name == PLACEHOLDER[0]:
00539                                         store.remove(iter)
00540 
00541                 elif event == gnomevfs.MONITOR_EVENT_DELETED:
00542                         iter = store.iter_children(dir_iter)
00543                         while iter:
00544                                 row_file = store.get_value(iter, 0)
00545                                 if row_file == file:
00546                                         break
00547                                 iter = store.iter_next(iter)
00548 
00549                         if iter:
00550                                 child_count = store.iter_n_children(dir_iter)
00551                                 if child_count == 1:
00552                                         store.prepend(dir_iter, PLACEHOLDER)
00553 
00554                                 is_dir = store.get_value(iter, 1)
00555                                 if is_dir:
00556                                         self._close_directory(store, iter)
00557 
00558                                 store.remove(iter)
00559 
00560         # Add/Remove
00561 
00562         def on_tree_add_activate(self, item):
00563                 dialog = self.glade.get_widget('tree_add_dialog')
00564                 dialog.show()
00565 
00566         def on_tree_add_response(self, dialog, response):
00567                 if response == gtk.RESPONSE_OK:
00568                         old_paths = self._get_contents()
00569                         new_paths = dialog.get_filenames()
00570 
00571                         paths = list(frozenset(old_paths) | frozenset(new_paths))
00572                         self._store_contents(paths)
00573 
00574                 dialog.hide()
00575 
00576         def on_tree_remove_activate(self, item):
00577                 store, iter, root_iter = self._get_selection_iters()
00578                 path = store.get_value(root_iter, 0)
00579 
00580                 paths = self._get_contents()
00581                 paths.remove(path)
00582 
00583                 self._store_contents(paths)
00584 
00585         # Drag-and-drop
00586 
00587         def on_treeview_get(self, treeview, context, selection, target, timestamp):
00588                 treeselection = treeview.get_selection()
00589                 store, iter = treeselection.get_selected()
00590 
00591                 if target == 0:
00592                         data = store.get_string_from_iter(iter)
00593                 else:
00594                         data = 'file://' + construct_path(store, iter)
00595 
00596                 selection.set(selection.target, 8, data)
00597 
00598         def on_treeview_received(self, treeview, context, x, y, selection, target, timestamp):
00599                 success = False
00600 
00601                 store = treeview.get_model()
00602                 row = treeview.get_dest_row_at_pos(x, y)
00603                 if row:
00604                         treepath, position = row
00605 
00606                         if target == 0:
00607                                 iter = store.get_iter_from_string(selection.data)
00608                                 success = self._move_dragged(store, iter, treepath)
00609                         else:
00610                                 for line in selection.data.split('\n'):
00611                                         line = line.rstrip('\r')
00612                                         components = line.split()
00613                                         uri = components[0]
00614 
00615                                         success = self._copy_dragged(store, uri, treepath)
00616                                         if not success:
00617                                                 break
00618 
00619                 context.finish(success, False, timestamp)
00620 
00621         def _move_dragged(self, store, source_iter, target_treepath):
00622                 tmp_name = store.get_value(source_iter, 0)
00623                 if tmp_name == PLACEHOLDER[0]:
00624                         return False
00625 
00626                 source_project, source_name = construct_project_name(store, source_iter)
00627                 if not source_name:
00628                         # TODO: display "Projects cannot be moved" error message?
00629                         return False
00630 
00631                 target_project, target_dir = self._get_drag_target(store, target_treepath)
00632 
00633                 filename = os.path.basename(source_name)
00634                 target_name = os.path.join(target_dir, filename)
00635 
00636                 if source_project == target_project:
00637                         controller = self._get_controller(store, target_treepath)
00638 
00639                         do_control = controller.is_controlled(source_name)
00640                         controller.rename(source_name, target_name, do_control)
00641                 else:
00642                         source_treepath = store.get_path(source_iter)
00643                         controller = self._get_controller(store, source_treepath)
00644 
00645                         do_control = controller.is_controlled(source_name)
00646                         if do_control:
00647                                 controller.release(source_name)
00648 
00649                         source_path = os.path.join(source_project, source_name)
00650                         target_path = os.path.join(target_project, target_name)
00651                         os.rename(source_path, target_path)
00652 
00653                         if do_control:
00654                                 controller = self._get_controller(store, target_treepath)
00655                                 controller.seize_hierarchy(target_name)
00656 
00657                 return True
00658 
00659         def _copy_dragged(self, store, source_uri, target_treepath):
00660                 prefix = 'file://'
00661                 hostname = 'localhost'
00662 
00663                 if not source_uri.startswith(prefix):
00664                         return False
00665 
00666                 source_path = source_uri[len(prefix):]
00667 
00668                 if source_path.startswith(hostname):
00669                         source_path = source_path[len(hostname):]
00670 
00671                 target_project, target_dir = self._get_drag_target(store, target_treepath)
00672 
00673                 filename = os.path.basename(source_path)
00674                 target_name = os.path.join(target_dir, filename)
00675 
00676                 target_path = os.path.join(target_project, target_name)
00677                 copy_hierarchy(source_path, target_path)
00678 
00679                 control = self._get_controller(store, target_treepath)
00680                 if control.is_controlled(target_dir):
00681                         control.seize_hierarchy(target_name)
00682 
00683                 return True
00684 
00685         def _get_drag_target(self, store, treepath):
00686                 iter = store.get_iter(treepath)
00687                 project, directory = construct_project_name(store, iter)
00688 
00689                 path = os.path.join(project, directory)
00690                 if not os.path.isdir(path):
00691                         directory = os.path.dirname(directory)
00692 
00693                 return project, directory
00694 
00695         # Popups
00696 
00697         def on_dir_popup_show(self, menu):
00698                 treeview = self.glade.get_widget('treeview')
00699                 selection = treeview.get_selection()
00700                 store, iter = selection.get_selected()
00701 
00702                 project, name = construct_project_name(store, iter)
00703                 path = os.path.join(project, name)
00704 
00705                 # Control
00706 
00707                 treepath = store.get_path(iter)
00708                 controller = self._get_controller(store, treepath)
00709 
00710                 controlled = controller.is_controlled(name)
00711 
00712                 for name in ('/dir-popup/control-dir', '/dir-popup/control-recursive'):
00713                         item = self.ui.get_widget(name)
00714                         item.set_property('visible', not controlled)
00715 
00716                 item = self.ui.get_widget('/dir-popup/release-dir')
00717                 item.set_property('visible', controlled)
00718 
00719         def on_file_popup_show(self, menu):
00720                 treeview = self.glade.get_widget('treeview')
00721                 selection = treeview.get_selection()
00722                 store, iter = selection.get_selected()
00723 
00724                 project, name = construct_project_name(store, iter)
00725                 path = os.path.join(project, name)
00726 
00727                 # Handlers
00728 
00729                 for handler, item in self.handlers:
00730                         support = handler.supports_file(path)
00731                         item.set_property('visible', support)
00732 
00733                 # Run
00734 
00735                 result = os.stat(path)
00736                 mode = stat.S_IMODE(result.st_mode)
00737                 executable = bool(mode & 0111)
00738 
00739                 item = self.ui.get_widget('/file-popup/run-file')
00740                 item.set_sensitive(executable)
00741 
00742                 # Control
00743 
00744                 treepath = store.get_path(iter)
00745                 controller = self._get_controller(store, treepath)
00746 
00747                 controlled = controller.is_controlled(name)
00748                 if controlled:
00749                         modified = controller.is_modified(name)
00750                 else:
00751                         modified = False
00752 
00753                 item = self.ui.get_widget('/file-popup/compare-file')
00754                 item.set_property('visible', controlled)
00755                 item.set_sensitive(modified)
00756 
00757                 item = self.ui.get_widget('/file-popup/control-file')
00758                 item.set_property('visible', not controlled)
00759 
00760                 item = self.ui.get_widget('/file-popup/release-file')
00761                 item.set_property('visible', controlled)
00762 
00763         def on_handler_action_activate(self, item, handler):
00764                 treeview = self.glade.get_widget('treeview')
00765                 selection = treeview.get_selection()
00766                 store, iter = selection.get_selected()
00767 
00768                 path = construct_path(store, iter)
00769                 handler.open_file(path)
00770 
00771         # Control
00772 
00773         def on_control_activate(self, item):
00774                 treeview = self.glade.get_widget('treeview')
00775                 selection = treeview.get_selection()
00776                 store, iter = selection.get_selected()
00777                 project, name = construct_project_name(store, iter)
00778 
00779                 treepath = store.get_path(iter)
00780                 controller = self._get_controller(store, treepath)
00781                 controller.control(name)
00782 
00783         def on_control_recursive_activate(self, item):
00784                 treeview = self.glade.get_widget('treeview')
00785                 selection = treeview.get_selection()
00786                 store, iter = selection.get_selected()
00787                 project, name = construct_project_name(store, iter)
00788 
00789                 treepath = store.get_path(iter)
00790                 controller = self._get_controller(store, treepath)
00791                 controller.control(name, recursive=True)
00792 
00793         def on_release_activate(self, item):
00794                 treeview = self.glade.get_widget('treeview')
00795                 selection = treeview.get_selection()
00796                 store, iter = selection.get_selected()
00797                 project, name = construct_project_name(store, iter)
00798 
00799                 treepath = store.get_path(iter)
00800                 controller = self._get_controller(store, treepath)
00801                 controller.release(name)
00802 
00803         def on_compare_activate(self, item):
00804                 treeview = self.glade.get_widget('treeview')
00805                 selection = treeview.get_selection()
00806                 store, iter = selection.get_selected()
00807 
00808                 treepath = store.get_path(iter)
00809                 controller = self._get_controller(store, treepath)
00810 
00811                 root, path = self._get_selected()
00812                 name = path[len(root)+1:]
00813 
00814                 data = controller.get_latest(name)
00815                 environment.compare(data, path)
00816 
00817         # Execute
00818 
00819         def on_tree_build_activate(self, item):
00820                 options, name = self._get_options_name()
00821                 if options:
00822                         self._build(options, None)
00823 
00824         def on_dir_build_activate(self, item):
00825                 options, name = self._get_options_name()
00826                 if options:
00827                         self._build(options, name)
00828 
00829         def on_tree_clean_activate(self, item):
00830                 options, name = self._get_options_name()
00831                 if options:
00832                         self._clean(options, None)
00833 
00834         def on_dir_clean_activate(self, item):
00835                 options, name = self._get_options_name()
00836                 if options:
00837                         self._clean(options, name)
00838 
00839         def _build(self, options, target):
00840                 batch = execution.Batch()
00841                 options.build(batch, target)
00842                 environment.execute(batch)
00843 
00844         def _clean(self, options, target):
00845                 batch = execution.Batch()
00846                 options.clean(batch, target)
00847                 environment.execute(batch)
00848 
00849         def on_file_run_activate(self, item):
00850                 options, name = self._get_options_name()
00851                 if not options:
00852                         return
00853 
00854                 batch = execution.Batch()
00855                 options.run(batch, name)
00856 
00857                 if not self.last_run_batch:
00858                         item = self.ui.get_widget("/menubar/project/run-last")
00859                         item.set_sensitive(True)
00860 
00861                 self.last_run_batch = batch
00862 
00863                 environment.execute(batch)
00864 
00865         def on_last_run_activate(self, item):
00866                 if self.last_run_batch:
00867                         self.last_run_batch.rewind()
00868                         environment.execute(self.last_run_batch)
00869 
00870         def _get_options_name(self):
00871                 treeview = self.glade.get_widget('treeview')
00872                 selection = treeview.get_selection()
00873                 store, iter = selection.get_selected()
00874 
00875                 if iter:
00876                         options = self._get_options(store, iter)
00877                         root, name = construct_project_name(store, iter)
00878 
00879                         return options, name
00880                 else:
00881                         return None, None
00882 
00883         # Tree
00884 
00885         def on_treeview_expanded(self, treeview, iter, treepath):
00886                 store = treeview.get_model()
00887                 self._open_directory(store, iter)
00888 
00889         def on_treeview_collapsed(self, treeview, iter, treepath):
00890                 store = treeview.get_model()
00891                 self._close_directory(store, iter)
00892                 store.append(iter, PLACEHOLDER)
00893 
00894         def on_treeview_activated(self, treeview, treepath, column):
00895                 store = treeview.get_model()
00896 
00897                 iter = store.get_iter(treepath)
00898                 is_dir = store.get_value(iter, 1)
00899                 path = construct_path(store, iter)
00900 
00901                 if is_dir:
00902                         treeview.expand_row(treepath, False)
00903                 else:
00904                         root_iter = store.get_iter(treepath[:1])
00905                         root_path = construct_path(store, root_iter)
00906 
00907                         environment.edit(path, workdir=root_path)
00908 
00909         def on_treeview_press(self, treeview, event):
00910                 if event.button != 3:
00911                         return
00912 
00913                 selection = treeview.get_selection()
00914                 store, iter = selection.get_selected()
00915                 if not iter:
00916                         return
00917 
00918                 root_path = construct_path(store, iter)
00919 
00920                 if not store.iter_parent(iter):
00921                         name = '/tree-popup'
00922                 elif store.iter_children(iter):
00923                         name = '/dir-popup'
00924                 else:
00925                         name = '/file-popup'
00926 
00927                 menu = self.ui.get_widget(name)
00928                 menu.popup(None, None, None, event.button, event.time)
00929 
00930         # Destroy
00931 
00932         def on_scrolledwindow_destroy(self, scrolledwindow):
00933                 self.conf.remove_all()
00934 
00935         # View
00936 
00937         def get_label(self):
00938                 return self.glade.get_widget('title')
00939 
00940         def get_widget(self):
00941                 return self.glade.get_widget('scrolledwindow')
00942 
00943         def cleanup(self):
00944                 pass
00945 
00946 gobject.type_register(View)
00947 
00948 # Extension types
00949 
00950 class OptionsExtension (extension.Extension):
00951         '''Project options.'''
00952 
00953         def __init__(self):
00954                 extension.Extension.__init__(self)
00955 
00956         def get_controller_name(self):
00957                 '''Get the class name of the controller that should be used for managing the
00958                    contents of this project.  None implies automatic
00959                    controller selection.'''
00960                 return None
00961 
00962         def build(self, batch, name=None):
00963                 '''Add jobs to BATCH for building target NAME of this project.  Build default
00964                    target if NAME is not set.'''
00965                 pass
00966 
00967         def clean(self, batch, name=None):
00968                 '''Add jobs to BATCH for cleaning up target NAME of this project.  Clean
00969                    default target if NAME is not set.'''
00970                 pass
00971 
00972         def run(self, batch, name=None):
00973                 '''Add job to BATCH for running target NAME of this project.  Run default
00974                    target if NAME is not set.'''
00975                 pass
00976 
00977 gobject.type_register(OptionsExtension)
00978 
00979 class ControllerExtension (extension.Extension):
00980         '''Project content management using version control system or such
00981            back-end.'''
00982 
00983         def __init__(self):
00984                 extension.Extension.__init__(self)
00985 
00986         def is_controlled(self, name):
00987                 '''Is NAME known to the system?'''
00988                 return False
00989 
00990         def is_modified(self, name):
00991                 '''Has NAME been modified since the last version known to the system?'''
00992                 return False
00993 
00994         def get_latest(self, name):
00995                 '''Get the contents of the last version of NAME that is known to the
00996                    system.'''
00997                 return None
00998 
00999         def control(self, name, recursive=False):
01000                 '''Register NAME with the system.  If recursive is set and NAME is a
01001                    directory, its contents should be registered.'''
01002                 pass
01003 
01004         def release(self, name):
01005                 '''Unregister NAME from the system.  If NAME is a directory, its contents
01006                    should be unregistered.'''
01007                 pass
01008 
01009         def make_file(self, name, control=False):
01010                 '''Create the empty NAME file.  Register it with the system if CONTROL is
01011                    set.'''
01012                 pass
01013 
01014         def make_directory(self, name, control=False):
01015                 '''Create the NAME directory.  Register it with the system if CONTROL is
01016                    set.'''
01017                 pass
01018 
01019         def remove(self, name, control=False):
01020                 '''Delete NAME.  If NAME is a directory, its contents should be deleted.
01021                    Unregister it from the system if CONTROL is set.'''
01022                 pass
01023 
01024         def rename(self, old_name, new_name, control=False):
01025                 '''Rename OLD_NAME as NEW_NAME.  Update the change to the system if CONTROL
01026                    is set.'''
01027                 pass
01028 
01029         def clone(self, old_name, new_name, control=False):
01030                 '''Copy OLD_NAME to NEW_NAME.  If OLD_NAME is a directory, its contents
01031                    should be copied.  Register the created files and
01032                    directories with the system if CONTROL is set.'''
01033                 pass
01034 
01035 gobject.type_register(ControllerExtension)

Generated on Thu Jan 18 09:47:40 2007 for Encode by  doxygen 1.4.7