-- | Like Step3 but with Cut / Copy / Paste support module Step4 (step4) where import Graphics.UI.WX import Graphics.UI.WXCore hiding (wxID_CUT, wxID_COPY, wxID_PASTE) data GUIContext = GUICtx { guiWin :: Frame (), guiEditor :: TextCtrl (), guiFile :: Var (Maybe FilePath), guiTimer :: TimerEx (), guiPast :: Var [String], guiFuture :: Var [String] } wxID_MYUNDO, wxID_MYREDO, wxID_CUT, wxID_COPY, wxID_PASTE :: Id wxID_MYUNDO = 5107 wxID_MYREDO = 5108 wxID_CUT = 5031 --HACK: They're not correctly numbered in WxcDefs wxID_COPY = 5032 wxID_PASTE = 5033 step4 :: IO () step4 = do win <- frame [text := "wxhNotepad - Step 4", visible := False] editor <- textCtrl win [font := fontFixed, text := "Again, Cut/Copy/Past support comes with " ++ "text controls, but we want to have " ++ "a menu item for it too.\n" ++ "Besides, we need to adapt our Undo/Redo " ++ "implementation to these functions.\n"] filePath <- varCreate Nothing refreshTimer <- timer win [] past <- varCreate [] future <- varCreate [] let guiCtx = GUICtx win editor filePath refreshTimer past future set editor [on keyboard := \_ -> restartTimer guiCtx >> propagateEvent] timerOnCommand refreshTimer $ updatePast guiCtx updatePast guiCtx -- We create a menu for the window with the same items from steps 2 & 3 -- and a new items in the edit menu mnuFile <- menuPane [text := "File"] mnuEdit <- menuPane [text := "Edit"] menuAppend mnuFile wxID_OPEN "&Open...\tCtrl-o" "Open Page" False menuAppend mnuFile wxID_SAVE "&Save\tCtrl-s" "Save Page" False menuAppend mnuFile wxID_SAVEAS "Save &as...\tCtrl-Shift-s" "Save Page as" False menuAppend mnuFile wxID_CLOSE "&Close\tCtrl-W" "Close Page" False menuAppend mnuEdit wxID_MYUNDO "&Undo\tCtrl-z" "Undo last action" False menuAppend mnuEdit wxID_MYREDO "&Redo\tCtrl-Shift-z" "Redo last undone action" False menuAppendSeparator mnuEdit menuAppend mnuEdit wxID_CUT "C&ut\tCtrl-x" "Cut" False menuAppend mnuEdit wxID_COPY "&Copy\tCtrl-c" "Copy" False menuAppend mnuEdit wxID_PASTE "&Paste\tCtrl-v" "Paste" False evtHandlerOnMenuCommand win wxID_OPEN $ openPage guiCtx evtHandlerOnMenuCommand win wxID_SAVE $ savePage guiCtx evtHandlerOnMenuCommand win wxID_SAVEAS $ savePageAs guiCtx evtHandlerOnMenuCommand win wxID_CLOSE $ windowClose win False >> return () evtHandlerOnMenuCommand win wxID_MYUNDO $ undo guiCtx evtHandlerOnMenuCommand win wxID_MYREDO $ redo guiCtx evtHandlerOnMenuCommand win wxID_CUT $ cut guiCtx evtHandlerOnMenuCommand win wxID_COPY $ copy guiCtx evtHandlerOnMenuCommand win wxID_PASTE $ paste guiCtx set win [menuBar := [mnuFile, mnuEdit]] set win [layout := fill $ widget editor, clientSize := sz 640 480] focusOn editor set win [visible := True] savePageAs, savePage, openPage, undo, redo, restartTimer, killTimer, updatePast, clearPast, cut, copy, paste :: GUIContext -> IO () openPage guiCtx@GUICtx{guiWin = win, guiEditor = editor, guiFile = filePath} = do maybePath <- fileOpenDialog win True True "Open file..." [("Haskells (*.hs)",["*.hs"]), ("Texts (*.txt)", ["*.txt"]), ("Any file (*.*)",["*.*"])] "" "" case maybePath of Nothing -> return () Just path -> do clearPast guiCtx textCtrlLoadFile editor path updatePast guiCtx set win [text := "wxhnotepad - " ++ path] varSet filePath $ Just path savePageAs GUICtx{guiWin = win, guiEditor = editor, guiFile = filePath} = do maybePath <- fileSaveDialog win True True "Save file..." [("Haskells (*.hs)",["*.hs"]), ("Texts (*.txt)", ["*.txt"]), ("Any file (*.*)",["*.*"])] "" "" case maybePath of Nothing -> return () Just path -> do textCtrlSaveFile editor path set win [text := "wxhnotepad - " ++ path] varSet filePath $ Just path savePage guiCtx@GUICtx{guiWin = win, guiEditor = editor, guiFile = filePath} = do maybePath <- varGet filePath case maybePath of Nothing -> savePageAs guiCtx Just path -> textCtrlSaveFile editor path >> return () undo guiCtx@GUICtx{guiEditor = editor, guiPast = past, guiFuture = future} = do updatePast guiCtx history <- varGet past case history of [] -> return () [t] -> return () tnow:tlast:ts -> do varUpdate future (tnow:) varSet past $ tlast:ts set editor [text := tlast] redo guiCtx@GUICtx{guiEditor = editor, guiPast = past, guiFuture = future} = do updatePast guiCtx coming <- varGet future case coming of [] -> return () t:ts -> do varSet future ts varUpdate past (t:) set editor [text := t] updatePast guiCtx@GUICtx{guiEditor = editor, guiPast = past, guiFuture = future} = do tnow <- get editor text history <- varGet past case history of [] -> varSet past [tnow] t:_ -> if t /= tnow then do varUpdate past (tnow:) varSet future [] else return () killTimer guiCtx clearPast GUICtx{guiPast = past, guiFuture = future} = do varSet past [] varSet future [] restartTimer guiCtx@GUICtx{guiWin = win, guiTimer = refreshTimer} = do started <- timerStart refreshTimer 1000 True if started then return () else do errorDialog win "Error" "Can't start more timers" wxcAppExit killTimer GUICtx{guiTimer = refreshTimer} = timerStop refreshTimer -- We just copy the selected text copy GUICtx{guiEditor = editor} = textCtrlCopy editor -- We cut the selected text and, as the editor is modified, we update the history cut guiCtx@GUICtx{guiEditor = editor} = textCtrlCut editor >> updatePast guiCtx -- We paste the clipboard contents on the editor and, as it's modified, we update the history paste guiCtx@GUICtx{guiEditor = editor} = textCtrlPaste editor >> updatePast guiCtx