# Part of the A-A-P GUI IDE: Navigator and ActyItem classes # Copyright (C) 2002-2003 Stichting NLnet Labs # Permission to copy and use this file is specified in the file COPYING. # If this file is missing you can find it here: http://www.a-a-p.org/COPYING import os.path import string import Util import Tool import Filetype # message translation def _(x): return Tool._gettext(x) class Navigator: """One view on the navigation within an Activity.""" def __init__(self, name, actyitem): self.name = name self.topitem = actyitem # toplevelitem self.topitem.navigator = self self.currentitem = None self.view = None def getTopItem(self): return self.topitem def close(self, shutdown): """Close the navigator, return non-zero when closing is OK.""" return self.topitem.close(shutdown) def foreground(self): self.topitem.foreground() def findItemByName(self, name): """Find an item in this Navigator with the name "name".""" return self.topitem.findItemByName(name) class ActyItemList: """List of activity items.""" def __init__(self): self.itemlist = [] self.view = None def __len__(self): return len(self.itemlist) def addItem(self, item): """Append an ActyItem to the list.""" if not item in self.itemlist: self.itemlist.append(item) item.acty.modified = 1 if item.navigator.view: item.navigator.view.addActyItem(item) def getItem(self, idx): return self.itemlist[idx] def getAllItems(self): return self.itemlist def delItem(self, item): """Delete "item" from the list.""" item.acty.modified = 1 self.itemlist.remove(item) def close(self, shutdown): """Close all tools for this activityItemList. Return non-zero if closing is OK.""" ok = 1 for c in self.itemlist: if not c.close(shutdown): ok = 0 return ok class ActyItem: """An item in a Navigator: file, directory, class, file location, etc.""" def __init__(self, name, dir, acty, parent, addnode = 1): self.name = name # Name of the item (unmodifed, may include # things like "~/") self.dir = dir # directory "name" is relative to self.acty = acty # Activity in which this item belongs. self.parent = parent # Parent ActyItem (None for root) if parent: # Navigator in which this item belongs. self.navigator = parent.navigator else: self.navigator = None # will be set by caller self.children = None # ActyItemList when there are children if addnode: self.node = findAddNode(self) # Associated Node (actual file name, tools # for the item) else: self.node = None self.toollist = [] # List of tools directly associated with # the item. Not used for items with a # Node! def getRoot(self): """Return the item that is the root for this item.""" item = self while item.parent: item = item.parent return item def rename(self, name, dir): """Change the name of this ActyItem.""" if name == self.name: return # nothing changed self.name = name self.dir = dir if self.node: oldnode = self.node self.node = findAddNode(self) oldnode.close(0, self) self.acty.modified = 1 # XXX delete unused node? Rename the node? def fullName(self): """Return the full path name of the item.""" if self.node: return self.node.fullName() return self.name def realName(self): """Concatenate the dir and name to get the real path name.""" if os.path.isabs(self.name): return self.name return os.path.join(self.dir, self.name) def shortName(self): """Return the path name relative to the current directory.""" return Util.shorten_name(os.path.expanduser(self.realName()), self.dir) def listName(self): if self.node: return self.node.listName() return self.name def findItemByName(self, name): """Find an item in this ActyItem or its children with the name "name".""" if Util.fname_equal(self.fullName(), name): return self if self.children: for child in self.children.itemlist: item = child.findItemByName(name) if item: return item return None def isFile(self): if self.node: return self.node.isFile() return 0 def addTool(self, tool, action = None): """Add a tool for this item.""" if self.node: self.node.addTool(tool, action) else: self.toollist.append(tool) def delTool(self, tool): """Remove a tool from this item. Used when the user closes the tool.""" if self.node: self.node.delTool(tool) else: self.toollist.remove(tool) def runTool(self, action = None): """Run the default tool for this item. When a tool is already open for this item, bring it to the foreground. Not for 'search' though.""" if (action != "search" and ((self.node and self.node.toollist.get(action or "all")) or (not self.node and self.toollist))): self.foreground() else: Tool.runTool(self, action) def displayBreakpoint(self, what, bp): """Update breakpoint "bp" with action "what".""" if self.node: self.node.displayBreakpoint(what, bp) else: for tool in self.toollist: Tool.displayBreakpoint(tool, what, bp) def findViewTool(self): """Find a running tool for this item that can view it.""" if self.node: # safety check return self.node.findViewTool() return None def build(self): """For a recipe build the default target, for target item in a recipe build that target.""" if self.node: self.node.build(self) else: print "Can't build ActyItem without node." def addChildByName(self, name): """Add a new child, creating it first. "name" is relative to the current directory. Return the new ActyItem.""" if not self.children: self.children = ActyItemList() if os.path.isabs(name) or os.path.exists(name) or '/' in name: name = Util.shorten_name(os.path.abspath(name), self.dir) item = ActyItem(name, self.dir, self.acty, self) self.children.addItem(item) self.acty.modifed = 1 return item def addTree(self, items, topdir, addnode): """Add a tree of items recursively. "items" is a dictionary, in which the key is the name of an item, the contents is a dictionary for nested items. "topdir" is the directory to which relative items refer. The toplevel items are only give a node if "addnode" is non-zero. Children do get a node, unless it's a readonly item.""" self.children = ActyItemList() keys = items.keys() keys.sort() # why doesn't sort() return the sorted list??!!?? for n in keys: item = ActyItem(n, topdir, self.acty, self, addnode = addnode) self.children.addItem(item) if len(items[n]): item.addTree(items[n], topdir, n[0] in string.letters + "./~") def delChild(self, item): """Shutdown "item" and delete it from the children. Return non-zero if deleting is OK.""" if item.close(0): self.children.delItem(item) return 1 return 0 def close(self, shutdown): """Close all tools for this ActivityItem. Return non-zero if closing is OK.""" if self.node: ok = self.node.close(shutdown, self) else: ok = 1 for tool in self.toollist: if not tool.close(shutdown): ok = 0 if self.children: if not self.children.close(shutdown): ok = 0 return ok def foreground(self): """Move this activity to the foreground.""" if self.node: self.node.foreground(self) else: for tool in self.toollist: tool.foreground(self) class NodeList: """List of nodes. Used at the toplevel for the nodelist view.""" def __init__(self): self.nodelist = [] self.view = None def __len__(self): return len(self.nodelist) def addNode(self, node): """Append a Node to the list.""" if not node in self.nodelist: self.nodelist.append(node) if self.view: self.view.addNode(node) def getNode(self, idx): return self.nodelist[idx] def remove(self, node): """Remove "node" from the list.""" if node in self.nodelist: i = self.nodelist.index(node) self.nodelist.remove(node) if self.view: self.view.delNode(i) class Node: """An item that an ActyItem refers to (file, directory, etc.). Several ActyItems can refer to the same Node. To be used for derived classes: FileNode, DirNode, etc.""" def __init__(self, actyitem): self.items = [ actyitem ] # items that refer to this Node self.name = Util.full_fname(os.path.expanduser(actyitem.realName())) # uniform name (ActyItems may use a # different name) self.toollist = {"all": []} # dictionary, key is action, value # is list of tools open for this # Node and action; "all" contains # all tools. self.topmodel = actyitem.acty.topmodel # link to AIDE object self.filetype = None # detected when needed. def fullName(self): """Return the full path name of the item.""" return self.name def listName(self): """Return the name that can be shown in a list.""" if self.isFile(): return Util.display_name(self.name) return self.name def getFiletype(self): """Get the filetype of this node. Detect it the first time, remember it for later.""" if self.filetype is None: self.filetype = Filetype.ft_detect(self.name) if self.filetype is None: self.filetype = '' return self.filetype def isFile(self): """Simplistic check if this item is a file.""" if self.getFiletype(): return self.getFiletype() != "directory" # Filetype not recognized, could be a dummy item or a file that we # don't recognize. dir = os.path.dirname(self.name) return os.path.exists(self.name) or (dir and os.path.isdir(dir)) def addActyItem(self, actyitem): """Add an ActyItem that uses this Node.""" self.items.append(actyitem) def addTool(self, tool, action = None): """Add a tool that is using this Node.""" # Add it to the list of "all" actions. if not self.toollist.has_key("all"): self.toollist["all"] = [] if not tool in self.toollist["all"]: self.toollist["all"].append(tool) # If an action is specified add it to that list. if action: if not self.toollist.has_key(action): self.toollist[action] = [] if not tool in self.toollist[action]: self.toollist[action].append(tool) def delTool(self, tool): """Remove a tool that was used for this Node. Used when the user closes a tool.""" for k in self.toollist.keys(): if tool in self.toollist[k]: self.toollist[k].remove(tool) def findViewTool(self): """Find a running tool for this node that can view it.""" for action in [ "view", "edit" ]: if self.toollist.get(action): return self.toollist[action][0] return None def displayBreakpoint(self, what, bp): """Update breakpoint "bp" with action "what".""" for action in [ "view", "edit" ]: if self.toollist.get(action): for tool in self.toollist[action]: Tool.displayBreakpoint(tool, what, bp) def close(self, shutdown, actyitem): """Close: remove this actyitem from the node. Stop tools for the node if it was the last actyitem. Return non-zero if closing is OK.""" ok = 1 # Shutdown tools for this node. Skip when already done. for t in self.toollist["all"][:]: t.delItem(actyitem) if t.allClosed(): if not t.close(shutdown): ok = 0 else: # XXX do remove when closing failed? Don't want to # try over and over again. for k in self.toollist.keys(): if t in self.toollist[k]: self.toollist[k].remove(t) if ok: self.items.remove(actyitem) if len(self.toollist["all"]) == 0: # No tools open for this item, remove it from the item list self.topmodel.nodelist.remove(self) return ok def foreground(self, actyitem = None): """Bring the tools for this Node to the foreground.""" for t in self.toollist["all"]: t.foreground(actyitem, node = self) def build(self, actyitem): """For a recipe build the default target, for target item in a recipe build that target.""" if actyitem.acty.modified: self.topmodel.consoleMsg(_("Project was changed, saving recipe...")) actyitem.acty.save() if actyitem.acty.modified: return # saving failed if self.getFiletype() == "aap": recipe = self.name target = None else: recipe = actyitem.navigator.topitem.name target = actyitem.name argv = [ "-f", recipe, "MESSAGE=changedir,error,info,system" ] if target: argv.append(target) self.topmodel.consoleMsg(_("Building %s...") % actyitem.listName()) import Main Main.execute(None, argv) self.topmodel.consoleMsg(_("Finished building %s.") % actyitem.listName()) # The list of all nodes, each file should appear only once. allnodelist = [] def findAddNode(actyitem): """Find a node for ActyItem "actyitem". If it doesn't exist yet, add a new one.""" n = findNodeByName(actyitem.realName()) if n: n.addActyItem(actyitem) else: n = Node(actyitem) allnodelist.append(n) return n def findNodeByName(name): """Find a node with name "name". Return None if not found.""" item_name = Util.full_fname(name) for n in allnodelist: if n.name == item_name: return n return None class ItemPos: """Stores the position in an item.""" def __init__(self, item, lnum = None, col = None, off = None, text = None): """Normally either lnum/col or off is given, possibly both.""" self.item = item self.lnum = lnum self.col = col self.off = off self.text = text # vim: set sw=4 et sts=4 tw=79 fo+=l: