// // Copyright (c) 2008, Brian Frank and Andy Frank // Licensed under the Academic Free License version 3.0 // // History: // 21 Jul 08 Brian Frank Creation // using fwt ** ** Resource represents the objects a user navigates, views, and ** edits in a flux application. Resources are mapped to objects ** via the '@fluxResource' facet - if keyed by this facet, then ** subclass must define a 'make(Uri, Obj)' constructor. ** ** See `docLib::Flux` for details. ** abstract class Resource { ** ** Get the root resources. ** static Resource[] roots() { return File.osRoots.map(Resource[,]) |File f->Obj| { return FileResource.makeFile(f) } } ** ** Resolve a uri into a resource: ** 1. Resolve uri to obj via `sys::Uri.get` ** 2. If obj is Resource, return it ** 3. Resolve to obj type to resource type via '@fluxResource' facet ** ** Throw UnresolvedErr if the uri can't be resolved, and UnsupportedErr ** if resource can't be mapped to a resource. ** static Resource resolve(Uri uri) { // 1. resolve uri obj := uri.get // 2. if already resource return it if (obj is Resource) return obj // 3. map via fluxResource facet rtype := Type.findByFacet("fluxResource", obj.type, true).first if (rtype == null) throw UnsupportedErr("No resource mapping for $obj.type") return rtype.make([uri, obj]) } ** ** Get the absolute Uri of this resource. ** abstract Uri uri() ** ** Get the display name of the resource. ** abstract Str name() ** ** Get a 16x16 icon for the resource. ** virtual Image icon() { return Flux.icon(`/x16/text-x-generic.png`) } ** ** Return if this resource has or might have children. This ** is an optimization to display the expansion control in a tree ** without loading all the children. The default calls 'children'. ** virtual Bool hasChildren() { c := children return c != null ? !c.isEmpty : false } ** ** Get the navigation children of the resource. Return an ** empty list or null to indicate no children. Default ** returns null. ** virtual Resource[]? children() { return null} ** ** Get the list of available `View` types for the resource. ** The first view should be the default view. The default ** implementation searches the type database for '@fluxView' ** bindings to this resource type. ** virtual Type[] views() { acc := Type.findByFacet("fluxView", type, true) acc = acc.exclude |Type t->Bool| { return t.isAbstract } return acc } ** ** Make a popup menu for this resource or return null. ** The default popup menu returns the `viewsMenu`. ** virtual Menu? popup(Frame? frame, Event? event) { return Menu { viewsMenu(frame, event) } } ** ** Return a menu to hyperlink to the views supported ** by this resource. ** virtual Menu? viewsMenu(Frame? frame, Event? event) { menu := Menu { text = type.loc("views.name") } views.each |Type v, Int i| { viewUri := i == 0 ? uri : uri.plusQuery(["view":v.qname]) c := Command(v.name, null, &frame.load(viewUri, LoadMode(event))) menu.add(MenuItem { command = c }) } return menu } ** ** Return `uri`. ** override Str toStr() { return uri.toStr } } ************************************************************************** ** ErrResource ************************************************************************** ** ** ErrResource models a resource that cannot be resolved. ** class ErrResource : Resource { new make(Uri uri) { this.uri = uri } override Uri uri override Str name() { n := uri.name; return n.isEmpty ? uri.toStr : n } override Image icon() { return Flux.icon(`/x16/dialog-error.png`) } override Type[] views() { return Type[,] } }