module Main where import Maybe import FranTk import ViewTypes import Label import Edit import List (intersperse) main :: IO () main = display $ treeEdit ------------------------------------------------------------------------------------------------------ -- tree editor treeEdit :: WComponent treeEdit = do -- input direction dirToggle <- mkBVar True let dirB = bvarBehavior dirToggle let dirMenu = dirmenu (bvarInput dirToggle) -- wire that all key inputs talk to keyWire <- mkWire -- xs :: Event (String,[Command]) xs <- scanner (event keyWire) -- wire that all commands talk to commandWire <- mkWire -- commands event - merges actual commands and key events let commEvents :: Event Command commEvents = event commandWire .|. (fromListE (xs ==> snd)) -- tree updates let commandsE :: Event Edit1 commandsE = withSnapE fromCommand dirB commEvents t <- mkTreeCollection (empty,[]) commandsE (input commandWire) -- file handling saveListener <- mkGUIL saveTree let saveListen = snapshotL_ (lift1 subtreeOfEdit (treeBehavior t)) (saveListener) loadListen <- mapGUIL_ loadTree (input commandWire) let fileMenu = filemenu loadListen saveListen -- status label for displaying partial commands commBehavior <- stepper [] (xs ==> fst) let commLabel = mkLabel [textB commBehavior] -- visible clipboard clipvis <- mkBVar True let visB = bvarBehavior clipvis let visMenu = vismenu (bvarInput clipvis) visB -- clipboard view let clipcontents = lift1 snd (treeBehavior t) let win = mkWindow [title "Clipboard Contents", onClose (tellL (bvarInput clipvis) False)] $ (mkScrollableEdit [readOnly True, textB $ lift1 prettyForest $ clipcontents, font $namedFont "Courier" 14 [Roman]] [] []) let clipwin = ifB visB win emptyComponent let menus = mkMenu [] [fileMenu,dirMenu,visMenu] docB <- fromTreeCollection t -- the window pile [ withRootWindow [title "Structure Editor",useMenu menus] $ do (keyPressAny (input keyWire) $ mkScrollableEdit [focus,setDocB docB, readOnly True,font $namedFont "Courier" 14 [Roman] ] [] []) `above` commLabel, clipwin ] ------------------------------------------------------------------------------------------------------ -- command dispatcher fromCommand :: Command -> Bool -> Edit1 fromCommand (Ins l) True = entry l fromCommand (Ins l) False = entryR l fromCommand Kill _ = lift kill fromCommand (Load t) _ = lift (replace t) -.- nav toRoot' fromCommand Up _ = nav up' fromCommand Lft _ = nav left' fromCommand Rght _ = nav right' fromCommand Down _ = nav down' fromCommand Cpy _ = copy fromCommand Pste _ = lift paste fromCommand Clear _ = clear fromCommand Open _ = open fromCommand Close True = close fromCommand Close False = closeR fromCommand Nxt True = nav (liftnav forward) fromCommand Nxt False = nav (liftnav backward) fromCommand Promo _ = lift promote fromCommand Demo True = lift demote fromCommand Demo False = lift demoteR fromCommand Flt _ = lift flatass fromCommand Skip _ = idEdit fromCommand Ct True = lift cut fromCommand Ct False = lift cutR fromCommand (Navigate loc) _ = navigate loc fromCommand (Bracket loc) _ = bracket loc fromCommand _ _ = undefined ------------------------------------------------------------------------------------------------------ -- file handling vismenu :: Listener Bool -> Behavior Bool -> MenuItem vismenu setvis isvis = mcascade [text "View"] (mkMenu [] [mcheckbutton [text "View Clipboard",checkValB isvis] setvis]) filemenu :: Listener () -> Listener () -> MenuItem filemenu load saver = mcascade [text "File"] (mkMenu [] [mbutton [text "Load"] load,mbutton [text "Save"] saver]) loadTree :: GUI Command loadTree = do n <- getOpenFileName case n of Nothing -> return Skip Just fn -> do xs <- liftIO $ readFile fn return (Load (read xs)) saveTree :: Bracket -> GUI () saveTree st = do n <- getSaveFileName case n of Nothing -> return () Just fn -> liftIO $ writeFile fn (show (host (flatten st))) ------------------------------------------------------------------------------------------------------ -- direction toggling dirmenu :: Listener Bool -> MenuItem dirmenu dir = mcascade [text "Direction"] (mkMenu [] [mcheckbutton [text "Left-to-right",checkVal True] dir]) showDir :: Bool -> String showDir True = "left-to-right" showDir False = "right-to-left" ------------------------------------------------------------------------------------------------------ -- tree collections type EStatus = EditSt Bracket [Tree] data TreeCollection = TreeCollection {treeInput :: Listener Command, treeEvent :: Event (EStatus, [Bracket]), treeBehavior :: Behavior EStatus, treeInit :: EStatus} mkTreeCollection :: EStatus -> Event Edit1 -> Listener Command -> GUI TreeCollection mkTreeCollection s ev' set = do ev <- scanlE (\(e,_) f -> f e) (s,[]) ev' bh <- stepper s (ev ==> fst) return $ TreeCollection set ev bh s fromTreeCollection :: TreeCollection -> GUI DocumentB fromTreeCollection (TreeCollection set e bh st) = let select :: Listener TreeTag select = mapL (Navigate . locOfTag ) set brack :: Listener TreeTag brack = mapL (Bracket . locOfTag) set mkTg :: EStatus -> Maybe (TIndex,TIndex) mkTg (s,c) = let x = getSelectedTag s in Just (tindexTagFirst x,tindexTagLast x) in mkDocumentB (insertTagIDoc (mkEditTag [backgroundB (grey 0.8), withIndexB $ lift1 mkTg bh]) $ structured (SGroup $ viewTree (subtreeOfEdit st) select brack) ) (mapE (alterOp select brack) e) getSelectedTag :: Bracket -> Ident getSelectedTag = identify . tagOfLoc . location . flatten alterOp :: Listener TreeTag -> Listener TreeTag -> (EditSt Bracket [Tree], [Bracket]) -> (IDoc -> IDoc) alterOp lst lst' ((st,c),past) = foldr (.) id $ reverse $ map alter past where alter nst = replaceStructured (head (viewTree nst lst lst')) (getSelectedTag nst) ------------------------------------------------------------------------------------------------------ -- scanner special c = elem c specialChars domain = map fst data Command = Ins TreeLabel | Kill | Up | Lft | Rght | Down | Open | Close | Load Tree | Skip | Nxt | Promo | Demo | Flt | Ct | Pste | Cpy | Clear| Navigate Loc | Bracket Loc newtoken (s,_) l = if not (null s) then ([],[Ins (Letter s), l]) else ([],[l]) scanner :: Event Key -> GUI (Event (String,[Command])) scanner e = accumE ("",[]) (e ==> apply) where apply (KeyChar '(') sx = newtoken sx Open apply (KeyChar ')') sx = newtoken sx Close apply (KeyChar x) (s,y) = case (lookup [x] commandStrings) of Nothing -> case (lookup sx commandStrings) of (Just j) -> ([],[Ins j]) Nothing -> if special x && not (null s) then ([x],[Ins (Letter s)]) else (sx,[]) Just m -> newtoken (s,y) (Ins m) where sx = s++[x] apply CursorUp sx = newtoken sx Up apply CursorDown sx = newtoken sx Down apply CursorRight sx = newtoken sx Rght apply CursorLeft sx = newtoken sx Lft apply Delete sx = newtoken sx Kill apply BackSpace (s,x) = if null s then (s,x) else (init s,x) apply Return sx = newtoken sx Nxt apply (F 1) sx = newtoken sx Cpy apply (F 2) sx = newtoken sx Pste apply (F 3) sx = newtoken sx Clear apply (F 4) sx = newtoken sx Promo apply (F 5) sx = newtoken sx Demo apply (F 6) sx = newtoken sx Flt apply (F 7) sx = newtoken sx Skip apply (F 8) sx = newtoken sx Ct apply _ (s,_) = if null s then ([],[]) else ([],[Ins (Letter s)]) ----------------------------------------------------------------------------------------------------- -- pretty printing prettyForest :: [Tree] -> String prettyForest = concat . intersperse "\n\n" . map prettyTree prettyTree :: Tree -> String prettyTree t = concat (map structuredToString (viewTree ([], t) undefined undefined)) viewTree :: Bracket -> Listener TreeTag -> Listener TreeTag -> [Structured] viewTree st lst lst' = [viewTree' (selected' st) (location (flatten st)) lst lst' (indentation st)] viewTree' :: Tree -> Loc -> Listener TreeTag -> Listener TreeTag-> Int -> Structured viewTree' (Fork l ts) loc lst lst' n = STextTagged (concat (map ($n) (map (($lst').($lst)) (viewLabel l (map (viewTree'' loc) (zip ts [0..])) tag) ))) (mkEditTag [useIdent (identify tag)]) where tag = tagOfLoc loc viewTree'' :: Loc -> (Tree,Int) -> Listener TreeTag -> Listener TreeTag -> Int -> [Structured] viewTree'' loc (t,i) lst lst' n = [viewTree' t (i:loc) lst lst' n] indentation :: Bracket -> Int indentation = sum . map ind . concat . fst structuredToString :: Structured -> String structuredToString (SText s) = s structuredToString (STextTagged ss _) = concat (map structuredToString ss) structuredToString (STextMark _) = []