00001
00002
00003
00004
00005
00006
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
00029
00030
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
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
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
00202
00203 item = self.ui.get_widget("/menubar/project/run-last")
00204 item.set_sensitive(False)
00205
00206
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
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
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
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
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
00276
00277 def _store_contents(self, paths):
00278 self.conf.set_strings('contents', paths)
00279
00280
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
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
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
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
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
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
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
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
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
00728
00729 for handler, item in self.handlers:
00730 support = handler.supports_file(path)
00731 item.set_property('visible', support)
00732
00733
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
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
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
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
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
00931
00932 def on_scrolledwindow_destroy(self, scrolledwindow):
00933 self.conf.remove_all()
00934
00935
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
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)