packages feed

ltk-0.16.1.0: src/Graphics/UI/Editor/Composite.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DataKinds #-}
-----------------------------------------------------------------------------
--
-- Module      :  Graphics.UI.Editor.Composite
-- Copyright   :  (c) Juergen Nicklisch-Franken, Hamish Mackenzie
-- License     :  GNU-GPL
--
-- Maintainer  :  <maintainer at leksah.org>
-- Stability   :  provisional
-- Portability :  portable
--
-- | Module for making composite editors
--
-----------------------------------------------------------------------------------

module Graphics.UI.Editor.Composite (
    maybeEditor
,   disableEditor
,   pairEditor
,   tupel3Editor
,   splitEditor
,   eitherOrEditor
,   multisetEditor
,   ColumnDescr(..)

,   filesEditor
,   textsEditor

,   versionEditor
,   versionRangeEditor
,   dependencyEditor
,   dependenciesEditor
) where

import Control.Monad
import Data.IORef
import Data.Maybe
import Data.Text (Text)
import qualified Data.Text as T (pack, unpack, null)

import Default
import Control.Event
import Graphics.UI.Utils
import Graphics.UI.Editor.Parameters
import Graphics.UI.Editor.Basics
import Graphics.UI.Editor.MakeEditor
import Graphics.UI.Editor.Simple
import Data.List (sortBy, nub, sort, elemIndex)
import Distribution.Simple
    (orEarlierVersion,
     orLaterVersion,
     VersionRange,
     PackageName(..),
     Dependency(..),
#if MIN_VERSION_Cabal(2,0,0)
     unPackageName,
     mkPackageName,
#endif
     PackageIdentifier(..))
import Distribution.Text (simpleParse, display)
import Distribution.Package (pkgName)
import MyMissing (forceJust)
import Unsafe.Coerce (unsafeCoerce)
import Debug.Trace (trace)
import GI.Gtk
       (gridSetRowSpacing, gridSetColumnSpacing, orientableSetOrientation,
        widgetSetHexpand, widgetSetVexpand, toWidget, buttonBoxNew,
        noWidget, widgetSetValign, widgetSetHalign, panedNew, gridNew,
        setCellRendererTextText, noTreeViewColumn, noAdjustment,
        FileChooserAction, treeModelGetPath, treeSelectionGetSelected,
        treeViewScrollToCell, treeViewGetColumn, treeSelectionSelectPath,
        onButtonClicked, onTreeSelectionChanged, treeViewSetHeadersVisible,
        cellLayoutPackStart, cellRendererTextNew, treeViewAppendColumn,
        treeViewColumnSetResizable, treeViewColumnSetTitle,
        treeViewColumnNew, treeSelectionSetMode, treeViewGetSelection,
        scrolledWindowSetMinContentHeight, scrolledWindowSetPolicy,
        scrolledWindowNew, widgetSetSizeRequest, treeViewNewWithModel,
        afterTreeModelRowDeleted, afterTreeModelRowInserted,
        buttonNewWithLabel, hButtonBoxNew, ButtonBox(..), vButtonBoxNew,
        CellRendererText, containerRemove, widgetSetSensitive,
        containerGetChildren, widgetHide, widgetShowAll, boxPackEnd,
        panedPack2, panedPack1, hPanedNew, Paned(..), vPanedNew,
        containerAdd, boxPackStart, vBoxNew, Box(..), hBoxNew, Widget(..))
import Data.GI.Base.ManagedPtr (unsafeCastTo, castTo)
import GI.Gtk.Enums
       (Orientation(..), PositionType(..), Align(..), ShadowType(..),
        SelectionMode(..), PolicyType(..))
import Data.GI.Gtk.ModelView.CellLayout
       (cellLayoutSetDataFunction)
import Data.GI.Gtk.ModelView.SeqStore
       (seqStoreAppend, seqStoreClear, seqStoreNew, seqStoreGetValue,
        seqStoreRemove, seqStoreToList, SeqStore(..))
import Data.GI.Gtk.ModelView.Types
       (treePathNewFromIndices', equalManagedPtr)
import GI.Gtk.Structs.TreePath (treePathGetIndices)
import Distribution.Version
       (Version(..),
#if MIN_VERSION_Cabal(2,0,0)
        majorBoundVersion,
#endif
        withinVersion, intersectVersionRanges, unionVersionRanges,
        earlierVersion, laterVersion, thisVersion, anyVersion,
        foldVersionRange')

#if !MIN_VERSION_Cabal(2,0,0)
mkPackageName = PackageName
#endif

--
-- | An editor which composes two subeditors
--
pairEditor :: (Editor alpha, Parameters) -> (Editor beta, Parameters) -> Editor (alpha,beta)
pairEditor (fstEd,fstPara) (sndEd,sndPara) parameters notifier = do
    coreRef <- newIORef Nothing
    noti1   <- emptyNotifier
    noti2   <- emptyNotifier
    mapM_ (propagateEvent notifier [noti1,noti2]) allGUIEvents
    fst@(fstFrame,inj1,ext1) <- fstEd fstPara noti1
    snd@(sndFrame,inj2,ext2) <- sndEd sndPara noti2
    mkEditor
        (\widget (v1,v2) -> do
            core <- readIORef coreRef
            case core of
                Nothing  -> do
                    grid <- gridNew
                    orientableSetOrientation grid $ getParameter paraOrientation parameters
                    gridSetColumnSpacing grid 1
                    gridSetRowSpacing grid 1
                    setPrimaryExpand grid fstFrame True
                    containerAdd grid fstFrame
                    setPrimaryExpand grid sndFrame True
                    containerAdd grid sndFrame
                    containerAdd widget grid
                    inj1 v1
                    inj2 v2
                    writeIORef coreRef (Just (fst,snd))
                Just ((_,inj1,_),(_,inj2,_)) -> do
                    inj1 v1
                    inj2 v2)
        (do core <- readIORef coreRef
            case core of
                Nothing -> return Nothing
                Just ((_,_,ext1),(_,_,ext2)) -> do
                    r1 <- ext1
                    r2 <- ext2
                    if isJust r1 && isJust r2
                        then return (Just (fromJust r1,fromJust r2))
                        else return Nothing)
        parameters
        notifier

tupel3Editor :: (Editor alpha, Parameters)
    -> (Editor beta, Parameters)
    -> (Editor gamma, Parameters)
    -> Editor (alpha,beta,gamma)
tupel3Editor p1 p2 p3 parameters notifier = do
    coreRef <- newIORef Nothing
    noti1   <- emptyNotifier
    noti2   <- emptyNotifier
    noti3   <- emptyNotifier
    mapM_ (propagateEvent notifier [noti1,noti2,noti3]) (Clicked : allGUIEvents)
    r1@(frame1,inj1,ext1) <- fst p1 (snd p1) noti1
    r2@(frame2,inj2,ext2) <- fst p2 (snd p2) noti2
    r3@(frame3,inj3,ext3) <- fst p3 (snd p3) noti3
    mkEditor
        (\widget (v1,v2,v3) -> do
            core <- readIORef coreRef
            case core of
                Nothing  -> do
                    grid <- gridNew
                    orientableSetOrientation grid $ getParameter paraOrientation parameters
                    setPrimaryExpand grid frame1 True
                    containerAdd grid frame1
                    setPrimaryExpand grid frame2 True
                    containerAdd grid frame2
                    setPrimaryExpand grid frame3 True
                    containerAdd grid frame3
                    containerAdd widget grid
                    inj1 v1
                    inj2 v2
                    inj3 v3
                    writeIORef coreRef (Just (r1,r2,r3))
                Just ((_,inj1,_),(_,inj2,_),(_,inj3,_)) -> do
                    inj1 v1
                    inj2 v2
                    inj3 v3)
        (do core <- readIORef coreRef
            case core of
                Nothing -> return Nothing
                Just ((_,_,ext1),(_,_,ext2),(_,_,ext3)) -> do
                    r1 <- ext1
                    r2 <- ext2
                    r3 <- ext3
                    if isJust r1 && isJust r2 && isJust r3
                        then return (Just (fromJust r1,fromJust r2, fromJust r3))
                        else return Nothing)
        parameters
        notifier

--
-- | Like a pair editor, but with a moveable split
--
splitEditor :: (Editor alpha, Parameters) -> (Editor beta, Parameters) -> Editor (alpha,beta)
splitEditor (fstEd,fstPara) (sndEd,sndPara) parameters notifier = do
    coreRef <- newIORef Nothing
    noti1   <- emptyNotifier
    noti2   <- emptyNotifier
    mapM_ (propagateEvent notifier [noti1,noti2]) allGUIEvents
    fst@(fstFrame,inj1,ext1) <- fstEd fstPara noti1
    snd@(sndFrame,inj2,ext2) <- sndEd sndPara noti2
    mkEditor
        (\widget (v1,v2) -> do
            core <- readIORef coreRef
            case core of
                Nothing  -> do
                    paned <- panedNew $ case getParameter paraOrientation parameters of
                        OrientationHorizontal -> OrientationVertical
                        OrientationVertical   -> OrientationHorizontal
                    panedPack1 paned fstFrame True True
                    panedPack2 paned sndFrame True True
                    containerAdd widget paned
                    inj1 v1
                    inj2 v2
                    writeIORef coreRef (Just (fst,snd))
                Just ((_,inj1,_),(_,inj2,_)) -> do
                    inj1 v1
                    inj2 v2)
        (do core <- readIORef coreRef
            case core of
                Nothing -> return Nothing
                Just ((_,_,ext1),(_,_,ext2)) -> do
                    r1 <- ext1
                    r2 <- ext2
                    if isJust r1 && isJust r2
                        then return (Just (fromJust r1,fromJust r2))
                        else return Nothing)
        parameters
        notifier

--
-- | An editor with a subeditor which gets active, when a checkbox is selected
-- or deselected (if the positive Argument is False)
--
maybeEditor :: Default beta => (Editor beta, Parameters) -> Bool -> Text -> Editor (Maybe beta)
maybeEditor (childEdit, childParams) positive boolLabel parameters notifier = do
    coreRef      <- newIORef Nothing
    childRef     <- newIORef Nothing
    notifierBool <- emptyNotifier
    cNoti        <- emptyNotifier
    mkEditor
        (\widget mbVal -> do
            core <- readIORef coreRef
            case core of
                Nothing  -> do
                    grid <- gridNew
                    orientableSetOrientation grid $ getParameter paraOrientation parameters
                    gridSetColumnSpacing grid 1
                    gridSetRowSpacing grid 1
                    be@(boolFrame,inj1,ext1) <- boolEditor
                        (paraName <<<- ParaName boolLabel $ emptyParams)
                        notifierBool
                    setPrimaryAlign grid boolFrame AlignStart
                    containerAdd grid boolFrame
                    containerAdd widget grid
                    registerEvent notifierBool Clicked (onClickedHandler widget coreRef childRef cNoti)
                    propagateEvent notifier [notifierBool] MayHaveChanged
                    case mbVal of
                        Nothing -> inj1 (not positive)
                        Just val -> do
                            (childWidget,inj2,ext2) <- getChildEditor childRef childEdit childParams cNoti
                            setPrimaryExpand grid childWidget True
                            containerAdd grid childWidget
                            widgetShowAll childWidget
                            inj1 positive
                            inj2 val
                    writeIORef coreRef (Just (be,grid))
                Just (be@(boolFrame,inj1,extt),grid) -> do
                    hasChild <- hasChildEditor childRef
                    case mbVal of
                        Nothing ->
                            if hasChild
                                then do
                                    (childWidget,_,_) <- getChildEditor childRef childEdit childParams cNoti
                                    inj1 (not positive)
                                    widgetHide childWidget
                                else inj1 (not positive)
                        Just val ->
                            if hasChild
                                then do
                                    inj1 positive
                                    (childWidget,inj2,_) <- getChildEditor childRef childEdit childParams cNoti
                                    widgetShowAll childWidget
                                    inj2 val
                                else do
                                    inj1 positive
                                    (childWidget,inj2,_) <- getChildEditor childRef childEdit childParams cNoti
                                    setPrimaryExpand grid childWidget True
                                    containerAdd grid childWidget
                                    widgetShowAll childWidget
                                    inj2 val)
        (do
            core <- readIORef coreRef
            case core of
                Nothing  -> return Nothing
                Just (be@(boolFrame,inj1,ext1),_) -> do
                    bool <- ext1
                    case bool of
                        Nothing -> return Nothing
                        Just bv | bv == positive -> do
                            (_,_,ext2) <- getChildEditor childRef childEdit childParams cNoti
                            value <- ext2
                            case value of
                                Nothing -> return Nothing
                                Just value -> return (Just (Just value))
                        _ -> return (Just Nothing))
        parameters
        notifier
    where
    onClickedHandler widget coreRef childRef cNoti event = do
        core <- readIORef coreRef
        case core of
            Nothing  -> error "Impossible"
            Just (be@(boolFrame,inj1,ext1),grid) -> do
                mbBool <- ext1
                case mbBool of
                    Just bool ->
                        if bool /= positive
                            then do
                                hasChild <- hasChildEditor childRef
                                when hasChild $ do
                                    (childWidget,_,_) <- getChildEditor childRef childEdit childParams cNoti
                                    widgetHide childWidget
                            else do
                                hasChild <- hasChildEditor childRef
                                (childWidget,inj2,ext2) <- getChildEditor childRef childEdit childParams cNoti
                                children <- containerGetChildren grid
                                unless (any (equalManagedPtr childWidget) children) $ do
                                    setPrimaryAlign grid childWidget AlignStart
                                    containerAdd grid childWidget
                                inj2 getDefault
                                widgetShowAll childWidget
                    Nothing -> return ()
                return (event {gtkReturn=True})
    getChildEditor childRef childEditor childParams cNoti =  do
        mb <- readIORef childRef
        case mb of
            Just editor -> return editor
            Nothing -> do
                let val = childEditor
                editor@(_,_,_) <- childEditor childParams cNoti
                mapM_ (propagateEvent notifier [cNoti]) allGUIEvents
                writeIORef childRef (Just editor)
                return editor
    hasChildEditor childRef =  do
        mb <- readIORef childRef
        return (isJust mb)


--
-- | An editor with a subeditor which gets active, when a checkbox is selected
-- or grayed out (if the positive Argument is False)
--
disableEditor :: Default beta => (Editor beta, Parameters) -> Bool -> Text -> Editor (Bool,beta)
disableEditor (childEdit, childParams) positive boolLabel parameters notifier = do
    coreRef      <- newIORef Nothing
    childRef     <- newIORef Nothing
    notifierBool <- emptyNotifier
    cNoti        <- emptyNotifier
    mkEditor
        (\widget mbVal -> do
            core <- readIORef coreRef
            case core of
                Nothing  -> do
                    grid <- gridNew
                    orientableSetOrientation grid $ getParameter paraOrientation parameters
                    be@(boolFrame,inj1,ext1) <- boolEditor
                        (paraName <<<- ParaName boolLabel $ emptyParams)
                        notifierBool
                    setPrimaryAlign grid boolFrame AlignStart
                    containerAdd grid boolFrame
                    containerAdd widget grid
                    registerEvent notifierBool Clicked
                        (onClickedHandler widget coreRef childRef cNoti)
                    propagateEvent notifier [notifierBool] MayHaveChanged
                    case mbVal of
                        (False,val) -> do
                            (childWidget,inj2,ext2) <- getChildEditor childRef childEdit childParams cNoti
                            setPrimaryExpand grid childWidget True
                            containerAdd grid childWidget
                            widgetShowAll childWidget
                            inj1 ( not positive)
                            inj2 val
                            widgetSetSensitive childWidget False
                        (True,val) -> do
                            (childWidget,inj2,ext2) <- getChildEditor childRef childEdit childParams cNoti
                            setPrimaryExpand grid childWidget True
                            containerAdd grid childWidget
                            widgetShowAll childWidget
                            inj1 positive
                            inj2 val
                            widgetSetSensitive childWidget True
                    writeIORef coreRef (Just (be,grid))
                Just (be@(boolFrame,inj1,extt),grid) -> do
                    hasChild <- hasChildEditor childRef
                    case mbVal of
                        (False,val) ->
                            if hasChild
                                then do
                                    (childWidget,_,_) <- getChildEditor childRef childEdit childParams cNoti
                                    inj1 (not positive)
                                    widgetSetSensitive childWidget False
                                else inj1 (not positive)
                        (True,val) ->
                            if hasChild
                                then do
                                    inj1 positive
                                    (childWidget,inj2,_) <- getChildEditor childRef childEdit childParams cNoti
                                    inj2 val
                                    widgetSetSensitive childWidget True
                                else do
                                    inj1 positive
                                    (childWidget,inj2,_) <- getChildEditor childRef childEdit childParams cNoti
                                    setPrimaryExpand grid childWidget True
                                    containerAdd grid childWidget
                                    widgetSetSensitive childWidget True
                                    inj2 val)
        (do
            core <- readIORef coreRef
            case core of
                Nothing  -> return Nothing
                Just (be@(boolFrame,inj1,ext1),_) -> do
                    bool <- ext1
                    case bool of
                        Nothing -> return Nothing
                        Just bv | bv == positive -> do
                            (_,_,ext2) <- getChildEditor childRef childEdit childParams cNoti
                            value <- ext2
                            case value of
                                Nothing -> return Nothing
                                Just value -> return (Just (True, value))
                        _ -> do
                            (_,_,ext2) <- getChildEditor childRef childEdit childParams cNoti
                            value <- ext2
                            case value of
                                Nothing -> return Nothing
                                Just value -> return (Just (False, value)))
        parameters
        notifier
    where
    onClickedHandler widget coreRef childRef cNoti event = do
        core <- readIORef coreRef
        case core of
            Nothing  -> error "Impossible"
            Just (be@(boolFrame,inj1,ext1),grid) -> do
                mbBool <- ext1
                case mbBool of
                    Just bool ->
                        if bool /= positive
                            then do

                                hasChild <- hasChildEditor childRef
                                when hasChild $ do
                                    (childWidget,_,_) <- getChildEditor childRef childEdit childParams cNoti
                                    widgetSetSensitive childWidget False
                            else do
                                hasChild <- hasChildEditor childRef
                                if hasChild
                                    then do
                                        (childWidget,_,_) <- getChildEditor childRef childEdit childParams cNoti
                                        widgetSetSensitive childWidget True
                                    else do
                                        (childWidget,inj2,_) <- getChildEditor childRef childEdit childParams cNoti
                                        setPrimaryAlign grid childWidget AlignStart
                                        containerAdd grid childWidget
                                        inj2 getDefault
                                        widgetSetSensitive childWidget True
                    Nothing -> return ()
                return (event {gtkReturn=True})
    getChildEditor childRef childEditor childParams cNoti =  do
        mb <- readIORef childRef
        case mb of
            Just editor -> return editor
            Nothing -> do
                let val = childEditor
                editor@(_,_,_) <- childEditor childParams cNoti
                mapM_ (propagateEvent notifier [cNoti]) allGUIEvents
                writeIORef childRef (Just editor)
                return editor
    hasChildEditor childRef =  do
        mb <- readIORef childRef
        return (isJust mb)

--
-- | An editor with a subeditor which gets active, when a checkbox is selected
-- or deselected (if the positive Argument is False)
eitherOrEditor :: (Default alpha, Default beta) => (Editor alpha, Parameters) ->
                        (Editor beta, Parameters) -> Text -> Editor (Either alpha beta)
eitherOrEditor (leftEditor,leftParams) (rightEditor,rightParams)
            label2 parameters notifier = do
    coreRef <- newIORef Nothing
    noti1 <- emptyNotifier
    noti2 <- emptyNotifier
    noti3 <- emptyNotifier
    mapM_ (propagateEvent notifier [noti1,noti2,noti3]) allGUIEvents
    be@(boolFrame,inj1,ext1) <- boolEditor2  (getParameter paraName rightParams) leftParams noti1
    le@(leftFrame,inj2,ext2) <- leftEditor (paraName <<<- ParaName "" $ leftParams) noti2
    re@(rightFrame,inj3,ext3) <- rightEditor (paraName <<<- ParaName "" $ rightParams) noti3
    mkEditor
        (\widget v -> do
            core <- readIORef coreRef
            case core of
                Nothing  -> do
                    registerEvent noti1 Clicked (onClickedHandler widget coreRef)
                    grid <- gridNew
                    orientableSetOrientation grid $ getParameter paraOrientation parameters
                    gridSetColumnSpacing grid 1
                    gridSetRowSpacing grid 1
                    setPrimaryAlign grid boolFrame AlignStart
                    setPrimaryAlign grid leftFrame AlignStart
                    setPrimaryAlign grid rightFrame AlignStart
                    containerAdd grid boolFrame
                    containerAdd widget grid
                    case v of
                        Left vl -> do
                          containerAdd grid leftFrame
                          inj2 vl
                          inj3 getDefault
                          inj1 True
                        Right vr  -> do
                          containerAdd grid rightFrame
                          inj3 vr
                          inj2 getDefault
                          inj1 False
                    writeIORef coreRef (Just (be,le,re,grid))
                Just ((_,inj1,_),(leftFrame,inj2,_),(rightFrame,inj3,_),grid) ->
                    case v of
                            Left vl -> do
                              containerRemove grid rightFrame
                              containerAdd grid leftFrame
                              inj2 vl
                              inj3 getDefault
                              inj1 True
                            Right vr  -> do
                              containerRemove grid leftFrame
                              containerAdd grid rightFrame
                              inj3 vr
                              inj2 getDefault
                              inj1 False)
        (do core <- readIORef coreRef
            case core of
                Nothing -> return Nothing
                Just ((_,_,ext1),(_,_,ext2),(_,_,ext3),_) -> do
                    mbbool <- ext1
                    case mbbool of
                        Nothing -> return Nothing
                        Just True   ->  do
                            value <- ext2
                            case value of
                                Nothing -> return Nothing
                                Just value -> return (Just (Left value))
                        Just False -> do
                            value <- ext3
                            case value of
                                Nothing -> return Nothing
                                Just value -> return (Just (Right value)))
        (paraName <<<- ParaName "" $ parameters)
        notifier
    where
    onClickedHandler widget coreRef event =  do
        core <- readIORef coreRef
        case core of
            Nothing  -> error "Impossible"
            Just (be@(_,_,ext1),(leftFrame,_,_),(rightFrame,_,_),grid) -> do
                mbBool <- ext1
                case mbBool of
                    Just bool ->
                            if bool then do
                              containerRemove grid rightFrame
                              containerAdd grid leftFrame
                              widgetShowAll grid
                            else do
                              containerRemove grid leftFrame
                              containerAdd grid rightFrame
                              widgetShowAll grid
                    Nothing -> return ()
                return event{gtkReturn=True}


-- a trivial example: (ColumnDescr False [("",(\row -> [cellText := show row]))])
-- and a nontrivial:
--  [("Package",\(Dependency str _) -> [cellText := str])
--  ,("Version",\(Dependency _ vers) -> [cellText := showVersionRange vers])])
data ColumnDescr row = ColumnDescr Bool [(Text, CellRendererText -> row -> IO ())]

--
-- | An editor with a subeditor, of which a list of items can be selected
multisetEditor :: (Show alpha, Default alpha, Eq alpha) => ColumnDescr alpha
    -> (Editor alpha, Parameters)
    -> Maybe ([alpha] -> [alpha]) -- ^ The 'mbSort' arg, a sort function if desired
    -> Maybe (alpha -> alpha -> Bool) -- ^ The 'mbReplace' arg, a function which is a criteria for removing an
                              --   old entry when adding a new value
    -> Editor [alpha]
multisetEditor (ColumnDescr showHeaders columnsDD) (singleEditor, sParams) mbSort mbReplace
        parameters notifier = do
    coreRef <- newIORef Nothing
    cnoti   <- emptyNotifier
    mkEditor
        (\widget vs -> do
            core <- readIORef coreRef
            case core of
                Nothing  -> do
                    let orientation = getParameter paraOrientation parameters
                    grid <- gridNew
                    orientableSetOrientation grid orientation
                    buttonBox <- case orientation of
                        OrientationHorizontal -> buttonBoxNew OrientationVertical
                        OrientationVertical -> buttonBoxNew OrientationHorizontal
                    (frameS,injS,extS) <- singleEditor sParams cnoti
                    mapM_ (propagateEvent notifier [cnoti]) allGUIEvents
                    addButton    <- buttonNewWithLabel "Add"
                    removeButton <- buttonNewWithLabel "Remove"
                    containerAdd buttonBox addButton
                    containerAdd buttonBox removeButton
                    seqStore <- seqStoreNew ([]:: [alpha])
                    activateEvent seqStore notifier
                        (Just (\ w h -> afterTreeModelRowInserted w (\ _ _ -> void h))) MayHaveChanged
                    activateEvent seqStore notifier
                        (Just (\ w h -> afterTreeModelRowDeleted w (\ _ -> void h))) MayHaveChanged
                    treeView    <-  treeViewNewWithModel seqStore
                    let minSize =   getParameter paraMinSize parameters
                    uncurry (widgetSetSizeRequest treeView) minSize
                    sw          <-  scrolledWindowNew noAdjustment noAdjustment
                    containerAdd sw treeView
                    scrolledWindowSetPolicy sw PolicyTypeAutomatic PolicyTypeAutomatic
                    scrolledWindowSetMinContentHeight sw (snd minSize)
                    sel         <-  treeViewGetSelection treeView
                    treeSelectionSetMode sel SelectionModeSingle
                    mapM_ (\(str,func) -> do
                            col <- treeViewColumnNew
                            treeViewColumnSetTitle  col str
                            treeViewColumnSetResizable col True
                            treeViewAppendColumn treeView col
                            renderer <- cellRendererTextNew
                            cellLayoutPackStart col renderer True
                            cellLayoutSetDataFunction col renderer seqStore (func renderer)
                        ) columnsDD
                    treeViewSetHeadersVisible treeView showHeaders
                    onTreeSelectionChanged sel $ selectionHandler sel seqStore injS
                    setPrimaryExpand grid sw True
                    setSecondaryExpand grid sw True
                    containerAdd grid sw
                    setPrimaryAlign grid buttonBox AlignEnd
                    setSecondaryExpand grid buttonBox True
                    containerAdd grid buttonBox
                    setPrimaryAlign grid frameS AlignEnd
                    setSecondaryExpand grid frameS True
                    containerAdd grid frameS
                    activateEvent treeView notifier Nothing FocusOut
                    containerAdd widget grid
                    seqStoreClear seqStore
                    mapM_ (seqStoreAppend seqStore)
                        (case mbSort of
                            Nothing -> vs
                            Just sortF -> sortF vs)
                    onButtonClicked addButton $ do
                        mbv <- extS
                        case mbv of
                            Just v -> do
                                case mbReplace of
                                    Nothing         -> return ()
                                    Just replaceF   -> do
                                         cont <- seqStoreToList seqStore
                                         mapM_ (seqStoreRemove seqStore . fst)
                                            . filter (\(_,e) -> replaceF v e)
                                                $ zip [0..] cont
                                case mbSort of
                                    Nothing    -> do
                                        seqStoreAppend seqStore v
                                        return ()
                                    Just sortF -> do
                                        cont <- seqStoreToList seqStore
                                        seqStoreClear seqStore
                                        mapM_ (seqStoreAppend seqStore) (sortF (v:cont))
                                cont <- seqStoreToList seqStore
                                case elemIndex v cont of
                                    Just idx -> do
                                        path <- treePathNewFromIndices' [fromIntegral idx]
                                        treeSelectionSelectPath sel path
                                        treeViewScrollToCell treeView (Just path) noTreeViewColumn False 0.0 0.0
                                    Nothing -> return ()
                            Nothing -> return ()
                    onButtonClicked removeButton $ do
                        mbi <- treeSelectionGetSelected sel
                        case mbi of
                            (True, _, iter) -> do
                                [i] <- treeModelGetPath seqStore iter >>= treePathGetIndices
                                seqStoreRemove seqStore i
                            _ -> return ()
                    writeIORef coreRef (Just seqStore)
                    injS getDefault
                Just seqStore -> do
                    seqStoreClear seqStore
                    mapM_ (seqStoreAppend seqStore)
                        (case mbSort of
                            Nothing -> vs
                            Just sortF -> sortF vs))
        (do core <- readIORef coreRef
            case core of
                Nothing -> return Nothing
                Just seqStore -> do
                    v <- seqStoreToList seqStore
                    return (Just v))
        (paraMinSize <<<- ParaMinSize (-1,-1) $ parameters)
        notifier
    where
--    selectionHandler :: TreeSelection -> SeqStore a -> Injector a -> IO ()
    selectionHandler sel seqStore inj = do
        ts <- treeSelectionGetSelected sel
        case ts of
            (True, _, iter) -> do
                [i] <- treeModelGetPath seqStore iter >>= treePathGetIndices
                v <- seqStoreGetValue seqStore i
                inj v
                return ()
            _ -> return ()


filesEditor :: Maybe FilePath -> FileChooserAction -> Text -> Editor [FilePath]
filesEditor fp act label p =
    multisetEditor
        (ColumnDescr False [("", \cell -> setCellRendererTextText cell . T.pack)])
        (fileEditor fp act label, emptyParams)
        (Just sort)
        (Just (==))
        (paraShadow <<<- ParaShadow ShadowTypeIn $
            paraOrientation  <<<- ParaOrientation OrientationVertical $ p)

textsEditor :: (Text -> Bool) -> Bool -> Editor [Text]
textsEditor validation trimBlanks p =
    multisetEditor
        (ColumnDescr False [("", setCellRendererTextText)])
        (textEditor validation trimBlanks, emptyParams)
        (Just sort)
        (Just (==))
        (paraShadow <<<- ParaShadow ShadowTypeIn $ p)

dependencyEditor :: [PackageIdentifier] -> Editor Dependency
dependencyEditor packages para noti = do
    (wid,inj,ext) <- pairEditor
        (comboEntryEditor ((sort . nub) (map (T.pack . display . pkgName) packages))
            , paraName <<<- ParaName "Select" $ emptyParams)
        (versionRangeEditor,paraName <<<- ParaName "Version" $ emptyParams)
        (paraOrientation <<<- ParaOrientation OrientationVertical $ para)
        noti
    let pinj (Dependency pn v) = inj (T.pack (unPackageName pn),v)
    let pext = do
        mbp <- ext
        case mbp of
            Nothing -> return Nothing
            Just ("",v) -> return Nothing
            Just (s,v) -> return (Just $ Dependency (mkPackageName (T.unpack s)) v)
    return (wid,pinj,pext)

dependenciesEditor :: [PackageIdentifier] -> Editor [Dependency]
dependenciesEditor packages p =
    multisetEditor
        (ColumnDescr True [("Package",\cell (Dependency pn _) -> setCellRendererTextText cell $ T.pack (unPackageName pn))
                           ,("Version",\cell (Dependency _ vers) -> setCellRendererTextText cell $ T.pack $ display vers)])
        (dependencyEditor packages,
            paraHAlign <<<- ParaHAlign AlignFill
                $ paraVAlign <<<- ParaVAlign AlignCenter
                   $ emptyParams)
        (Just (sortBy (\ (Dependency p1 _) (Dependency p2 _) -> compare p1 p2)))
        (Just (\ (Dependency p1 _) (Dependency p2 _) -> p1 == p2))
        (paraShadow <<<- ParaShadow ShadowTypeIn
            $ paraHAlign <<<- ParaHAlign AlignFill
                $ paraVAlign <<<- ParaVAlign AlignCenter
                    $ paraOrientation <<<- ParaOrientation OrientationVertical
                        $ paraPack <<<- ParaPack PackGrow
                            $ p)

versionRangeEditor :: Editor VersionRange
versionRangeEditor para noti = do
    (wid,inj,ext) <-
        maybeEditor
            (eitherOrEditor
               (pairEditor (comboSelectionEditor v1 (T.pack . show), emptyParams)
                  (versionEditor,
                   paraName <<<- ParaName "Enter Version" $ emptyParams),
                paraOrientation <<<- ParaOrientation OrientationVertical $
                   paraName <<<- ParaName "Simple" $
                      paraHAlign <<<- ParaHAlign AlignStart $
                         paraVAlign <<<- ParaVAlign AlignStart $
                            paraMargin <<<- ParaMargin (0, 0, 0, 0) $ emptyParams)
               (tupel3Editor
                  (comboSelectionEditor v2 (T.pack . show), emptyParams)
                  (versionRangeEditor,
                   paraShadow <<<- ParaShadow ShadowTypeIn $ emptyParams)
                  (versionRangeEditor,
                   paraShadow <<<- ParaShadow ShadowTypeIn $ emptyParams),
                paraName <<<- ParaName "Complex" $
                   paraOrientation <<<- ParaOrientation OrientationVertical $
                      paraHAlign <<<- ParaHAlign AlignStart $
                         paraVAlign <<<- ParaVAlign AlignStart $
                            paraMargin <<<- ParaMargin (0, 0, 0, 0) $ emptyParams)
               "Select version range",
             emptyParams)
            False "Any Version"
            (paraOrientation <<<- ParaOrientation OrientationVertical $ para)
            noti
    let vrinj = inj . snd . foldVersionRange'
                    (anyVersion, Nothing)
                    (\v -> (thisVersion v, Just (Left (ThisVersionS,v))))
                    (\v -> (laterVersion v, Just (Left (LaterVersionS,v))))
                    (\v -> (earlierVersion v, Just (Left (EarlierVersionS,v))))
                    (\v -> (unionVersionRanges (thisVersion v) (laterVersion v), Just (Left (ThisOrLaterVersionS,v))))
                    (\v -> (unionVersionRanges (thisVersion v) (earlierVersion v), Just (Left (ThisOrEarlierVersionS,v))))
                    (\v1 v2 -> (withinVersion v1, Just (Left (WildcardVersionS,v1))))
#if MIN_VERSION_Cabal(2,0,0)
                    (\v1 v2 -> (majorBoundVersion v1, Just (Left (MajorBoundVersionS,v1))))
#endif
                    (\(vr1, r1) (vr2, r2) -> (unionVersionRanges vr1 vr2, Just (Right (UnionVersionRangesS,vr1,vr2))))
                    (\(vr1, r1) (vr2, r2) -> (intersectVersionRanges vr1 vr2, Just (Right (IntersectVersionRangesS,vr1,vr2))))
                    id
    let vrext = do  mvr <- ext
                    case mvr of
                        Nothing -> return (Just anyVersion)
                        Just Nothing -> return (Just anyVersion)
                        Just (Just (Left (ThisVersionS,v)))          -> return (Just (thisVersion v))
                        Just (Just (Left (WildcardVersionS,v)))      -> return (Just (withinVersion v))
#if MIN_VERSION_Cabal(2,0,0)
                        Just (Just (Left (MajorBoundVersionS,v)))    -> return (Just (majorBoundVersion v))
#endif
                        Just (Just (Left (LaterVersionS,v)))         -> return (Just (laterVersion v))
                        Just (Just (Left (EarlierVersionS,v)))       -> return (Just (earlierVersion v))

                        Just (Just (Left (ThisOrLaterVersionS,v)))   -> return (Just (orLaterVersion v))
                        Just (Just (Left (ThisOrEarlierVersionS,v))) -> return (Just (orEarlierVersion v))
                        Just (Just (Right (UnionVersionRangesS,v1,v2)))
                                                        -> return (Just (unionVersionRanges v1 v2))
                        Just (Just (Right (IntersectVersionRangesS,v1,v2)))
                                                        -> return (Just (intersectVersionRanges v1 v2))
    return (wid,vrinj,vrext)
        where
            v1 = [ThisVersionS,WildcardVersionS
#if MIN_VERSION_Cabal(2,0,0)
                 ,MajorBoundVersionS
#endif
                 ,LaterVersionS,ThisOrLaterVersionS,EarlierVersionS,ThisOrEarlierVersionS]
            v2 = [UnionVersionRangesS,IntersectVersionRangesS]

data Version1 = ThisVersionS | WildcardVersionS | MajorBoundVersionS | LaterVersionS | ThisOrLaterVersionS | EarlierVersionS | ThisOrEarlierVersionS
    deriving (Eq)
instance Show Version1 where
    show ThisVersionS          = "This Version"
    show WildcardVersionS      = "Wildcard Version"
    show MajorBoundVersionS    = "Major Bound Version"
    show LaterVersionS         = "Later Version"
    show ThisOrLaterVersionS   = "This or later Version"
    show EarlierVersionS       = "Earlier Version"
    show ThisOrEarlierVersionS = "This or earlier Version"

data Version2 = UnionVersionRangesS | IntersectVersionRangesS
    deriving (Eq)
instance Show Version2 where
    show UnionVersionRangesS     = "Union Version Ranges"
    show IntersectVersionRangesS = "Intersect Version Ranges"

versionEditor :: Editor Version
versionEditor para noti = do
    (wid,inj,ext) <- stringEditor (not . null) True para noti
    let pinj v = inj (display v)
    let pext = do
        s <- ext
        case s of
            Nothing -> return Nothing
            Just s -> return (simpleParse s)
    return (wid, pinj, pext)

instance Default Version1
    where getDefault = ThisVersionS

instance Default Version2
    where getDefault = UnionVersionRangesS

instance Default Version
    where getDefault = forceJust (simpleParse "0") "PackageEditor>>default version"

instance Default VersionRange
    where getDefault = anyVersion

instance Default Dependency
    where getDefault = Dependency getDefault getDefault

instance Default PackageName
    where getDefault = mkPackageName getDefault