packages feed

maxent-learner-hw-gui (empty) → 0.2.0

raw patch · 9 files changed

+1360/−0 lines, 9 filesdep +Chartdep +Chart-cairodep +arraysetup-changed

Dependencies added: Chart, Chart-cairo, array, async, base, bytestring, cairo, containers, deepseq, file-embed, frpnow, frpnow-gtk3, glib, gtk3, maxent-learner-hw, mtl, parallel, random, raw-strings-qq, text, transformers, tuple

Files

+ LICENSE view
@@ -0,0 +1,339 @@+                    GNU GENERAL PUBLIC LICENSE+                       Version 2, June 1991++ Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA+ Everyone is permitted to copy and distribute verbatim copies+ of this license document, but changing it is not allowed.++                            Preamble++  The licenses for most software are designed to take away your+freedom to share and change it.  By contrast, the GNU General Public+License is intended to guarantee your freedom to share and change free+software--to make sure the software is free for all its users.  This+General Public License applies to most of the Free Software+Foundation's software and to any other program whose authors commit to+using it.  (Some other Free Software Foundation software is covered by+the GNU Lesser General Public License instead.)  You can apply it to+your programs, too.++  When we speak of free software, we are referring to freedom, not+price.  Our General Public Licenses are designed to make sure that you+have the freedom to distribute copies of free software (and charge for+this service if you wish), that you receive source code or can get it+if you want it, that you can change the software or use pieces of it+in new free programs; and that you know you can do these things.++  To protect your rights, we need to make restrictions that forbid+anyone to deny you these rights or to ask you to surrender the rights.+These restrictions translate to certain responsibilities for you if you+distribute copies of the software, or if you modify it.++  For example, if you distribute copies of such a program, whether+gratis or for a fee, you must give the recipients all the rights that+you have.  You must make sure that they, too, receive or can get the+source code.  And you must show them these terms so they know their+rights.++  We protect your rights with two steps: (1) copyright the software, and+(2) offer you this license which gives you legal permission to copy,+distribute and/or modify the software.++  Also, for each author's protection and ours, we want to make certain+that everyone understands that there is no warranty for this free+software.  If the software is modified by someone else and passed on, we+want its recipients to know that what they have is not the original, so+that any problems introduced by others will not reflect on the original+authors' reputations.++  Finally, any free program is threatened constantly by software+patents.  We wish to avoid the danger that redistributors of a free+program will individually obtain patent licenses, in effect making the+program proprietary.  To prevent this, we have made it clear that any+patent must be licensed for everyone's free use or not licensed at all.++  The precise terms and conditions for copying, distribution and+modification follow.++                    GNU GENERAL PUBLIC LICENSE+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION++  0. This License applies to any program or other work which contains+a notice placed by the copyright holder saying it may be distributed+under the terms of this General Public License.  The "Program", below,+refers to any such program or work, and a "work based on the Program"+means either the Program or any derivative work under copyright law:+that is to say, a work containing the Program or a portion of it,+either verbatim or with modifications and/or translated into another+language.  (Hereinafter, translation is included without limitation in+the term "modification".)  Each licensee is addressed as "you".++Activities other than copying, distribution and modification are not+covered by this License; they are outside its scope.  The act of+running the Program is not restricted, and the output from the Program+is covered only if its contents constitute a work based on the+Program (independent of having been made by running the Program).+Whether that is true depends on what the Program does.++  1. You may copy and distribute verbatim copies of the Program's+source code as you receive it, in any medium, provided that you+conspicuously and appropriately publish on each copy an appropriate+copyright notice and disclaimer of warranty; keep intact all the+notices that refer to this License and to the absence of any warranty;+and give any other recipients of the Program a copy of this License+along with the Program.++You may charge a fee for the physical act of transferring a copy, and+you may at your option offer warranty protection in exchange for a fee.++  2. You may modify your copy or copies of the Program or any portion+of it, thus forming a work based on the Program, and copy and+distribute such modifications or work under the terms of Section 1+above, provided that you also meet all of these conditions:++    a) You must cause the modified files to carry prominent notices+    stating that you changed the files and the date of any change.++    b) You must cause any work that you distribute or publish, that in+    whole or in part contains or is derived from the Program or any+    part thereof, to be licensed as a whole at no charge to all third+    parties under the terms of this License.++    c) If the modified program normally reads commands interactively+    when run, you must cause it, when started running for such+    interactive use in the most ordinary way, to print or display an+    announcement including an appropriate copyright notice and a+    notice that there is no warranty (or else, saying that you provide+    a warranty) and that users may redistribute the program under+    these conditions, and telling the user how to view a copy of this+    License.  (Exception: if the Program itself is interactive but+    does not normally print such an announcement, your work based on+    the Program is not required to print an announcement.)++These requirements apply to the modified work as a whole.  If+identifiable sections of that work are not derived from the Program,+and can be reasonably considered independent and separate works in+themselves, then this License, and its terms, do not apply to those+sections when you distribute them as separate works.  But when you+distribute the same sections as part of a whole which is a work based+on the Program, the distribution of the whole must be on the terms of+this License, whose permissions for other licensees extend to the+entire whole, and thus to each and every part regardless of who wrote it.++Thus, it is not the intent of this section to claim rights or contest+your rights to work written entirely by you; rather, the intent is to+exercise the right to control the distribution of derivative or+collective works based on the Program.++In addition, mere aggregation of another work not based on the Program+with the Program (or with a work based on the Program) on a volume of+a storage or distribution medium does not bring the other work under+the scope of this License.++  3. You may copy and distribute the Program (or a work based on it,+under Section 2) in object code or executable form under the terms of+Sections 1 and 2 above provided that you also do one of the following:++    a) Accompany it with the complete corresponding machine-readable+    source code, which must be distributed under the terms of Sections+    1 and 2 above on a medium customarily used for software interchange; or,++    b) Accompany it with a written offer, valid for at least three+    years, to give any third party, for a charge no more than your+    cost of physically performing source distribution, a complete+    machine-readable copy of the corresponding source code, to be+    distributed under the terms of Sections 1 and 2 above on a medium+    customarily used for software interchange; or,++    c) Accompany it with the information you received as to the offer+    to distribute corresponding source code.  (This alternative is+    allowed only for noncommercial distribution and only if you+    received the program in object code or executable form with such+    an offer, in accord with Subsection b above.)++The source code for a work means the preferred form of the work for+making modifications to it.  For an executable work, complete source+code means all the source code for all modules it contains, plus any+associated interface definition files, plus the scripts used to+control compilation and installation of the executable.  However, as a+special exception, the source code distributed need not include+anything that is normally distributed (in either source or binary+form) with the major components (compiler, kernel, and so on) of the+operating system on which the executable runs, unless that component+itself accompanies the executable.++If distribution of executable or object code is made by offering+access to copy from a designated place, then offering equivalent+access to copy the source code from the same place counts as+distribution of the source code, even though third parties are not+compelled to copy the source along with the object code.++  4. You may not copy, modify, sublicense, or distribute the Program+except as expressly provided under this License.  Any attempt+otherwise to copy, modify, sublicense or distribute the Program is+void, and will automatically terminate your rights under this License.+However, parties who have received copies, or rights, from you under+this License will not have their licenses terminated so long as such+parties remain in full compliance.++  5. You are not required to accept this License, since you have not+signed it.  However, nothing else grants you permission to modify or+distribute the Program or its derivative works.  These actions are+prohibited by law if you do not accept this License.  Therefore, by+modifying or distributing the Program (or any work based on the+Program), you indicate your acceptance of this License to do so, and+all its terms and conditions for copying, distributing or modifying+the Program or works based on it.++  6. Each time you redistribute the Program (or any work based on the+Program), the recipient automatically receives a license from the+original licensor to copy, distribute or modify the Program subject to+these terms and conditions.  You may not impose any further+restrictions on the recipients' exercise of the rights granted herein.+You are not responsible for enforcing compliance by third parties to+this License.++  7. If, as a consequence of a court judgment or allegation of patent+infringement or for any other reason (not limited to patent issues),+conditions are imposed on you (whether by court order, agreement or+otherwise) that contradict the conditions of this License, they do not+excuse you from the conditions of this License.  If you cannot+distribute so as to satisfy simultaneously your obligations under this+License and any other pertinent obligations, then as a consequence you+may not distribute the Program at all.  For example, if a patent+license would not permit royalty-free redistribution of the Program by+all those who receive copies directly or indirectly through you, then+the only way you could satisfy both it and this License would be to+refrain entirely from distribution of the Program.++If any portion of this section is held invalid or unenforceable under+any particular circumstance, the balance of the section is intended to+apply and the section as a whole is intended to apply in other+circumstances.++It is not the purpose of this section to induce you to infringe any+patents or other property right claims or to contest validity of any+such claims; this section has the sole purpose of protecting the+integrity of the free software distribution system, which is+implemented by public license practices.  Many people have made+generous contributions to the wide range of software distributed+through that system in reliance on consistent application of that+system; it is up to the author/donor to decide if he or she is willing+to distribute software through any other system and a licensee cannot+impose that choice.++This section is intended to make thoroughly clear what is believed to+be a consequence of the rest of this License.++  8. If the distribution and/or use of the Program is restricted in+certain countries either by patents or by copyrighted interfaces, the+original copyright holder who places the Program under this License+may add an explicit geographical distribution limitation excluding+those countries, so that distribution is permitted only in or among+countries not thus excluded.  In such case, this License incorporates+the limitation as if written in the body of this License.++  9. The Free Software Foundation may publish revised and/or new versions+of the General Public License from time to time.  Such new versions will+be similar in spirit to the present version, but may differ in detail to+address new problems or concerns.++Each version is given a distinguishing version number.  If the Program+specifies a version number of this License which applies to it and "any+later version", you have the option of following the terms and conditions+either of that version or of any later version published by the Free+Software Foundation.  If the Program does not specify a version number of+this License, you may choose any version ever published by the Free Software+Foundation.++  10. If you wish to incorporate parts of the Program into other free+programs whose distribution conditions are different, write to the author+to ask for permission.  For software which is copyrighted by the Free+Software Foundation, write to the Free Software Foundation; we sometimes+make exceptions for this.  Our decision will be guided by the two goals+of preserving the free status of all derivatives of our free software and+of promoting the sharing and reuse of software generally.++                            NO WARRANTY++  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,+REPAIR OR CORRECTION.++  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE+POSSIBILITY OF SUCH DAMAGES.++                     END OF TERMS AND CONDITIONS++            How to Apply These Terms to Your New Programs++  If you develop a new program, and you want it to be of the greatest+possible use to the public, the best way to achieve this is to make it+free software which everyone can redistribute and change under these terms.++  To do so, attach the following notices to the program.  It is safest+to attach them to the start of each source file to most effectively+convey the exclusion of warranty; and each file should have at least+the "copyright" line and a pointer to where the full notice is found.++    {description}+    Copyright (C) {year}  {fullname}++    This program is free software; you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation; either version 2 of the License, or+    (at your option) any later version.++    This program is distributed in the hope that it will be useful,+    but WITHOUT ANY WARRANTY; without even the implied warranty of+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+    GNU General Public License for more details.++    You should have received a copy of the GNU General Public License along+    with this program; if not, write to the Free Software Foundation, Inc.,+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.++Also add information on how to contact you by electronic and paper mail.++If the program is interactive, make it output a short notice like this+when it starts in an interactive mode:++    Gnomovision version 69, Copyright (C) year name of author+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.+    This is free software, and you are welcome to redistribute it+    under certain conditions; type `show c' for details.++The hypothetical commands `show w' and `show c' should show the appropriate+parts of the General Public License.  Of course, the commands you use may+be called something other than `show w' and `show c'; they could even be+mouse-clicks or menu items--whatever suits your program.++You should also get your employer (if you work as a programmer) or your+school, if any, to sign a "copyright disclaimer" for the program, if+necessary.  Here is a sample; alter the names:++  Yoyodyne, Inc., hereby disclaims all copyright interest in the program+  `Gnomovision' (which makes passes at compilers) written by James Hacker.++  {signature of Ty Coon}, 1 April 1989+  Ty Coon, President of Vice++This General Public License does not permit incorporating your program into+proprietary programs.  If your program is a subroutine library, you may+consider it more useful to permit linking proprietary applications with the+library.  If this is what you want to do, use the GNU Lesser General+Public License instead of this License.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ app/FeatureTableEditor.hs view
@@ -0,0 +1,264 @@+{-# LANGUAGE LambdaCase, OverloadedStrings, ExtendedDefaultRules #-}++module FeatureTableEditor (+    createEditableFT,+    displayFeatureMatrix,+    displayDynFeatureTable+) where++import Graphics.UI.Gtk+import Control.FRPNow hiding (swap)+import Control.FRPNow.GTK+import Control.FRPNow.GTK.MissingFFI+import Control.Monad+import Control.Exception+import Data.Tuple+import Text.PhonotacticLearner.PhonotacticConstraints+import qualified Data.Text as T+import qualified Data.Text.Encoding as T+import Data.Array.IArray+import qualified Data.Map.Lazy as M+import qualified Data.ByteString as B+import Control.DeepSeq++default (T.Text)+++data FTRow = FTRow T.Text (M.Map SegRef FeatureState) deriving (Eq, Show)++fsTrue FPlus = True+fsTrue FMinus = False+fsTrue FOff = False++fsInc FPlus = False+fsInc FMinus = True+fsInc FOff = False++fsCycle FPlus = FMinus+fsCycle FMinus = FOff+fsCycle FOff = FPlus++ft2rows ft = ffor (assocs . featNames $ ft) $ \(fi,fn) -> let+    sbounds = bounds (segNames ft)+    sidxs = indices (segNames ft)+    fsmap = M.fromList [(i,ftlook ft i fi) | i <- sidxs]+    in FTRow fn fsmap++rows2ft :: Array SegRef String -> [FTRow] -> FeatureTable String+rows2ft segs rows = FeatureTable ftarr fnames segs flook slook where+    nf = length rows+    (sa,sb) = bounds segs+    nrows = zip [1..] rows+    fnames = array (1,nf) [(n,fn) | (n,FTRow fn _) <- nrows]+    ftarr = array ((sa,1),(sb,nf)) $ do+        (fi, FTRow _ fsmap) <- nrows+        (si,fs) <- M.assocs fsmap+        return ((si,fi),fs)+    slook = M.fromList (fmap swap (assocs segs))+    flook = M.fromList (fmap swap (assocs fnames))+++setFTContents :: TreeView -> FeatureTable String -> IO (Array SegRef String, ListStore FTRow)+setFTContents editor newft = do+    let segs = segNames newft+        rows = ft2rows newft+    model <- listStoreNew rows+    oldcols <- treeViewGetColumns editor+    forM_ oldcols $ \col -> treeViewRemoveColumn editor col+    treeViewSetModel editor model++    lcol <- treeViewColumnNew+    set lcol [treeViewColumnTitle := "Feature"]+    lcell <- cellRendererTextNew+    set lcell [cellTextEditable := True]+    cellLayoutPackStart lcol lcell True+    treeViewAppendColumn editor lcol+    cellLayoutSetAttributes lcol lcell model $ \(FTRow fn _) ->+        [cellText := fn]+    on lcell edited $ \[i] newtext -> do+        FTRow _ fsmap <- listStoreGetValue model i+        listStoreSetValue model i (FTRow newtext fsmap)++    forM_ (assocs segs) $ \(si,sn) -> do+        col <- treeViewColumnNew+        set col [treeViewColumnTitle := sn]+        cell <- cellRendererToggleNew+        cellLayoutPackStart col cell True+        treeViewAppendColumn editor col+        cellLayoutSetAttributes col cell model $ \(FTRow _ fsmap) ->+            [cellToggleActive := fsTrue (fsmap M.! si),+            cellToggleInconsistent := fsInc (fsmap M.! si)]+        on cell cellToggled $ \tpStr -> do+            let [i] = stringToTreePath tpStr+            FTRow fn fsmap <- listStoreGetValue model i+            let newrow = FTRow fn (M.adjust fsCycle si fsmap)+            listStoreSetValue model i newrow+    pcol <- treeViewColumnNew+    treeViewAppendColumn editor pcol+    return (segs, model)+++watchFtModel :: (Array SegRef String, ListStore FTRow) -> Now (Behavior (FeatureTable String))+watchFtModel (segs, model) = do+    (rowsChanged, rowcb) <- callbackStream+    let changecb = listStoreToList model >>= rowcb+    sync $ do+        on model rowChanged $ \_ _ -> changecb+        on model rowInserted $ \_ _ -> changecb+        on model rowDeleted $ \_ -> changecb+    initrows <- sync $ listStoreToList model+    dynrows <- sample $ fromChanges initrows rowsChanged+    return $ fmap (rows2ft segs) dynrows++loadFTfromFile :: FilePath -> IO (Maybe (FeatureTable String))+loadFTfromFile fp = fmap join . checkIOError $ do+    bincsv <- B.readFile fp+    evaluate $ force . csvToFeatureTable id . T.unpack =<< either (const Nothing) Just (T.decodeUtf8' bincsv)++createEditableFT :: Maybe Window -> FeatureTable String -> Now (VBox, Behavior (FeatureTable String))+createEditableFT transwin initft = do+    editor <- sync treeViewNew+    editor' <- createScrolledWindow editor+    sync $ set editor' [scrolledWindowOverlay := False]++    (loadButton, loadPressed) <- createButton (Just "document-open") (Just "Load Table")+    (saveButton, savePressed) <- createButton (Just "document-save") (Just "Save Table")+    (addButton, addPressed) <- createButton (Just "list-add") Nothing+    (delButton, delPressed) <- createButton (Just "list-remove") Nothing+    (editButton, isEditing) <- createToggleButton (Just "accessories-text-editor") (Just "Edit Table") False+    setAttr widgetSensitive addButton isEditing+    setAttr widgetSensitive delButton isEditing++    (ftReplaced, replaceft) <- callbackStream+    (modelReplaced, replaceModel) <- callbackStream+    initmodel <- sync $ setFTContents editor initft+    currentModel <- sample $ fromChanges initmodel modelReplaced++    initdft <- watchFtModel initmodel+    callStream (sync . replaceft <=< watchFtModel . last) modelReplaced+    currentft <- sample$ foldrSwitch initdft ftReplaced++    viewer <- displayDynFeatureTable currentft++    bar <- createHBox 2 $ do+        bpack addButton+        bpack delButton+        bspacer+        bpack editButton+        bpack loadButton+        bpack saveButton+++    top <- sync stackNew+    sync $ do+        stackAddNamed top viewer "False"+        stackAddNamed top editor' "True"+    setAttr stackVisibleChildName top (fmap show isEditing)++    vb <- createVBox 2 $ do+        bstretch =<< createFrame ShadowIn top+        bpack bar++    csvfilter <- sync fileFilterNew+    allfilter <- sync fileFilterNew+    sync $ do+        fileFilterAddMimeType csvfilter "text/csv"+        fileFilterSetName csvfilter "CSV"+        fileFilterAddPattern allfilter "*"+        fileFilterSetName allfilter "All Files"++    loadDialog <- sync $ fileChooserDialogNew (Just "Open Feature Table") transwin FileChooserActionOpen+        [("gtk-cancel", ResponseCancel), ("gtk-open", ResponseAccept)]+    sync $ fileChooserAddFilter loadDialog csvfilter+    sync $ fileChooserAddFilter loadDialog allfilter+    flip callStream loadPressed $ \_ -> do+        filePicked <- runFileChooserDialog loadDialog+        emft <- planNow . ffor filePicked $ \case+            Nothing -> return never+            Just fn -> async $ loadFTfromFile fn+        planNow . ffor (join emft) $ \case+            Nothing -> sync $ putStrLn "Invalid CSV table."+            Just newft -> sync $ do+                newmodel <- setFTContents editor newft+                replaceModel newmodel+                putStrLn "Feature table sucessfully loaded."+        return ()++    saveDialog <- sync $ fileChooserDialogNew (Just "Save Feature Table") transwin FileChooserActionSave+        [("gtk-cancel", ResponseCancel), ("gtk-save", ResponseAccept)]+    sync $ fileChooserAddFilter saveDialog csvfilter+    sync $ fileChooserAddFilter saveDialog allfilter+    flip callStream savePressed $ \_  -> do+        savePicked <- runFileChooserDialog saveDialog+        planNow . ffor savePicked $ \case+            Nothing -> return ()+            Just fn -> do+                ft <- sample currentft+                async $ do+                    let csv = featureTableToCsv id ft+                        bincsv = T.encodeUtf8 (T.pack csv)+                    B.writeFile fn bincsv+                    putStrLn $ "Wrote Feature Table " ++ fn+                return ()+        return ()++    flip callStream addPressed $ \_ -> do+        (segs, store) <- sample currentModel+        let newRow = FTRow "" (M.fromList [(s,FOff) | s <- indices segs])+        sync $ listStoreAppend store newRow+        return ()+    flip callStream delPressed $ \_ -> do+        (segs, store) <- sample currentModel+        (cur, _) <- sync $ treeViewGetCursor editor+        sync $ case cur of+            [i] -> listStoreRemove store i+            _ -> return ()++    return (vb, currentft)++displayFeatureMatrix :: FeatureTable String -> IO Grid+displayFeatureMatrix ft = do+    g <- gridNew+    set g [widgetName := Just "featuretable"]+    --set g [ containerBorderWidth := 5 ]+    --gridSetColumnSpacing g 2+    forM_ (assocs (segNames ft)) $ \(Seg n,s) -> do+        l <- labelNew (Just s)+        let oddclass = if odd n then ["oddcol"] else []+        widgetAddClasses (["segheader"] ++ oddclass) l+        gridAttach g l n 0 1 1+    forM_ (assocs (featNames ft)) $ \(n,f) -> do+        l <- labelNew (Just f)+        widgetAddClasses ["featheader"] l+        set l [miscXalign := 0]+        gridAttach g l 0 n 1 1+    forM_ (assocs (featTable ft)) $ \((Seg s, f), fs) -> do+        l <- case fs of+            FPlus -> labelNew (Just "+")+            FMinus -> labelNew (Just "−")+            FOff -> do+                l <- labelNew (Just "0")+                widgetAddClasses ["featzero"] l+                return l+        let oddclass = if odd s then ["oddcol"] else []+        widgetAddClasses oddclass l+        gridAttach g l s f 1 1+    return g++displayDynFeatureTable :: Behavior (FeatureTable String) -> Now ScrolledWindow+displayDynFeatureTable dynft = do+    initft <- sample dynft+    scr <- sync $ scrolledWindowNew Nothing Nothing+    done <- getUnrealize scr+    let ftchanged = toChanges dynft `beforeEs` done+    initwidget <- sync $ displayFeatureMatrix initft+    sync $ scrolledWindowAddWithViewport scr initwidget+    Just vp' <- sync $ binGetChild scr+    let vp = castToViewport vp'+    flip callIOStream ftchanged $ \newft -> do+        Just oldw <- binGetChild vp+        widgetDestroy oldw+        newwidget <- displayFeatureMatrix newft+        containerAdd vp newwidget+        widgetShowAll newwidget+    return scr
+ app/GrammarEditor.hs view
@@ -0,0 +1,157 @@+{-# LANGUAGE LambdaCase, OverloadedStrings, ExtendedDefaultRules, TupleSections, ScopedTypeVariables #-}++module GrammarEditor where++import Graphics.UI.Gtk+import Control.FRPNow hiding (swap, when)+import Control.FRPNow.GTK+import Control.Monad+import Control.Monad.IO.Class+import Control.Exception+import Data.Foldable+import Data.Maybe+import Text.PhonotacticLearner.PhonotacticConstraints+import Text.PhonotacticLearner.PhonotacticConstraints.FileFormats+import Text.PhonotacticLearner.MaxentGrammar+import Text.PhonotacticLearner.Util.Ring+import qualified Data.Text as T+import qualified Data.Text.Encoding as T+import qualified Data.Text.Encoding.Error as T+import qualified Data.Map.Lazy as M+import qualified Data.ByteString as B+import qualified Data.Set as S+import Data.Array.IArray+import Control.DeepSeq+import Text.Read (readMaybe)+import Data.IORef+import Numeric++import qualified Graphics.Rendering.Cairo as C+import Graphics.Rendering.Chart.Easy hiding (indices, set')+import Graphics.Rendering.Chart.Renderable+import Graphics.Rendering.Chart.Geometry+import Graphics.Rendering.Chart.Drawing+import Graphics.Rendering.Chart.Backend.Cairo+import Graphics.Rendering.Chart.State(EC, execEC)++default (T.Text)++createChartWidget :: (Eq a, ToRenderable c) => Behavior a -> (a -> c) -> Now DrawingArea+createChartWidget datafeed chartmaker = do+    box <- sync $ drawingAreaNew+    initdata <- sample datafeed+    chartref <- sync $ newIORef (chartmaker initdata)+    let doDraw :: C.Render ()+        doDraw = do+            w <- fromIntegral <$> liftIO (widgetGetAllocatedWidth box)+            h <- fromIntegral <$> liftIO (widgetGetAllocatedHeight box)+            chart <- liftIO $ readIORef chartref+            void $ runBackend (defaultEnv bitmapAlignmentFns) (render (toRenderable chart) (w,h))+    sync $ on box draw doDraw+    callIOStream (\dat -> writeIORef chartref (chartmaker dat) >> widgetQueueDraw box) (toChanges datafeed)+    return box++createLoadableGrammar :: Maybe Window -> Behavior (S.Set T.Text) -> EvStream PhonoGrammar -> Now (VBox, Behavior (Maybe PhonoGrammar))+createLoadableGrammar transwin validsegs extreplace = do++    (grammarLoaded, loadReplace) <- callbackStream+    let grammarChanged = merge grammarLoaded (fmap Just extreplace)+    currentGrammar <- sample $ fromChanges Nothing grammarChanged++    let lenData = fmap (fmap lengthDist) currentGrammar+        lenChart mdat = layout where+            layout = def & layout_x_axis . laxis_title .~ "Word Length (Segments)"+                         & layout_y_axis . laxis_title .~ "Frequency"+                         & layout_x_axis . laxis_override .~ axisGridHide+                         & layout_left_axis_visibility .~ AxisVisibility True False False+                         & layout_y_axis . laxis_override .~ (\ad -> ad{_axis_grid = [], _axis_ticks = [], _axis_labels = []})+                         & layout_legend .~ Nothing+                         & layout_plots .~ fmap plotBars mbars+            mbars = case mdat of+                Nothing -> []+                Just arr -> let+                    total :: Double = fromIntegral (sum arr)+                    bardata = fmap (\(x,y) -> (x, [fromIntegral y / total])) (assocs arr)+                    barplot = def & plot_bars_titles .~ [""]+                                  & plot_bars_values .~ bardata+                                  & plot_bars_spacing .~ BarsFixGap 0 5+                    in [barplot]++    let rulelist (PhonoGrammar _ cs ws) = reverse (zip cs (coords ws))+        rulefeed = fmap (maybe [] rulelist) currentGrammar+        gvalid = do+            segs <- validsegs+            let allValid (ClassGlob _ _ gp) = all (\(_,NClass _ fs) -> all (\(_,f) -> S.member f segs) fs) gp+            cg <- currentGrammar+            return $ fmap (\(PhonoGrammar _ g _) -> all allValid g) cg+        validdesc = ffor gvalid $ \case+            Just True -> ""+            Just False -> "Invalid Features Detected, will ignore"+            Nothing -> "No Grammar Loaded"++    lenchartdisplay <- createChartWidget lenData lenChart+    validlabel <- createLabelDisplay validdesc+    (ruledisplay,_) <- createListDisplay rulefeed $ do+        tvColumn "Weight" $ tvTextDisplay (\(_,w) -> T.pack (showFFloat (Just 2) w []))+        tvColumn "Constraint" $ tvShowDisplay fst++    (loadButton, loadPressed) <- createButton (Just "document-open") (Just "Load Grammar")+    (saveButton, savePressed) <- createButton (Just "document-save") (Just "Save Grammar")+    setAttr widgetSensitive saveButton (fmap isJust currentGrammar)++    vb <- createVBox 2 $ do+        bpack =<< createFrame ShadowIn =<< set' [widgetHeightRequest := 125] lenchartdisplay+        bstretch  =<< createFrame ShadowIn =<< createScrolledWindow ruledisplay+        bpack <=< createHBox 2 $ do+            bstretch =<< set' [widgetWidthRequest := 125, widgetMarginRight := 10] validlabel+            bpack loadButton+            bpack saveButton++    txtfilter <- sync fileFilterNew+    allfilter <- sync fileFilterNew++    sync $ do+        fileFilterAddMimeType txtfilter "text/*"+        fileFilterSetName txtfilter "Text Files"+        fileFilterAddPattern allfilter "*"+        fileFilterSetName allfilter "All Files"++    loadDialog <- sync $ fileChooserDialogNew (Just "Load Grammar") transwin FileChooserActionOpen+        [("gtk-cancel", ResponseCancel), ("gtk-open", ResponseAccept)]+    sync $ fileChooserAddFilter loadDialog txtfilter+    sync $ fileChooserAddFilter loadDialog allfilter+    flip callStream loadPressed $ \_ -> do+        filePicked <- runFileChooserDialog loadDialog+        planNow . ffor filePicked $ \case+            Nothing -> return never+            Just fn -> async $ do+                rawfile <- fmap (T.decodeUtf8With T.lenientDecode) (B.readFile fn)+                mg <- evaluate . force $ parseGrammar rawfile+                loadReplace mg+                putStrLn $ case mg of+                    Just _ -> "Grammar loaded: " ++ fn+                    Nothing -> "Invalid Grammar: " ++ fn+        return ()++    saveDialog <- sync $ fileChooserDialogNew (Just "Save Grammar") transwin FileChooserActionSave+        [("gtk-cancel", ResponseCancel), ("gtk-save", ResponseAccept)]+    sync $ fileChooserAddFilter saveDialog txtfilter+    sync $ fileChooserAddFilter saveDialog allfilter+    flip callStream savePressed $ \_  -> do+        mg <- sample currentGrammar+        case mg of+            Just g -> do+                savePicked <- runFileChooserDialog saveDialog+                planNow . ffor savePicked $ \case+                    Nothing -> return ()+                    Just fn -> do+                        async $ do+                            let out = serGrammar g+                                binout = T.encodeUtf8 out+                            B.writeFile fn binout+                            putStrLn $ "Wrote Grammar " ++ fn+                        return ()+                return ()+            Nothing -> return ()++    return (vb, currentGrammar)
+ app/LearnerControls.hs view
@@ -0,0 +1,220 @@+{-# LANGUAGE LambdaCase, OverloadedStrings, ExtendedDefaultRules, TupleSections, RecursiveDo #-}+module LearnerControls where++import Graphics.UI.Gtk+import Control.FRPNow as FRP hiding (swap, when)+import Control.FRPNow.GTK+import Control.Monad+import Control.Monad.Trans+import Control.Monad.Reader+import Control.Exception+import Control.Concurrent+import Control.Concurrent.MVar+import Control.Monad.State+import Data.Foldable+import Data.Monoid+import Data.Tuple+import Data.Tuple.Select+import Data.Maybe+import Data.List+import Text.PhonotacticLearner.PhonotacticConstraints+import Text.PhonotacticLearner.PhonotacticConstraints.Generators+import Text.PhonotacticLearner.PhonotacticConstraints.FileFormats+import Text.PhonotacticLearner.MaxentGrammar+import Text.PhonotacticLearner.Util.Probability+import Text.PhonotacticLearner.DFST+import Text.PhonotacticLearner+import qualified Data.Text as T+import qualified Data.Text.Encoding as T+import qualified Data.Text.Encoding.Error as T+import qualified Data.ByteString as B+import qualified Data.Map.Lazy as M+import qualified Data.Set as S+import Data.Array.IArray+import Control.DeepSeq+import Control.Arrow+import Text.Read (readMaybe)+import System.Random (getStdRandom)+import System.IO+import Control.Parallel++default (T.Text)++isSorted :: (Ord a) => [a] -> Bool+isSorted []       = True+isSorted [x]      = True+isSorted (x:xs@(y:_)) = x < y && isSorted xs++parAhead :: [a] -> [a]+parAhead [] = []+parAhead [x] = [x]+parAhead (x:y:xs) = (y `par` x : parAhead (y:xs))++chunkList :: Int -> [a] -> [[a]]+chunkList _ [] = []+chunkList n xs = let (y,ys) = splitAt n xs in y : chunkList n ys++parListChunk :: (NFData a) => Int -> [a] -> [a]+parListChunk n = join . parAhead . fmap force . chunkList n++generateSalad :: (Double -> T.Text -> IO ()) -> FeatureTable String -> PhonoGrammar -> Int -> IO [LexRow]+generateSalad progcb ft (PhonoGrammar lendist rules ws) n = do+    let nrules = length rules+        lencdf = massToCdf (fmap (second fromIntegral) (assocs lendist))+        blankdfa :: MulticountDFST SegRef+        blankdfa = pruneAndPack . nildfa $ srBounds ft+        addRule :: ClassGlob -> (MulticountDFST SegRef, Int) -> IO (MulticountDFST SegRef, Int)+        addRule r (g,n) = do+            g' <- evaluate . pruneAndPack $ rawIntersection consMC (unpackDFA . cgMatchCounter ft $ r) (unpackDFA g)+            progcb (0.95 * (fromIntegral n / fromIntegral nrules)) (T.pack ("Building DFA: " ++ show n ++ "/" ++ show nrules))+            return (g',n+1)++    dfa <- fmap fst $ foldrM addRule (blankdfa,1) rules++    progcb 1 "Running Generator"++    salad <- getStdRandom . runState $ sampleWordSalad (fmap (maxentProb ws) (unpackDFA dfa)) lencdf n++    let saladLex = wordFreqs . sortLexicon . fmap (\x -> (x,1)) $ salad+    return $ fmap (\(w,f) -> LexRow (refsToSegs ft w) f) saladLex++++createPhonotacticLearnerWidget :: Behavior (FeatureTable String)+                               -> Behavior [LexRow]+                               -> Behavior (Maybe PhonoGrammar)+                               -> Now (Notebook, HBox, EvStream [LexRow], EvStream PhonoGrammar)+createPhonotacticLearnerWidget dynft dynlex dyngrammar = mdo+    (grammarOut,outputGrammar) <- callbackStream+    (lexOut, outputLex) <- callbackStream++    let dynfeats = fmap (elems . featNames) dynft+        dynCoreItems = fmap (fmap (\f -> (f, "[±" <> f <> "]"))) dynfeats++    (cbEdges,useEdges) <- createCheckButton "Allow word-boundary constraints" False+    (cbTrigrams,useTrigrams) <- createCheckButton "Allow trigram constraints using these core classes:" False+    (listTrigrams,trigramsCore) <- createDynamicChecklist dynCoreItems+    (cbBroken,useBroken) <- createCheckButton "Allow trigram constraints using these separator classes:" False+    (listBroken,brokenCore) <- createDynamicChecklist dynCoreItems++    sync $ set listTrigrams [widgetMarginLeft := 20]+    sync $ set listBroken [widgetMarginLeft := 20]+    setAttr widgetSensitive listTrigrams useTrigrams+    setAttr widgetSensitive listBroken useBroken++    (threshEntry,threshText) <- createFilteredEntry (`elem` ("1234567890. "::String)) "0.01 0.1 0.2 0.3"+    (learnSaladEntry, learnSaladVal) <- createIntSpinEntry (100,10000) 100 3000++    (learnStartButton, learnStartPressed) <- createButton (Just "system-run") (Just "Start")++    (gibberSizeEntry,gibberSizeVal) <- createIntSpinEntry (100,10000) 100 3000+    (gibberStartButton, gibberStartPressed) <- createButton (Just "system-run") (Just "Start")+++    let threshVals :: Behavior (Maybe [Double])+        threshVals = ffor threshText $ mfilter (all (<1)) . mfilter isSorted . mapM readMaybe . words+        candsettings = do+            ft <- dynft+            ue <- useEdges+            utri <- useTrigrams+            mtri <- if utri then fmap Just trigramsCore else pure Nothing+            ubrk <- useBroken+            mbrk <- if ubrk then fmap Just brokenCore else pure Nothing+            return (ft, CandidateSettings ue mtri mbrk)+    (candidateData,candidatePending) <- mapBAsync done (0,0,[]) (force . uncurry candidateGrammar) candsettings++    ugSpinner <- createSpinner candidatePending+    ugHeader <- createLabelDisplay . ffor candidateData $ \(nclasses,ncand,_) ->+        "UG currently contains " <> T.pack (show nclasses) <> " classes and " <> T.pack (show ncand) <> " candidates."++    (progressSet, setProgress) <- callbackStream+    progress <- sample $ fromChanges Nothing progressSet+    let isPending = fmap isJust progress++    pbar <- createProgressBar progress+    (stopButton, stopPressed) <- createButton (Just "process-stop") (Just "Stop")+    setAttr widgetSensitive stopButton isPending++    setAttr widgetSensitive gibberStartButton $ do+        mg <- dyngrammar+        p <- isPending+        return $ isJust mg && not p+    setAttr widgetSensitive learnStartButton $ do+        l <- dynlex+        gp <- candidatePending+        p <- isPending+        tv <- threshVals+        return $ not (null l) && not gp && not p && isJust tv++    flip callStream gibberStartPressed $ \_ -> do+        ft <- sample dynft+        mgrammar <- sample dyngrammar+        n <- sample gibberSizeVal+        case mgrammar of+            Just pg -> void $ do+                (tdone, amdone) <- callback+                clickedStop <- sample $ next stopPressed+                sync $ setProgress (Just (0,"Statred Gibbering"))+                thread <- sync . forkIO . flip finally (amdone () >> setProgress Nothing) $ do+                    let progcb p s = mask_ $ setProgress (Just (p,s))+                    salad <- generateSalad progcb ft pg n+                    mask_ $ outputLex salad+                doStop <- sample $ (sync (killThread thread) <$ clickedStop) `beforeE` tdone+                planNow doStop+            Nothing -> return ()++    flip callStream learnStartPressed $ \_ -> do+        ft <- sample dynft+        l <- sample dynlex+        (_,ncands,candglobs) <- sample candidateData+        mthresh <- sample threshVals+        nsalad <- sample learnSaladVal+        when (not (null l) && isJust mthresh) $ do+            let thresh = fromJust mthresh+            (tdone, amdone) <- callback+            clickedStop <- sample $ next stopPressed+            sync $ setProgress (Just (0,"Statred Learning"))+            thread <- sync . forkIO . flip finally (amdone () >> setProgress Nothing) $ let+                nthresh = length thresh+                cands = parListChunk 500 $ fmap (force . (id *** matchCounter)) candglobs+                progresscb pass cand = mask_ $ let+                    prg = (fromIntegral (pass-1) + (fromIntegral cand / fromIntegral ncands)) / fromIntegral nthresh+                    txt = T.pack $ "Pass " ++ show pass ++ "/" ++ show nthresh ++ ": Processing candidate " ++ show cand ++ "/" ++ show ncands ++ "."+                    in setProgress (Just (prg,txt))+                grammarcb lenarr rules _ ws = mask_ $ outputGrammar (PhonoGrammar lenarr rules ws)+                lexlist = ffor l $ \(LexRow w f) -> (segsToRefs ft w, f)+                in void $ generateGrammarCB progresscb grammarcb nsalad thresh cands lexlist+            doStop <- sample $ (sync (killThread thread) <$ clickedStop) `beforeE` tdone+            planNow doStop+            return ()++    let allmargins n = [widgetMarginLeft := n, widgetMarginRight := n, widgetMarginTop := n, widgetMarginBottom := n]+    controls <- createNotebook $ do+        nbpage "Learn" <=< createScrolledViewport <=< set' (allmargins 10) <=< createVBox 5 $ do+            bpack <=< set' [widgetMarginLeft := 5, widgetMarginRight := 5] <=< createHBox 0 $ do+                bstretch ugHeader+                bpack ugSpinner+            bpack cbEdges+            bpack cbTrigrams+            bpack =<< set' [widgetMarginLeft := 20] listTrigrams+            bpack cbBroken+            bpack =<< set' [widgetMarginLeft := 20] listBroken+            bpack <=< createExpander "Advanced Settings" False <=< set' [widgetMarginLeft := 20] <=< createGrid 5 2 $ do+                gcell (1,1) =<< createLabel "Selection thresholds"+                gcell (2,1) =<< set' [widgetHExpand := True] threshEntry+                gcell (1,2) =<< createLabel "Selection salad size"+                gcell (2,2) =<< set' [widgetHExpand := True] learnSaladEntry+            bpack learnStartButton+        nbpage "Gibber" <=< set' (allmargins 10) <=< createVBox 2 $ do+            bpack <=< createHBox 5 $ do+                bpack =<< createLabel "Number of words"+                bstretch gibberSizeEntry+            bspacer+            bpack gibberStartButton++    done <- getUnrealize controls+    progcontrols <- createHBox 2 $ do+        bstretch pbar+        bpack stopButton++    return (controls, progcontrols, lexOut, grammarOut)
+ app/LexiconEditor.hs view
@@ -0,0 +1,227 @@+{-# LANGUAGE LambdaCase, OverloadedStrings, ExtendedDefaultRules, TupleSections #-}++module LexiconEditor (+    LexRow(..), createEditableLexicon, segsFromFt+) where++import Graphics.UI.Gtk+import Control.FRPNow hiding (swap, when)+import Control.FRPNow.GTK+import Control.Monad+import Control.Exception+import Data.Foldable+import Data.Tuple+import Data.Tuple.Select+import Data.Maybe+import Text.PhonotacticLearner.PhonotacticConstraints+import Text.PhonotacticLearner.PhonotacticConstraints.FileFormats+import Text.PhonotacticLearner+import qualified Data.Text as T+import qualified Data.Text.Encoding as T+import qualified Data.Text.Encoding.Error as T+import qualified Data.ByteString as B+import qualified Data.Map.Lazy as M+import qualified Data.Set as S+import Control.DeepSeq+import Text.Read (readMaybe)++default(T.Text)++data LexSourceType = FieroList | FieroText++data LexSource = Custom | FromStored LexSourceType T.Text++segsFromFt :: FeatureTable String -> S.Set String+segsFromFt = M.keysSet . segLookup+++setLexContents :: TreeView -> S.Set String -> [LexRow] -> IO (ListStore LexRow)+setLexContents editor segs initlist = do+    model <- listStoreNew initlist+    oldcols <- treeViewGetColumns editor+    forM_ oldcols $ \col -> treeViewRemoveColumn editor col+    treeViewSetModel editor model++    wcol <- treeViewColumnNew+    set wcol [treeViewColumnTitle := "Word", treeViewColumnExpand := True]+    wcell <- cellRendererTextNew+    set wcell [cellTextEditable := True]+    cellLayoutPackStart wcol wcell True+    treeViewAppendColumn editor wcol+    cellLayoutSetAttributes wcol wcell model $ \(LexRow w _) -> [cellText := joinFiero segs w]+    on wcell edited $ \[i] rawword -> do+        let newword = segmentFiero segs rawword+        LexRow w f <- listStoreGetValue model i+        when (w /= newword) $ listStoreSetValue model i (LexRow newword f)++    adj <- adjustmentNew 1 1 10000 1 10 0+    fcol <- treeViewColumnNew+    set fcol [treeViewColumnTitle := "Frequency"]+    fcell <- cellRendererSpinNew+    set fcell [cellTextEditable := True, cellRendererSpinAdjustment := adj]+    cellLayoutPackStart fcol fcell True+    treeViewAppendColumn editor fcol+    cellLayoutSetAttributes fcol fcell model $ \(LexRow _ f) -> [cellText := show f]+    on fcell edited $ \[i] newval -> do+        LexRow w f <- listStoreGetValue model i+        case readMaybe newval of+            Just newfreq | (f /= newfreq) -> listStoreSetValue model i (LexRow w newfreq)+            _ -> return ()++    return model++watchLexModel :: LexSource -> S.Set String -> ListStore LexRow -> Now (Behavior (LexSource, S.Set String, [LexRow]))+watchLexModel src segs model = do+    (lexChanged, changeLex) <- callbackStream+    initlist <- sync $ listStoreToList model+    let changecb = listStoreToList model >>= changeLex+    sync $ do+        on model rowChanged $ \_ _ -> changecb+        on model rowInserted $ \_ _ -> changecb+        on model rowDeleted $ \_ -> changecb+    sample $ fromChanges (src, segs, initlist) (fmap (Custom, segs, ) lexChanged)++++createEditableLexicon :: Maybe Window -> Behavior (S.Set String) -> EvStream [LexRow] -> Now (VBox, Behavior [LexRow])+createEditableLexicon transwin currentsegs extreplace = do+    vb <- sync $ vBoxNew False 2+    editor <- sync treeViewNew++    (addButton,addPressed) <- createButton (Just "list-add") Nothing+    (delButton,delPressed) <- createButton (Just "list-remove") Nothing+    (loadListButton, loadListPressed) <- createButton (Just "document-open") (Just "Load Lexicon")+    (loadTextButton, loadTextPressed) <- createButton (Just "document-open") (Just "Collate Text")+    (saveButton, savePressed) <- createButton (Just "document-save") (Just "Save Lexicon")++    vb <- createVBox 2 $ do+        bstretch =<< createFrame ShadowIn =<< createScrolledWindow editor+        bpack <=< createHBox 2 $ do+            bpack addButton+            bpack delButton+            bspacer+            bpack loadListButton+            bpack loadTextButton+            bpack saveButton++    let segsChanged = toChanges currentsegs+    (modelChanged, changeModel) <- callbackStream+    (dLexChanged, changeDLex) <- callbackStream+    initsegs <- sample $ currentsegs+    initmodel <- sync $ setLexContents editor initsegs []+    initDLex <- watchLexModel Custom initsegs initmodel++    currentModel <- sample $ fromChanges initmodel modelChanged+    currentLex <- sample $ foldrSwitch initDLex dLexChanged++    flip callStream addPressed $ \_ -> do+        (store) <- sample currentModel+        let newRow = LexRow [] 1+        sync $ listStoreAppend store newRow+        return ()+    flip callStream delPressed $ \_ -> do+        (store) <- sample currentModel+        (cur, _) <- sync $ treeViewGetCursor editor+        sync $ case cur of+            [i] -> listStoreRemove store i+            _ -> return ()++    txtfilter <- sync fileFilterNew+    allfilter <- sync fileFilterNew+    sync $ do+        fileFilterAddMimeType txtfilter "text/*"+        fileFilterSetName txtfilter "Text Files"+        fileFilterAddPattern allfilter "*"+        fileFilterSetName allfilter "All Files"++    saveDialog <- sync $ fileChooserDialogNew (Just "Save Lexicon") transwin FileChooserActionSave+        [("gtk-cancel", ResponseCancel), ("gtk-save", ResponseAccept)]+    sync $ fileChooserAddFilter saveDialog txtfilter+    sync $ fileChooserAddFilter saveDialog allfilter+    flip callStream savePressed $ \_  -> do+        (_,segs,rows) <- sample currentLex+        savePicked <- runFileChooserDialog saveDialog+        planNow . ffor savePicked $ \case+            Nothing -> return ()+            Just fn -> do+                async $ do+                    let out = serWordlist segs rows+                        binout = T.encodeUtf8 out+                    B.writeFile fn binout+                    putStrLn $ "Wrote Feature Table " ++ fn+                return ()+        return ()++    loadListDialog <- sync $ fileChooserDialogNew (Just "Load Lexicon") transwin FileChooserActionOpen+        [("gtk-cancel", ResponseCancel), ("gtk-open", ResponseAccept)]+    sync $ fileChooserAddFilter loadListDialog txtfilter+    sync $ fileChooserAddFilter loadListDialog allfilter+    flip callStream loadListPressed $ \_ -> do+        filePicked <- runFileChooserDialog loadListDialog+        loaded <- planNow . ffor filePicked $ \case+            Nothing -> return never+            Just fn -> async $ do+                rawfile <- fmap (T.decodeUtf8With T.lenientDecode) (B.readFile fn)+                evaluate rawfile+                return (fn,rawfile)+        planNow . ffor (join loaded) $ \(fn,rawfile) -> do+            segs <- sample currentsegs+            let initrows = parseWordlist segs rawfile+                src = FromStored FieroList rawfile+            newmodel <- sync $ setLexContents editor segs initrows+            newDLex <- watchLexModel src segs newmodel+            sync $ do+                changeModel newmodel+                changeDLex newDLex+                putStrLn "Lexicon sucessfully loaded."+        return ()++    loadTextDialog <- sync $ fileChooserDialogNew (Just "Load Text For New Lexicon") transwin FileChooserActionOpen+        [("gtk-cancel", ResponseCancel), ("gtk-open", ResponseAccept)]+    sync $ fileChooserAddFilter loadTextDialog allfilter+    sync $ fileChooserAddFilter loadTextDialog txtfilter+    flip callStream loadTextPressed $ \_ -> do+        filePicked <- runFileChooserDialog loadTextDialog+        loaded <- planNow . ffor filePicked $ \case+            Nothing -> return never+            Just fn -> async $ do+                rawfile <- fmap (T.decodeUtf8With T.lenientDecode) (B.readFile fn)+                evaluate rawfile+                return (fn,rawfile)+        planNow . ffor (join loaded) $ \(fn,rawfile) -> do+            segs <- sample currentsegs+            let initrows = collateWordlist segs rawfile+                src = FromStored FieroText rawfile+            newmodel <- sync $ setLexContents editor segs initrows+            newDLex <- watchLexModel src segs newmodel+            sync $ do+                changeModel newmodel+                changeDLex newDLex+                putStrLn "Lexicon sucessfully created."+        return ()++    flip callStream segsChanged $ \newsegs' -> do+        let newsegs = last newsegs'+        (src, oldsegs, oldrows) <- sample currentLex+        let newrows = case src of+                Custom -> let raw = serWordlist oldsegs oldrows+                          in parseWordlist newsegs raw+                FromStored FieroList raw -> parseWordlist newsegs raw+                FromStored FieroText raw -> collateWordlist newsegs raw+        newmodel <- sync $ setLexContents editor newsegs newrows+        newDLex <- watchLexModel src newsegs newmodel+        sync $ do+            changeModel newmodel+            changeDLex newDLex+            putStrLn "Lexicon resegmented."++    flip callStream extreplace $ \newrows' -> do+        let newrows = last newrows'+        segs <- sample currentsegs+        newmodel <- sync $ setLexContents editor segs newrows+        newDLex <- watchLexModel Custom segs newmodel+        sync $ do+            changeModel newmodel+            changeDLex newDLex++    return (vb, fmap sel3 currentLex)
+ app/Main.hs view
@@ -0,0 +1,84 @@+{-# LANGUAGE TypeOperators, RecursiveDo, ScopedTypeVariables, TemplateHaskell, QuasiQuotes, OverloadedStrings #-}++module Main where++import Graphics.UI.Gtk+import Graphics.UI.Gtk.General.CssProvider+import Graphics.UI.Gtk.General.StyleContext+import Graphics.UI.Gtk.Abstract.Widget+import Control.FRPNow+import Control.FRPNow.GTK+import Control.FRPNow.GTK.MissingFFI+import Control.Monad+import Control.Applicative+import Control.Concurrent+import Control.Monad.Trans+import qualified Data.Text as T+import qualified Data.Text.Encoding as T+import qualified Data.Map.Lazy as M+import qualified Data.Set as S+import Text.PhonotacticLearner.PhonotacticConstraints+import Data.Array.IArray+import Data.FileEmbed+import Text.RawString.QQ+import Data.Maybe+import FeatureTableEditor+import LexiconEditor+import GrammarEditor+import LearnerControls+import System.IO++ipaft :: FeatureTable String+ipaft = fromJust . csvToFeatureTable id . T.unpack . T.decodeUtf8 $ $(embedFile "app/ft-ipa.csv")++css :: String+css = [r|+#featuretable{+    background-color: @theme_base_color;+    padding: 5px;+    font-size: 80%;+}+#featuretable label {+    padding: 0 2px;+}+#featuretable .oddcol {+    background-color: mix(@theme_base_color,@theme_bg_color,0.5);+}+#featuretable .segheader {font-weight: bold;}+#featuretable .featheader {font-weight: bold;}+#featuretable .featzero {color: mix(@theme_fg_color, transparent, 0.35);}++|]++main :: IO ()+main = runNowGTK $ do+    sync $ hSetEncoding stdout utf8+    sync $ hSetBuffering stdout LineBuffering+    -- example gtk app+    -- initialization code+    window <- sync $ windowNew++    rec (fteditor, dynft) <- createEditableFT (Just window) ipaft+        (lexeditor, dynlex) <- createEditableLexicon (Just window) (fmap segsFromFt dynft) lexout+        (grammareditor, dyngrammar) <- createLoadableGrammar (Just window) (fmap (M.keysSet . featLookup) dynft) grammarout+        (controls,pbar,lexout,grammarout) <- createPhonotacticLearnerWidget dynft dynlex dyngrammar++    sync $ do+        sp <- cssProviderNew+        cssProviderLoadFromString sp css+        thescreen <- widgetGetScreen window+        styleContextAddProviderForScreen thescreen sp 600++        centerpanes <- set' [panedWideHandle := False] =<< createVPaned controls fteditor+        rpanes <- set' [panedWideHandle := False] =<< createHPaned centerpanes grammareditor+        lpanes <- set' [panedWideHandle := False] =<< createHPaned lexeditor rpanes+        box <- createVBox 0 $ do+            bstretch lpanes+            bpack =<< liftIO (vSeparatorNew)+            bpack pbar++        containerAdd window box++    sync $ window `on` deleteEvent $ liftIO mainQuit >> return False++    sync $ widgetShowAll window
+ app/ft-ipa.csv view
@@ -0,0 +1,17 @@+            ,t,d,s,z,ɬ,ɮ,ʃ,ʒ,c,ɟ,p,b,f,v,k,ɡ,x,ɣ,h,ɦ,ʔ,m,n,ŋ,ɲ,l,ʎ,ʟ,ⱱ,ɽ,r,ʀ,ʋ,j,w,i,y,ɪ,ʏ,ɯ,u,ʊ,e,ø,ɤ,o,ə,ɛ,œ,ʌ,ɔ,æ,ɐ,a,ɶ,ɑ,ɒ+syllabic,    -,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,++sonorant,    -,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,++consonantal, +,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,-,-,-,+,+,+,+,+,+,+,+,+,+,+,+,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-+continuant,  -,-,+,+,+,+,+,+,-,-,-,-,+,+,-,-,+,+,+,+,-,-,-,-,-,+,+,+,-,-,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,++nasal,       -,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,+,+,+,+,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-+lateral,     -,-,-,-,+,+,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,+,+,+,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-+labial,      -,-,-,-,-,-,-,-,-,-,+,+,+,+,-,-,-,-,-,-,-,+,-,-,-,-,-,-,+,-,-,-,+,+,+,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0+coronal,     +,+,+,+,+,+,+,+,+,+,-,-,-,-,-,-,-,-,-,-,-,-,+,-,+,+,+,-,-,+,+,-,-,-,-,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0+anterior,    +,+,+,+,+,+,-,-,-,-,0,0,0,0,0,0,0,0,0,0,0,0,+,0,-,+,-,0,0,-,+,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0+dorsal,      -,-,-,-,-,-,-,-,-,-,-,-,-,-,+,+,+,+,-,-,-,-,-,+,-,-,-,+,-,-,-,+,-,-,+,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0+round,       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-,+,-,+,+,+,+,-,+,-,+,-,-,+,-,+,-,-,-,+,-,++back,        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-,-,-,-,+,+,+,-,-,+,+,-,-,-,+,+,-,-,-,-,+,++high,        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,+,+,+,+,+,+,+,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-+low,         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,-,+,+,+,+,+,++ATR,         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,+,+,-,-,-,+,-,+,+,+,+,0,-,-,-,-,-,-,-,-,-,-+voice,       -,+,-,+,-,+,-,+,-,+,-,+,-,+,-,+,-,+,-,+,-,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+,+
+ maxent-learner-hw-gui.cabal view
@@ -0,0 +1,50 @@+name:                maxent-learner-hw-gui+version:             0.2.0+synopsis:            GUI for maxent-learner-hw+description:         This is a GUI frontent for maxent-learner-hw using GTK.+homepage:            https://github.com/george-steel/maxent-learner+license:             GPL+license-file:        LICENSE+author:              George Steel+maintainer:          george.steel@gmail.com+copyright:           2016-2017 George Steel and Peter Jurgec+category:            Linguistics+build-type:          Simple+extra-source-files:  app/ft-ipa.csv+cabal-version:       >=1.10++executable phono-learner-hw-gui+  hs-source-dirs:      app+  main-is:             Main.hs+  other-modules:       FeatureTableEditor+                       GrammarEditor+                       LearnerControls+                       LexiconEditor+  ghc-options:         -Wall -threaded -rtsopts -with-rtsopts=-N+  build-depends:       base >= 4.7 && < 5+                     , maxent-learner-hw == 0.2.0+                     , containers == 0.5.*+                     , text == 1.2.*+                     , file-embed+                     , parallel == 3.2.*+                     , deepseq == 1.4.*+                     , array >= 0.3 && < 0.6+                     , mtl >= 2.1 && < 2.3+                     , transformers+                     , random == 1.1+                     , gtk3+                     , glib+                     , frpnow+                     , frpnow-gtk3 == 0.2.0+                     , raw-strings-qq+                     , bytestring+                     , tuple+                     , async+                     , cairo+                     , Chart+                     , Chart-cairo+  default-language:    Haskell2010++source-repository head+  type:     git+  location: https://github.com/george-steel/maxent-learner