packages feed

INblobs-0.1.1: src/Functional/UI.hs

{-|
    Module      :  Functional.UI
    Copyright   :  (c) Miguel Vilaça and Daniel Mendes 2007

    Maintainer  :  jmvilaca@di.uminho.pt and danielgomesmendes@gmail.com
    Stability   :  experimental
    Portability :  portable


    Small Functional Language: Graphical stuff for integration in INblobs.

-}
module Functional.UI
	( compileUI
	) where

import State
import StateUtil
import qualified PersistentDocument as PD
import Common
import CommonIO
import CommonUI
import InfoKind
import Text.XML.HaXml.XmlContent (XmlContent)

import Graphics.UI.WX
import Graphics.UI.WXCore

import Functional.Language
import Functional.Parser
import Functional.Compiler

import Data.List
import Data.Char
import qualified Control.Exception as E


-- | List of strategies for which compilation is available.
listCallBy :: [CallBy]
listCallBy = [ Name, Value ]

-- | GUI for compilation of BNL to INs.
compileUI :: (InfoKind n g, InfoKind e g, XmlContent g) => State g n e -> g -> n -> e -> IO ()
compileUI state g n e =
   closeDocAndThen state $
     do
	theFrame <- getNetworkFrame state

	res <- parseDialog theFrame
	case res of
	  Nothing -> return ()
	  Just (term, callBy)
	   |not . null $ freeVars term ->
		errorDialog theFrame "Free variables"
		 $ "Only closed terms are allowed and this one have the following free vars:\n" ++ commasAnd (freeVars term)
	  Just (term, callBy) ->
	     do
		let iterSymbs = listIteratorSymbols term
		names <- if null iterSymbs
			   then return (Just [])
			   else meaningfulNamesDialog iterSymbs theFrame
		case names of
		  Nothing -> return ()
		  Just names ->
		   flip E.catch (\exception ->
			     do
				logMessage "END COMPILATION BADLY"
				wxcEndBusyCursor
				errorDialog theFrame "Compilation failed..." (show exception)
				) $
		     do
			-- loadDocument
			let fname = "examples/New-Token-Passing/CallBy"
					++ show callBy ++
					"ForClosedTerms+BNL-Iterators.INblobs"
			openNetworkFile fname state (Just theFrame)
			pDoc <- getDocument state
			doc  <- PD.getDocument pDoc
			wxcBeginBusyCursor
			logMessage "COMPILING..."
			doc2 <- compile g n e callBy term names doc
			logMessage "END COMPILATION SUCCESSFULLY"
			wxcEndBusyCursor
			let fname2 = "examples/New-Token-Passing/GENERATED-CallBy"
					++ show callBy ++
					"ForClosedTerms+BNL-Iterators.INblobs"
			PD.resetDocument (Just fname2) doc2 pDoc

			-- Redraw
			applyCanvasSize state
			buildVisiblePalette state
			reAddRules2Tree state
			repaintAll state

-- | Deal with collecting a term from the user.
-- Graphical treatment of parsing errors is done here.
parseDialog :: Frame () -> IO (Maybe (FuncLang,CallBy))
parseDialog theFrame =
     do
	dial <- dialog theFrame [text := "Functional term to IN system", position := pt 200 20]
	p <- panel dial []
	termbox <- textCtrlRich p [text := ""]
	help <- button p
		[ text :=  "Help"
		, on command := infoDialog dial "Help" helpString
		]

	okB <- button p [ text := "Ok" ]
	canB <- button p [ text := "Cancel" ]

	-- one button per functional language constructor
	listBs <- mapM (\(name,term) -> button p
					[ text := name
					, on command := addTxtInPlace2TextCtrl termbox $ show term
					])
			listLangConstructors

	-- CallByX
	when (null listCallBy) (error "No strategies available.")
	callByUI <- radioBox p Vertical (map show listCallBy)
                      [ text := "Which evaluation strategy?"
                      , selection := 0
		      ]

	errorsUI <- textCtrl p [ visible := False ]

	set dial [ layout := container p $
	                         margin 10 $
	                            column 5 [ label "Input your term:"
	                                     , row 2 [ hfill $ widget termbox
						     , column 10 (map widget listBs)
						     ]
					     , widget callByUI
					     , minsize (sz 30 30) . fill $ widget errorsUI
					     , widget help
					     , hrule 350
	                                     , floatRight $ row 5 [widget okB, widget canB]
	                                     ]
	          ]

	showModal dial $ \stop ->
	   do
		set canB [on command := stop Nothing]
		set okB [on command :=
			     do
				set errorsUI [ visible := False, text := ""]
				txt <- get termbox text
				i <- get callByUI selection

				case parse txt of
				  Left term -> stop $ Just (term, listCallBy !! i)
				  Right errMsg ->
				     do
					set errorsUI [ visible := True
						     , text := "Parse error:\n" ++ errMsg
						     ]
					errorDialog theFrame "Parsing Error" "Parsing error.\nSee report."
			]

     where helpString = "Use the buttons on the right side to create a term or write it if already familiar with the syntax."

-- | Allow the user to supply more meaningful names for symbols that correspond to iterators with internalized arguments.
-- Checks if:
--   * given names don't contain blank or control characters
--   * given names are disjoint
meaningfulNamesDialog :: [FuncLang] -> Frame () -> IO (Maybe [(FuncLang, String)])
meaningfulNamesDialog terms theFrame =
     do
	dial <- dialog theFrame [text := "Meaningful names"
				, position := pt 200 20
				, resizeable := True
				]
	p <- panel dial []

	listG <- listCtrl p [ columns := [ ("Symbol name", AlignRight, -1)
					 , ("Term", AlignLeft, 200)
					 ]
                            , items := [[name, showAgent term] | (term, name) <- giveNames terms]
			    , style :~ (wxLC_EDIT_LABELS .+. wxLC_VRULES .+.)
			    ]

	okB <- button p [ text := "Ok" ]
	canB <- button p [ text := "Cancel" ]

	errorsUI <- textCtrl p [ visible := False ]

	set dial [ layout := container p $
	                         margin 10 $
	                            column 5 [ label "Edit the \"Symbol name\" column to give more meaningful names."
					     , fill $ widget listG
					     , fill $ widget errorsUI
					     , hrule 350
	                                     , floatRight $ row 5 [widget okB, widget canB]
	                                     ]
	          ]

	showModal dial $ \stop ->
	   do set okB  [ on command :=
			     do
				newNames <- get listG items
				let mapp = zip terms (map head newNames)
				    -- Check that given names are good names.
				    badNames = filter (badName . snd) mapp
				    -- Check that given names are disjoint.
				    equalNames = filter ((> 1) . length)
						   $ groupBy (\(_,a) (_,b) -> a == b) mapp
				case (null badNames, null equalNames) of
				  (True, True) -> stop $ Just mapp
				  (False, _) -> -- some bad names
					set errorsUI [ visible := True
						     , text := "The symbol's name(s) for the following term(s) contain invalid (control or blank) characters:\n"
								++ (indent 1 . unlines) (map (showAgent . fst) badNames)
						     ]
				  (True, False) ->  -- repeated names
					set errorsUI [ visible := True
						     , text := "The following terms have the same name(s):\n "
								++ beautify equalNames
						     ]
		       ]
	      set canB [on command := stop Nothing]
  where
	beautify :: [[(FuncLang,String)]] -> String
	beautify = unlines . map (\l -> snd (head l) ++ "\n"
				    ++ indent 1 (unlines $ map (showAgent.fst) l))
	badName = any (\c -> isSpace c || isControl c)