packages feed

hawitter (empty) → 0.2

raw patch · 8 files changed

+999/−0 lines, 8 filesdep +HTTPdep +basedep +containerssetup-changed

Dependencies added: HTTP, base, containers, gconf, glade, gtk, json, network, old-locale, regex-compat, time, utf8-string

Files

+ LICENSE view
@@ -0,0 +1,25 @@+Copyright (c) 2010 Daiki Handa+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:+1. Redistributions of source code must retain the above copyright+   notice, this list of conditions and the following disclaimer.+2. Redistributions in binary form must reproduce the above copyright+   notice, this list of conditions and the following disclaimer in the+   documentation and/or other materials provided with the distribution.+3. The name of the author may not be used to endorse or promote products+   derived from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+
+ Main.hs view
@@ -0,0 +1,333 @@+module Main where+import Control.Monad+import qualified Codec.Binary.UTF8.String as U+import Data.IORef+import Data.List+import Data.Maybe+import Data.Ratio+import Data.Time.Clock+import Data.Time.Format+import qualified Data.Map as M+import Graphics.UI.Gtk+import Graphics.UI.Gtk.Glade+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 qualified Paths_hawitter+++main=do+    initGUI+    +    si<-statusIconNewFromFile =<< Paths_hawitter.getDataFileName "hawitter_128sq.svg"+    statusIconSetVisible si True+    +    gladeFile<-Paths_hawitter.getDataFileName "hawitter.glade"+    Just gxml<-xmlNewWithRootAndDomain gladeFile (Just "mainwindow") Nothing+    window<-xmlGetWidget gxml castToWindow "mainwindow" +    +    (mv,showTweets)<-newMessageView+        +    swt<-xmlGetWidget gxml castToViewport "vptimeline"+    set swt [containerChild:=mv]+    +    pTweets<-newIORef []+    pLastId<-newIORef Nothing+    pImageCache<-newIORef emptyImageCache+    let updateTL=update showTweets pTweets pLastId pImageCache+    +    -- 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 "help-about" showAboutDialog+    onDestroy window mainQuit+    timeoutAdd (updateTL >> return True) (60*1000)+    +    updateTL+    widgetShowAll window+    mainGUI++-- [newest..oldest]+newMessageView :: IO (Widget,[Tweet] -> IO ())+newMessageView=do+    vb<-vBoxNew False 2+    let+        append x=boxPackStart vb x PackNatural 3+        f ts=do+            containerGetChildren vb >>= mapM_ (containerRemove vb)+            mapM_ (\t->allocTweet t >>= append) ts+            widgetShowAll vb+    return (castToWidget vb,f)++newAccountDialog=do+    gladeFile<-Paths_hawitter.getDataFileName "hawitter.glade"+    gconf<-gconfGetDefault+    Just gxml<-xmlNewWithRootAndDomain gladeFile (Just "accountdialog") Nothing+    +    d<-xmlGetWidget gxml castToDialog "accountdialog"+    id<-xmlGetWidget gxml castToEntry "identry"+    ps<-xmlGetWidget gxml castToEntry "pswdentry"++    entrySetText id =<< gconfGetString gconf "/apps/hawitter/basic/user"+    entrySetText ps =<< gconfGetString gconf "/apps/hawitter/basic/pswd"+    +    dialogRun d+    widgetDestroy d+    +    entryGetText id >>= gconfSet gconf "/apps/hawitter/basic/user" . GConfValueString+    entryGetText ps >>= gconfSet gconf "/apps/hawitter/basic/pswd" . GConfValueString+    putStrLn "gconf configuration complete"++newPostWindow=do+    gladeFile<-Paths_hawitter.getDataFileName "hawitter.glade"+    Just gxml<-xmlNewWithRootAndDomain gladeFile (Just "postdialog") Nothing+    d<-xmlGetWidget gxml castToDialog "postdialog"+    lb<-xmlGetWidget gxml castToLabel "remaining"+    tv<-xmlGetWidget gxml castToTextView "tweetbody"+    +    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 ()+    widgetDestroy d++updateTweetInfo buf lb=do+    st<-textBufferGetStartIter buf+    en<-textBufferGetEndIter buf+    txt<-textBufferGetText buf st en False+    +    labelSetText lb $ show (140-length txt)++" characters left"++showAboutDialog=do+    d<-aboutDialogNew+    aboutDialogSetName d "hawitter"+    aboutDialogSetVersion d "0.2"+    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"+    dialogRun d+    widgetDestroy d++++-- URL -> hashtag -> users+-- URL may contain #+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_.]+"+        +        mkLink  url="<a href=\""++url++"\"><span underline=\"none\">"++url++"</span></a>"+        mkHash hash="<span color=\"brown\">"++hash++"</span>"+        mkUser user="<span color=\"aquamarine\">"++user++"</span>"++modifyWithRegex rx f s=case matchRegexAll rx s of+    Nothing -> s+    Just (pre,target,post,_) -> pre++f target++modifyWithRegex rx f post+++allocTweet :: Tweet -> IO Widget+allocTweet (Tweet icon (User id) (Source src) date msg)=do+    t0<-getCurrentTime+    +    im<-imageNewFromPixbuf icon+    set im [miscYalign:=0]+    +    -- 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>"+    labelSetMarkup lbUser $ unwords ["<b>"++id++"</b>","-",showPastTime t0 date,"-","from",fromLink]+    labelSetLineWrap lbUser False+    labelSetSingleLineMode lbUser True+    set lbUser [miscXalign:=0]+    +    lbMsg<-labelNew Nothing+    labelSetMarkup lbMsg $ markupMessage msg+    labelSetLineWrap lbMsg True+    labelSetSingleLineMode lbMsg False+    set lbMsg [miscXalign:=0]+    +    vbCont<-vBoxNew False 1+    boxPackStart vbCont lbUser PackNatural 0+    boxPackStart vbCont lbMsg PackGrow 0+    +    hbTweet<-hBoxNew False 3+    boxPackStart hbTweet im PackNatural 0+    boxPackStart hbTweet vbCont PackGrow 0+    +    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 '<'="&lt;"+        f '>'="&gt;"+        f x=[x]+    ++showPastTime now past+    |ss<60     = show ss++" seconds before"+    |ms<60     = show ms++" minutes before"+    |hs<24     = show hs++" hours before"+    |otherwise = formatTime defaultTimeLocale "%Y/%m/%d %H:%M:%S %Z" past+    where+        ss=ceiling $ now `diffUTCTime` past+        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)+++update showTweets ts 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+++updateWithTL showTweets ts li ic (JSArray ss)=do+    unless (null ids) $ writeIORef li $ Just $ maximum ids++    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+    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+        ++-- example: "Wed Nov 18 18:54:12 +0000 2009"+readDate :: String -> UTCTime+readDate=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+++pixbufNewFromURL :: String -> IO Pixbuf+pixbufNewFromURL url=do+    r<-simpleHTTP (getRequest url)+    case r of+        Left x  -> pixbufNew ColorspaceRgb False 8 73 73+        Right x -> do+            (path,h)<-openBinaryTempFile "/tmp" "hawitter"+            hPutStr h $ rspBody x+            hFlush h+            n<-pixbufNewFromFile path+            hClose h+            return n+    ++++indexJSA :: String -> JSValue -> JSValue+indexJSA key (JSObject o)=fromJust $ lookup key $ fromJSObject o++fromJSS :: JSValue -> String+fromJSS (JSString x)=fromJSString x++fromJSI :: JSValue -> Int+fromJSI (JSRational False x)=fromIntegral $ numerator x+    ++{-+oauthRequestToken=do+    Request (fromJust $ parseURI "http://twitter.com/oauth/request_token") GET [] ""+    request c (hmacsha1_signature c) +    where+        c=Unauthenticated consumerKey consumerSecret+-}    +++-- example:+-- callJSON GET "/account/rate_limit_status"+callJSON :: RequestMethod -> String -> [(String,String)] -> IO (Maybe JSValue)+callJSON met cmd args=do+    print $ "http://api.twitter.com/1"++cmd++".json"++"?"++urlEncodeVars args+    x<-callAPI met $ "http://api.twitter.com/1"++cmd++".json"++"?"++urlEncodeVars args+    case x of+        Nothing -> return Nothing+        Just y  -> case decode y of+                       Error e -> error e+                       Ok x    -> return $ Just x++-- example:+-- callAPI "http://api.twitter.com/1/account/rate_limit_status.json"+callAPI :: RequestMethod -> String -> IO (Maybe String)+callAPI met url=do+    gconf<-gconfGetDefault+    user<-gconfGetString gconf "/apps/hawitter/basic/user"+    pswd<-gconfGetString gconf "/apps/hawitter/basic/pswd"+    let clearUserData=mapM_ (gconfUnset gconf) ["/apps/hawitter/basic/user","/apps/hawitter/basic/pswd"]+    +    let au=AuthBasic undefined user pswd undefined+    let rq=Request (fromJust $ parseURI url) met [] ""+    let rq'=insertHeader HdrAuthorization (withAuthority au rq) rq+    +    if null user+        then return Nothing+        else do rs<-simpleHTTP rq'+                case rs of+                    Left er -> error $ show er+                    Right q -> case rspCode q of+                                   (2,0,0) -> return $ Just $ rspBody q+                                   (4,0,1) -> print "401" >> clearUserData >> return Nothing+                                   _       -> print rs >> return Nothing++gconfGetString gconf key=catch (liftM unpack $ gconfGet gconf key) (const $ return "")+    where unpack (GConfValueString s)=s+    +
+ README.en view
@@ -0,0 +1,16 @@+Hawitter 0.2++== Installation ==+It should be possible to run 'cabal install hawitter'.+++== Technical Notes ==+This application uses following gconf keys.+* /apps/hawitter/basic/user :: str+* /apps/hawitter/basic/pswd :: str++== Contact ==+If you encounter errors or have suggentions, contact me with:++* email: xanxys at gmail dot com+* twitter: @xanxys_
+ README.ja view
@@ -0,0 +1,16 @@+Hawitter 0.2++== インストール ==+'cabal install hawitter'で動くはず。動かない場合は連絡を。++== 技術的メモ ==+gconfの次のキーを使っています。+* /apps/hawitter/basic/user :: str+* /apps/hawitter/basic/pswd :: str++== 連絡先 ==+問題/提案がある場合は、++* email: xanxys at gmail dot com+* twitter: @xanxys_+* ブログの該当記事のコメント欄(初期バージョンのみ)
+ Setup.hs view
@@ -0,0 +1,3 @@+import Distribution.Simple+main=defaultMain+
+ hawitter.cabal view
@@ -0,0 +1,24 @@+name:            hawitter+cabal-version:   >=1.2+version:         0.2+author:          Daiki Handa <xanxys@gmail.com>+maintainer:      Daiki Handa <xanxys@gmail.com>+synopsis:        A twitter client for GTK+. Alpha version.+license:         BSD3+license-file:    LICENSE+category:        Web+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'.+  +build-type:      Simple+data-files:      hawitter.glade hawitter_128sq.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,+        gtk>=0.10, glade>=0.10, gconf>=0.10,+        network>=2.2, HTTP>=4000, json>=0.4, utf8-string>=0.3+
+ hawitter.glade view
@@ -0,0 +1,472 @@+<?xml version="1.0"?>+<glade-interface>+  <!-- interface-requires gtk+ 2.16 -->+  <!-- interface-naming-policy project-wide -->+  <widget class="GtkWindow" id="mainwindow">+    <property name="title" translatable="yes">hawitter</property>+    <property name="icon">hawitter_128sq.svg</property>+    <child>+      <widget class="GtkVBox" id="vbox1">+        <property name="visible">True</property>+        <property name="orientation">vertical</property>+        <child>+          <widget class="GtkMenuBar" id="menubar1">+            <property name="visible">True</property>+            <child>+              <widget class="GtkMenuItem" id="menuitem1">+                <property name="visible">True</property>+                <property name="label" translatable="yes">_File</property>+                <property name="use_underline">True</property>+                <child>+                  <widget class="GtkMenu" id="menu1">+                    <property name="visible">True</property>+                    <child>+                      <widget class="GtkMenuItem" id="file-account">+                        <property name="visible">True</property>+                        <property name="label" translatable="yes">_Account</property>+                        <property name="use_underline">True</property>+                      </widget>+                    </child>+                    <child>+                      <widget class="GtkMenuItem" id="file-quit">+                        <property name="visible">True</property>+                        <property name="label" translatable="yes">_Quit</property>+                        <property name="use_underline">True</property>+                        <accelerator key="q" signal="activate" modifiers="GDK_CONTROL_MASK"/>+                      </widget>+                    </child>+                  </widget>+                </child>+              </widget>+            </child>+            <child>+              <widget class="GtkMenuItem" id="menuitem3">+                <property name="visible">True</property>+                <property name="label" translatable="yes">_Timeline</property>+                <property name="use_underline">True</property>+                <child>+                  <widget class="GtkMenu" id="menu2">+                    <property name="visible">True</property>+                    <child>+                      <widget class="GtkMenuItem" id="timeline-post">+                        <property name="visible">True</property>+                        <property name="label" translatable="yes">_Post</property>+                        <property name="use_underline">True</property>+                        <accelerator key="n" signal="activate" modifiers="GDK_CONTROL_MASK"/>+                      </widget>+                    </child>+                    <child>+                      <widget class="GtkMenuItem" id="timeline-refresh">+                        <property name="visible">True</property>+                        <property name="label" translatable="yes">_Refresh</property>+                        <property name="use_underline">True</property>+                        <accelerator key="F5" signal="activate"/>+                      </widget>+                    </child>+                  </widget>+                </child>+              </widget>+            </child>+            <child>+              <widget class="GtkMenuItem" id="menuitem4">+                <property name="visible">True</property>+                <property name="label" translatable="yes">_Help</property>+                <property name="use_underline">True</property>+                <child>+                  <widget class="GtkMenu" id="menu3">+                    <property name="visible">True</property>+                    <child>+                      <widget class="GtkMenuItem" id="help-about">+                        <property name="visible">True</property>+                        <property name="label" translatable="yes">_About</property>+                        <property name="use_underline">True</property>+                      </widget>+                    </child>+                  </widget>+                </child>+              </widget>+            </child>+          </widget>+          <packing>+            <property name="expand">False</property>+            <property name="position">0</property>+          </packing>+        </child>+        <child>+          <widget class="GtkHBox" id="hbox1">+            <property name="visible">True</property>+            <child>+              <widget class="GtkLabel" id="label1">+                <property name="visible">True</property>+                <property name="label" translatable="yes">modifier:</property>+              </widget>+              <packing>+                <property name="expand">False</property>+                <property name="position">0</property>+              </packing>+            </child>+            <child>+              <widget class="GtkEntry" id="entry1">+                <property name="visible">True</property>+                <property name="can_focus">True</property>+                <property name="invisible_char">&#x25CF;</property>+              </widget>+              <packing>+                <property name="position">1</property>+              </packing>+            </child>+          </widget>+          <packing>+            <property name="expand">False</property>+            <property name="position">1</property>+          </packing>+        </child>+        <child>+          <widget class="GtkScrolledWindow" id="swtimeline">+            <property name="visible">True</property>+            <property name="can_focus">True</property>+            <property name="hscrollbar_policy">never</property>+            <child>+              <widget class="GtkViewport" id="vptimeline">+                <property name="visible">True</property>+                <property name="resize_mode">queue</property>+                <child>+                  <placeholder/>+                </child>+              </widget>+            </child>+          </widget>+          <packing>+            <property name="position">2</property>+          </packing>+        </child>+      </widget>+    </child>+  </widget>+  <widget class="GtkDialog" id="accountdialog">+    <property name="border_width">5</property>+    <property name="title" translatable="yes">hawitter - account</property>+    <property name="resizable">False</property>+    <property name="modal">True</property>+    <property name="type_hint">normal</property>+    <property name="has_separator">False</property>+    <child internal-child="vbox">+      <widget class="GtkVBox" id="dialog-vbox1">+        <property name="visible">True</property>+        <property name="orientation">vertical</property>+        <property name="spacing">2</property>+        <child>+          <widget class="GtkVBox" id="vbox3">+            <property name="visible">True</property>+            <property name="orientation">vertical</property>+            <child>+              <widget class="GtkRadioButton" id="radiobutton2">+                <property name="label" translatable="yes">OAuth(recommended)</property>+                <property name="visible">True</property>+                <property name="sensitive">False</property>+                <property name="can_focus">True</property>+                <property name="receives_default">False</property>+                <property name="draw_indicator">True</property>+              </widget>+              <packing>+                <property name="expand">False</property>+                <property name="position">0</property>+              </packing>+            </child>+            <child>+              <widget class="GtkAlignment" id="alignment2">+                <property name="visible">True</property>+                <property name="left_padding">20</property>+                <child>+                  <widget class="GtkTable" id="table2">+                    <property name="visible">True</property>+                    <property name="sensitive">False</property>+                    <property name="n_rows">4</property>+                    <property name="n_columns">2</property>+                    <property name="column_spacing">1</property>+                    <property name="row_spacing">2</property>+                    <child>+                      <widget class="GtkLabel" id="label23">+                        <property name="visible">True</property>+                        <property name="xalign">1</property>+                        <property name="label" translatable="yes">step 1:</property>+                      </widget>+                      <packing>+                        <property name="x_options">GTK_FILL</property>+                        <property name="y_options">GTK_FILL</property>+                      </packing>+                    </child>+                    <child>+                      <widget class="GtkLabel" id="label4">+                        <property name="visible">True</property>+                        <property name="label" translatable="yes">follow the link and click accept</property>+                      </widget>+                      <packing>+                        <property name="left_attach">1</property>+                        <property name="right_attach">2</property>+                        <property name="y_options">GTK_FILL</property>+                      </packing>+                    </child>+                    <child>+                      <widget class="GtkLabel" id="label5">+                        <property name="visible">True</property>+                        <property name="xalign">1</property>+                        <property name="label" translatable="yes">step 2:</property>+                      </widget>+                      <packing>+                        <property name="top_attach">2</property>+                        <property name="bottom_attach">3</property>+                        <property name="x_options">GTK_FILL</property>+                        <property name="y_options">GTK_FILL</property>+                      </packing>+                    </child>+                    <child>+                      <widget class="GtkLabel" id="label6">+                        <property name="visible">True</property>+                        <property name="label" translatable="yes">click the 'save token' button</property>+                      </widget>+                      <packing>+                        <property name="left_attach">1</property>+                        <property name="right_attach">2</property>+                        <property name="top_attach">2</property>+                        <property name="bottom_attach">3</property>+                        <property name="y_options">GTK_FILL</property>+                      </packing>+                    </child>+                    <child>+                      <widget class="GtkLabel" id="label7">+                        <property name="visible">True</property>+                        <property name="label" translatable="yes">http://www.example.com/</property>+                      </widget>+                      <packing>+                        <property name="left_attach">1</property>+                        <property name="right_attach">2</property>+                        <property name="top_attach">1</property>+                        <property name="bottom_attach">2</property>+                        <property name="y_options">GTK_FILL</property>+                      </packing>+                    </child>+                    <child>+                      <widget class="GtkAlignment" id="alignment3">+                        <property name="visible">True</property>+                        <property name="left_padding">50</property>+                        <property name="right_padding">50</property>+                        <child>+                          <widget class="GtkButton" id="button3">+                            <property name="label" translatable="yes">save token</property>+                            <property name="visible">True</property>+                            <property name="can_focus">True</property>+                            <property name="receives_default">True</property>+                          </widget>+                        </child>+                      </widget>+                      <packing>+                        <property name="left_attach">1</property>+                        <property name="right_attach">2</property>+                        <property name="top_attach">3</property>+                        <property name="bottom_attach">4</property>+                        <property name="x_options">GTK_FILL</property>+                        <property name="y_options">GTK_FILL</property>+                      </packing>+                    </child>+                    <child>+                      <placeholder/>+                    </child>+                    <child>+                      <placeholder/>+                    </child>+                  </widget>+                </child>+              </widget>+              <packing>+                <property name="position">1</property>+              </packing>+            </child>+            <child>+              <widget class="GtkRadioButton" id="radiobutton1">+                <property name="label" translatable="yes">unencrypted authentication</property>+                <property name="visible">True</property>+                <property name="can_focus">True</property>+                <property name="receives_default">False</property>+                <property name="active">True</property>+                <property name="draw_indicator">True</property>+                <property name="group">radiobutton2</property>+              </widget>+              <packing>+                <property name="expand">False</property>+                <property name="position">2</property>+              </packing>+            </child>+            <child>+              <widget class="GtkAlignment" id="alignment1">+                <property name="visible">True</property>+                <property name="left_padding">20</property>+                <child>+                  <widget class="GtkTable" id="table1">+                    <property name="visible">True</property>+                    <property name="n_rows">2</property>+                    <property name="n_columns">2</property>+                    <property name="column_spacing">1</property>+                    <property name="row_spacing">2</property>+                    <child>+                      <widget class="GtkLabel" id="label2">+                        <property name="visible">True</property>+                        <property name="xalign">1</property>+                        <property name="label" translatable="yes">user id:</property>+                      </widget>+                      <packing>+                        <property name="x_options">GTK_FILL</property>+                        <property name="y_options">GTK_FILL</property>+                      </packing>+                    </child>+                    <child>+                      <widget class="GtkLabel" id="label3">+                        <property name="visible">True</property>+                        <property name="xalign">1</property>+                        <property name="label" translatable="yes">password:</property>+                      </widget>+                      <packing>+                        <property name="top_attach">1</property>+                        <property name="bottom_attach">2</property>+                        <property name="x_options">GTK_FILL</property>+                        <property name="y_options">GTK_FILL</property>+                      </packing>+                    </child>+                    <child>+                      <widget class="GtkEntry" id="identry">+                        <property name="visible">True</property>+                        <property name="can_focus">True</property>+                        <property name="invisible_char">&#x25CF;</property>+                      </widget>+                      <packing>+                        <property name="left_attach">1</property>+                        <property name="right_attach">2</property>+                        <property name="x_options">GTK_FILL</property>+                        <property name="y_options">GTK_FILL</property>+                      </packing>+                    </child>+                    <child>+                      <widget class="GtkEntry" id="pswdentry">+                        <property name="visible">True</property>+                        <property name="can_focus">True</property>+                        <property name="invisible_char">&#x25CF;</property>+                      </widget>+                      <packing>+                        <property name="left_attach">1</property>+                        <property name="right_attach">2</property>+                        <property name="top_attach">1</property>+                        <property name="bottom_attach">2</property>+                        <property name="x_options">GTK_FILL</property>+                        <property name="y_options">GTK_FILL</property>+                      </packing>+                    </child>+                  </widget>+                </child>+              </widget>+              <packing>+                <property name="position">3</property>+              </packing>+            </child>+          </widget>+          <packing>+            <property name="position">1</property>+          </packing>+        </child>+        <child internal-child="action_area">+          <widget class="GtkHButtonBox" id="dialog-action_area1">+            <property name="visible">True</property>+            <property name="layout_style">end</property>+            <child>+              <widget class="GtkButton" id="button1">+                <property name="label">gtk-close</property>+                <property name="visible">True</property>+                <property name="can_focus">True</property>+                <property name="receives_default">True</property>+                <property name="use_stock">True</property>+              </widget>+              <packing>+                <property name="expand">False</property>+                <property name="fill">False</property>+                <property name="position">0</property>+              </packing>+            </child>+          </widget>+          <packing>+            <property name="expand">False</property>+            <property name="pack_type">end</property>+            <property name="position">0</property>+          </packing>+        </child>+      </widget>+    </child>+  </widget>+  <widget class="GtkDialog" id="postdialog">+    <property name="width_request">400</property>+    <property name="height_request">150</property>+    <property name="border_width">5</property>+    <property name="title" translatable="yes">hawitter - tweet</property>+    <property name="resizable">False</property>+    <property name="type_hint">normal</property>+    <property name="has_separator">False</property>+    <child internal-child="vbox">+      <widget class="GtkVBox" id="dialog-vbox2">+        <property name="visible">True</property>+        <property name="orientation">vertical</property>+        <property name="spacing">2</property>+        <child>+          <widget class="GtkVBox" id="vbox2">+            <property name="visible">True</property>+            <property name="orientation">vertical</property>+            <child>+              <widget class="GtkLabel" id="remaining">+                <property name="visible">True</property>+                <property name="label" translatable="yes">140 characters left</property>+              </widget>+              <packing>+                <property name="expand">False</property>+                <property name="position">0</property>+              </packing>+            </child>+            <child>+              <widget class="GtkTextView" id="tweetbody">+                <property name="visible">True</property>+                <property name="can_focus">True</property>+              </widget>+              <packing>+                <property name="position">1</property>+              </packing>+            </child>+          </widget>+          <packing>+            <property name="position">1</property>+          </packing>+        </child>+        <child internal-child="action_area">+          <widget class="GtkHButtonBox" id="dialog-action_area2">+            <property name="visible">True</property>+            <property name="layout_style">end</property>+            <child>+              <widget class="GtkButton" id="button2">+                <property name="label" translatable="yes">send</property>+                <property name="visible">True</property>+                <property name="can_focus">True</property>+                <property name="receives_default">True</property>+                <property name="xalign">0.50999999046325684</property>+              </widget>+              <packing>+                <property name="expand">False</property>+                <property name="fill">False</property>+                <property name="position">0</property>+              </packing>+            </child>+          </widget>+          <packing>+            <property name="expand">False</property>+            <property name="pack_type">end</property>+            <property name="position">0</property>+          </packing>+        </child>+      </widget>+    </child>+  </widget>+</glade-interface>
+ hawitter_128sq.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>