hawitter 0.2 → 0.3
raw patch · 9 files changed
+448/−217 lines, 9 filesdep +mtldep +parsecsetup-changed
Dependencies added: mtl, parsec
Files
- Main.hs +199/−94
- README.en +3/−2
- README.ja +3/−2
- Setup.hs +1/−0
- hawitter.cabal +8/−7
- hawitter.glade +14/−2
- hawitter.svg +110/−0
- hawitter_128sq.svg +0/−110
- hawitter_busy.svg +110/−0
Main.hs view
@@ -1,10 +1,14 @@+-- | Reference: <http://apiwiki.twitter.com/Twitter-API-Documentation> module Main where import Control.Monad+import Control.Monad.Trans import qualified Codec.Binary.UTF8.String as U-import Data.IORef+import Data.Char import Data.List import Data.Maybe import Data.Ratio+import Data.IORef+import Data.Version import Data.Time.Clock import Data.Time.Format import qualified Data.Map as M@@ -13,50 +17,118 @@ import Network.URI import Network.HTTP import Network.HTTP.Auth-import Text.JSON-import Text.Regex import System.IO import System.Locale import System.Gnome.GConf+import Text.JSON+import Text.Regex+import Text.Parsec.String+import Text.Parsec hiding (Error,Ok) import qualified Paths_hawitter +consumerKey="lCHiDjvMKkqHIGYTON3Ecw"+consumerSecret="R10OHGwq6XKzXCshkiO4aVWjiKVcUg4DVfPTllIco" main=do initGUI - si<-statusIconNewFromFile =<< Paths_hawitter.getDataFileName "hawitter_128sq.svg"- statusIconSetVisible si True- + -- construct GUI from glade file and extract pointers to some widget gladeFile<-Paths_hawitter.getDataFileName "hawitter.glade" Just gxml<-xmlNewWithRootAndDomain gladeFile (Just "mainwindow") Nothing- window<-xmlGetWidget gxml castToWindow "mainwindow" + window<-xmlGetWidget gxml castToWindow "mainwindow"+ men<-xmlGetWidget gxml castToEntry "modifierentry" + swt<-xmlGetWidget gxml castToViewport "vptimeline" (mv,showTweets)<-newMessageView- - swt<-xmlGetWidget gxml castToViewport "vptimeline" set swt [containerChild:=mv] + let toggleVisibility=do vis<-get window widgetVisible+ (if vis then widgetHideAll else widgetShowAll) window+ + -- initialize global state (TODO: make it pure) pTweets<-newIORef [] pLastId<-newIORef Nothing- pImageCache<-newIORef emptyImageCache- let updateTL=update showTweets pTweets pLastId pImageCache+ pIconCache<-mkIconCache - -- handlers+ -- setup status icon+ iconN<-pixbufNewFromFile =<< Paths_hawitter.getDataFileName "hawitter.svg"+ iconB<-pixbufNewFromFile =<< Paths_hawitter.getDataFileName "hawitter_busy.svg"+ + si<-statusIconNewFromPixbuf iconN++ let -- apply filter here? -> no. more general framework needed.+ addTL delta ts=(length d',d'++ts)+ where d'=(reverse $ sort delta) \\ ts+ insertTL raise ts=do+ t0<-readIORef pTweets+ let (n,t1)=addTL ts t0+ writeIORef pTweets t1+ + when (n>0) $ do+ showTweets t1+ visible<-get window widgetVisible+ when (raise && not visible) $ statusIconSetFromPixbuf si iconB+ updateTL raise=fetchTL pLastId pIconCache >>= insertTL raise++ -- register handlers let onAction name f=xmlGetWidget gxml castToMenuItem name >>= flip onActivateLeaf f onAction "file-account" newAccountDialog onAction "file-quit" mainQuit- onAction "timeline-post" newPostWindow- onAction "timeline-refresh" updateTL+ onAction "timeline-post" $ newPostWindow >>= maybe (return ())+ (\x->insertTL False . (:[]) . snd =<< parseTweet pIconCache x)+ onAction "timeline-refresh" $ updateTL True onAction "help-about" showAboutDialog- onDestroy window mainQuit- timeoutAdd (updateTL >> return True) (60*1000) - updateTL- widgetShowAll window+ on window deleteEvent $ liftIO $ widgetHideAll window >> return True+ timeoutAdd (updateTL True >> return True) (60*1000)+ + on si statusIconActivate $ statusIconSetFromPixbuf si iconN >> toggleVisibility+ timeoutAdd (statusIconSetVisible si True >> return True) (1*1000)+ + -- end initialization and enter idle state+ updateTL True mainGUI --- [newest..oldest]++{-+data MExpr+ =MAdd MExpr MExpr -- plus+ |MSub MExpr MExpr -- minus+ |MAnd MExpr MExpr -- and+ |MXor MExpr MExpr -- hat+ |Hash String+ |Gen UExpr++data UExpr+ =Primitive String+ |Follower UExpr+ |Followee UExpr++parseMExpr :: Parser MExpr+parseMExpr=between (char '(') (char ')') parseMExpr <|> parseHash <|> parseMArith++parseMArith :: Parser MExpr+parseMArith=do+ x<-parseMExpr+ op<-oneOf (map fst table)+ y<-parseMExpr+ return $! (fromJust $ lookup op table) x y+ where table=[('+',MAdd),('-',MSub),('&',MAnd),('^',MXor)]+ ++parseHash :: Parser MExpr+parseHash=do+ x<-char '#'+ xs<-many1 $ satisfy (\x->isAlphaNum x||x=='_')+ return $ Hash $ x:xs+-}+++++-- | Create message view 'Widget' and return it with updater.+-- 'Tweet's should be in descending order. newMessageView :: IO (Widget,[Tweet] -> IO ()) newMessageView=do vb<-vBoxNew False 2@@ -87,6 +159,7 @@ entryGetText ps >>= gconfSet gconf "/apps/hawitter/basic/pswd" . GConfValueString putStrLn "gconf configuration complete" +newPostWindow :: IO (Maybe JSValue) newPostWindow=do gladeFile<-Paths_hawitter.getDataFileName "hawitter.glade" Just gxml<-xmlNewWithRootAndDomain gladeFile (Just "postdialog") Nothing@@ -97,20 +170,18 @@ buf<-textViewGetBuffer tv onBufferChanged buf $ updateTweetInfo buf lb - rid<-dialogRun d - - when (rid==ResponseUser 0) $ do- st<-textBufferGetStartIter buf- en<-textBufferGetEndIter buf- txt<-textBufferGetText buf st en False- - let txtU8=U.utf8Encode txt- callJSON POST "/statuses/update" [("status",txtU8)]- return ()+ tw<-if rid==ResponseUser 0+ then do st<-textBufferGetStartIter buf+ en<-textBufferGetEndIter buf+ txt<-textBufferGetText buf st en False+ callJSON POST "/statuses/update" [("status",U.utf8Encode txt)]+ else return Nothing widgetDestroy d+ return tw + updateTweetInfo buf lb=do st<-textBufferGetStartIter buf en<-textBufferGetEndIter buf@@ -121,10 +192,10 @@ showAboutDialog=do d<-aboutDialogNew aboutDialogSetName d "hawitter"- aboutDialogSetVersion d "0.2"+ aboutDialogSetVersion d $ showVersion Paths_hawitter.version aboutDialogSetComments d "Hawitter is a twitter client for GTK, written in Haskell." aboutDialogSetAuthors d ["xanxys <xanxys@gmail.com>"]- aboutDialogSetLogo d . Just =<< pixbufNewFromFile =<< Paths_hawitter.getDataFileName "hawitter_128sq.svg"+ aboutDialogSetLogo d . Just =<< pixbufNewFromFile =<< Paths_hawitter.getDataFileName "hawitter_128.svg" dialogRun d widgetDestroy d @@ -135,12 +206,12 @@ markupMessage :: String -> String markupMessage=modifyWithRegex ruser mkUser . modifyWithRegex rhash mkHash . modifyWithRegex rurl mkLink where- rurl=mkRegex "http://[-a-zA-Z0-9_./#]+"- rhash=mkRegex "#[-a-zA-Z0-9_.]+"- ruser=mkRegex "@[-a-zA-Z0-9_.]+"+ rurl=mkRegex "http://[-a-zA-Z0-9_./#?&=]+"+ rhash=mkRegex "#[a-zA-Z0-9_]+"+ ruser=mkRegex "@[a-zA-Z0-9_]+" mkLink url="<a href=\""++url++"\"><span underline=\"none\">"++url++"</span></a>"- mkHash hash="<span color=\"brown\">"++hash++"</span>"+ mkHash hash="<span color=\"chocolate\">"++hash++"</span>" mkUser user="<span color=\"aquamarine\">"++user++"</span>" modifyWithRegex rx f s=case matchRegexAll rx s of@@ -148,8 +219,9 @@ Just (pre,target,post,_) -> pre++f target++modifyWithRegex rx f post +-- | Convert 'Tweet' to 'Widget' using 'IconCache'. allocTweet :: Tweet -> IO Widget-allocTweet (Tweet icon (User id) (Source src) date msg)=do+allocTweet (Tweet user@(User icon id _) (Source srcN srcU) date msg)=do t0<-getCurrentTime im<-imageNewFromPixbuf icon@@ -157,21 +229,15 @@ -- begin vbCont lbUser<-labelNew Nothing- let- (cname,curl)=extractClientName src- fromLink- |null curl = "<span color=\"lightblue\" underline=\"none\">"++cname++"</span>"- |otherwise = "<a href=\""++curl++"\"><span color=\"lightblue\" underline=\"none\">"++cname++"</span></a>"+ let fromLink=case srcU of+ Nothing -> "<span color=\"lightblue\" underline=\"none\">"++srcN++"</span>"+ Just u -> "<a href=\""++u++"\"><span color=\"lightblue\" underline=\"none\">"++srcN++"</span></a>" labelSetMarkup lbUser $ unwords ["<b>"++id++"</b>","-",showPastTime t0 date,"-","from",fromLink]- labelSetLineWrap lbUser False- labelSetSingleLineMode lbUser True- set lbUser [miscXalign:=0]+ set lbUser [labelLineWrap:=False,labelSingleLineMode:=True,miscXalign:=0] lbMsg<-labelNew Nothing labelSetMarkup lbMsg $ markupMessage msg- labelSetLineWrap lbMsg True- labelSetSingleLineMode lbMsg False- set lbMsg [miscXalign:=0]+ set lbMsg [labelLineWrap:=True,labelSingleLineMode:=False,labelSelectable:=True,miscXalign:=0] vbCont<-vBoxNew False 1 boxPackStart vbCont lbUser PackNatural 0@@ -184,22 +250,8 @@ return $ castToWidget hbTweet --- example:--- <a href="http://example.com" rel="nofollow">TwitterClient</a>--- ->--- ("TwitterClient","http://example.com")-extractClientName :: String -> (String,String)-extractClientName s=case matchRegex rx s of- Just [u,n] -> (escapeBRs n,escapeBRs u)- _ -> (escapeBRs s,"")- where- rx=mkRegex "href=\"([^\"]+)[^>]*>([^<]+)<"- escapeBRs=concatMap f- f '<'="<"- f '>'=">"- f x=[x]- + showPastTime now past |ss<60 = show ss++" seconds before" |ms<60 = show ms++" minutes before"@@ -210,53 +262,106 @@ ms=ss `div` 60 hs=ms `div` 60 -data Tweet=Tweet Pixbuf User Source UTCTime String -newtype Source=Source String deriving(Show,Eq,Ord)-newtype User=User String deriving(Show,Eq,Ord)+-- | single tweet+data Tweet=Tweet User Source UTCTime String +instance Eq Tweet where+ (Tweet u0 _ d0 msg0)==(Tweet u1 _ d1 msg1)=(d0,u0,msg0)==(d1,u1,msg1) -update showTweets ts li ic=do+instance Ord Tweet where+ compare (Tweet u0 _ d0 msg0) (Tweet u1 _ d1 msg1)=compare (d0,u0,msg0) (d1,u1,msg1)++-- | client name and its URL+data Source=Source String (Maybe String) deriving(Show,Eq,Ord)++-- | icon, id and name+data User=User Pixbuf String String++instance Show User where+ show (User _ id name)=id++" | "++name++instance Eq User where+ (User _ i0 _)==(User _ i1 _)=i0==i1++instance Ord User where+ compare (User _ i0 _) (User _ i1 _)=compare i0 i1+++++++escapeBracket :: String -> String+escapeBracket=concatMap f+ where+ f '<'="<"+ f '>'=">"+ f x=[x]+++fetchTL :: IORef (Maybe Int) -> IconCache -> IO [Tweet]+fetchTL li ic=do last_id<-readIORef li resp<-callJSON GET "/statuses/home_timeline" (maybe [] (\x->[("since_id",show x)]) last_id)- case resp of- Nothing -> print "couldn't fetch TL"- Just rb -> updateWithTL showTweets ts li ic rb+ xs<-case resp of+ Nothing -> print "couldn't fetch TL" >> return []+ Just (JSArray ss) -> mapM (parseTweet ic) ss+ let (ids,ts)=unzip xs+ unless (null ids) $ writeIORef li $ Just $ maximum ids+ return ts +-- | parse /source/ parameter+parseSource :: String -> Source+parseSource s=case matchRegex rx s of+ Just [u,n] -> Source (escapeBracket n) $ Just $ escapeBracket u+ _ -> Source (escapeBracket s) Nothing+ where+ rx=mkRegex "href=\"([^\"]+)\"[^>]*>([^<]+)<" -updateWithTL showTweets ts li ic (JSArray ss)=do- unless (null ids) $ writeIORef li $ Just $ maximum ids+-- | Use 'IconCache' to create 'User' from 'JSValue'.+parseTweet :: IconCache -> JSValue -> IO (Int,Tweet)+parseTweet ic x=do+ user<-parseUser ic $ indexJSA "user" x+ return (id,Tweet user source date message)+ where+ id=fromJSI $ indexJSA "id" x+ source=parseSource $ fromJSS $ indexJSA "source" x+ date=parseDate $ fromJSS $ indexJSA "created_at" x+ message=fromJSS $ indexJSA "text" x - imgCache<-updateImageCache (zip users icons) =<< readIORef ic- writeIORef ic imgCache- - let tweets=zipWith4 (\u m d s->Tweet (imgCache M.! u) u s d m) users msgs dates sources- modifyIORef ts (tweets++)- - readIORef ts >>= showTweets+-- | Use 'IconCache' to create 'User' from 'JSValue'.+parseUser :: IconCache -> JSValue -> IO User+parseUser ic x=do+ icon<-fetchIconCache ic iconURL+ return $ User icon userId userName where- ids=map (fromJSI . indexJSA "id") ss- msgs=map (fromJSS . indexJSA "text") ss- dates=map (readDate . fromJSS . indexJSA "created_at") ss- users=map (User . fromJSS . indexJSA "screen_name" . indexJSA "user") ss- icons=map (fromJSS . indexJSA "profile_image_url" . indexJSA "user") ss- sources=map (Source . fromJSS . indexJSA "source") ss- + iconURL =fromJSS $ indexJSA "profile_image_url" x+ userId =fromJSS $ indexJSA "screen_name" x+ userName=fromJSS $ indexJSA "name" x -- example: "Wed Nov 18 18:54:12 +0000 2009"-readDate :: String -> UTCTime-readDate=readTime defaultTimeLocale "%a %b %e %H:%M:%S %Z %Y"+parseDate :: String -> UTCTime+parseDate=readTime defaultTimeLocale "%a %b %e %H:%M:%S %Z %Y" -emptyImageCache=M.empty -updateImageCache :: [(User,String)] -> M.Map User Pixbuf -> IO (M.Map User Pixbuf)-updateImageCache uss m=do- imgs<-mapM pixbufNewFromURL $ map snd nus- return $! M.fromList (zip (map fst nus) imgs) `M.union` m- where nus=filter (flip M.notMember m . fst) $ nubBy (\x y->fst x==fst y) uss+type IconCache=IORef (M.Map String Pixbuf) +mkIconCache :: IO IconCache+mkIconCache=newIORef M.empty +fetchIconCache :: IconCache -> String -> IO Pixbuf+fetchIconCache c url=do+ c1<-insertIconCache url =<< readIORef c+ writeIORef c c1+ return $ c1 M.! url++insertIconCache :: String -> M.Map String Pixbuf -> IO (M.Map String Pixbuf)+insertIconCache url m+ |M.member url m = return m+ |otherwise = liftM (\x->M.insert url x m) $ pixbufNewFromURL url + pixbufNewFromURL :: String -> IO Pixbuf pixbufNewFromURL url=do r<-simpleHTTP (getRequest url)@@ -266,7 +371,7 @@ (path,h)<-openBinaryTempFile "/tmp" "hawitter" hPutStr h $ rspBody x hFlush h- n<-pixbufNewFromFile path+ n<-pixbufNewFromFileAtSize path 48 48 hClose h return n
README.en view
@@ -1,7 +1,7 @@-Hawitter 0.2+Hawitter 0.3 == Installation ==-It should be possible to run 'cabal install hawitter'.+It should be possible to install by running 'cabal install hawitter'. == Technical Notes ==@@ -14,3 +14,4 @@ * email: xanxys at gmail dot com * twitter: @xanxys_+
README.ja view
@@ -1,4 +1,4 @@-Hawitter 0.2+Hawitter 0.3 == インストール == 'cabal install hawitter'で動くはず。動かない場合は連絡を。@@ -13,4 +13,5 @@ * email: xanxys at gmail dot com * twitter: @xanxys_-* ブログの該当記事のコメント欄(初期バージョンのみ)+* ブログの該当記事のコメント欄(バージョン1未満のみ)+
Setup.hs view
@@ -1,3 +1,4 @@ import Distribution.Simple+ main=defaultMain
hawitter.cabal view
@@ -1,24 +1,25 @@ name: hawitter cabal-version: >=1.2-version: 0.2+version: 0.3 author: Daiki Handa <xanxys@gmail.com> maintainer: Daiki Handa <xanxys@gmail.com>-synopsis: A twitter client for GTK+. Alpha version.+synopsis: A twitter client for GTK+. Beta version. license: BSD3 license-file: LICENSE category: Web+homepage: http://d.hatena.ne.jp/xanxys/20100318/1268917714 description:- THIS SOFTWARE IS STILL IN ALPHA STATE.- A slick twitter client for GTK. Features syntax highlighting of tweets, URL linking/shortening and- 'timeline modifier'.+ A slick twitter client for GTK. Features syntax highlighting of tweets, URL linking.+ Automatic URL shortening and 'timeline modifier' will be implemented in future versions. build-type: Simple-data-files: hawitter.glade hawitter_128sq.svg README.en README.ja+data-files: hawitter.glade hawitter.svg hawitter_busy.svg README.en README.ja tested-with: GHC==6.10.4 executable hawitter main-is: Main.hs build-depends:- base>=3, base<5, containers>=0.2, regex-compat>=0.7, time>=1.1.4, old-locale>=1,+ base>=3, base<5, containers>=0.2, mtl>=1, time>=1.1.4, old-locale>=1, gtk>=0.10, glade>=0.10, gconf>=0.10,+ parsec>=3, regex-compat>=0.7, network>=2.2, HTTP>=4000, json>=0.4, utf8-string>=0.3
hawitter.glade view
@@ -4,7 +4,7 @@ <!-- interface-naming-policy project-wide --> <widget class="GtkWindow" id="mainwindow"> <property name="title" translatable="yes">hawitter</property>- <property name="icon">hawitter_128sq.svg</property>+ <property name="icon">hawitter.svg</property> <child> <widget class="GtkVBox" id="vbox1"> <property name="visible">True</property>@@ -106,7 +106,7 @@ </packing> </child> <child>- <widget class="GtkEntry" id="entry1">+ <widget class="GtkEntry" id="modifierentry"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="invisible_char">●</property>@@ -140,6 +140,16 @@ <property name="position">2</property> </packing> </child>+ <child>+ <widget class="GtkStatusbar" id="statusbar">+ <property name="visible">True</property>+ <property name="spacing">2</property>+ </widget>+ <packing>+ <property name="expand">False</property>+ <property name="position">3</property>+ </packing>+ </child> </widget> </child> </widget>@@ -148,6 +158,7 @@ <property name="title" translatable="yes">hawitter - account</property> <property name="resizable">False</property> <property name="modal">True</property>+ <property name="icon">hawitter.svg</property> <property name="type_hint">normal</property> <property name="has_separator">False</property> <child internal-child="vbox">@@ -406,6 +417,7 @@ <property name="border_width">5</property> <property name="title" translatable="yes">hawitter - tweet</property> <property name="resizable">False</property>+ <property name="icon">hawitter.svg</property> <property name="type_hint">normal</property> <property name="has_separator">False</property> <child internal-child="vbox">
+ hawitter.svg view
@@ -0,0 +1,110 @@+<?xml version="1.0" encoding="UTF-8" standalone="no"?>+<svg+ xmlns:dc="http://purl.org/dc/elements/1.1/"+ xmlns:cc="http://creativecommons.org/ns#"+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"+ xmlns:svg="http://www.w3.org/2000/svg"+ xmlns="http://www.w3.org/2000/svg"+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"+ width="128"+ height="128"+ viewBox="0 0 102.4 102.39999"+ version="1.1"+ id="svg2900"+ inkscape:version="0.47 r22583"+ sodipodi:docname="hawitter_centered.svg"+ inkscape:export-filename="/home/xyx/devenv/hawitter/hawitter_128sq.png"+ inkscape:export-xdpi="16.002222"+ inkscape:export-ydpi="16.002222">+ <metadata+ id="metadata2920">+ <rdf:RDF>+ <cc:Work+ rdf:about="">+ <dc:format>image/svg+xml</dc:format>+ <dc:type+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />+ <dc:title></dc:title>+ </cc:Work>+ </rdf:RDF>+ </metadata>+ <sodipodi:namedview+ pagecolor="#ffffff"+ bordercolor="#666666"+ borderopacity="1"+ objecttolerance="10"+ gridtolerance="10"+ guidetolerance="10"+ inkscape:pageopacity="0"+ inkscape:pageshadow="2"+ inkscape:window-width="838"+ inkscape:window-height="1023"+ id="namedview2918"+ showgrid="false"+ inkscape:zoom="1.716227"+ inkscape:cx="-52.998198"+ inkscape:cy="78.331889"+ inkscape:window-x="0"+ inkscape:window-y="25"+ inkscape:window-maximized="0"+ inkscape:current-layer="svg2900" />+ <defs+ id="defs2902">+ <inkscape:perspective+ sodipodi:type="inkscape:persp3d"+ inkscape:vp_x="0 : 212.59839 : 1"+ inkscape:vp_y="0 : 1000 : 0"+ inkscape:vp_z="602.36212 : 212.59839 : 1"+ inkscape:persp3d-origin="301.18106 : 141.73226 : 1"+ id="perspective2922" />+ <clipPath+ id="clip1">+ <path+ d="m 0,340.15625 481.89062,0 L 481.89062,0 0,0 0,340.15625 z m 0,0"+ id="path2905" />+ </clipPath>+ <inkscape:perspective+ id="perspective3049"+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"+ inkscape:vp_z="1 : 0.5 : 1"+ inkscape:vp_y="0 : 1000 : 0"+ inkscape:vp_x="0 : 0.5 : 1"+ sodipodi:type="inkscape:persp3d" />+ <inkscape:perspective+ id="perspective3072"+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"+ inkscape:vp_z="1 : 0.5 : 1"+ inkscape:vp_y="0 : 1000 : 0"+ inkscape:vp_x="0 : 0.5 : 1"+ sodipodi:type="inkscape:persp3d" />+ </defs>+ <g+ id="g2818"+ transform="translate(6.4546249,0.48166647)">+ <path+ transform="matrix(0.17313019,0,0,0.183031,-3.6257093,19.552725)"+ d="M 0,-8.5625 A 8.5580266,8.5580266 0 0 0 -7.125,4.75 L 103.09375,170.0625 -7.125,335.40625 A 8.5580266,8.5580266 0 0 0 0,348.71875 l 85.03125,0 a 8.5580266,8.5580266 0 0 0 7.125,-3.8125 L 205.5625,174.8125 a 8.5580266,8.5580266 0 0 0 0,-9.5 L 92.15625,-4.75 a 8.5580266,8.5580266 0 0 0 -7.125,-3.8125 L 0,-8.5625 z"+ id="path3098"+ style="fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none"+ inkscape:original="M 0 0 L 113.375 170.0625 L 0 340.15625 L 85.03125 340.15625 L 198.4375 170.0625 L 85.03125 0 L 0 0 z "+ inkscape:radius="8.5571709"+ sodipodi:type="inkscape:offset" />+ <path+ transform="matrix(0.17313019,0,0,0.183031,-2.2406683,19.552725)"+ d="m 113.375,-8.3125 a 8.300195,8.300195 0 0 0 -6.90625,12.90625 l 110.34375,165.46875 -110.34375,165.5 a 8.300195,8.300195 0 0 0 6.90625,12.90625 l 85.0625,0 A 8.300195,8.300195 0 0 0 205.34375,344.75 L 269.28125,248.8125 333.25,344.75 a 8.300195,8.300195 0 0 0 6.90625,3.71875 l 85.03125,0 a 8.300195,8.300195 0 0 0 6.90625,-12.90625 L 205.34375,-4.59375 A 8.300195,8.300195 0 0 0 198.4375,-8.3125 l -85.0625,0 z"+ id="path3096"+ style="fill:#999999;fill-opacity:1;fill-rule:nonzero;stroke:none"+ inkscape:original="M 113.375 0 L 226.78125 170.0625 L 113.375 340.15625 L 198.4375 340.15625 L 269.28125 233.84375 L 340.15625 340.15625 L 425.1875 340.15625 L 198.4375 0 L 113.375 0 z "+ inkscape:radius="8.299365"+ sodipodi:type="inkscape:offset" />+ <path+ transform="matrix(0.13850415,0,0,0.1464248,-0.57861732,19.552725)"+ d="M 282.875,-11.3125 A 11.566895,11.566895 0 0 0 273.25,6.65625 l 283.46875,425.1875 a 11.566895,11.566895 0 0 0 9.625,5.15625 l 109.28125,0 a 11.566895,11.566895 0 0 0 11.5625,-11.5625 l 0,-70.875 A 11.566895,11.566895 0 0 0 675.625,343 l -44.0625,0 -123.65625,-185.4375 67.46875,0 A 11.566895,11.566895 0 0 0 586.9375,146 l 0,-70.875 A 11.566895,11.566895 0 0 0 575.375,63.5625 l -130.125,0 -46.46875,-69.71875 a 11.566895,11.566895 0 0 0 -9.625,-5.15625 l -106.28125,0 z"+ id="path3094"+ style="fill:#33ccff;fill-opacity:1;fill-rule:nonzero;stroke:none"+ inkscape:original="M 282.875 0.25 L 566.34375 425.4375 L 675.625 425.4375 L 675.625 354.5625 L 625.375 354.5625 L 486.3125 146 L 575.375 146 L 575.375 75.125 L 439.0625 75.125 L 389.15625 0.25 L 282.875 0.25 z "+ inkscape:radius="11.565739"+ sodipodi:type="inkscape:offset" />+ </g>+</svg>
− hawitter_128sq.svg
@@ -1,110 +0,0 @@-<?xml version="1.0" encoding="UTF-8" standalone="no"?>-<svg- xmlns:dc="http://purl.org/dc/elements/1.1/"- xmlns:cc="http://creativecommons.org/ns#"- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"- xmlns:svg="http://www.w3.org/2000/svg"- xmlns="http://www.w3.org/2000/svg"- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"- width="128"- height="128"- viewBox="0 0 102.4 102.39999"- version="1.1"- id="svg2900"- inkscape:version="0.47 r22583"- sodipodi:docname="hawitter_centered.svg"- inkscape:export-filename="/home/xyx/devenv/hawitter/hawitter_128sq.png"- inkscape:export-xdpi="16.002222"- inkscape:export-ydpi="16.002222">- <metadata- id="metadata2920">- <rdf:RDF>- <cc:Work- rdf:about="">- <dc:format>image/svg+xml</dc:format>- <dc:type- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />- <dc:title></dc:title>- </cc:Work>- </rdf:RDF>- </metadata>- <sodipodi:namedview- pagecolor="#ffffff"- bordercolor="#666666"- borderopacity="1"- objecttolerance="10"- gridtolerance="10"- guidetolerance="10"- inkscape:pageopacity="0"- inkscape:pageshadow="2"- inkscape:window-width="838"- inkscape:window-height="1023"- id="namedview2918"- showgrid="false"- inkscape:zoom="1.716227"- inkscape:cx="-52.998198"- inkscape:cy="78.331889"- inkscape:window-x="0"- inkscape:window-y="25"- inkscape:window-maximized="0"- inkscape:current-layer="svg2900" />- <defs- id="defs2902">- <inkscape:perspective- sodipodi:type="inkscape:persp3d"- inkscape:vp_x="0 : 212.59839 : 1"- inkscape:vp_y="0 : 1000 : 0"- inkscape:vp_z="602.36212 : 212.59839 : 1"- inkscape:persp3d-origin="301.18106 : 141.73226 : 1"- id="perspective2922" />- <clipPath- id="clip1">- <path- d="m 0,340.15625 481.89062,0 L 481.89062,0 0,0 0,340.15625 z m 0,0"- id="path2905" />- </clipPath>- <inkscape:perspective- id="perspective3049"- inkscape:persp3d-origin="0.5 : 0.33333333 : 1"- inkscape:vp_z="1 : 0.5 : 1"- inkscape:vp_y="0 : 1000 : 0"- inkscape:vp_x="0 : 0.5 : 1"- sodipodi:type="inkscape:persp3d" />- <inkscape:perspective- id="perspective3072"- inkscape:persp3d-origin="0.5 : 0.33333333 : 1"- inkscape:vp_z="1 : 0.5 : 1"- inkscape:vp_y="0 : 1000 : 0"- inkscape:vp_x="0 : 0.5 : 1"- sodipodi:type="inkscape:persp3d" />- </defs>- <g- id="g2818"- transform="translate(6.4546249,0.48166647)">- <path- transform="matrix(0.17313019,0,0,0.183031,-3.6257093,19.552725)"- d="M 0,-8.5625 A 8.5580266,8.5580266 0 0 0 -7.125,4.75 L 103.09375,170.0625 -7.125,335.40625 A 8.5580266,8.5580266 0 0 0 0,348.71875 l 85.03125,0 a 8.5580266,8.5580266 0 0 0 7.125,-3.8125 L 205.5625,174.8125 a 8.5580266,8.5580266 0 0 0 0,-9.5 L 92.15625,-4.75 a 8.5580266,8.5580266 0 0 0 -7.125,-3.8125 L 0,-8.5625 z"- id="path3098"- style="fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none"- inkscape:original="M 0 0 L 113.375 170.0625 L 0 340.15625 L 85.03125 340.15625 L 198.4375 170.0625 L 85.03125 0 L 0 0 z "- inkscape:radius="8.5571709"- sodipodi:type="inkscape:offset" />- <path- transform="matrix(0.17313019,0,0,0.183031,-2.2406683,19.552725)"- d="m 113.375,-8.3125 a 8.300195,8.300195 0 0 0 -6.90625,12.90625 l 110.34375,165.46875 -110.34375,165.5 a 8.300195,8.300195 0 0 0 6.90625,12.90625 l 85.0625,0 A 8.300195,8.300195 0 0 0 205.34375,344.75 L 269.28125,248.8125 333.25,344.75 a 8.300195,8.300195 0 0 0 6.90625,3.71875 l 85.03125,0 a 8.300195,8.300195 0 0 0 6.90625,-12.90625 L 205.34375,-4.59375 A 8.300195,8.300195 0 0 0 198.4375,-8.3125 l -85.0625,0 z"- id="path3096"- style="fill:#999999;fill-opacity:1;fill-rule:nonzero;stroke:none"- inkscape:original="M 113.375 0 L 226.78125 170.0625 L 113.375 340.15625 L 198.4375 340.15625 L 269.28125 233.84375 L 340.15625 340.15625 L 425.1875 340.15625 L 198.4375 0 L 113.375 0 z "- inkscape:radius="8.299365"- sodipodi:type="inkscape:offset" />- <path- transform="matrix(0.13850415,0,0,0.1464248,-0.57861732,19.552725)"- d="M 282.875,-11.3125 A 11.566895,11.566895 0 0 0 273.25,6.65625 l 283.46875,425.1875 a 11.566895,11.566895 0 0 0 9.625,5.15625 l 109.28125,0 a 11.566895,11.566895 0 0 0 11.5625,-11.5625 l 0,-70.875 A 11.566895,11.566895 0 0 0 675.625,343 l -44.0625,0 -123.65625,-185.4375 67.46875,0 A 11.566895,11.566895 0 0 0 586.9375,146 l 0,-70.875 A 11.566895,11.566895 0 0 0 575.375,63.5625 l -130.125,0 -46.46875,-69.71875 a 11.566895,11.566895 0 0 0 -9.625,-5.15625 l -106.28125,0 z"- id="path3094"- style="fill:#33ccff;fill-opacity:1;fill-rule:nonzero;stroke:none"- inkscape:original="M 282.875 0.25 L 566.34375 425.4375 L 675.625 425.4375 L 675.625 354.5625 L 625.375 354.5625 L 486.3125 146 L 575.375 146 L 575.375 75.125 L 439.0625 75.125 L 389.15625 0.25 L 282.875 0.25 z "- inkscape:radius="11.565739"- sodipodi:type="inkscape:offset" />- </g>-</svg>
+ hawitter_busy.svg view
@@ -0,0 +1,110 @@+<?xml version="1.0" encoding="UTF-8" standalone="no"?>+<svg+ xmlns:dc="http://purl.org/dc/elements/1.1/"+ xmlns:cc="http://creativecommons.org/ns#"+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"+ xmlns:svg="http://www.w3.org/2000/svg"+ xmlns="http://www.w3.org/2000/svg"+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"+ width="128"+ height="128"+ viewBox="0 0 102.4 102.39999"+ version="1.1"+ id="svg2900"+ inkscape:version="0.47 r22583"+ sodipodi:docname="hawitter_busy.svg"+ inkscape:export-filename="/home/xyx/devenv/hawitter/hawitter_128sq.png"+ inkscape:export-xdpi="16.002222"+ inkscape:export-ydpi="16.002222">+ <metadata+ id="metadata2920">+ <rdf:RDF>+ <cc:Work+ rdf:about="">+ <dc:format>image/svg+xml</dc:format>+ <dc:type+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />+ <dc:title />+ </cc:Work>+ </rdf:RDF>+ </metadata>+ <sodipodi:namedview+ pagecolor="#ffffff"+ bordercolor="#666666"+ borderopacity="1"+ objecttolerance="10"+ gridtolerance="10"+ guidetolerance="10"+ inkscape:pageopacity="0"+ inkscape:pageshadow="2"+ inkscape:window-width="1413"+ inkscape:window-height="952"+ id="namedview2918"+ showgrid="false"+ inkscape:zoom="1.716227"+ inkscape:cx="-49.361244"+ inkscape:cy="30.421033"+ inkscape:window-x="0"+ inkscape:window-y="25"+ inkscape:window-maximized="0"+ inkscape:current-layer="svg2900" />+ <defs+ id="defs2902">+ <inkscape:perspective+ sodipodi:type="inkscape:persp3d"+ inkscape:vp_x="0 : 212.59839 : 1"+ inkscape:vp_y="0 : 1000 : 0"+ inkscape:vp_z="602.36212 : 212.59839 : 1"+ inkscape:persp3d-origin="301.18106 : 141.73226 : 1"+ id="perspective2922" />+ <clipPath+ id="clip1">+ <path+ d="m 0,340.15625 481.89062,0 L 481.89062,0 0,0 0,340.15625 z m 0,0"+ id="path2905" />+ </clipPath>+ <inkscape:perspective+ id="perspective3049"+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"+ inkscape:vp_z="1 : 0.5 : 1"+ inkscape:vp_y="0 : 1000 : 0"+ inkscape:vp_x="0 : 0.5 : 1"+ sodipodi:type="inkscape:persp3d" />+ <inkscape:perspective+ id="perspective3072"+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"+ inkscape:vp_z="1 : 0.5 : 1"+ inkscape:vp_y="0 : 1000 : 0"+ inkscape:vp_x="0 : 0.5 : 1"+ sodipodi:type="inkscape:persp3d" />+ </defs>+ <g+ id="g3597"+ transform="translate(6.4546249,0.48166647)">+ <path+ transform="matrix(0.17313019,0,0,0.183031,-3.6257093,19.552725)"+ d="M 0,-8.5625 A 8.5580266,8.5580266 0 0 0 -7.125,4.75 L 103.09375,170.0625 -7.125,335.40625 A 8.5580266,8.5580266 0 0 0 0,348.71875 l 85.03125,0 a 8.5580266,8.5580266 0 0 0 7.125,-3.8125 L 205.5625,174.8125 a 8.5580266,8.5580266 0 0 0 0,-9.5 L 92.15625,-4.75 a 8.5580266,8.5580266 0 0 0 -7.125,-3.8125 L 0,-8.5625 z"+ id="path3098"+ style="fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none"+ inkscape:original="M 0 0 L 113.375 170.0625 L 0 340.15625 L 85.03125 340.15625 L 198.4375 170.0625 L 85.03125 0 L 0 0 z "+ inkscape:radius="8.5571709"+ sodipodi:type="inkscape:offset" />+ <path+ transform="matrix(0.17313019,0,0,0.183031,-2.2406683,19.552725)"+ d="m 113.375,-8.3125 a 8.300195,8.300195 0 0 0 -6.90625,12.90625 l 110.34375,165.46875 -110.34375,165.5 a 8.300195,8.300195 0 0 0 6.90625,12.90625 l 85.0625,0 A 8.300195,8.300195 0 0 0 205.34375,344.75 L 269.28125,248.8125 333.25,344.75 a 8.300195,8.300195 0 0 0 6.90625,3.71875 l 85.03125,0 a 8.300195,8.300195 0 0 0 6.90625,-12.90625 L 205.34375,-4.59375 A 8.300195,8.300195 0 0 0 198.4375,-8.3125 l -85.0625,0 z"+ id="path3096"+ style="fill:#999999;fill-opacity:1;fill-rule:nonzero;stroke:none"+ inkscape:original="M 113.375 0 L 226.78125 170.0625 L 113.375 340.15625 L 198.4375 340.15625 L 269.28125 233.84375 L 340.15625 340.15625 L 425.1875 340.15625 L 198.4375 0 L 113.375 0 z "+ inkscape:radius="8.299365"+ sodipodi:type="inkscape:offset" />+ <path+ transform="matrix(0.13850415,0,0,0.1464248,-0.57861732,19.552725)"+ d="M 282.875,-11.3125 A 11.566895,11.566895 0 0 0 273.25,6.65625 l 283.46875,425.1875 a 11.566895,11.566895 0 0 0 9.625,5.15625 l 109.28125,0 a 11.566895,11.566895 0 0 0 11.5625,-11.5625 l 0,-70.875 A 11.566895,11.566895 0 0 0 675.625,343 l -44.0625,0 -123.65625,-185.4375 67.46875,0 A 11.566895,11.566895 0 0 0 586.9375,146 l 0,-70.875 A 11.566895,11.566895 0 0 0 575.375,63.5625 l -130.125,0 -46.46875,-69.71875 a 11.566895,11.566895 0 0 0 -9.625,-5.15625 l -106.28125,0 z"+ id="path3094"+ style="fill:#ff5333;fill-opacity:1;fill-rule:nonzero;stroke:none"+ inkscape:original="M 282.875 0.25 L 566.34375 425.4375 L 675.625 425.4375 L 675.625 354.5625 L 625.375 354.5625 L 486.3125 146 L 575.375 146 L 575.375 75.125 L 439.0625 75.125 L 389.15625 0.25 L 282.875 0.25 z "+ inkscape:radius="11.565739"+ sodipodi:type="inkscape:offset" />+ </g>+</svg>