1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
|
{-# LANGUAGE CPP #-}
-- -*-haskell-*-
-- GIMP Toolkit (GTK) Interface Editable
--
-- Author : Axel Simon, Duncan Coutts
--
-- Created: 30 July 2004
--
-- Copyright (C) 1999-2005 Axel Simon, Duncan Coutts
--
-- This library is free software; you can redistribute it and/or
-- modify it under the terms of the GNU Lesser General Public
-- License as published by the Free Software Foundation; either
-- version 2.1 of the License, or (at your option) any later version.
--
-- This library is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-- Lesser General Public License for more details.
--
-- |
-- Maintainer : gtk2hs-users@lists.sourceforge.net
-- Stability : provisional
-- Portability : portable (depends on GHC)
--
-- Interface for text-editing widgets
--
module Graphics.UI.Gtk.Entry.Editable (
-- * Detail
--
-- | The 'Editable' interface is an interface which should be implemented by
-- text editing widgets, such as 'Entry'.
-- It contains functions for generically manipulating an editable
-- widget, a large number of action signals used for key bindings, and several
-- signals that an application can connect to to modify the behavior of a
-- widget.
--
-- * Class Hierarchy
-- |
-- @
-- | GInterface
-- | +----Editable
-- @
-- * Types
Editable,
EditableClass,
castToEditable, gTypeEditable,
toEditable,
-- * Methods
editableSelectRegion,
editableGetSelectionBounds,
editableInsertText,
editableDeleteText,
editableGetChars,
editableCutClipboard,
editableCopyClipboard,
editablePasteClipboard,
editableDeleteSelection,
editableSetEditable,
editableGetEditable,
editableSetPosition,
editableGetPosition,
-- * Attributes
editablePosition,
editableEditable,
-- * Signals
onEditableChanged,
afterEditableChanged,
onDeleteText,
afterDeleteText,
stopDeleteText,
onInsertText,
afterInsertText,
stopInsertText
) where
import Control.Monad (liftM)
import System.Glib.FFI
import System.Glib.UTFString
import System.Glib.Attributes
{#import Graphics.UI.Gtk.Types#}
{#import Graphics.UI.Gtk.Signals#}
{# context lib="gtk" prefix="gtk" #}
--------------------
-- Methods
-- | Selects a region of text. The characters that are selected are those
-- characters at positions from @startPos@ up to, but not including @endPos@.
-- If @endPos@ is negative, then the the characters selected will be those
-- characters from @startPos@ to the end of the text.
--
-- Calling this function with @start@=1 and @end@=4 it will mark \"ask\" in
-- the string \"Haskell\".
--
editableSelectRegion :: EditableClass self => self
-> Int -- ^ @start@ - the starting position.
-> Int -- ^ @end@ - the end position.
-> IO ()
editableSelectRegion self start end =
{# call editable_select_region #}
(toEditable self)
(fromIntegral start)
(fromIntegral end)
-- | Gets the current selection bounds, if there is a selection.
--
editableGetSelectionBounds :: EditableClass self => self
-> IO (Int,Int) -- ^ @(start, end)@ - the starting and end positions. This
-- pair is not ordered. The @end@ index represents the
-- position of the cursor. The @start@ index is the other end
-- of the selection. If both numbers are equal there is in
-- fact no selection.
editableGetSelectionBounds self =
alloca $ \startPtr ->
alloca $ \endPtr -> do
{# call unsafe editable_get_selection_bounds #}
(toEditable self)
startPtr
endPtr
start <- liftM fromIntegral $ peek startPtr
end <- liftM fromIntegral $ peek endPtr
return (start,end)
-- | Inserts text at a given position.
--
editableInsertText :: EditableClass self => self
-> String -- ^ @newText@ - the text to insert.
-> Int -- ^ @position@ - the position at which to insert the text.
-> IO Int -- ^ returns the position after the newly inserted text.
editableInsertText self newText position =
with (fromIntegral position) $ \positionPtr ->
withUTFStringLen newText $ \(newTextPtr, newTextLength) -> do
{# call editable_insert_text #}
(toEditable self)
newTextPtr
(fromIntegral newTextLength)
positionPtr
position <- peek positionPtr
return (fromIntegral position)
-- | Deletes a sequence of characters. The characters that are deleted are
-- those characters at positions from @startPos@ up to, but not including
-- @endPos@. If @endPos@ is negative, then the the characters deleted will be
-- those characters from @startPos@ to the end of the text.
--
editableDeleteText :: EditableClass self => self
-> Int -- ^ @startPos@ - the starting position.
-> Int -- ^ @endPos@ - the end position.
-> IO ()
editableDeleteText self startPos endPos =
{# call editable_delete_text #}
(toEditable self)
(fromIntegral startPos)
(fromIntegral endPos)
-- | Retrieves a sequence of characters. The characters that are retrieved are
-- those characters at positions from @startPos@ up to, but not including
-- @endPos@. If @endPos@ is negative, then the the characters retrieved will be
-- those characters from @startPos@ to the end of the text.
--
editableGetChars :: EditableClass self => self
-> Int -- ^ @startPos@ - the starting position.
-> Int -- ^ @endPos@ - the end position.
-> IO String -- ^ returns the characters in the indicated region.
editableGetChars self startPos endPos =
{# call unsafe editable_get_chars #}
(toEditable self)
(fromIntegral startPos)
(fromIntegral endPos)
>>= readUTFString
-- | Causes the characters in the current selection to be copied to the
-- clipboard and then deleted from the widget.
--
editableCutClipboard :: EditableClass self => self -> IO ()
editableCutClipboard self =
{# call editable_cut_clipboard #}
(toEditable self)
-- | Causes the characters in the current selection to be copied to the
-- clipboard.
--
editableCopyClipboard :: EditableClass self => self -> IO ()
editableCopyClipboard self =
{# call editable_copy_clipboard #}
(toEditable self)
-- | Causes the contents of the clipboard to be pasted into the given widget
-- at the current cursor position.
--
editablePasteClipboard :: EditableClass self => self -> IO ()
editablePasteClipboard self =
{# call editable_paste_clipboard #}
(toEditable self)
-- | Deletes the current contents of the widgets selection and disclaims the
-- selection.
--
editableDeleteSelection :: EditableClass self => self -> IO ()
editableDeleteSelection self =
{# call editable_delete_selection #}
(toEditable self)
-- | Sets the cursor position.
--
editableSetPosition :: EditableClass self => self
-> Int -- ^ @position@ - the position of the cursor. The cursor is
-- displayed before the character with the given (base 0) index in
-- the widget. The value must be less than or equal to the number of
-- characters in the widget. A value of -1 indicates that the
-- position should be set after the last character in the entry.
-> IO ()
editableSetPosition self position =
{# call editable_set_position #}
(toEditable self)
(fromIntegral position)
-- | Retrieves the current cursor position.
--
editableGetPosition :: EditableClass self => self
-> IO Int -- ^ returns the position of the cursor. The cursor is displayed
-- before the character with the given (base 0) index in the widget.
-- The value will be less than or equal to the number of characters
-- in the widget. Note that this position is in characters, not in
-- bytes.
editableGetPosition self =
liftM fromIntegral $
{# call unsafe editable_get_position #}
(toEditable self)
-- | Determines if the user can edit the text in the editable widget or not.
--
editableSetEditable :: EditableClass self => self
-> Bool -- ^ @isEditable@ - @True@ if the user is allowed to edit the text
-- in the widget.
-> IO ()
editableSetEditable self isEditable =
{# call editable_set_editable #}
(toEditable self)
(fromBool isEditable)
-- | Retrieves whether the text is editable. See 'editableSetEditable'.
--
editableGetEditable :: EditableClass self => self -> IO Bool
editableGetEditable self =
liftM toBool $
{# call editable_get_editable #}
(toEditable self)
--------------------
-- Attributes
-- | \'position\' property. See 'editableGetPosition' and
-- 'editableSetPosition'
--
editablePosition :: EditableClass self => Attr self Int
editablePosition = newAttr
editableGetPosition
editableSetPosition
-- | \'editable\' property. See 'editableGetEditable' and
-- 'editableSetEditable'
--
editableEditable :: EditableClass self => Attr self Bool
editableEditable = newAttr
editableGetEditable
editableSetEditable
--------------------
-- Signals
-- | The 'onEditableChanged' signal is emitted at the end of a single
-- user-visible operation on the contents of the 'Editable'.
--
-- * For inctance, a paste operation that replaces the contents of the
-- selection will cause only one signal emission (even though it is
-- implemented by first deleting the selection, then inserting the new
-- content, and may cause multiple 'onEditableInserText' signals to be
-- emitted).
--
onEditableChanged, afterEditableChanged :: EditableClass ec => ec -> IO () ->
IO (ConnectId ec)
onEditableChanged = connect_NONE__NONE "changed" False
afterEditableChanged = connect_NONE__NONE "changed" True
-- | Emitted when a piece of text is deleted from the 'Editable' widget.
--
-- * See 'onInsertText' for information on how to use this signal.
--
onDeleteText, afterDeleteText :: EditableClass self => self
-> (Int -> Int -> IO ()) -- ^ @(\startPos endPos -> ...)@
-> IO (ConnectId self)
onDeleteText = connect_INT_INT__NONE "delete_text" False
afterDeleteText = connect_INT_INT__NONE "delete_text" True
-- | Stop the current signal that deletes text.
stopDeleteText :: EditableClass self => ConnectId self -> IO ()
stopDeleteText (ConnectId _ obj) =
signalStopEmission obj "delete_text"
-- | Emitted when a piece of text is inserted into the 'Editable' widget.
--
-- * The connected signal receives the text that is inserted, together with
-- the position in the entry widget. The return value should be the position
-- in the entry widget that lies past the recently inserted text (i.e.
-- you should return the given position plus the length of the string).
--
-- * To modify the text that the user inserts, you need to connect to this
-- signal, modify the text the way you want and then call
-- 'editableInsertText'. To avoid that this signal handler is called
-- recursively, you need to temporarily block it using
-- 'signalBlock'. After the default signal
-- handler has inserted your modified text, it is important that you
-- prevent the default handler from being executed again when this signal
-- handler returns. To stop the current signal, use 'stopInsertText'.
-- The following code is an example of how to turn all input into uppercase:
--
-- > idRef <- newIORef undefined
-- > id <- onInsertText entry $ \str pos -> do
-- > id <- readIORef idRef
-- > signalBlock id
-- > pos' <- editableInsertText entry (map toUpper str) pos
-- > signalUnblock id
-- > stopInsertText id
-- > return pos'
-- > writeIORef idRef id
--
-- Note that the 'afterInsertText' function is not very useful, except to
-- track editing actions.
--
onInsertText, afterInsertText :: EditableClass self => self
-> (String -> Int -> IO Int)
-> IO (ConnectId self)
onInsertText obj handler =
connect_PTR_INT_PTR__NONE "insert_text" False obj
(\strPtr strLen posPtr -> do
str <- if strLen<0 then peekUTFString strPtr
else peekUTFStringLen (strPtr, strLen)
pos <- peek (posPtr :: Ptr {#type gint#})
pos' <- handler str (fromIntegral pos)
poke (posPtr :: Ptr {#type gint#}) (fromIntegral pos')
)
afterInsertText obj handler =
connect_PTR_INT_PTR__NONE "insert_text" True obj
(\strPtr strLen posPtr -> do
str <- if strLen<0 then peekUTFString strPtr
else peekUTFStringLen (strPtr, strLen)
pos <- peek (posPtr :: Ptr {#type gint#})
pos' <- handler str (fromIntegral pos)
poke (posPtr :: Ptr {#type gint#}) (fromIntegral pos')
)
-- | Stop the current signal that inserts text.
stopInsertText :: EditableClass self => ConnectId self -> IO ()
stopInsertText (ConnectId _ obj) =
signalStopEmission obj "insert_text"
|