leksah-0.16.2.2: src/IDE/SymbolNavigation.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE LambdaCase #-}
{-# OPTIONS_GHC -fno-warn-warnings-deprecations #-}
-----------------------------------------------------------------------------
--
-- Module : IDE.SymbolNavigation
-- Copyright : (c) Sanny Sannof, Juergen Nicklisch-Franken
-- License : GNU-GPL
--
-- Maintainer : <maintainer at leksah.org>
-- Stability : provisional
-- Portability : portable
--
-- | The source editor part of Leksah
--
-----------------------------------------------------------------------------------
module IDE.SymbolNavigation (
createHyperLinkSupport,
mapControlCommand
) where
import IDE.TextEditor (TextEditor(..), EditorView(..), EditorIter(..))
import IDE.Core.Types (IDEM)
import Control.Monad.IO.Class (MonadIO(..))
import IDE.Utils.GUIUtils (mapControlCommand)
import Data.IORef (writeIORef, readIORef, newIORef)
import Control.Monad (join, when)
import Data.Maybe (fromJust, isJust)
import Control.Monad.Reader.Class (MonadReader(..))
import IDE.Core.State (reflectIDE)
import Control.Applicative ((<$>))
import Data.Text (Text)
import qualified Data.Text as T (length)
import GI.Gtk.Objects.ScrolledWindow
(getScrolledWindowVadjustment, getScrolledWindowHadjustment,
ScrolledWindow(..))
import Graphics.UI.Editor.Basics (Connection(..), Connection)
import GI.Pango.Enums (Underline(..))
import GI.Gdk.Objects.Cursor (cursorNew)
import GI.Gdk.Enums (GrabOwnership(..), CursorType(..))
import GI.Gtk.Objects.Widget
(onWidgetButtonPressEvent, onWidgetMotionNotifyEvent,
widgetGetWindow, widgetGetAllocation, onWidgetLeaveNotifyEvent)
import GI.Gdk.Structs.EventCrossing (getEventCrossingTime)
import GI.Gdk.Functions (pointerGrab, pointerUngrab)
import GI.Gtk.Objects.Adjustment (adjustmentGetValue)
import Graphics.UI.Frame.Rectangle
(getRectangleHeight, getRectangleWidth)
import GI.Gdk.Objects.Screen (screenGetDefault)
import qualified GI.Gdk.Objects.Window as Gdk (noWindow)
import GI.Gdk.Structs.EventMotion
(getEventMotionXRoot, getEventMotionY, getEventMotionX,
getEventMotionState, getEventMotionTime, getEventMotionIsHint)
import GI.Gdk.Structs.EventButton
(getEventButtonXRoot, getEventButtonY, getEventButtonX,
getEventButtonState, getEventButtonTime)
import GI.Gdk.Objects.Window (windowGetOrigin)
import GI.Gdk
(getEventButtonDevice, getEventMotionDevice,
deviceGetDisplay, cursorNewForDisplay, displayGetDefault)
import Data.Foldable (forM_)
-- TODO fix seat code
#undef MIN_VERSION_GTK_3_20
#ifdef MIN_VERSION_GTK_3_20
import GI.Gdk.Flags
(SeatCapabilities(..), EventMask(..), ModifierType(..))
import GI.Gdk.Objects.Device (deviceGetSeat)
import GI.Gdk.Objects.Seat (seatGrab, seatUngrab)
#else
import GI.Gdk.Flags
(EventMask(..), ModifierType(..))
import GI.Gdk.Objects.Device (deviceGrab, deviceUngrab)
#endif
data Locality = LocalityPackage | LocalityWorkspace | LocalitySystem -- in which category symbol is located
deriving (Ord,Eq,Show)
-- |
createHyperLinkSupport
:: TextEditor editor
=> EditorView editor -- ^ source buffer view
-> ScrolledWindow -- ^ container window
-> (Bool -> Bool -> EditorIter editor -> IDEM (EditorIter editor, EditorIter editor)) -- ^ identifiermapper (bools=control,shift)
-> (Bool -> Bool -> Text -> IDEM ()) -- ^ click handler
-> IDEM [Connection]
createHyperLinkSupport sv sw identifierMapper clickHandler = do
tv <- getEditorWidget sv
tvb <- getBuffer sv
ttt <- getTagTable tvb
linkTag <- newTag ttt "link"
underline linkTag UnderlineSingle Nothing
-- id1 <- ConnectC sw <$> onWidgetLeaveNotifyEvent sw (\e -> do
-- getEventCrossingTime e >>= pointerUngrab
-- return True)
let moveOrClick mbDevice eventX eventY mods eventTime mbMotion = do
#ifdef MIN_VERSION_GTK_3_20
mbSeat <- mapM deviceGetSeat mbDevice
let ungrab = mapM_ seatUngrab mbSeat
#else
let ungrab = mapM_ (`deviceUngrab` eventTime) mbDevice
#endif
mbHand <- mapM deviceGetDisplay mbDevice
>>= mapM (`cursorNewForDisplay` CursorTypeHand2)
sx <- getScrolledWindowHadjustment sw >>= adjustmentGetValue
sy <- getScrolledWindowVadjustment sw >>= adjustmentGetValue
let ex = eventX + sx
ey = eventY + sy
ctrlPressed = mapControlCommand ModifierTypeControlMask `elem` mods
shiftPressed = ModifierTypeShiftMask `elem` mods
iter <- getIterAtLocation sv (round ex) (round ey)
rect <- widgetGetAllocation sw
szx <- getRectangleWidth rect
szy <- getRectangleHeight rect
if eventX < 0 || eventY < 0
|| round eventX > szx || round eventY > szy then do
ungrab
return True
else do
(beg, en) <- identifierMapper ctrlPressed shiftPressed iter
slice <- getSlice tvb beg en True
removeTagByName tvb "link"
offs <- getLineOffset beg
offsc <- getLineOffset iter
if T.length slice > 1 then
case mbMotion of
Nothing -> do
ungrab
clickHandler ctrlPressed shiftPressed slice
#ifdef MIN_VERSION_GTK_3_20
Just motion -> do
#else
Just _ -> do
#endif
applyTagByName tvb "link" beg en
screen <- screenGetDefault
widgetGetWindow tv >>= \case
Nothing -> return ()
Just dw ->
#ifdef MIN_VERSION_GTK_3_20
forM_ mbSeat $ \seat ->
seatGrab seat dw [SeatCapabilitiesPointer] False mbHand (_convertToEvent <$> motion) Nothing
#else
forM_ mbDevice $ \device ->
deviceGrab device dw GrabOwnershipNone False
[EventMaskPointerMotionMask,EventMaskButtonPressMask,EventMaskLeaveNotifyMask]
mbHand eventTime
#endif
else ungrab
return True
lineNumberBugFix <- liftIO $ newIORef Nothing
let fixBugWithX mods isHint (eventX, eventY) ptrx' = do
Just window <- widgetGetWindow sw
(_, ox, _) <- windowGetOrigin window
let ptrx = ptrx' - (fromIntegral ox)
let hasNoControlModifier = mapControlCommand ModifierTypeControlMask `notElem` mods
lnbf <- readIORef lineNumberBugFix
-- print ("ishint?, adjusted, event.x, ptr.x, adjustment,hasControl?",isHint, eventX, ptrx, lnbf, ox, hasNoControlModifier)
-- when (isHint && hasNoControlModifier) $
when (abs (ptrx - eventX) > 2) $
-- get difference between event X and pointer x
-- event X is in coordinates of sourceView text
-- pointer X is in coordinates of window (remember "show line numbers" ?)
liftIO $ writeIORef lineNumberBugFix $ Just (ptrx - eventX) -- captured difference
-- When control key is pressed, mostly NON-HINT events come,
-- GTK gives (mistakenly?) X in window coordinates in such cases
let nx = if isJust lnbf && not isHint
then ptrx - fromJust lnbf -- translate X back
else eventX
return (nx, eventY)
ideR <- ask
id2 <- ConnectC sw <$> onWidgetMotionNotifyEvent sw (\e -> do
isHint <- (/=0) <$> getEventMotionIsHint e
eventTime <- getEventMotionTime e
mods <- getEventMotionState e
oldX <- getEventMotionX e
oldY <- getEventMotionY e
rootX <- getEventMotionXRoot e
device <- getEventMotionDevice e
(eventX, eventY) <- liftIO $ fixBugWithX mods isHint (oldX, oldY) rootX
-- print ("move adjustment: isHint, old, new root", isHint, eventX, oldX, rootX)
(`reflectIDE` ideR) $ moveOrClick device eventX eventY mods eventTime (Just e)
return True)
id3 <- ConnectC sw <$> onWidgetButtonPressEvent sw (\e -> do
eventTime <- getEventButtonTime e
mods <- getEventButtonState e
-- liftIO $ print ("button press")
oldX <- getEventButtonX e
oldY <- getEventButtonY e
rootX <- getEventButtonXRoot e
device <- getEventButtonDevice e
(eventX, eventY) <- liftIO $ fixBugWithX mods False (oldX, oldY) rootX
-- liftIO $ print ("click adjustment: old, new", eventX, oldX)
(`reflectIDE` ideR) $ moveOrClick device eventX eventY mods eventTime Nothing)
return [{-id1,-} id2, id3]