-- -----------------------------------------------------------------------
--
-- $Source: /repository/uni/htk/toolkit/Notepad.hs,v $
--
-- HTk - a GUI toolkit for Haskell - (c) Universitaet Bremen
--
-- $Revision: 1.26 $ from $Date: 2002/03/05 19:12:52 $
-- Last modification by $Author: ludi $
--
-- -----------------------------------------------------------------------
---
-- A simple drag and drop field.
module Notepad (
Notepad,
NotepadItem,
newNotepad,
createNotepadItem,
getFreeItemPosition,
getItemValue,
ScrollType(..),
module Name,
setName,
updNotepadScrollRegion,
selectAll,
deselectAll,
selectItem,
selectAnotherItem,
selectItemsWithin,
deselectItem,
getItems,
getSelectedItems,
isNotepadItemSelected,
deleteItem,
clearNotepad,
undoLastMotion,
bindNotepadEv, {- :: Notepad a -> IO (Event (NotepadEvent a), IO ()) -}
NotepadEvent(..),
NotepadExportItem(..),
NotepadState,
exportNotepadState,
importNotepadState,
module CItem
) where
import HTk
import CanvasItemAux
import ScrollBox
import ReferenceVariables
import Name
import Examples(watch)
import Core
import Maybe
import CItem
import FiniteMap
import IOExts(unsafePerformIO)
getCoords :: EventInfo -> IO (Distance, Distance)
getCoords eventInfo = return (x eventInfo, y eventInfo)
char_px = 8
-------------------
-- Notepad items --
-------------------
-- type
---
-- The NotepadItem datatype.
data NotepadItem a =
NotepadItem { it_img :: ImageItem, -- image
it_img_size :: Size, -- size of image
it_txt :: TextItem, -- displayed name
it_val :: Ref a, -- value
it_long_name_bg :: Ref (Maybe Rectangle), -- long names bg
it_bg :: Ref (Maybe (Rectangle, Rectangle)) }
-- bg if selected
-- handler for enter events
enteredItem :: CItem c => Notepad c -> NotepadItem c -> IO ()
enteredItem notepad item =
synchronize item
(do
v <- getRef (it_val item)
nm <- getName v
let fullnm = full nm
it_txt item # text fullnm
mlast_bg <- getRef (it_long_name_bg item)
case mlast_bg of
Nothing -> do
Just (x1, y1, x2, y2) <-
bbox (canvas notepad) (it_txt item)
(_, (sizex, _)) <- getScrollRegion (canvas notepad)
let dx = if x1 < 0 then -x1 + 6
else if x2 > sizex then (sizex - x2)
else 0
moveItem (it_txt item) dx 0
b <- isNotepadItemSelected notepad item
rect <- createRectangle (canvas notepad)
(coord [(x1 - 5 + dx, y1 - 1),
(x2 + 5 + dx, y2 + 1)] :
(if b then [filling "blue",
outline "blue"]
else [filling "white",
outline "black"]))
putItemOnTop rect
putItemOnTop (it_txt item)
setRef (it_long_name_bg item) (Just rect)
_ -> done
done)
text_gap :: Int
text_gap = 11
-- handler for leave events
leftItem :: CItem c => Notepad c -> NotepadItem c -> IO ()
leftItem notepad item =
synchronize item
(do
(x, y) <- getPosition item
let (Distance iwidth, Distance iheight) = img_size notepad
it_txt item # position (x, y + Distance (div iheight 2 + text_gap))
let (Distance dx, _) = img_size notepad
len = div (dx + 80) char_px
v <- getRef (it_val item)
nm <- getName v
let shortnm = short nm len
it_txt item # text shortnm
mlast_bg <- getRef (it_long_name_bg item)
case mlast_bg of
Just last_bg -> destroy last_bg >>
setRef (it_long_name_bg item) Nothing
_ -> done
done)
-- constructor
---
-- Creates a new notepad item and returns a handler.
-- @param val - the notepad item's value.
-- @param notepad - the concerned notepad.
-- @param updscrollregion - True if the notepad's
-- - scrollregion should be updated.
-- @param cnf - the list of configuration options for this notepad
-- - item.
-- @return result - A notepad item.
createNotepadItem :: CItem c => c -> Notepad c -> Bool ->
[Config (NotepadItem c)] ->
IO (NotepadItem c)
createNotepadItem val notepad updscrollregion cnf =
do
pho <- getIcon val
img <- createImageItem (canvas notepad) [coord [(-200, -200)],
photo pho]
let (Distance dx, _) = img_size notepad
len = div (dx + 80) char_px
nm <- getName val
txt <- createTextItem (canvas notepad) [coord [(-200, -200)],
font (Helvetica, 10 :: Int),
text (short nm len)]
itemval <- newRef val
itemsel <- newRef Nothing
lnbg <- newRef Nothing
let item = NotepadItem { it_img = img,
it_img_size = (img_size notepad),
it_txt = txt,
it_val = itemval,
it_long_name_bg = lnbg,
it_bg = itemsel }
foldl (>>=) (return item) cnf
(entered, _) <- bindSimple item Enter
(left, _) <- bindSimple item Leave
spawnEvent (forever ((entered >>>
(do st <- getIntState notepad
(if st /= Mov then
do
last <- getRef (entered_item notepad)
if not (isJust last)
then do setRef (entered_item notepad)
(Just item)
enteredItem notepad item
else if fromJust last /= item
then do leftItem notepad
(fromJust last)
setRef
(entered_item notepad)
(Just item)
enteredItem notepad item
else done
else done)) ) +>
(left >>>
(do st <- getIntState notepad
(if st /= Mov then
do
last <- getRef (entered_item notepad)
setRef (entered_item notepad) Nothing
if isJust last
then leftItem notepad (fromJust last)
else done
else done)))))
addItemToState notepad item
if updscrollregion then updNotepadScrollRegion notepad else done
return item
---
-- Returns a free item position on the notepad.
-- @param notepad - the concerned notepad.
-- @return result - the free position on the notepad.
getFreeItemPosition :: CItem c => Notepad c -> IO Position
getFreeItemPosition notepad =
let num_cols = 4
(Distance iwidth, Distance iheight) = img_size notepad
dy_n = Distance (div iheight 2)
dy_s = Distance (div iheight 2 + 18)
dx = Distance (max (div iwidth 2) 40)
overlaps (x, y) (item : items) =
do
(ix, iy) <- getPosition item
(if (( (x - dx >= ix - dx && x - dx <= ix + dx) ||
(x + dx > ix - dx && x + dx < ix + dx) ) &&
( (y - dy_n >= iy - dy_n && y - dy_n <= iy + dy_s) ||
(y + dy_s > iy - dy_n && y + dy_s < iy + dy_s) )) then
return True
else overlaps (x, y) items)
overlaps _ _ = return False
in do
items <- getRef (items notepad)
let getPos pos@(x, y) =
do
b <- overlaps pos items
(if b then
getPos (if x + 2 * dx + 10 > 10 + dx + (num_cols * 2 * dx)
then (10 + dx, y + dy_s + dy_n + 10)
else (x + 2 * dx + 10, y))
else return pos)
getPos (10 + dx, 10 + dy_n)
---
-- Gets the value from a notepad item.
getItemValue :: NotepadItem a -> IO a
getItemValue item = getRef (it_val item)
-- instances --
---
-- Internal.
instance Eq (NotepadItem a) where
---
-- Internal.
item1 == item2 = it_img item1 == it_img item2
---
-- Internal.
instance GUIObject (NotepadItem a) where
---
-- Internal.
toGUIObject item = toGUIObject (it_img item)
---
-- Internal.
cname _ = "NotepadItem"
---
-- You can synchronize on a notepad item.
instance Synchronized (NotepadItem a) where
---
-- Synchronizes on a notepad item.
synchronize item = synchronize (toGUIObject (it_img item))
---
-- A notepad item has a position on the associated notepad.
instance HasPosition (NotepadItem a) where
---
-- Sets the notepad item's position.
position p@(x, y) item =
itemPositionD2 p (it_img item) >>
let (Distance iwidth, Distance iheight) = it_img_size item
in itemPositionD2 (x, y + Distance (div iheight 2 + text_gap))
(it_txt item) >>
return item
---
-- Gets the notepad item's position.
getPosition item = getItemPositionD2 (it_img item)
---
-- A notepad item can be destroyed.
instance Destroyable (NotepadItem a) where
---
-- Destroys a notepad item.
destroy item =
do
destroy (it_img item)
destroy (it_txt item)
mrects <- getRef (it_bg item)
case mrects of
Just (rect1, rect2) -> destroy rect1 >> destroy rect2
_ -> done
---
-- (Re-)sets the name of a notepad item.
setName :: CItem c => NotepadItem c -> Name -> IO ()
setName item nm =
do
let (Distance dx, _) = it_img_size item
len = div (dx + 80) char_px
it_txt item # text (short nm len)
done
--------------------------------------------------------------------------
-- notepad events
--------------------------------------------------------------------------
---
-- Binds a listener for notepad events to the notepad and returns
-- a corresponding event and an unbind action.
-- @param np - the concerned notepad.
-- @return result - A pair of (event, unbind action).
bindNotepadEv :: Notepad a -> IO (Event (NotepadEvent a), IO ())
bindNotepadEv np =
do
ch <- newChannel
setRef (event_queue np) (Just ch)
return (receive ch, setRef (event_queue np) Nothing)
---
-- The NotepadEvent datatype.
data NotepadEvent a =
Dropped (NotepadItem a, [NotepadItem a]) --' Drop event.
| Selected (NotepadItem a) --' Selection event.
| Deselected (NotepadItem a) --' Deselection event.
| Doubleclick (NotepadItem a) --' Doubleclick event.
| Rightclick [NotepadItem a] --' Rightclick event.
| ReleaseSelection --' Buttonrelease after a selection.
| ReleaseMovement EventInfo --' Buttonrelease after a movement.
sendEv :: Notepad a -> NotepadEvent a -> IO ()
sendEv np ev =
do
mch <- getRef (event_queue np)
case mch of
Just ch -> syncNoWait (send ch ev)
_ -> done
--------------------------------------------------------------------------
-- Notepad type
--------------------------------------------------------------------------
---
-- The Notepad datatype.
data Notepad a =
Notepad { -- main canvas widget
canvas :: Canvas,
-- scrollbox if scrolled
scrollbox :: Maybe (ScrollBox Canvas),
-- size of item images
img_size :: Size,
-- contained items
items :: Ref ([NotepadItem a]),
-- selected items
selected_items :: Ref ([NotepadItem a]),
-- entered item (mouse over item)
entered_item :: Ref (Maybe (NotepadItem a)),
-- undo last motion action (needed for drag and drop with
-- other widgets)
undo_last_motion :: Ref UndoMotion,
-- entered item when other items dragged /
-- rectangles (highlight)
drop_item :: (Ref (Maybe (NotepadItem a, Rectangle,
Rectangle))),
-- event queue
event_queue :: Ref (Maybe (Channel (NotepadEvent a))),
-- clean up when destroyed
clean_up :: [IO ()],
-- notepad state
npstate :: Ref IntState }
data IntState = Norm | Mov deriving Eq
setIntState :: Notepad a -> IntState -> IO ()
setIntState np st = setRef (npstate np) st
getIntState :: Notepad a -> IO IntState
getIntState np = getRef (npstate np)
---
-- The ScrollType datatype.
data ScrollType = Scrolled | NotScrolled deriving Eq
data UndoMotion = ToPerform (IO ()) | Performed
-- state --
addItemToState :: Notepad a -> NotepadItem a -> IO ()
addItemToState notepad item =
do
notepaditems <- getRef (items notepad)
setRef (items notepad) (item : notepaditems)
highlight :: Canvas -> NotepadItem a -> IO ()
highlight cnv item =
do
let (Distance iwidth, Distance iheight) = it_img_size item
it_txt item # filling "white"
s <- getRef (it_bg item)
case s of
Nothing -> do
(x, y) <- getPosition item
rect1 <- createRectangle cnv
[filling "blue", outline "blue"]
putItemAtBottom rect1
rect1 # coord [(x - Distance (div iwidth 2 + 1),
y - Distance (div iheight 2 + 1)),
(x + Distance (div iwidth 2),
y + Distance (div iheight 2))]
rect2 <- createRectangle cnv
[filling "blue", outline "blue"]
putItemAtBottom rect2
rect2 #
coord [(x - Distance
(max (div iwidth 2 + 40) 40),
y + Distance (div iheight 2 + 4)),
(x + Distance
(max (div iwidth 2 + 40) 40),
y + Distance
(div iheight 2 + 18))]
setRef (it_bg item) (Just (rect1, rect2))
Just _ -> done
deHighlight :: NotepadItem a -> IO ()
deHighlight item =
do
it_txt item # filling "black"
s <- getRef (it_bg item)
case s of
Just (rect1, rect2) ->
destroy rect1 >> destroy rect2 >> setRef (it_bg item) Nothing
_ -> done
---
-- Selects a specific notepad item.
-- @param np - the concerned notepad.
-- @param item - the concerned notepad item.
-- @return result - None.
selectItem :: Notepad a -> NotepadItem a -> IO ()
selectItem np item =
do
deselectAll np
highlight (canvas np) item
selecteditems <- getRef (selected_items np)
setRef (selected_items np) (item : selecteditems)
sendEv np (Selected item)
---
-- Adds an item to the notepad's selection.
-- @param np - the concerned notepad.
-- @param item - the concerned notepad item.
-- @return result - None.
selectAnotherItem :: Notepad a -> NotepadItem a -> IO ()
selectAnotherItem np item =
do
highlight (canvas np) item
selecteditems <- getRef (selected_items np)
setRef (selected_items np) (item : selecteditems)
sendEv np (Selected item)
---
-- Deselects a notepad item.
-- @param np - the concerned notepad.
-- @param item - the concerned notepad item.
-- @return result - None.
deselectItem :: Notepad a -> NotepadItem a -> IO ()
deselectItem np item =
do
deHighlight item
selecteditems <- getRef (selected_items np)
setRef (selected_items np) (filter ((/=) item) selecteditems)
sendEv np (Deselected item)
---
-- Selects all items inside the notepad.
-- @param np - the concerned notepad.
-- @return result - None.
selectAll :: Notepad a -> IO ()
selectAll np =
do
notepaditems <- getRef (items np)
mapM (highlight (canvas np)) notepaditems
mapM (\item -> do
b <- isNotepadItemSelected np item
if b then done else sendEv np (Selected item))
notepaditems
setRef (selected_items np) notepaditems
---
-- Deselects all items inside the notepad.
-- @param np - the concerned notepad.
-- @return result - None.
deselectAll :: Notepad a -> IO ()
deselectAll np =
do
notepaditems <- getRef (items np)
selecteditems <- getRef (selected_items np)
mapM deHighlight selecteditems
mapM (\item -> do
b <- isNotepadItemSelected np item
if b then sendEv np (Deselected item) else done)
notepaditems
setRef (selected_items np) []
---
-- Deletes an item from a notepad.
-- @param np - the concerned notepad.
-- @param item - the concerned notepad item.
-- @return result - None.
deleteItem :: CItem c => Notepad c -> NotepadItem c -> IO ()
deleteItem np item =
synchronize np
(do
notepaditems <- getRef (items np)
selecteditems <- getRef (selected_items np)
entereditem <- getRef (entered_item np)
(if isJust entereditem then setRef (entered_item np) Nothing >>
leftItem np (fromJust entereditem)
else done)
setRef (items np) (filter ((/=) item) notepaditems)
setRef (selected_items np) (filter ((/=) item) selecteditems)
destroy item)
---
-- Deletes all items from a notepad.
-- @param np - the concerned notepad.
-- @return result - None.
clearNotepad :: Notepad a -> IO ()
clearNotepad np =
do
notepaditems <- getRef (items np)
mapM destroy notepaditems
setRef (items np) []
setRef (selected_items np) []
---
-- Internal (for use with GenGUI).
undoLastMotion :: Notepad a -> IO ()
undoLastMotion np =
synchronize np (do
act <- getRef (undo_last_motion np)
case act of
ToPerform act' -> setRef (undo_last_motion np)
Performed >>
act'
_ -> done)
---
-- True if the given notepad item is selected.
-- @param np - the concerned notepad.
-- @param item - the concerned notepad item.
-- @return result - True if the given notepad item is
-- - selected, otherwise False.
isNotepadItemSelected :: Notepad a -> NotepadItem a -> IO Bool
isNotepadItemSelected np item =
do
selecteditems <- getRef (selected_items np)
return (any ((==) item) selecteditems)
---
-- Selects all items within the specified region.
-- @param p1 - the upper left coordinate of the region.
-- @param p2 - the lower right coordinate of the region.
-- @param np - the concerned notepad.
-- @return result - None.
selectItemsWithin :: Position -> Position -> Notepad a -> IO ()
selectItemsWithin p1@(x0, y0) p2@(x1, y1) np =
do
notepaditems <- getRef (items np)
let within :: Position -> Bool
within (x, y) =
((x0 <= x && x <= x1) || (x1 <= x && x <= x0)) &&
((y0 <= y && y <= y1) || (y1 <= y && y <= y0))
mapM (\ item -> do
pos <- getPosition item
b <- isNotepadItemSelected np item
(if within pos then
if b then done else selectAnotherItem np item
else
if b then deselectItem np item else done))
notepaditems
done
---
-- Gets the items from a notepad.
-- @param np - the concerned notepad.
-- @return result - A list of the contained notepad items.
getItems :: Notepad a -> IO [NotepadItem a]
getItems np = getRef (items np)
---
-- Gets the selected items from a notepad.
-- @param np - the concerned notepad.
-- @return result - A list of the selected notepad items.
getSelectedItems :: Notepad a -> IO [NotepadItem a]
getSelectedItems np = getRef (selected_items np)
getView :: Notepad a -> IO (Distance, Distance, Distance, Distance)
getView np =
do (dx_norm, dx_displ_norm) <- view Horizontal (canvas np)
(dy_norm, dy_displ_norm) <- view Vertical (canvas np)
(_, (Distance sizex, Distance sizey)) <- getScrollRegion (canvas np)
let p1_x = Distance (round (dx_norm * fromInteger (toInteger sizex)))
p1_y = Distance (round (dy_norm * fromInteger (toInteger sizey)))
p2_x = p1_x + Distance (round (dx_displ_norm *
fromInteger (toInteger sizex)))
p2_y = p1_y + Distance (round (dy_displ_norm *
fromInteger (toInteger sizey)))
return (p1_x, p1_y, p2_x, p2_y)
--------------------------------------------------------------------------
-- notepad construction
--------------------------------------------------------------------------
---
-- Constructs a new notepad and returns a handler.
-- @param par - the parent widget (which has to be a container
-- - widget).
-- @param scrolltype - the scrolltype for this notepad.
-- @param imgsize - the size of the notepad items images for this
-- - notepad.
-- @param mstate - an optional previous notepad state to recover.
-- @param cnf - the list of configuration options for this notepad.
-- @return result - A notepad.
newNotepad :: (CItem c, Container par) =>
par -> ScrollType -> Size -> Maybe (NotepadState c) ->
[Config (Notepad c)] -> IO (Notepad c)
newNotepad par scrolltype imgsize mstate cnf =
do
let scrolled = (scrolltype == Scrolled)
notepaditemsref <- newRef []
selecteditemsref <- newRef []
entereditemref <- newRef Nothing
dropref <- newRef Nothing
ulm <- newRef Performed
evq <- newRef Nothing
nps <- newRef Norm
(cnv, notepad) <- if scrolled then
do
(scrollbox, cnv) <-
newScrollBox par (\p -> newCanvas p []) []
return (cnv,
Notepad { canvas = cnv,
scrollbox = Just scrollbox,
img_size = imgsize,
items = notepaditemsref,
selected_items =
selecteditemsref,
entered_item = entereditemref,
drop_item = dropref,
event_queue = evq,
undo_last_motion = ulm,
clean_up = [],
npstate = nps })
else
do
cnv <- newCanvas par []
return (cnv,
Notepad { canvas = cnv,
scrollbox = Nothing,
img_size = imgsize,
items = notepaditemsref,
selected_items =
selecteditemsref,
entered_item = entereditemref,
drop_item = dropref,
event_queue = evq,
undo_last_motion = ulm,
clean_up = [],
npstate = nps })
(click, _) <- bind cnv [WishEvent [] (ButtonPress (Just 1))]
(rightclick, _) <- bind cnv
[WishEvent [] (ButtonPress (Just 2))]
(motion', _) <- bind cnv [WishEvent [] Motion]
(motion, _) <- Examples.watch motion'
(clickmotion', _) <- bind cnv [WishEvent [Button1] Motion]
(clickmotion, _) <- Examples.watch clickmotion'
(doubleclick, _) <- bind cnv [WishEvent [Double]
(ButtonPress (Just 1))]
(shiftclick, _) <- bind cnv [WishEvent [Shift]
(ButtonPress (Just 1))]
(release, _) <- bind cnv [WishEvent [] (ButtonRelease (Just 1))]
(leave, _) <- bindSimple cnv Leave
stopListening <- newChannel
let getD :: IO (Distance, Distance)
getD = do
(dx_norm, dx_displ_norm) <- view Horizontal cnv
(dy_norm, _) <- view Vertical cnv
(_, (Distance sizex, Distance sizey)) <-
getScrollRegion cnv
return (Distance (round (dx_norm *
fromInteger (toInteger sizex))),
Distance (round (dy_norm *
fromInteger (toInteger sizey))))
addToTag :: CanvasTag -> NotepadItem a -> IO ()
addToTag tag item =
do
it_img item # tags [tag]
it_txt item # tags [tag]
rects <- getRef (it_bg item)
case rects of
Nothing -> done
Just(rect1, rect2) -> do
rect1 # tags [tag]
rect2 # tags [tag]
done
createTagFromSelection :: Notepad a -> IO CanvasTag
createTagFromSelection notepad =
do
notepaditems <- getRef (items notepad)
selecteditems <- getRef (selected_items notepad)
tag <- createCanvasTag (canvas notepad) []
mapM (addToTag tag) selecteditems
return tag
selectByRectangle :: Distance -> Distance -> Position ->
Rectangle -> Event ()
selectByRectangle dx dy pos rect =
let selectByRectangle' :: Position -> Rectangle -> Event ()
selectByRectangle' pos@(x, y) rect =
(do
(x1, y1) <- clickmotion >>>= getCoords
always (rect # coord [(x + dx, y + dy),
(x1 + dx, y1 + dy)])
always (selectItemsWithin (x + dx, y + dy)
(x1 + dx, y1 + dy) notepad)
selectByRectangle' pos rect) +>
(do
ev_inf <- release
always (do
(dx, dy) <- getD
(x1,y1) <- getCoords ev_inf
sendEv notepad ReleaseSelection
selectItemsWithin (x + dx, y + dy)
(x1 + dx, y1 + dy) notepad
destroy rect))
in selectByRectangle' pos rect
checkPositions :: [NotepadItem a] -> IO (Distance, Distance)
checkPositions (item : items) =
do
let (Distance iwidth, Distance iheight) = it_img_size item
(Distance x, Distance y) <- getPosition item
(Distance dx, Distance dy) <- checkPositions items
(_, (Distance sizex, Distance sizey)) <-
getScrollRegion (canvas notepad)
let min_x = x - (max (div iwidth 2 + 30) 40)
min_y = y - (div iheight 2 + 1)
dx' = max dx (-min_x)
{-if dx < 0 then min min_x dx
else if dx == 0 then
if min_x < 0 then min_x
else if max_x > sizex then max_x - sizex
else 0
else if dx > 0 then
max dx (max_x - sizex)
else 0-}
dy' = max dy (-min_y)
{-if dy < 0 then min min_y dy
else if dy == 0 then
if min_y < 0 then min_y
else if max_y > sizey then max_y - sizey
else 0
else if dy > 0 then
max dy (max_y - sizey)
else 0-}
return (Distance dx', Distance dy')
checkPositions [] = return (Distance 0, Distance 0)
{-
checkPositions :: [NotepadItem a] -> IO (Distance, Distance)
checkPositions (item : items) =
do
let (Distance iwidth, Distance iheight) = it_img_size item
(Distance x, Distance y) <- getPosition item
(Distance dx, Distance dy) <- checkPositions items
(_, (Distance sizex, Distance sizey)) <-
getScrollRegion (canvas notepad)
let min_x = x - (max (div iwidth 2 + 30) 40)
max_x = x + (max (div iwidth 2 + 30) 40)
min_y = y - (div iheight 2 + 1)
max_y = y + (div iheight 2 + 18)
dx' = if dx < 0 then min min_x dx
else if dx == 0 then
if min_x < 0 then min_x
else if max_x > sizex then max_x - sizex
else 0
else if dx > 0 then
max dx (max_x - sizex)
else 0
dy' = if dy < 0 then min min_y dy
else if dy == 0 then
if min_y < 0 then min_y
else if max_y > sizey then max_y - sizey
else 0
else if dy > 0 then
max dy (max_y - sizey)
else 0
return (Distance dx', Distance dy')
checkPositions [] = return (Distance 0, Distance 0)
-}
grid_x :: Int
grid_x = 10
grid_y :: Int
grid_y = 10
checkDropZones :: CItem a =>
FiniteMap (Int, Int) [NotepadItem a] ->
Notepad a -> Distance -> Distance -> IO ()
checkDropZones it_map notepad x@(Distance ix) y@(Distance iy) =
let doSet item =
do
(x, y) <- getPosition item
let (Distance iwidth, Distance iheight) =
it_img_size item
rect1 <- createRectangle (canvas notepad)
[coord [(x - Distance (div iwidth 2 + 1),
y - Distance (div iheight 2 + 1)),
(x + Distance (div iwidth 2),
y + Distance (div iheight 2))],
filling "yellow", outline "yellow"]
putItemAtBottom rect1
rect2 <- createRectangle (canvas notepad)
[coord [(x - Distance
(max (div iwidth 2 + 40) 40),
y + Distance (div iheight 2)),
(x + Distance
(max (div iwidth 2 + 40) 40),
y + Distance (div iheight 2 + 18))],
filling "yellow", outline "yellow"]
putItemAtBottom rect2
setRef (drop_item notepad) (Just (item, rect1, rect2))
setDropRef item =
do
drop <- getRef (drop_item notepad)
case drop of
Nothing -> doSet item
Just (ditem, rect1, rect2) ->
if item == ditem then done
else destroy rect1 >> destroy rect2 >> doSet item
inDropZone item =
do
(x_it, y_it) <- getPosition (it_img item)
return (if x_it - 30 < x && x_it + 30 > x &&
y_it - 10 < y && y_it + 30 > y then True
else False)
checkDropZones' (item : items) =
do
b <- inDropZone item
(if b then setDropRef item else checkDropZones' items)
checkDropZones' [] =
do
maybeitem <- getRef (drop_item notepad)
case maybeitem of
Just (_, rect1, rect2) ->
destroy rect1 >> destroy rect2 >>
setRef (drop_item notepad) Nothing
Nothing -> done
in do (_, (Distance sizex, Distance sizey)) <-
getScrollRegion (canvas notepad)
let idx@(idx_x, idx_y) = (div ix (div sizex grid_x), div iy (div sizey grid_y))
items = (lookupWithDefaultFM it_map [] idx)
checkDropZones' (lookupWithDefaultFM it_map [] idx)
buildMap :: CItem a => Notepad a -> IO (FiniteMap (Int, Int) [NotepadItem a])
buildMap notepad =
do notepaditems <- getRef (items notepad)
selecteditems <- getRef (selected_items notepad)
let nonselecteditems =
filter (\item -> not(any ((==) item) selecteditems))
notepaditems
fmref <- newRef emptyFM
let add (idx_x, idx_y) notepaditem =
if idx_x >= 0 && idx_x < grid_x &&
idx_y >= 0 && idx_y < grid_y then
do fm <- getRef fmref
let mnotepaditems = lookupFM fm (idx_x, idx_y)
let nufm = case mnotepaditems of
Just notepaditems ->
addToFM fm (idx_x, idx_y)
(notepaditem : notepaditems)
_ -> addToFM fm (idx_x, idx_y)
[notepaditem]
setRef fmref nufm
else done
getCenterIndex notepaditem =
do (_, (Distance sizex, Distance sizey)) <-
getScrollRegion (canvas notepad)
(Distance x, Distance y) <- getPosition notepaditem
return (div x (div sizex grid_x),
div y (div sizey grid_y))
addNotepadItem notepaditem =
do idx@(idx_x, idx_y) <- getCenterIndex notepaditem
add idx notepaditem
add (idx_x - 1, idx_y ) notepaditem
add (idx_x - 1, idx_y - 1) notepaditem
add (idx_x , idx_y - 1) notepaditem
add (idx_x + 1, idx_y - 1) notepaditem
add (idx_x + 1, idx_y ) notepaditem
add (idx_x + 1, idx_y + 1) notepaditem
add (idx_x , idx_y + 1) notepaditem
add (idx_x - 1, idx_y + 1) notepaditem
mapM addNotepadItem nonselecteditems
getRef fmref
moveSelectedItems it_map rpos@(rootx, rooty) (x0, y0) t =
(do
(x, y) <- clickmotion >>>= getCoords
always (do
(dx, dy) <- getD
checkDropZones it_map notepad (x + dx) (y + dy)
setRef (undo_last_motion notepad)
(ToPerform (moveItem t (rootx - x0)
(rooty - y0)))
moveItem t (x - x0) (y - y0))
moveSelectedItems it_map rpos (x, y) t)
+> (do
ev_inf <- release
always (do
sendEv notepad (ReleaseMovement ev_inf)
drop <- getRef dropref
case drop of
Just (item, rect1, rect2) ->
do
act <- getRef (undo_last_motion notepad)
case act of
Performed -> done
_ -> do
undoLastMotion notepad
selecteditems <-
getRef selecteditemsref
sendEv notepad
(Dropped (item,
selecteditems))
setRef dropref Nothing
destroy rect1
destroy rect2
_ -> do
selecteditems <-
getRef selecteditemsref
(dx, dy) <-
checkPositions selecteditems
-- moveItem t (-dx) (-dy)))
moveItem t dx dy
updNotepadScrollRegion notepad))
checkEnteredItem (x, y) =
let overItem item =
do
(dx, dy) <- getD
(x_it, y_it) <- getPosition (it_img item)
return (if x_it - 30 < x + dx && x_it + 30 > x + dx &&
y_it - 10 < y + dy && y_it + 30 > y + dy then True
else False)
checkItems (item : items) =
do
b <- overItem item
(if b then setRef entereditemref (Just item)
else checkItems items)
checkItems _ = setRef entereditemref Nothing
in synchronize notepad
(do
last <- getRef entereditemref
items <- getRef notepaditemsref
checkItems items
new <- getRef entereditemref
(if isJust last then
if isJust new then
if fromJust last == fromJust new then done
else
leftItem notepad (fromJust last) >>
enteredItem notepad (fromJust new)
else
leftItem notepad (fromJust last)
else
if isJust new then
enteredItem notepad (fromJust new)
else
done))
listenNotepad :: Event ()
listenNotepad =
(leave >> always (do
mentereditem <- getRef entereditemref
(if isJust mentereditem then
leftItem notepad
(fromJust mentereditem) >>
setRef entereditemref Nothing
else done)) >>
listenNotepad)
-- -----
{-
+> (do
(x, y) <- motion >>>= getCoords
always (checkEnteredItem (x, y))
listenNotepad)
-}
-- -------
+> (do
(x, y) <- click >>>= getCoords
always
(do
entereditem <- getRef entereditemref
case entereditem of
Nothing -> do
deselectAll notepad
(dx, dy) <- getD
rect <- createRectangle cnv
[coord [(x + dx, y + dy),
(x + dx, y + dy)]]
sync
(selectByRectangle dx dy (x, y)
rect)
done
Just item ->
do
leftItem notepad item
b <- isNotepadItemSelected notepad item
if b then done else selectItem notepad item
t <- createTagFromSelection notepad
sync (do mp <- always (buildMap notepad)
always (setIntState notepad Mov)
moveSelectedItems mp (x, y) (x, y) t
always (setIntState notepad Norm))
done)
listenNotepad)
+> (do
(x, y) <- rightclick >>>= getCoords
always
(do
entereditem <- getRef entereditemref
case entereditem of
Nothing -> do
deselectAll notepad
sendEv notepad (Rightclick [])
Just entereditem ->
do
b <- isNotepadItemSelected notepad
entereditem
(if b then
do
selecteditems <- getRef selecteditemsref
sendEv notepad (Rightclick selecteditems)
else
do
selectItem notepad entereditem
sendEv notepad
(Rightclick [entereditem])))
listenNotepad)
+> (doubleclick >> do
always (do
entereditem <-
getRef entereditemref
case entereditem of
Just item ->
sendEv notepad
(Doubleclick item)
_ -> done)
listenNotepad)
+> (shiftclick >> do
always (do
entereditem <-
getRef entereditemref
case entereditem of
Just item ->
do
b <- isNotepadItemSelected
notepad item
(if b then
deselectItem notepad
item
else
selectAnotherItem
notepad item)
_ -> done)
listenNotepad)
+> (release >> listenNotepad) -- avoid cueing of release events
+> receive stopListening
spawnEvent listenNotepad
foldl (>>=) (return notepad) cnf
case mstate of
Just state -> importNotepadState notepad state
_ -> done
return notepad
updNotepadScrollRegion :: Notepad a -> IO ()
updNotepadScrollRegion np =
let getMax (item : items) mx my =
do (x, y) <- getPosition item
let nux = max x mx
nuy = max y my
getMax items nux nuy
getMax _ mx my = return (mx, my)
in do (x1, y1, x2, y2) <- getView np
items <- getItems np
(x, y) <- getMax items 0 0
np # size (x + 80, y + 40)
done
-- instances --
---
-- Internal.
instance GUIObject (Notepad a) where
---
-- Internal.
toGUIObject np =
case (scrollbox np) of Nothing -> toGUIObject (canvas np)
Just box -> toGUIObject box
---
-- Internal.
cname _ = "Notepad"
---
-- A notepad can be destroyed.
instance Destroyable (Notepad a) where
---
-- Destroys a notepad.
destroy = destroy . toGUIObject -- TD : clean up !!!
---
-- A notepad has standard widget properties
-- (concerning focus, cursor).
instance Widget (Notepad a)
---
-- You can synchronize on a notepad object.
instance Synchronized (Notepad a) where
---
-- Synchronizes on a notepad object.
synchronize w = synchronize (toGUIObject w)
---
-- A notepad has a configureable border.
instance HasBorder (Notepad a)
---
-- A notepad has a configureable background colour.
instance HasColour (Notepad a) where
---
-- Internal.
legalColourID np = hasBackGroundColour (canvas np)
---
-- Internal.
setColour notepad cid col =
setColour (canvas notepad) cid col >> return notepad
---
-- Internal.
getColour np cid = getColour (canvas np) cid
---
-- A notepad has a configureable size.
instance HasSize (Notepad a) where
---
-- Sets the notepad's width.
width s np =
do
(_, (_, sizey)) <- getScrollRegion (canvas np)
canvas np # scrollRegion ((0, 0), (s, sizey))
if isJust (scrollbox np) then done else canvas np # width s >> done
return np
---
-- Gets the notepad's width.
getWidth np = getWidth (canvas np)
---
-- Sets the notepad's height.
height s np =
do
(_, (sizex, _)) <- getScrollRegion (canvas np)
canvas np # scrollRegion ((0, 0), (sizex, s))
(if (isJust (scrollbox np)) then done
else canvas np # height s >> done)
return np
---
-- Gets the notepad's height.
getHeight np = getHeight (canvas np)
-- -----------------------------------------------------------------------
-- state import / export
-- -----------------------------------------------------------------------
---
-- The NotepadExportItem datatype.
data CItem c => NotepadExportItem c =
NotepadExportItem { val :: c,
pos :: Position,
selected :: Bool }
type NotepadState c = [NotepadExportItem c]
---
-- Exports a notepad's state.
-- @param np - the concerned notepad.
-- @return result - The notepad's state.
exportNotepadState :: CItem c => Notepad c -> IO (NotepadState c)
exportNotepadState np =
synchronize np (do
items' <- getRef (items np)
exportNotepadState' np items')
where exportNotepadState' :: CItem c => Notepad c -> [NotepadItem c] ->
IO (NotepadState c)
exportNotepadState' np (item : items) =
do
val' <- getRef (it_val item)
pos <- getPosition (it_img item)
is_selected <- isNotepadItemSelected np item
rest <- exportNotepadState' np items
return (NotepadExportItem { val = val',
pos = pos,
selected = is_selected } : rest)
exportNotepadState' _ _ = return []
---
-- Imports a notepad's state.
-- @param np - the concerned notepad.
-- @return result - None.
importNotepadState :: CItem c => Notepad c -> NotepadState c -> IO ()
importNotepadState np st =
synchronize np (do
clearNotepad np
addItems np st
updNotepadScrollRegion np)
where addItems :: CItem c => Notepad c -> NotepadState c -> IO ()
addItems np (it : items) =
do
new_it <- createNotepadItem (val it) np False
[position (pos it)]
if selected it then selectAnotherItem np new_it else done
addItems np items
addItems _ _ = done