Hatena::ブログ(Diary)

趣味的にっき このページをアンテナに追加 RSSフィード

2006-12-15

pathcopy

[] ドラッグ & ドロップしたファイルパスクリップボードコピーする 23:35  ドラッグ & ドロップしたファイルのパスをクリップボードにコピーするを含むブックマーク  ドラッグ & ドロップしたファイルのパスをクリップボードにコピーするのブックマークコメント

Win32SDKを使って、ドラッグ & ドロップしたファイルパスクリップボードコピーするプログラムを書いてみました。うーん、見事なほどにIOの山。ついでにスーパーpre記法のシンタックス・ハイライトを使ってソースコードに色をつけてみました。でもHaskellはあんまりきれいに色がつかないなー。

ファイル: pathcopy.hs

module Main (main) where

import Data.Bits ((.&.), (.|.))
import Data.IORef (IORef, newIORef, readIORef, modifyIORef)
import Data.List (intersperse)
import Foreign.C.String (withCStringLen, peekCAString)
import Foreign.Marshal.Alloc (allocaBytes)
import Foreign.Ptr (castPtr)
import Graphics.Win32
import System.Exit (exitWith, ExitCode (ExitSuccess))
import System.Win32

putTextIntoClipboard :: String -> IO ()
putTextIntoClipboard s = 
  withCStringLen s $ \ (cstr, len) -> do
    let len' = fromInteger $ toInteger $ len + 1
    h <- globalAlloc (gMEM_DDESHARE .&. gMEM_MOVEABLE) len'
    s <- globalLock h
    copyMemory (castPtr s) cstr len'
    globalUnlock h
    openClipboard nullPtr
    emptyClipboard
    setClipboardData cF_TEXT h
    closeClipboard 

foreign import stdcall unsafe "DragQueryFileA" cDragQueryFile :: 
  WPARAM -> UINT -> LPTSTR -> UINT -> IO UINT

dragQueryFiles :: WPARAM -> IO [FilePath]
dragQueryFiles wParam = do
  f 0 =<< cDragQueryFile wParam (-1) nullPtr 0
  where
    bufSize = 4096

    f i n
      | i == n    = return []
      | otherwise = do
          file  <- allocaBytes bufSize $ \ p -> do 
                     cDragQueryFile wParam i p (fromInteger $ toInteger bufSize)
                     peekCAString $ castPtr p 
          files <- f (i + 1) n
          return $ file : files

createMainWindow :: String -> Pos -> Pos -> IORef [String] -> IO HWND
createMainWindow title width height var = do
  hinst  <- getModuleHandle Nothing
  icon   <- loadIcon   Nothing iDI_APPLICATION
  cursor <- loadCursor Nothing iDC_ARROW
  brush  <- getStockBrush lTGRAY_BRUSH
  registerClass 
    ((cS_HREDRAW .|. cS_VREDRAW), hinst, 
     Just icon, Just cursor, Just brush,
     Nothing, mkClassName $ title)
  hwnd <- createWindowEx
    wS_EX_ACCEPTFILES (mkClassName $ title) title
    wS_OVERLAPPEDWINDOW Nothing Nothing
    (Just width) (Just height) Nothing Nothing hinst $ wndProc var
  showWindow hwnd sW_SHOWNORMAL
  updateWindow hwnd
  return hwnd

wndProc :: IORef [String] -> HWND -> WindowMessage -> WPARAM -> LPARAM ->
           IO LRESULT
wndProc var hwnd wmsg wParam lParam
  | wmsg == wM_DESTROY     = onDestroy
  | wmsg == wM_PAINT       = onPaint
  | wmsg == wM_DROPFILES   = onDropFiles
  | wmsg == wM_LBUTTONDOWN = onClicked
  | wmsg == wM_KEYDOWN     = onKeyDown
  | otherwise              = defWindowProc (Just hwnd) wmsg wParam lParam
  where
    onDestroy = do
      putTextIntoClipboard . formatCopyString =<< readIORef var
      sendMessage hwnd wM_QUIT 1 0

    onPaint = do
      files <- readIORef var
      allocaPAINTSTRUCT $ \ lpps -> do
        hdc <- beginPaint hwnd lpps
        setBkMode hdc tRANSPARENT
        setTextColor hdc $ rgb 0 0 0
        textOut hdc 40 60 "Drop on me!!"
        textOut hdc 40 80 $ (show $ length files) ++ " files dropped."
        endPaint hwnd lpps
        updateWindow hwnd
        return 0

    onDropFiles = do
      files <- dragQueryFiles wParam
      modifyIORef var (\ ss -> ss ++ files)
      invalidateRect (Just hwnd) (Just nullPtr) True
      return 0

    onClicked = sendMessage hwnd wM_CLOSE 1 0
    onKeyDown = onClicked

messageLoop :: HWND -> IO ()
messageLoop = allocaMessage . loop
  where
    loop hwnd msg = do
      getMessage msg (Just hwnd)
        `catch` \ _ -> exitWith ExitSuccess
      translateMessage msg
      dispatchMessage msg
      loop hwnd msg

basename :: FilePath -> String
basename = reverse . takeWhile (/= '\\') . reverse

formatCopyString :: [FilePath] -> String
formatCopyString = concat . intersperse "\n" . map f
  where
    f file = "\x81\x9b " ++ basename file ++ "\n<" ++ file ++ ">\n"

main :: IO ()
main = messageLoop =<< createMainWindow "Drop on me!!" 200 200 =<< newIORef []

-- Local Variables:
-- compile-command: "ghc -W -fno-warn-unused-matches -optl-mwindows -ffi -package Win32 -lshell32 -o pathcopy pathcopy.hs"
-- End:

コンパイル方法。

$ ghc -W -fno-warn-unused-matches -optl-mwindows -ffi -package Win32 -lshell32 -o pathcopy pathcopy.hs

使い方。

参照: ログファイルの出力をのぞき見する方法

[] 指定した桁数で改行を入れる 02:30  指定した桁数で改行を入れるを含むブックマーク  指定した桁数で改行を入れるのブックマークコメント

おおー、id:hakobe932くんもHaskellかー。id:hakobe932:20061215:1166201940

僕も大体同じ動きをするプログラムを書いてみました。参考まで。

module Main (main) where

import System (getArgs)
import System.IO (stderr, hPutStrLn)

readFiles :: [String] -> IO String
readFiles []    = getContents
readFiles files = return . concat =<< mapM readFile files

groupn :: Int -> [a] -> [[a]]
groupn _ [] = []
groupn n s  = s1 : groupn n s2
  where
    (s1, s2) = splitAt n s

filln :: Int -> [FilePath] -> IO ()
filln column files =
  putStr . unlines . concatMap (groupn column) . lines =<< readFiles files

main :: IO ()
main = do
  args <- getArgs
  case args of
    (column : files) -> filln (read column) files
    _                -> usage
  where
    usage = hPutStrLn stderr $ "usage: filln column files..."

Hello, world!といえば… 00:01 Hello, world!といえば…を含むブックマーク Hello, world!といえば…のブックマークコメント

バグで行こう - まなびと雑記@はてな

世の中、いかに小さいHello, world!を作るかというすばらしいチャレンジをされているかたがいるようです。こんなの年賀状でもらっても…

そういえばしばらく前にBinary Hacksは買ったんだけど、目次をパラパラめくってそのまま積ん読です。うーむ。冬休みの課題かなー。(その前に僕のPCにまともに動くUNIXを入れなきゃ。)

Binary Hacks ―ハッカー秘伝のテクニック100選

Binary Hacks ―ハッカー秘伝のテクニック100選

あ、そうそうBinary HacksといえばBinary 2.0 カンファレンス本日開催されているようです。こちらの資料も楽しみ。

Binary 2.0カンファレンス2006 - bkブログ

そうだ京都行こう 00:14 そうだ京都行こうを含むブックマーク そうだ京都行こうのブックマークコメント

明日Ruby勉強会で京都に行きます。折角なので京都ガイドブックを買ってみました。明日早起きできたら早めに京都に行ってぶらぶらしてみよう。

ついでに神戸ガイドブックも買ってみました。普通自分が住んでる街のガイドブックってあまり買わないと思うのですが、意外と行ってみたいお店がいっぱいありました。こういう刺激がないといつも行くお店が固定化されて行動範囲が広がりにくいのかも。

[][] My Family and Other Animals(6,900語) 23:51  My Family and Other Animals(6,900語)を含むブックマーク  My Family and Other Animals(6,900語)のブックマークコメント

"My Family and Other Animals" (Penguin Readers (Graded Readers))

話の進展早すぎ。でも動物ものは話の流れを想像しやすいので楽しく読めました。一番上の兄のラリーはおもしろかった。

[] Eの押え方 01:26  Eの押え方を含むブックマーク  Eの押え方のブックマークコメント

id:M-Ohta:20061216#1166198249

僕は、4弦から4(中指)4(中指)4(薬指)2(人差し指)かな。人差し指は2フレットをセーハしてしまうことも多いです。Eを使うキーでは、セーハが必要がコードが多いような気がするので…

[] 23:51 を含むブックマーク のブックマークコメント

18:00ごろ帰りました。

muroyanmuroyan 2006/12/16 01:34 はじめまして。キーワードで飛んできました。プログラム書けるなんてすごいですね! 昔NECのPC-9821のWindows3.1のとき、VB買って、挫折しました。

ha-tanha-tan 2006/12/17 11:06 はじめまして。こちらこそよろしくお願いします。
プログラム。趣味ですから… あ、音楽も趣味で普段はウクレレをへろへろ弾いています。