packages feed

hbro-1.3.0.0: library/Hbro/WebView/Signals.hs

{-# LANGUAGE ConstraintKinds   #-}
{-# LANGUAGE FlexibleContexts  #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies      #-}
module Hbro.WebView.Signals where

-- {{{ Imports
import           Hbro.Error
import           Hbro.Event
import           Hbro.Gdk.KeyVal
import           Hbro.Keys                                  as Keys
import           Hbro.Keys.Model                            ((.|))
import           Hbro.Logger
import           Hbro.Prelude                               hiding (on)

import           Graphics.UI.Gtk.WebKit.Lifted              as Lifted
import           Graphics.UI.Gtk.WebKit.Lifted.WebView      as W

import           Data.Set                                   as S hiding (map)

import           Graphics.UI.Gtk.Abstract.Object
import           Graphics.UI.Gtk.Abstract.Widget            hiding (KeyVal)
import           Graphics.UI.Gtk.Gdk.EventM                 as Gdk
import           Graphics.UI.Gtk.General.General.Extended
import           Graphics.UI.Gtk.WebKit.Download            as W hiding
                                                                  (Download, downloadGetSuggestedFilename, downloadGetUri)
import           Graphics.UI.Gtk.WebKit.WebNavigationAction
import           Graphics.UI.Gtk.WebKit.WebPolicyDecision

import           Network.URI.Extended

import qualified System.Glib.Attributes                     as Glib
import           System.Glib.GError
import           System.Glib.Signals                        hiding (Signal)
-- }}}

data Download = Download deriving(Show)
instance Event Download where
  type Input Download = (URI, Text, Maybe Int)
  describeInput _ (uri, _, _) = Just $ "Requested download <" ++ tshow uri ++ ">"

data LinkClicked = LinkClicked deriving(Show)
instance Event LinkClicked where
  type Input LinkClicked = (URI, MouseButton)
  describeInput _ (uri, _) = Just $ "Link clicked <" ++ tshow uri ++ ">"

data LinkHovered = LinkHovered deriving(Show)
instance Event LinkHovered where
  type Input LinkHovered = (URI, Maybe Text)
  describeInput _ (uri, _) = Just $ "Link hovered <" ++ tshow uri ++ ">"

data LinkUnhovered = LinkUnhovered deriving(Show)
instance Event LinkUnhovered where
  describeInput _ _ = Just "Link unhovered"

data LoadCommitted = LoadCommitted deriving(Show)
instance Event LoadCommitted where
  describeInput _ _ = Just "Load committed"

data LoadFailed = LoadFailed deriving(Show)
instance Event LoadFailed where
  type Input LoadFailed = (URI, GError)
  describeInput _ (uri, e) = Just $ "Error loading <" ++ tshow uri ++ "> : " ++ tshow e

data LoadFinished = LoadFinished deriving(Show)
instance Event LoadFinished where
  describeInput _ _ = Just "Load finished"

data LoadRequested = LoadRequested deriving(Show)
instance Event LoadRequested where
  type Input LoadRequested = URI
  describeInput _ uri = Just $ "Load requested <" ++ tshow uri ++ ">"

data LoadStarted = LoadStarted deriving(Show)
instance Event LoadStarted where
  describeInput _ _ = Just "Load started"

data NewWindow = NewWindow deriving(Show)
instance Event NewWindow where
  type Input NewWindow = URI
  describeInput _ uri = Just $ "New window <" ++ tshow uri ++ ">"

data ProgressChanged = ProgressChanged deriving(Show)
instance Event ProgressChanged where
  type Input ProgressChanged = Int
  describeInput _ percent = Just $ "Load progress: " ++ tshow percent ++ "%"

data ResourceOpened = ResourceOpened deriving(Show)
instance Event ResourceOpened where
  type Input ResourceOpened = (URI, Text)
  describeInput _ _ = Just "Resource opened"

data TitleChanged = TitleChanged deriving(Show)
instance Event TitleChanged where
  type Input TitleChanged = Text
  describeInput _ = Just . (++) "Title changed to: "

data URIChanged = URIChanged deriving(Show)
instance Event URIChanged where
  type Input URIChanged = URI
  describeInput _ = Just . (++) "URI changed to: " . tshow

data ZoomLevelChanged = ZoomLevelChanged deriving(Show)
instance Event ZoomLevelChanged where
  type Input ZoomLevelChanged = Float
  describeInput _ value = Just $ "Zoom level changed to: " ++ tshow value

data ResourceAction = Load | Download' deriving(Show)
instance Describable ResourceAction where describe = tshow


attachDownload :: (ControlIO m, MonadLogger m) => WebView -> Signal Download -> m (ConnectId WebView)
attachDownload webView signal = liftBaseWith $ \runInIO -> gSync . on webView downloadRequested $ \d -> do
  runInIO . runExceptT . logErrors $ do
    amount <- io $ downloadGetTotalSize d
    uri    <- downloadGetUri d
    name   <- downloadGetSuggestedFilename d

    emit signal (uri, name, Just amount)
  return False


attachLinkHovered :: (ControlIO m, MonadLogger m) => WebView -> Signal LinkHovered -> Signal LinkUnhovered -> m (ConnectId WebView)
attachLinkHovered webView hoveredSignal unhoveredSignal = liftBaseWith $ \runInIO -> gSync $ on webView hoveringOverLink (\a b -> void . runInIO $ callback a b)
  where callback title (Just uri) = void . runExceptT . logErrors $ do
          u <- parseURIM $ pack uri
          emit hoveredSignal (u, pack <$> title)
        callback _ _ = emit unhoveredSignal ()


attachLoadCommitted :: (ControlIO m, MonadLogger m) => WebView -> Signal LoadCommitted -> m (ConnectId WebView)
attachLoadCommitted webView signal = liftBaseWith $ \runInIO -> gSync . on webView loadCommitted $ \_frame -> void . runInIO $ emit signal ()


attachLoadFailed :: (ControlIO m, MonadLogger m) => WebView -> Signal LoadFailed -> m (ConnectId WebView)
attachLoadFailed webView signal = liftBaseWith $ \runInIO -> gSync . on webView loadError $ \_frame uri e -> do
  runInIO . runExceptT . logErrors $ do
    uri' <- parseURIReferenceM uri
    emit signal (uri', e)
  return False


attachLoadFinished :: (ControlIO m, MonadLogger m) => WebView -> Signal LoadFinished -> m (ConnectId WebView)
attachLoadFinished webView signal = liftBaseWith $ \runInIO -> gSync . on webView loadFinished $ \_frame -> void . runInIO $ emit signal ()


attachLoadStarted :: (ControlIO m, MonadLogger m) => WebView -> Signal LoadStarted -> m (ConnectId WebView)
attachLoadStarted webView signal = liftBaseWith $ \runInIO -> gSync . on webView loadStarted $ \_frame -> void . runInIO $ emit signal ()

attachNavigationRequest :: (ControlIO m, MonadLogger m) => WebView -> Signal LinkClicked -> Signal LoadRequested -> m (ConnectId WebView)
attachNavigationRequest webView signal1 signal2 = liftBaseWith $ \runInIO -> gSync . on webView navigationPolicyDecisionRequested $ \_frame request action decision -> do
    reason <- webNavigationActionGetReason action
    button <- toMouseButton <$> webNavigationActionGetButton action

    -- io . putStrLn . ("Request data: " ++) =<< networkRequestGetBody request
    -- io . putStrLn . ("Request type: " ++) . describe =<< networkRequestGetContentType request
    -- io . putStrLn . ("Request type: " ++) . describe =<< networkRequestGetURI request

    runInIO . runExceptT $ do
        uri <- networkRequestGetUri request

        case (reason, button) of
            (WebNavigationReasonLinkClicked, Just b) -> do
                emit signal1 (uri, b)
                io $ webPolicyDecisionIgnore decision
            (WebNavigationReasonOther, _) -> do
                debug $ "Navigation request to <" ++ tshow uri ++ ">"
                io $ webPolicyDecisionUse decision
            (WebNavigationReasonBackForward, _) -> do
                io $ webPolicyDecisionUse decision
            (WebNavigationReasonReload, _) -> do
                io $ webPolicyDecisionUse decision
            (WebNavigationReasonFormSubmitted, _) -> do
                debug $ "Form submitted to <" ++ tshow uri ++ ">"
                io $ webPolicyDecisionUse decision
            _ -> do
                debug $ "Navigation request [" ++ tshow reason ++ "] to <" ++ tshow uri ++ ">"
                emit signal2 uri
                io $ webPolicyDecisionIgnore decision
      `catchError` \e -> do
        error e
        io $ webPolicyDecisionUse decision

    return True
  where
    toMouseButton 1 = Just LeftButton
    toMouseButton 2 = Just MiddleButton
    toMouseButton 3 = Just RightButton
    toMouseButton _ = Nothing


-- Triggered in 2 cases:
--  1/ Javascript window.open()
--  2/ Context menu "Open in new window"
attachNewWebView :: (ControlIO m, MonadLogger m) => WebView -> Signal NewWindow -> m (ConnectId WebView)
attachNewWebView webView signal = liftBaseWith $ \runInIO -> gSync . on webView createWebView $ \_frame -> do
    webView' <- webViewNew

    on webView' webViewReady $ return True
    on webView' navigationPolicyDecisionRequested $ \_ request _ decision -> do
        runInIO . runExceptT . logErrors $ networkRequestGetUri request >>= emit signal
        webPolicyDecisionIgnore decision
        return True

    return webView'


attachNewWindow :: (ControlIO m, MonadLogger m) => WebView -> Signal NewWindow -> m (ConnectId WebView)
attachNewWindow webView signal = liftBaseWith $ \runInIO -> gSync . on webView newWindowPolicyDecisionRequested $ \_frame request _action decision -> do
    runInIO . runExceptT . logErrors $ networkRequestGetUri request >>= emit signal
    webPolicyDecisionIgnore decision
    return True


attachProgressChanged :: (ControlIO m, MonadLogger m) => WebView -> Signal ProgressChanged -> m (ConnectId WebView)
attachProgressChanged webView signal = liftBaseWith $ \runInIO -> gSync . on webView progressChanged $ void . runInIO . emit signal

-- attachResourceOpened :: (MonadIO m) => WebView -> Signal ResourceOpened -> m (ConnectId WebView)
-- attachResourceOpened webView signal = liftBaseWith $ \runInIO -> gSync . on webView mimeTypePolicyDecisionRequested $ \_frame request mimetype decision -> do
--     action <- logErrors $ do
--         uri <- networkRequestGetUri request
--         debug $ "Opening resource [MIME type=" ++ mimetype ++ "] at <" ++ tshow uri ++ ">"
--         -- io . waitForResult =<<
--         emit signal (uri, mimetype)

--     debug "debug" $ "decision made: " ++ tshow action
--     case action of
--         Just Load -> webPolicyDecisionUse decision
--         Just Download' -> webPolicyDecisionDownload decision
--         _ -> webPolicyDecisionIgnore decision
--     return True

-- waitForResult output = do
--   debug "hbro.debug" "iteration" >> mainIteration
--   maybe (waitForResult output) return =<< (atomically $ tryTakeTMVar output)


attachTitleChanged :: (ControlIO m, MonadLogger m) => WebView -> Signal TitleChanged -> m (ConnectId WebView)
attachTitleChanged webView signal = liftBaseWith $ \runInIO -> gSync . on webView W.titleChanged $ \_frame title -> void . runInIO $ emit signal title

attachUriChanged :: (ControlIO m, MonadLogger m) => WebView -> Signal URIChanged -> m (ConnectId WebView)
attachUriChanged webView signal = liftBaseWith $ \runInIO ->
  gSync . on webView (notifyProperty W.webViewUri) . void . runInIO . runExceptT . logErrors $
    io (Glib.get webView webViewUri) >>= maybe (throwError "Invalid URI") return >>= parseURIM >>= emit signal

attachZoomLevelChanged :: (ControlIO m, MonadLogger m) => WebView -> Signal ZoomLevelChanged -> m (ConnectId WebView)
attachZoomLevelChanged webView signal = liftBaseWith $ \runInIO -> gSync . on webView (notifyProperty webViewZoomLevel) . void . runInIO $ emit signal =<< io (Glib.get webView webViewZoomLevel)


attachKeyPressed :: (ControlIO m, MonadLogger m) => WebView -> Signal KeyPressed -> m (ConnectId WebView)
attachKeyPressed webView signal = liftBaseWith $ \runInIO -> gSync . on webView keyPressEvent $ do
    modifiers <- Modifier . S.delete Gdk.Shift . S.fromList <$> Gdk.eventModifier
    key       <- KeyVal <$> Gdk.eventKeyVal

    io . runInIO . runFailT $ do
        guard . not $ isModifier key || isModalKey key
        emit signal $ modifiers .| key

    return False