{-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE UndecidableInstances #-} -- | -- Description: Basic Interface for Graph Display -- -- In UniForM we need ways of displaying typed directed graphs. -- In the original UniForM, it was only possible to use the DaVinci -- encapsulation for displaying directed graphs. While this is very good, -- in the new UniForM it is intended to factor out this encapsulation -- so that it will not be too difficult to replace DaVinci by other -- graph-drawing package (or variants of DaVinci) for particular graphs. -- Example alternatives that might be considered: -- (1) some sort of text-only interface. -- (2) Windows-style displaying of a tree structure using clickable -- folders. -- In this module we present the classes that any such \"graph-drawing package\" -- is supposed to implement. -- -- This module is in two parts. -- -- The first part contains the -- \"user-friendly\" versions of the functions. For these, it is assumed -- (as will usually be the case) that there is only one -- node\/nodeType\/arc\/arcType around for a particular graph. The whole lot -- is indexed by the GraphAll, which contains ALL the functionality -- required for accessing the graphs (apart from configuration options). -- For example, the only daVinci-specific thing you should need to use -- to write a program which calls daVinci will be the daVinciSort variable. -- -- The second part contains the \"user-hateful\" versions. All the -- user-hateful functions have names ending in \"Prim\". -- Graph display implementations only have to implement the user-hateful -- versions. The user-hateful versions should only be of interest to other -- people if the graph display provides more than one implementation of -- the NodeClass, NodeTypeClass (or whatever) implementation. One -- disadvantage to the user of using the user-hateful versions of the -- functions is that because of all the overloading, you have to put -- in lots of explicit types, or else get the most hideous type errors. -- -- Configuring things like graph titles, shape of node boxes, menus, -- and so on should also be implemented, where possible, by graph display -- interfaces. The various options are documented in GraphConfigure.hs. -- They should be applied using the Computation.HasConfig interface. -- -- The types which are supposed in various combinations to be instances -- of the classes are as follows: -- -- graph. This corresponds to one graph display. -- graphConfig. This is configuration information for a graph. -- This might be a window title or size for example. -- graphParms. This is a collection of graphConfig's used to -- construct a graph. -- -- Nodes and arcs carry values. Thus all the following carry -- a type parameter. But, for ease of implementation with, for example, -- DaVinci, the type parameter is required to be an instance of 'Typeable'. -- -- * node. A value of this type is an actual node in a graph. -- (Will be an instance of 'Typeable' via 'Typeable1'.) -- -- * nodeType. Nodes are created with a particular UniForM \"type\" which -- is a Haskell value of type nodetype. In fact a graph might -- conceivably have multiply Haskell types corresponding to node -- and nodeType, meaning that nodes, or their UniForM types, -- will be distinguished additionally by the Haskell type system. -- -- * nodeTypeConfig. Configuration information for a nodeType. -- This might include how a node with this type is to be displayed -- graphically. This also includes information on what to do when the -- node is clicked. -- -- * nodeTypeParms. A collection of nodeTypeConfig's used to construct -- a nodeType -- -- Similar constructions for arcs . . . -- arc. -- arcType. -- arcTypeConfig. -- arcTypeParms. -- -- -- There are quite a lot of classes. This is partly because of the need -- to have a separate class for each subset of the type variables -- which is actually used in the type of a function. -- -- This file is fairly repetitive, mainly because of the need to -- repeat the configuration machinery over and over again. -- -- The functionality provided in this file is inspired by that -- provided by DaVinci. However we extend it by allowing -- nodes to have labels. -- -- This file should be read in conjunction with "GraphConfigure", -- which contains various configuration options to be used for -- graph objects. -- -- Additional Notes -- ---------------- -- -- (1) At the end of a program using a GraphDisp instance, -- 'shutdown' should be called. For example, -- in the case of the DaVinci instance this is -- required to get rid of the DaVinci and HTk processes. -- -- (2) It is more cumbersome writing the Graph Editor than I would -- like because the menu code doesn't give you -- direct access to the node or arc type. Unfortunately doing this -- would make the classes in this file even more complicated than -- they are now. -- module Graphs.GraphDisp( -- * User-Friendly Interface. -- | You should not need any more than this for drawing graphs. Graph(..), -- (a type) -- Graph takes a lot of parameters I don't want to mentiond -- I shall write them in this module signature as "...". newGraph, -- :: Graph ... -> graphParms -- The argument to newGraph is just there to provide the types -- and isn't looked at. Each implementation will provide a trivial -- one to start off with; for daVinci it is daVinciSort. -- -> IO (Graph ...) redraw, -- :: Graph ... -> IO () getMultipleNodes, -- :: Graph ... -> (Event (WrappedNode node) -> IO a) -> IO a GraphParms(emptyGraphParms), newNode, -- :: Graph ... -> nodeType value -> value -> IO (node value) setNodeType, -- :: Graph ... -> node value -> nodeType value -> IO () deleteNode, -- :: Graph ... -> node value -> IO () setNodeFocus, -- :: Graph ... -> node value -> IO () getNodeValue, -- :: Graph ... -> node value -> IO value setNodeValue, -- :: Graph ... -> node value -> value -> IO () -- WARNING. setNodeValue should not be used with nodes with -- types which specify ValueTitleSource, as the results are -- undefined. newNodeType, -- :: Graph ... -> nodeTypeParms value -> IO (nodeType value) NodeTypeParms(..), newArc, -- :: Graph ... -> arcType value -> value -- -> node nodeFromValue -> node nodeToValue -- -> IO (arc value) WrappedNode(..), newArcListDrawer, -- :: Graph .. -> node nodeFromValue -- -> ListDrawer (arcType value,value,WrappedNode node) -- (arc value) deleteArc, -- :: Graph ... -> arc value -- -> IO () setArcValue, -- :: Graph ... -> arc value -- -> value -> IO () setArcType, -- :: Graph ... -> arc value -> value -> IO () -- WARNING. For daVinci at least, this function is not currently -- implemented getArcValue, -- :: Graph ... -> arc value -- -> IO value newArcType, -- :: Graph ... -> arcTypeParms value -> IO (arcType value) ArcTypeParms(..), Eq1(..),Ord1(..), -- Classes with nodes and arcs should instance, -- allowing comparisongs on them. -- * User-Hateful Interface -- | This is only needed -- by people wanting to implement new implementations of the interface. GraphAll(displaySort), GraphClass(..), NewGraph(..), GraphConfig, NewNode(..), DeleteNode(..), SetNodeFocus(..), NodeClass, NodeTypeClass, NewNodeType(..), NodeTypeConfig, NewArc(..), SetArcType(..), DeleteArc(..), ArcClass, ArcTypeClass(..), NewArcType(..), ArcTypeConfig, ) where import Data.Typeable import Util.VariableList hiding (redraw) import Util.Delayer(HasDelayer(..)) import Events.Events(Event) import Events.Destructible ------------------------------------------------------------------------ -- This is the start of the user-friendly interface. ------------------------------------------------------------------------ --- Graphs -- | The graph implementation will provide a value of this type to -- get you started. For example, for daVinci this is called 'daVinciSort'. -- However you then need to use it as an argument to 'newGraph' to construct -- the actual graph. newtype GraphAll graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms => Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms = Graph graph deriving (Eq,Ord) instance (GraphAll graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) => Destroyable (Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) where destroy (Graph graph) = destroy graph instance (GraphAll graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) => Destructible (Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) where destroyed (Graph graph) = destroyed graph instance (GraphAll graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) => HasDelayer (Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) where toDelayer (Graph graph) = toDelayer graph -- | Construct a new graph. The input value will be something like -- "DaVinciGraph"'s value 'daVinciSort'; the resulting graph will be -- returned. newGraph :: (GraphAll graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) => (Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) -> graphParms -> IO (Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) newGraph (_::(Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms)) (graphParms :: graphParms) = do (graph :: graph) <- newGraphPrim graphParms return (Graph graph :: Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) -- | Redraw the graph. This is needed when you want to show updates. redraw :: (GraphAll graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) => (Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) -> IO () redraw (Graph graph) = redrawPrim graph -- | Take over all interaction on the graph, and perform the given -- action, supplying it with an event which is activated when the user -- double-clicks a node. This is helpful when you need an interaction -- selecting several nodes. getMultipleNodes :: (GraphAll graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) => (Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) -> (Event (WrappedNode node) -> IO a) -> IO a getMultipleNodes (Graph graph) = getMultipleNodesPrim graph class GraphParms graphParms where emptyGraphParms :: graphParms -- Nodes -- | construct a new node. newNode :: (GraphAll graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms,Typeable value) => (Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) -> nodeType value -> value -> IO (node value) newNode ((Graph graph :: Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms)) (nodeType :: nodeType value) (value :: value) = (newNodePrim graph nodeType value) :: IO (node value) -- | set a node's type setNodeType :: (GraphAll graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms,Typeable value) => (Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) -> node value -> nodeType value -> IO () setNodeType ((Graph graph :: Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms)) (node :: node value) (nodeType :: nodeType value) = (setNodeTypePrim graph node nodeType) :: IO () -- | delete a node deleteNode :: (GraphAll graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms,Typeable value) => (Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) -> node value -> IO () deleteNode ((Graph graph :: Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms)) (node :: node value) = deleteNodePrim graph node setNodeFocus :: (GraphAll graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms,Typeable value) => (Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) -> node value -> IO () setNodeFocus ((Graph graph :: Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms)) (node :: node value) = setNodeFocusPrim graph node -- | get the value associated with a node getNodeValue :: (GraphAll graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms,Typeable value) => (Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) -> node value -> IO value getNodeValue (Graph graph :: Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) (node :: node value) = getNodeValuePrim graph node -- | set the value associated with a node. setNodeValue :: (GraphAll graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms,Typeable value) => (Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) -> node value -> value -> IO () setNodeValue (Graph graph :: Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) (node :: node value) value = setNodeValuePrim graph node value -- | construct a node type. newNodeType :: (GraphAll graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms,Typeable value) => (Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) -> nodeTypeParms value -> IO (nodeType value) newNodeType ((Graph graph :: Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms)) (nodeTypeParms :: nodeTypeParms value) = (newNodeTypePrim graph nodeTypeParms) :: IO (nodeType value) class NodeTypeParms nodeTypeParms where emptyNodeTypeParms :: Typeable value => nodeTypeParms value coMapNodeTypeParms :: (Typeable value1,Typeable value2) => (value2 -> value1) -> nodeTypeParms value1 -> nodeTypeParms value2 -- Arcs -- | construct a new arc. newArc :: (GraphAll graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms, Typeable value,Typeable nodeFromValue,Typeable nodeToValue) => (Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) -> arcType value -> value -> node nodeFromValue -> node nodeToValue -> IO (arc value) newArc (Graph graph :: Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) (arcType :: arcType value) (value :: value) (nodeFrom :: node nodeFromValue) (nodeTo :: node nodeToValue) = (newArcPrim graph arcType value nodeFrom nodeTo) :: IO (arc value) -- | Given a node, construct a 'ListDrawer' which can be used as a way -- of drawing ordered sets of out-arcs from that node. -- (NB. At the moment daVinci does not do this properly, but that is -- daVinci's fault, not mine.) newArcListDrawer :: (GraphAll graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms, Typeable value,Typeable nodeFromValue) => (Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) -> node nodeFromValue -> ListDrawer (arcType value,value,WrappedNode node) (arc value) newArcListDrawer (Graph graph :: Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) (nodeFrom :: node nodeFromValue) = (newArcListDrawerPrim graph nodeFrom) -- :: (ListDrawer (arcType value,value,WrappedNode node) (arc value)) -- | delete an arc deleteArc :: (GraphAll graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms, Typeable value)=> (Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) -> arc value -> IO () deleteArc (Graph graph :: Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) (arc :: arc value) = deleteArcPrim graph arc -- | set the value associated with an arc setArcValue :: (GraphAll graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms,Typeable value) => (Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) -> arc value -> value -> IO () setArcValue (Graph graph :: Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) (arc :: arc value) (value :: value) = setArcValuePrim graph arc value setArcType :: (GraphAll graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms,Typeable value) => (Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) -> arc value -> arcType value -> IO () setArcType (Graph graph :: Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) (arc :: arc value) (arcType :: arcType value) = setArcTypePrim graph arc arcType -- | get the value associated with an arc getArcValue :: (GraphAll graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms,Typeable value) => (Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) -> arc value -> IO value getArcValue (Graph graph :: Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) (arc :: arc value) = getArcValuePrim graph arc -- | create a new arc type newArcType :: (GraphAll graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms,Typeable value) => (Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) -> arcTypeParms value -> IO (arcType value) newArcType (Graph graph :: Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms) (arcTypeParms :: arcTypeParms value) = (newArcTypePrim graph arcTypeParms) :: IO (arcType value) class ArcTypeParms arcTypeParms where emptyArcTypeParms :: Typeable value => arcTypeParms value -- This is a special arc type parms which will not be drawn at all. -- The effect of setting options to an invisibleArcTypeParms object -- is for now undefined. invisibleArcTypeParms :: Typeable value => arcTypeParms value coMapArcTypeParms :: (Typeable value1,Typeable value2) => (value2 -> value1) -> arcTypeParms value1 -> arcTypeParms value2 ------------------------------------------------------------------------ -- This is the start of the user-hateful interface. ------------------------------------------------------------------------ -- The GraphAll class indicates that a set of types have the complete -- graph-displaying functionality we need. -- class (GraphClass graph,NewGraph graph graphParms,GraphParms graphParms, NewNode graph node nodeType,DeleteNode graph node,SetNodeFocus graph node, NodeClass node,Typeable1 node,NodeTypeClass nodeType, NewNodeType graph nodeType nodeTypeParms,NodeTypeParms nodeTypeParms, NewArc graph node node arc arcType, SetArcType graph arc arcType, DeleteArc graph arc, ArcClass arc,Typeable1 arc,ArcTypeClass arcType, NewArcType graph arcType arcTypeParms ) => GraphAll graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms where displaySort :: Graph graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms -- displaySort is a parameter which can be passed to something -- which produces graphs and displays them, to indicate what -- types we are using. The only definition of it is about to -- be given . . . instance (GraphClass graph,NewGraph graph graphParms,GraphParms graphParms, NewNode graph node nodeType,DeleteNode graph node,SetNodeFocus graph node, NodeClass node,NodeTypeClass nodeType, NewNodeType graph nodeType nodeTypeParms,NodeTypeParms nodeTypeParms, NewArc graph node node arc arcType, SetArcType graph arc arcType, DeleteArc graph arc, ArcClass arc,ArcTypeClass arcType, NewArcType graph arcType arcTypeParms ) => GraphAll graph graphParms node nodeType nodeTypeParms arc arcType arcTypeParms where displaySort = displaySort ------------------------------------------------------------------------ -- Graphs ------------------------------------------------------------------------ class (Destructible graph,Ord graph,Typeable graph,HasDelayer graph) => GraphClass graph where redrawPrim :: graph -> IO () -- done after updates have been added -- The Delayer temporarily disables redraw actions, or at least tries to. -- (For daVinci the situation is that redraw actions currently have to -- be forced anyway when attributes are changed.) class (GraphClass graph,GraphParms graphParms) => NewGraph graph graphParms where newGraphPrim :: graphParms -> IO graph class GraphConfig graphConfig -- empty to prevent just anything being usable for the graphConfig -- function. ------------------------------------------------------------------------ -- Nodes ------------------------------------------------------------------------ class (GraphClass graph,NodeClass node,NodeTypeClass nodeType) => NewNode graph node nodeType where newNodePrim :: Typeable value => graph -> nodeType value -> value -> IO (node value) setNodeTypePrim :: Typeable value => graph -> node value -> nodeType value -> IO () class (GraphClass graph,NodeClass node) => DeleteNode graph node where deleteNodePrim :: Typeable value => graph -> node value -> IO () getNodeValuePrim :: Typeable value => graph -> node value -> IO value setNodeValuePrim :: Typeable value => graph -> node value -> value -> IO () -- WARNING. setNodeValuePrim should not be used with nodes with -- types which specify ValueTitleSource, as the results are -- undefined. getMultipleNodesPrim :: graph -> (Event (WrappedNode node) -> IO a) -> IO a -- Running this function disables all other user interaction on -- the graph and creates -- a new event which occurs each time the node is double-clicked, -- assuming this graph was created with drag-and-drop enabled. -- Using this event we execute the given action, until it terminates, -- when we restore normal user interaction. -- NB. This function must not be nested, or it will block. class (GraphClass graph,NodeClass node) => SetNodeFocus graph node where setNodeFocusPrim :: Typeable value => graph -> node value -> IO () class (Typeable1 node,Ord1 node) => NodeClass node class Typeable1 nodeType => NodeTypeClass nodeType class (GraphClass graph,NodeTypeClass nodeType,NodeTypeParms nodeTypeParms) => NewNodeType graph nodeType nodeTypeParms where newNodeTypePrim :: Typeable value => graph -> nodeTypeParms value -> IO (nodeType value) class Kind1 nodeTypeConfig => NodeTypeConfig nodeTypeConfig -- empty to prevent just anything being usable for the nodeTypeConfig -- function. ------------------------------------------------------------------------ -- Arcs ------------------------------------------------------------------------ class (GraphClass graph,NodeClass nodeFrom,NodeClass nodeTo,ArcClass arc, ArcTypeClass arcType) => NewArc graph nodeFrom nodeTo arc arcType where newArcPrim :: (Typeable value,Typeable nodeFromValue,Typeable nodeToValue) => graph -> arcType value -> value -> nodeFrom nodeFromValue -> nodeTo nodeToValue -> IO (arc value) newArcListDrawerPrim :: (Typeable value,Typeable nodeFromValue) => graph -> nodeFrom nodeFromValue -> ListDrawer (arcType value,value,WrappedNode nodeTo) (arc value) class (ArcClass arc,ArcTypeClass arcType) => SetArcType graph arc arcType where setArcTypePrim :: Typeable value => graph -> arc value -> arcType value -> IO () data WrappedNode node = forall value . Typeable value => WrappedNode (node value) class (GraphClass graph,ArcClass arc) => DeleteArc graph arc where deleteArcPrim :: (Typeable value) => graph -> arc value -> IO () setArcValuePrim :: Typeable value => graph -> arc value -> value -> IO () getArcValuePrim :: Typeable value => graph -> arc value -> IO value class (Typeable1 arc,Ord1 arc) => ArcClass arc class (Typeable1 arcType,Ord1 arcType) => ArcTypeClass arcType where -- This is a special arc type which stops the arc being drawn at all. invisibleArcType :: Typeable value => arcType value class (GraphClass graph,ArcTypeClass arcType,ArcTypeParms arcTypeParms) => NewArcType graph arcType arcTypeParms where newArcTypePrim :: Typeable value => graph -> arcTypeParms value -> IO (arcType value) class Kind1 arcTypeConfig => ArcTypeConfig arcTypeConfig ------------------------------------------------------------------------ -- The Kind* classes are a silly hack so that we -- can define empty classes of things which take a fixed number of -- type parameters. ------------------------------------------------------------------------ class Kind1 takesParm where kindOne :: takesParm value -> () instance Kind1 takesParm where kindOne _ = () class Kind2 takes2Parms where kindTwo :: takes2Parms value1 value2-> () instance Kind2 takesParms where kindTwo _ = () class Kind3 takes3Parms where kindThree :: takes3Parms value1 value2 value3 -> () instance Kind3 takesParms where kindThree _ = () -- ---------------------------------------------------------------------- -- Classes for comparing and equality for types of kind 1 -- ---------------------------------------------------------------------- class Eq1 takesParm where eq1 :: takesParm value1 -> takesParm value1 -> Bool class Eq1 takesParm => Ord1 takesParm where compare1 :: takesParm value1 -> takesParm value1 -> Ordering