This should be all the changes for the new release, will confirm after commit
authorepage <eopage@byu.net>
Sat, 28 Jun 2008 20:00:37 +0000 (20:00 +0000)
committerepage <eopage@byu.net>
Sat, 28 Jun 2008 20:00:37 +0000 (20:00 +0000)
git-svn-id: file:///svnroot/gc-dialer/trunk@13 c39d3808-3fe2-4d86-a59f-b7f623ee9f21

12 files changed:
gc_dialer/GrandcentralDialer.pypackager [new file with mode: 0644]
gc_dialer/browser_emu.py [new file with mode: 0644]
gc_dialer/build_script.sh [new file with mode: 0755]
gc_dialer/gc_dialer.glade [deleted file]
gc_dialer/gc_dialer.py
gc_dialer/gc_dialer.xml [new file with mode: 0644]
gc_dialer/gc_dialer256.png [deleted file]
gc_dialer/gc_dialer_256.png [new file with mode: 0644]
gc_dialer/gc_dialer_26.png
gc_dialer/gc_dialer_64.png
gc_dialer/gc_dialer_new.glade [new file with mode: 0644]
gc_dialer/gcbackend.py [new file with mode: 0644]

diff --git a/gc_dialer/GrandcentralDialer.pypackager b/gc_dialer/GrandcentralDialer.pypackager
new file mode 100644 (file)
index 0000000..3bdcce0
--- /dev/null
@@ -0,0 +1,25 @@
+(S'Grandcentral_Dialer'
+S'0.6'
+S'user/Communication'
+S'all'
+S'Optional'
+S'Mark Bergman <bergman@mertech.com>'
+S'python2.5, python2.5-hildon, python2.5-gtk2'
+S'Python script that provides a telephone dial-pad in order to enter\nphone numbers to be dialed through Grandcentral.com.\n\nRequirements:\n\tan account with Grandcentral'
+S'/tmp/gc_dialer/build'
+S'/tmp/gc_dialer/gc_dialer_26.png'
+S'#!/bin/sh\n\ngtk-update-icon-cache -f /usr/share/icons/hicolor'
+S''
+S''
+S''
+S'0.6 "Daddy brain"\n\nNative login, UI updates, cleanup, and much more\n\n\n0.5\n\nAdded dialing history, changed UI.'
+S'extras'
+S'chinook'
+S'low'
+S'email@email.com'
+S''
+S'/tmp/gc_dialer/build'
+S'z2n'
+S'1'
+tp1
+.
\ No newline at end of file
diff --git a/gc_dialer/browser_emu.py b/gc_dialer/browser_emu.py
new file mode 100644 (file)
index 0000000..ae2a0de
--- /dev/null
@@ -0,0 +1,226 @@
+"""
+@author:         Laszlo Nagy
+@copyright:   (c) 2005 by Szoftver Messias Bt.
+@licence:       BSD style
+
+Objects of the MozillaEmulator class can emulate a browser that is capable of:
+
+       - cookie management
+       - caching
+       - configurable user agent string
+       - GET and POST
+       - multipart POST (send files)
+       - receive content into file
+       - progress indicator
+
+I have seen many requests on the python mailing list about how to emulate a browser. I'm using this class for years now, without any problems. This is how you can use it:
+
+       1. Use firefox
+       2. Install and open the livehttpheaders plugin
+       3. Use the website manually with firefox
+       4. Check the GET and POST requests in the livehttpheaders capture window
+       5. Create an instance of the above class and send the same GET and POST requests to the server.
+
+Optional steps:
+
+       - For testing, use a MozillaCacher instance - this will cache all pages and make testing quicker
+       - You can change user agent string in the build_opened method
+       - The "encode_multipart_formdata" function can be used alone to create POST data from a list of field values and files
+
+TODO:
+
+- should have a method to save/load cookies
+"""
+
+from __future__ import with_statement
+
+import os
+import md5
+import urllib
+import urllib2
+#import mimetypes
+import cookielib
+
+class MozillaEmulator(object):
+
+       def __init__(self,cacher={},trycount=0):
+               """Create a new MozillaEmulator object.
+
+               @param cacher: A dictionary like object, that can cache search results on a storage device.
+                       You can use a simple dictionary here, but it is not recommended.
+                       You can also put None here to disable caching completely.
+               @param trycount: The download() method will retry the operation if it fails. You can specify -1 for infinite retrying.
+                        A value of 0 means no retrying. A value of 1 means one retry. etc."""
+               self.cacher = cacher
+               self.cookies = cookielib.LWPCookieJar()
+               self.debug = False
+               self.trycount = trycount
+
+       def build_opener(self,url,postdata=None,extraheaders={},forbid_redirect=False):
+               txheaders = {
+                       'Accept':'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png',
+                       'Accept-Language':'en,en-us;q=0.5',
+#                      'Accept-Encoding': 'gzip, deflate',
+                       'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
+#                      'Keep-Alive': '300',
+#                      'Connection': 'keep-alive',
+#                      'Cache-Control': 'max-age=0',
+               }
+               for key,value in extraheaders.iteritems():
+                       txheaders[key] = value
+               req = urllib2.Request(url, postdata, txheaders)
+               self.cookies.add_cookie_header(req)
+               if forbid_redirect:
+                       redirector = HTTPNoRedirector()
+               else:
+                       redirector = urllib2.HTTPRedirectHandler()
+
+               http_handler = urllib2.HTTPHandler(debuglevel=self.debug)
+               https_handler = urllib2.HTTPSHandler(debuglevel=self.debug)
+
+               u = urllib2.build_opener(http_handler,https_handler,urllib2.HTTPCookieProcessor(self.cookies),redirector)
+               u.addheaders = [('User-Agent','Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.8) Gecko/20050511 Firefox/1.0.4')]
+               if not postdata is None:
+                       req.add_data(postdata)
+               return (req,u)
+
+       def download(self,url,postdata=None,extraheaders={},forbid_redirect=False,
+                       trycount=None,fd=None,onprogress=None,only_head=False):
+               """Download an URL with GET or POST methods.
+
+               @param postdata: It can be a string that will be POST-ed to the URL.
+                       When None is given, the method will be GET instead.
+               @param extraheaders: You can add/modify HTTP headers with a dict here.
+               @param forbid_redirect: Set this flag if you do not want to handle
+                       HTTP 301 and 302 redirects.
+               @param trycount: Specify the maximum number of retries here.
+                       0 means no retry on error. Using -1 means infinite retring.
+                       None means the default value (that is self.trycount).
+               @param fd: You can pass a file descriptor here. In this case,
+                       the data will be written into the file. Please note that
+                       when you save the raw data into a file then it won't be cached.
+               @param onprogress: A function that has two parameters:
+                       the size of the resource and the downloaded size. This will be
+                       called for each 1KB chunk. (If the HTTP header does not contain
+                       the content-length field, then the size parameter will be zero!)
+               @param only_head: Create the openerdirector and return it. In other
+                       words, this will not retrieve any content except HTTP headers.
+
+               @return: The raw HTML page data, unless fd was specified. When fd
+                       was given, the return value is undefined.
+               """
+               if trycount is None:
+                       trycount = self.trycount
+               cnt = 0
+               while True:
+                       try:
+                               req,u = self.build_opener(url,postdata,extraheaders,forbid_redirect)
+                               openerdirector = u.open(req)
+                               if self.debug:
+                                       print req.get_method(),url
+                                       print openerdirector.code,openerdirector.msg
+                                       print openerdirector.headers
+                               self.cookies.extract_cookies(openerdirector,req)
+                               if only_head:
+                                       return openerdirector
+                               if openerdirector.headers.has_key('content-length'):
+                                       length = long(openerdirector.headers['content-length'])
+                               else:
+                                       length = 0
+                               dlength = 0
+                               if fd:
+                                       while True:
+                                               data = openerdirector.read(1024)
+                                               dlength += len(data)
+                                               fd.write(data)
+                                               if onprogress:
+                                                       onprogress(length,dlength)
+                                               if not data:
+                                                       break
+                               else:
+                                       data = ''
+                                       while True:
+                                               newdata = openerdirector.read(1024)
+                                               dlength += len(newdata)
+                                               data += newdata
+                                               if onprogress:
+                                                       onprogress(length,dlength)
+                                               if not newdata:
+                                                       break
+                                       #data = openerdirector.read()
+                                       if not (self.cacher is None):
+                                               self.cacher[key] = data
+                               #try:
+                               #       d2= GzipFile(fileobj=cStringIO.StringIO(data)).read()
+                               #       data = d2
+                               #except IOError:
+                               #       pass
+                               return data
+                       except urllib2.URLError:
+                               cnt += 1
+                               if (trycount > -1) and (trycount < cnt):
+                                       raise
+                               # Retry :-)
+                               if self.debug:
+                                       print "MozillaEmulator: urllib2.URLError, retryting ",cnt
+
+#      def post_multipart(self,url,fields, files, forbid_redirect=True):
+#              """Post fields and files to an http host as multipart/form-data.
+#              fields is a sequence of (name, value) elements for regular form fields.
+#              files is a sequence of (name, filename, value) elements for data to be uploaded as files
+#              Return the server's response page.
+#              """
+#              content_type, post_data = encode_multipart_formdata(fields, files)
+#              result = self.download(url,post_data, {
+#                              'Content-Type': content_type,
+#                              'Content-Length': str(len(post_data))
+#                      },
+#                      forbid_redirect=forbid_redirect
+#              )
+#              return result
+
+
+class HTTPNoRedirector(urllib2.HTTPRedirectHandler):
+       """This is a custom http redirect handler that FORBIDS redirection."""
+
+       def http_error_302(self, req, fp, code, msg, headers):
+               e = urllib2.HTTPError(req.get_full_url(), code, msg, headers, fp)
+               if e.code in (301,302):
+                       if 'location' in headers:
+                               newurl = headers.getheaders('location')[0]
+                       elif 'uri' in headers:
+                               newurl = headers.getheaders('uri')[0]
+                       e.newurl = newurl
+               raise e
+
+
+#def encode_multipart_formdata(fields, files):
+#      """
+#      fields is a sequence of (name, value) elements for regular form fields.
+#      files is a sequence of (name, filename, value) elements for data to be uploaded as files
+#      Return (content_type, body) ready for httplib.HTTP instance
+#      """
+#      BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$'
+#      CRLF = '\r\n'
+#      L = []
+#      for (key, value) in fields:
+#              L.append('--' + BOUNDARY)
+#              L.append('Content-Disposition: form-data; name="%s"' % key)
+#              L.append('')
+#              L.append(value)
+#      for (key, filename, value) in files:
+#              L.append('--' + BOUNDARY)
+#              L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename))
+#              L.append('Content-Type: %s' % get_content_type(filename))
+#              L.append('')
+#              L.append(value)
+#      L.append('--' + BOUNDARY + '--')
+#      L.append('')
+#      body = CRLF.join(L)
+#      content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
+#      return content_type, body
+#
+#
+#def get_content_type(filename):
+#      return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
+#
diff --git a/gc_dialer/build_script.sh b/gc_dialer/build_script.sh
new file mode 100755 (executable)
index 0000000..3ee1db9
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+# Create PyPackager directory structure from the original files
+# Please make sure the following files are in this directory before
+# running this script
+
+# gc_dialer_256.png
+# gc_dialer_64.png
+# gc_dialer_26.png
+# gc_dialer.py
+# gc_dialer.xml
+# gc_dialer.desktop
+# gcbackend.py
+# browser_emu.py
+
+# The script creates the directories and concatenates the .py into a
+# single python script
+
+mkdir -p build/usr/share/icons/hicolor/scalable/hildon
+mkdir -p build/usr/share/icons/hicolor/26x26/hildon
+mkdir -p build/usr/share/icons/hicolor/64x64/hildon
+mkdir -p build/usr/share/applications/hildon
+mkdir -p build/usr/local/bin
+mkdir -p build/usr/local/lib
+
+cp gc_dialer_256.png build/usr/share/icons/hicolor/scalable/hildon/gc_dialer.png
+cp gc_dialer_64.png  build/usr/share/icons/hicolor/64x64/hildon/gc_dialer.png
+cp gc_dialer_26.png  build/usr/share/icons/hicolor/26x26/hildon/gc_dialer.png
+
+cp gc_dialer.desktop build/usr/share/applications/hildon
+
+cp gc_dialer.xml     build/usr/local/lib
+
+# Compress whitespace for 30% savings, make sure we are a HildonWindow
+sed -i 's/^[ \t]*//;s/[ \t]*$//;s/GtkWindow/HildonWindow/' build/usr/local/lib/gc_dialer.xml
+
+echo "#!/usr/bin/python" > build/usr/local/bin/gc_dialer.py
+echo "from __future__ import with_statement" >> build/usr/local/bin/gc_dialer.py
+cat gc_dialer.py gcbackend.py browser_emu.py | grep -e '^import ' | sort -u >> build/usr/local/bin/gc_dialer.py
+echo "import hildon" >> build/usr/local/bin/gc_dialer.py
+cat browser_emu.py gcbackend.py gc_dialer.py | grep -v 'import ' | grep -v "#!" >> build/usr/local/bin/gc_dialer.py
+chmod 755 build/usr/local/bin/gc_dialer.py
diff --git a/gc_dialer/gc_dialer.glade b/gc_dialer/gc_dialer.glade
deleted file mode 100644 (file)
index c45b468..0000000
+++ /dev/null
@@ -1,322 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
-<!--Generated with glade3 3.4.1 on Wed Jun 11 12:40:37 2008 -->
-<glade-interface>
-  <widget class="GtkWindow" id="Dialpad">
-    <property name="visible">True</property>
-    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-    <property name="title" translatable="yes">Grandcentral Dialer</property>
-    <property name="resizable">False</property>
-    <property name="default_width">160</property>
-    <property name="default_height">120</property>
-    <property name="icon">/usr/share/icons/hicolor/scalable/hildon/gc_dialer.png</property>
-    <property name="icon_name">gc_dialer</property>
-    <property name="deletable">False</property>
-    <child>
-      <widget class="GtkVPaned" id="vpaned1">
-        <property name="visible">True</property>
-        <property name="can_focus">True</property>
-        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-        <child>
-          <widget class="GtkHBox" id="hbox1">
-            <property name="visible">True</property>
-            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-            <child>
-              <widget class="GtkComboBox" id="dialer_history">
-                <property name="visible">True</property>
-                <property name="events">GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="no_show_all">True</property>
-                <property name="tooltip" translatable="yes">History of dialed numbers</property>
-                <signal name="changed" handler="on_dialer_history_changed"/>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkEntry" id="numberdisplay">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="editable">False</property>
-              </widget>
-              <packing>
-                <property name="position">1</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkButton" id="About">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="tooltip" translatable="yes">About</property>
-                <property name="label" translatable="yes">gtk-about</property>
-                <property name="use_stock">True</property>
-                <property name="response_id">0</property>
-                <signal name="clicked" handler="on_About_clicked" object="About"/>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="padding">4</property>
-                <property name="position">2</property>
-              </packing>
-            </child>
-          </widget>
-          <packing>
-            <property name="resize">False</property>
-            <property name="shrink">False</property>
-          </packing>
-        </child>
-        <child>
-          <widget class="GtkTable" id="Dialpad">
-            <property name="visible">True</property>
-            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-            <property name="n_rows">5</property>
-            <property name="n_columns">3</property>
-            <property name="column_spacing">8</property>
-            <property name="row_spacing">8</property>
-            <property name="homogeneous">True</property>
-            <child>
-              <placeholder/>
-            </child>
-            <child>
-              <widget class="GtkButton" id="Backspace">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="tooltip" translatable="yes">Backspace</property>
-                <property name="label" translatable="yes">gtk-go-back</property>
-                <property name="use_stock">True</property>
-                <property name="response_id">0</property>
-                <signal name="clicked" handler="on_Backspace_clicked" object="Backspace"/>
-              </widget>
-              <packing>
-                <property name="left_attach">2</property>
-                <property name="right_attach">3</property>
-                <property name="top_attach">3</property>
-                <property name="bottom_attach">4</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkButton" id="Dial">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="label" translatable="yes">Dial</property>
-                <property name="response_id">0</property>
-                <signal name="clicked" handler="on_Dial_clicked" object="Dial"/>
-              </widget>
-              <packing>
-                <property name="left_attach">2</property>
-                <property name="right_attach">3</property>
-                <property name="top_attach">4</property>
-                <property name="bottom_attach">5</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkButton" id="Clear">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="label" translatable="yes">Clear</property>
-                <property name="use_underline">True</property>
-                <property name="response_id">0</property>
-                <signal name="clicked" handler="on_Clear_clicked" object="Clear"/>
-              </widget>
-              <packing>
-                <property name="top_attach">3</property>
-                <property name="bottom_attach">4</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkButton" id="button8">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="label" translatable="yes">  8
-TUV</property>
-                <property name="response_id">0</property>
-                <signal name="clicked" handler="on_number_clicked" object="8"/>
-              </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>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkButton" id="button9">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="label" translatable="yes">   9
-WXYZ</property>
-                <property name="response_id">0</property>
-                <signal name="clicked" handler="on_number_clicked" object="9"/>
-              </widget>
-              <packing>
-                <property name="left_attach">2</property>
-                <property name="right_attach">3</property>
-                <property name="top_attach">2</property>
-                <property name="bottom_attach">3</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkButton" id="button0">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="label" translatable="yes">0</property>
-                <property name="response_id">0</property>
-                <signal name="clicked" handler="on_number_clicked" object="0"/>
-              </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>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkButton" id="Cancel">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="label" translatable="yes">gtk-quit</property>
-                <property name="use_stock">True</property>
-                <property name="response_id">0</property>
-                <signal name="clicked" handler="on_Cancel_clicked" object="Cancel"/>
-              </widget>
-              <packing>
-                <property name="top_attach">4</property>
-                <property name="bottom_attach">5</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkButton" id="button4">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="label" translatable="yes">  4
-GHI</property>
-                <property name="response_id">0</property>
-                <signal name="clicked" handler="on_number_clicked" object="4"/>
-              </widget>
-              <packing>
-                <property name="top_attach">1</property>
-                <property name="bottom_attach">2</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkButton" id="button5">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="label" translatable="yes">  5
-JKL</property>
-                <property name="response_id">0</property>
-                <signal name="clicked" handler="on_number_clicked" object="5"/>
-              </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>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkButton" id="button6">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="label" translatable="yes">  6
-MNO</property>
-                <property name="response_id">0</property>
-                <signal name="clicked" handler="on_number_clicked" object="6"/>
-              </widget>
-              <packing>
-                <property name="left_attach">2</property>
-                <property name="right_attach">3</property>
-                <property name="top_attach">1</property>
-                <property name="bottom_attach">2</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkButton" id="button7">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="label" translatable="yes">   7
-PQRS</property>
-                <property name="response_id">0</property>
-                <signal name="clicked" handler="on_number_clicked" object="7"/>
-              </widget>
-              <packing>
-                <property name="top_attach">2</property>
-                <property name="bottom_attach">3</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkButton" id="button1">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="label" translatable="yes">1</property>
-                <property name="response_id">0</property>
-                <signal name="clicked" handler="on_number_clicked" object="1"/>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkButton" id="button2">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="label" translatable="yes">  2
-ABC</property>
-                <property name="response_id">0</property>
-                <signal name="clicked" handler="on_number_clicked" object="2"/>
-              </widget>
-              <packing>
-                <property name="left_attach">1</property>
-                <property name="right_attach">2</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkButton" id="button3">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="label" translatable="yes">  3
-DEF</property>
-                <property name="response_id">0</property>
-                <signal name="clicked" handler="on_number_clicked" object="3"/>
-              </widget>
-              <packing>
-                <property name="left_attach">2</property>
-                <property name="right_attach">3</property>
-              </packing>
-            </child>
-          </widget>
-          <packing>
-            <property name="resize">False</property>
-            <property name="shrink">False</property>
-          </packing>
-        </child>
-      </widget>
-    </child>
-  </widget>
-</glade-interface>
index 1c5589e..f793211 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/python 
+#!/usr/bin/python2.5 
 
 # Grandcentral Dialer
 # Python front-end to a wget script to use grandcentral.com to place outbound VOIP calls.
 
 import sys
 import os
-import time
 import re
-try:
-       import pygtk
-       pygtk.require("2.0")
-except:
-       pass
-try:
-       import gtk
-       import gtk.glade
-except:
-       sys.exit(1)
-
-histfile=os.path.expanduser("~")
-histfile=os.path.join(histfile,".gcdialerhist")        # Use the native OS file separator
-liststore = gtk.ListStore(str)
-
-class GCDialer:
-       _wgetOKstrRe    = re.compile("This may take a few seconds", re.M)       # string from Grandcentral.com on successful dial 
-       _validateRe     = re.compile("^[0-9]{7,}$")
-
-       _wgetoutput     = "/tmp/gc_dialer.output"       # results from wget command
-       _cookiefile     = os.path.join(os.path.expanduser("~"),".mozilla/microb/cookies.txt")   # file with browser cookies
-       _wgetcmd        = "wget -nv -O %s --load-cookie=\"%s\" --referer=http://www.grandcentral.com/mobile/messages http://www.grandcentral.com/mobile/calls/click_to_call?destno=%s"
-
-       def __init__(self):
-               self._msg = ""
-               if ( os.path.isfile(GCDialer._cookiefile) == False ) :
-                       self._msg = 'Error: Failed to locate a file with saved browser cookies at \"' + cookiefile + '\".\n\tPlease use the web browser on your tablet to connect to www.grandcentral.com and then re-run Grandcentral Dialer.'
-
-       def validate(self,number):
-               return GCDialer._validateRe.match(number) != None
-
-       def dial(self,number):
-               self._msg = ""
-               if self.validate(number) == False:
-                       self._msg = "Invalid number format %s" % (number)
-                       return False
-
-               # Remove any existing output file...
-               if os.path.isfile(GCDialer._wgetoutput) :
-                       os.unlink(GCDialer._wgetoutput)
-               child_stdout, child_stdin, child_stderr = os.popen3(GCDialer._wgetcmd % (GCDialer._wgetoutput, GCDialer._cookiefile, number))
-               stderr=child_stderr.read()
-
-               child_stdout.close()
-               child_stderr.close()
-               child_stdin.close()
-
-               try:
-                       wgetresults = open(GCDialer._wgetoutput, 'r' )
-               except IOError:
-                       self._msg = 'IOError: No /tmp/gc_dialer.output file...dial attempt failed\n\tThis probably means that there is no active internet connection, or that\nthe site www.grandcentral.com is inacessible.'
-                       return False
-               
-               data = wgetresults.read()
-               wgetresults.close()
+import time
+import gobject
+import gtk
+import gc
+#import hildon
 
-               if GCDialer._wgetOKstrRe.search(data) != None:
-                       return True
-               else:
-                       self._msg = 'Error: Failed to login to www.grandcentral.com.\n\tThis probably means that there is no saved cookie for that site.\n\tPlease use the web browser on your tablet to connect to www.grandcentral.com and then re-run Grandcentral Dialer.'
-                       return False
-
-
-def load_history_list(histfile,liststore):
-       # read the history list, load it into the liststore variable for later
-       # assignment to the combobox menu
-
-       # clear out existing entries
-       dialhist = []
-       liststore.clear()
-       if os.path.isfile(histfile) :
-               histFH = open(histfile,"r")
-               for line in histFH.readlines() :
-                       fields = line.split()   # split the input lines on whitespace
-                       number=fields[0]                #...save only the first field (the phone number)
-                       search4num=re.compile('^' + number + '$')
-                       newnumber=True  # set a flag that the current phone number is not on the history menu
-                       for num in dialhist :
-                               if re.match(search4num,num):
-                                       # the number is already in the drop-down menu list...set the
-                                       # flag and bail out
-                                       newnumber = False
-                                       break
-                       if newnumber == True :
-                               dialhist.append(number) # append the number to the history list
-       
-               histlen=len(dialhist)
-               if histlen > 10 :
-                       dialhist=dialhist[histlen - 10:histlen]         # keep only the last 10 entries
-               dialhist.reverse()      # reverse the list, so that the most recent entry is now first
-       
-               # Now, load the liststore with the entries, for later assignment to the Gtk.combobox menu
-               for entry in dialhist :
-                       entry=makepretty(entry)
-                       liststore.append([entry])
-       # else :
-       #        print "The history file " + histfile + " does not exist"
+from gcbackend import GCDialer
 
 def makeugly(prettynumber):
        # function to take a phone number and strip out all non-numeric
@@ -123,6 +32,9 @@ def makepretty(phonenumber):
        #                       (...)-...-....
        #               else if phonenumber is 10 digits:
        #                       ...-....
+       if phonenumber is None:
+               return ""
+
        if len(phonenumber) < 3 :
                return phonenumber
 
@@ -136,6 +48,9 @@ def makepretty(phonenumber):
                        else:
                                prettynumber = "+" + phonenumber[0:3] + "-(" + phonenumber[3:6] + ")-" + phonenumber[6:9] + "-" + phonenumber[9:]
                        return prettynumber
+       elif phonenumber[0] == "1" and len(phonenumber) > 8:
+               prettynumber = "1 (" + phonenumber[1:4] + ")-" + phonenumber[4:7] + "-" + phonenumber[7:]
+               return prettynumber
        elif len(phonenumber) <= 7 :
                        prettynumber = phonenumber[0:3] + "-" + phonenumber[3:] 
        elif len(phonenumber) > 7 :
@@ -144,52 +59,143 @@ def makepretty(phonenumber):
 
 class Dialpad:
 
-       phonenumber = ""
-
        def __init__(self):
-               if os.path.isfile("/usr/local/lib/gc_dialer.glade") :
-                       self.gladefile = "/usr/local/lib/gc_dialer.glade"  
-               elif os.path.isfile("./gc_dialer.glade") :
-                       self.gladefile = "./gc_dialer.glade"
-
+               self.phonenumber = ""
+               self.prettynumber = ""
+               self.areacode = "518"
                self.gcd = GCDialer()
-               if self.gcd._msg != "":
-                       self.ErrPopUp(self.gcd._msg)
-                       sys.exit(1)
+               self.wTree = gtk.Builder()
 
-               self.wTree = gtk.glade.XML(self.gladefile)
-               self.window = self.wTree.get_widget("Dialpad")
-               if (self.window):
-                       self.window.connect("destroy", gtk.main_quit)
+               for path in [ './gc_dialer.xml',
+                               '../lib/gc_dialer.xml',
+                               '/usr/local/lib/gc_dialer.xml' ]:
+                       if os.path.isfile(path):
+                               self.wTree.add_from_file(path)
+                               break
+
+               self.window = self.wTree.get_object("Dialpad")
                #Get the buffer associated with the number display
-               self.numberdisplay = self.wTree.get_widget("numberdisplay")
-               self.dialer_history = self.wTree.get_widget("dialer_history")
+               self.numberdisplay = self.wTree.get_object("numberdisplay")
+               self.setNumber("")
 
-               # Load the liststore array with the numbers from the history file
-               load_history_list(histfile,liststore)
-               # load the dropdown menu with the numbers from the dial history
-               self.dialer_history.set_model(liststore)
-               cell = gtk.CellRendererText()
-               self.dialer_history.pack_start(cell, True)
-               self.dialer_history.set_active(-1)
-               self.dialer_history.set_attributes(cell, text=0)
+               self.recentview = self.wTree.get_object("recentview")
+               self.recentmodel = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
+               self.recentview.set_model(self.recentmodel)
+               textrenderer = gtk.CellRendererText()
 
-               self.about_dialog = None
-               self.error_dialog = None
+               # Add the column to the treeview
+               column = gtk.TreeViewColumn("Calls", textrenderer, text=1)
+               column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
+               self.recentview.append_column(column)
 
-               dic = {
-                       # Routine for processing signal from the combobox (ie., when the
-                       # user selects an entry from the dropdown history
-                       "on_dialer_history_changed" : self.on_dialer_history_changed,
+               self.recentviewselection = self.recentview.get_selection()
+               self.recentviewselection.set_mode(gtk.SELECTION_SINGLE)
+               self.recenttime = 0.0
+
+               self.notebook = self.wTree.get_object("notebook")
+
+               self.isHildon = False
+               #if True:
+               try:
+                       self.app = hildon.Program()
+                       self.wTree.get_object("callbackentry").set_property('hildon-input-mode', 1|(1 << 4))
+                       self.isHildon = True
+               except:
+                       print "No hildon"
 
+               if (self.window):
+                       self.window.connect("destroy", gtk.main_quit)
+                       self.window.show_all()
+
+               dic = {
                        # Process signals from buttons
-                       "on_number_clicked"  : self.on_number_clicked,
-                       "on_Clear_clicked"   : self.on_Clear_clicked,
-                       "on_Dial_clicked"    : self.on_Dial_clicked,
-                       "on_Backspace_clicked" : self.Backspace,
-                       "on_Cancel_clicked"  : self.on_Cancel_clicked,
-                       "on_About_clicked"   : self.on_About_clicked}
-               self.wTree.signal_autoconnect(dic)
+                       "on_digit_clicked"  : self.on_digit_clicked,
+                       "on_dial_clicked"    : self.on_dial_clicked,
+                       "on_loginbutton_clicked" : self.on_loginbutton_clicked,
+                       "on_clearcookies_clicked" : self.on_clearcookies_clicked,
+                       "on_callbackentry_changed" : self.on_callbackentry_changed,
+                       "on_notebook_switch_page" : self.on_notebook_switch_page,
+                       "on_recentview_row_activated" : self.on_recentview_row_activated,
+                       "on_back_clicked" : self.Backspace }
+               self.wTree.connect_signals(dic)
+
+               self.attemptLogin(3)
+               if self.gcd.getCallbackNumber() is None:
+                       self.gcd.setSaneCallback()
+               
+               self.setAccountNumber()
+               self.setupCallbackCombo()
+               self.reduce_memory()
+
+       def reduce_memory(self):
+               re.purge()
+               num = gc.collect()
+               #print "collect %d objects" % ( num )
+
+       def on_recentview_row_activated(self, treeview, path, view_column):
+               model, iter = self.recentviewselection.get_selected()
+               if iter:
+                       self.setNumber(self.recentmodel.get_value(iter,0))
+                       self.notebook.set_current_page(0)
+                       self.recentviewselection.unselect_all()
+
+       def on_notebook_switch_page(self, notebook, page, page_num):
+               if page_num == 1 and (time.time() - self.recenttime) > 300:
+                       self.populate_recentview()
+
+       def populate_recentview(self):
+               print "Populating"
+               self.recentmodel.clear()
+               for item in self.gcd.get_recent():
+                       self.recentmodel.append(item)
+               self.recenttime = time.time()
+
+       def on_clearcookies_clicked(self, data=None):
+               self.gcd.reset()
+               self.attemptLogin(3)
+
+       def setupCallbackCombo(self):
+               combobox = self.wTree.get_object("callbackcombo")
+               self.callbacklist = gtk.ListStore(gobject.TYPE_STRING)
+               combobox.set_model(self.callbacklist)
+               combobox.set_text_column(0)
+               for k,v in self.gcd.getCallbackNumbers().iteritems():
+                       self.callbacklist.append([makepretty(k)] )
+               
+               self.wTree.get_object("callbackentry").set_text(makepretty(self.gcd.getCallbackNumber()))
+
+       def on_callbackentry_changed(self, data=None):
+               text = makeugly(self.wTree.get_object("callbackentry").get_text())
+               if self.gcd.validate(text) and text != self.gcd.getCallbackNumber():
+                       self.gcd.setCallbackNumber(text)
+                       self.wTree.get_object("callbackentry").set_text(self.wTree.get_object("callbackentry").get_text())
+               self.reduce_memory()
+
+
+       def attemptLogin(self, times = 1):
+               if self.isHildon:
+                       dialog = hildon.LoginDialog(self.window)
+                       dialog.set_message("Grandcentral Login")
+               else:
+                       dialog = self.wTree.get_object("login_dialog")
+
+               while ( (times > 0) and (self.gcd.isAuthed() == False) ):
+                       if dialog.run() == gtk.RESPONSE_OK:
+                               if self.isHildon:
+                                       username = dialog.get_username()
+                                       password = dialog.get_password()
+                               else:
+                                       username = self.wTree.get_object("usernameentry").get_text()
+                                       password = self.wTree.get_object("passwordentry").get_text()
+                                       self.wTree.get_object("passwordentry").set_text("")
+                               self.gcd.login(username, password)
+                               dialog.hide()
+                               times = times - 1
+                       else:
+                               times = 0
+
+               if self.isHildon:
+                       dialog.destroy()
 
        def ErrPopUp(self,msg):
                error_dialog = gtk.MessageDialog(None,0,gtk.MESSAGE_ERROR,gtk.BUTTONS_CLOSE,msg)
@@ -197,94 +203,49 @@ class Dialpad:
                        editor.about_dialog = None
                        dialog.destroy()
                error_dialog.connect("response", close, self)
-               # error_dialog.connect("delete-event", delete_event, self)
                self.error_dialog = error_dialog
                error_dialog.run()
 
-       def on_About_clicked(self, menuitem, data=None):
-               if self.about_dialog: 
-                       self.about_dialog.present()
-                       return
+       def on_loginbutton_clicked(self, data=None):
+               self.wTree.get_object("login_dialog").response(gtk.RESPONSE_OK)
 
-               authors = [ "Mark Bergman <bergman@merctech.com>",
-                               "Eric Warnke <ericew@gmail.com>" ]
+       def on_dial_clicked(self, widget):
+               self.attemptLogin(3)
 
-               about_dialog = gtk.AboutDialog()
-               about_dialog.set_transient_for(None)
-               about_dialog.set_destroy_with_parent(True)
-               about_dialog.set_name("Grandcentral Dialer")
-               about_dialog.set_version("0.5")
-               about_dialog.set_copyright("Copyright \xc2\xa9 2008 Mark Bergman")
-               about_dialog.set_comments("GUI front-end to initiate outbound call from Grandcentral.com, typically with Grancentral configured to connect the outbound call to a VOIP number accessible via Gizmo on the Internet Tablet.\n\nRequires an existing browser cookie from a previous login session to http://www.grandcentral.com/mobile/messages and the program 'wget'.")
-               about_dialog.set_authors            (authors)
-               about_dialog.set_logo_icon_name     (gtk.STOCK_EDIT)
-
-               # callbacks for destroying the dialog
-               def close(dialog, response, editor):
-                       editor.about_dialog = None
-                       dialog.destroy()
+               if not self.gcd.isAuthed() or self.gcd.getCallbackNumber() == "":
+                       self.ErrPopUp("Backend link with grandcentral is not working, please try again")
+                       return
 
-               def delete_event(dialog, event, editor):
-                       editor.about_dialog = None
-                       return True
-
-               about_dialog.connect("response", close, self)
-               about_dialog.connect("delete-event", delete_event, self)
-               self.about_dialog = about_dialog
-               about_dialog.show()
-
-       def on_Dial_clicked(self, widget):
-               # Strip the leading "1" before the area code, if present
-               if len(Dialpad.phonenumber) == 11 and Dialpad.phonenumber[0] == "1" :
-                               Dialpad.phonenumber = Dialpad.phonenumber[1:]
-               prettynumber = makepretty(Dialpad.phonenumber)
-               if len(Dialpad.phonenumber) < 7 :
-                       # It's too short to be a phone number
-                       msg = 'Phone number "%s" is too short' % ( prettynumber )
-                       self.ErrPopUp(msg)
-               else :
-                       timestamp=time.asctime(time.localtime())
+               #if len(self.phonenumber) == 7:
+               #       #add default area code
+               #       self.phonenumber = self.areacode + self.phonenumber
                        
-                       if self.gcd.dial(Dialpad.phonenumber) == True : 
-                               histFH = open(histfile,"a")
-                               histFH.write("%s dialed at %s\n" % ( Dialpad.phonenumber, timestamp ) )
-                               histFH.close()
-
-                               # Re-load the updated history of dialed numbers
-                               load_history_list(histfile,liststore)
-                               self.dialer_history.set_active(-1)
-                               self.on_Clear_clicked(widget)
-                       else:
-                               self.ErrPopUp(self.gcd._msg)
-
-       def on_Cancel_clicked(self, widget):
-               sys.exit(1)
-
-       def Backspace(self, widget):
-               Dialpad.phonenumber = Dialpad.phonenumber[:-1]
-               prettynumber = makepretty(Dialpad.phonenumber)
-               self.numberdisplay.set_text(prettynumber)
+               if self.gcd.dial(self.phonenumber) == False: 
+                       self.ErrPopUp(self.gcd._msg)
+               else:
+                       self.setNumber("")
 
-       def on_Clear_clicked(self, widget):
-               Dialpad.phonenumber = ""
-               self.numberdisplay.set_text(Dialpad.phonenumber)
+               self.recentmodel.clear()
+               self.recenttime = 0.0
+               self.reduce_memory()
 
-       def on_dialer_history_changed(self,widget):
-               # Set the displayed number to the number chosen from the history list
-               history_list = self.dialer_history.get_model()
-               history_index = self.dialer_history.get_active()
-               prettynumber = history_list[history_index][0]
-               Dialpad.phonenumber = makeugly(prettynumber)
-               self.numberdisplay.set_text(prettynumber)
+       def setNumber(self, number):
+               self.phonenumber = makeugly(number)
+               self.prettynumber = makepretty(self.phonenumber)
+               self.numberdisplay.set_label("<span size='30000' weight='bold'>%s</span>" % ( self.prettynumber ) )
 
-       def on_number_clicked(self, widget):
-               Dialpad.phonenumber = Dialpad.phonenumber + re.sub('\D','',widget.get_label())
-               prettynumber = makepretty(Dialpad.phonenumber)
-               self.numberdisplay.set_text(prettynumber)
+       def setAccountNumber(self):
+               accountnumber = self.gcd.getAccountNumber()
+               self.wTree.get_object("gcnumberlabel").set_label("<span size='23000' weight='bold'>%s</span>" % (accountnumber))
 
+       def Backspace(self, widget):
+               self.setNumber(self.phonenumber[:-1])
 
+       def on_digit_clicked(self, widget):
+               self.setNumber(self.phonenumber + re.sub('\D','',widget.get_label()))
 
 if __name__ == "__main__":
+       gc.set_threshold(50,3,3)
        title = 'Dialpad'
        handle = Dialpad()
        gtk.main()
diff --git a/gc_dialer/gc_dialer.xml b/gc_dialer/gc_dialer.xml
new file mode 100644 (file)
index 0000000..ae97629
--- /dev/null
@@ -0,0 +1,572 @@
+<?xml version="1.0"?>
+<!--Generated with glade3 3.4.4 on Sat Jun 28 00:20:36 2008 -->
+<interface>
+  <object class="GtkWindow" id="Dialpad">
+    <property name="width_request">400</property>
+    <property name="height_request">300</property>
+    <property name="title" translatable="yes">Dialer</property>
+    <child>
+      <object class="GtkNotebook" id="notebook">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="tab_pos">GTK_POS_BOTTOM</property>
+        <property name="show_border">False</property>
+        <property name="homogeneous">True</property>
+        <signal handler="on_notebook_switch_page" name="switch_page"/>
+        <child>
+          <object class="GtkVBox" id="vbox2">
+            <property name="visible">True</property>
+            <child>
+              <object class="GtkLabel" id="numberdisplay">
+                <property name="height_request">50</property>
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">&lt;span size="30000" weight="bold"&gt;(518) 555-1212&lt;/span&gt;</property>
+                <property name="use_markup">True</property>
+                <property name="justify">GTK_JUSTIFY_CENTER</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkTable" id="keypadview">
+                <property name="visible">True</property>
+                <property name="n_rows">4</property>
+                <property name="n_columns">3</property>
+                <property name="homogeneous">True</property>
+                <child>
+                  <object class="GtkButton" id="digit1">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">1</property>
+                    <property name="focus_on_click">False</property>
+                    <signal handler="on_digit_clicked" name="clicked"/>
+                    <accelerator key="1" modifiers="" signal="clicked"/>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkButton" id="digit2">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">2 ABC</property>
+                    <property name="focus_on_click">False</property>
+                    <signal handler="on_digit_clicked" name="clicked"/>
+                    <accelerator key="c" modifiers="" signal="clicked"/>
+                    <accelerator key="b" modifiers="" signal="clicked"/>
+                    <accelerator key="a" modifiers="" signal="clicked"/>
+                    <accelerator key="2" modifiers="" signal="clicked"/>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="digit3">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">3 DEF</property>
+                    <property name="focus_on_click">False</property>
+                    <signal handler="on_digit_clicked" name="clicked"/>
+                    <accelerator key="f" modifiers="" signal="clicked"/>
+                    <accelerator key="e" modifiers="" signal="clicked"/>
+                    <accelerator key="d" modifiers="" signal="clicked"/>
+                    <accelerator key="3" modifiers="" signal="clicked"/>
+                  </object>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="digit4">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">4 GHI</property>
+                    <property name="focus_on_click">False</property>
+                    <signal handler="on_digit_clicked" name="clicked"/>
+                    <accelerator key="i" modifiers="" signal="clicked"/>
+                    <accelerator key="h" modifiers="" signal="clicked"/>
+                    <accelerator key="g" modifiers="" signal="clicked"/>
+                    <accelerator key="4" modifiers="" signal="clicked"/>
+                  </object>
+                  <packing>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="digit5">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">5 JKL</property>
+                    <property name="focus_on_click">False</property>
+                    <signal handler="on_digit_clicked" name="clicked"/>
+                    <accelerator key="l" modifiers="" signal="clicked"/>
+                    <accelerator key="k" modifiers="" signal="clicked"/>
+                    <accelerator key="j" modifiers="" signal="clicked"/>
+                    <accelerator key="5" modifiers="" signal="clicked"/>
+                  </object>
+                  <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>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="digit6">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">6 MNO</property>
+                    <property name="focus_on_click">False</property>
+                    <signal handler="on_digit_clicked" name="clicked"/>
+                    <accelerator key="o" modifiers="" signal="clicked"/>
+                    <accelerator key="n" modifiers="" signal="clicked"/>
+                    <accelerator key="m" modifiers="" signal="clicked"/>
+                    <accelerator key="6" modifiers="" signal="clicked"/>
+                  </object>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="digit7">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">7 PQRS</property>
+                    <property name="focus_on_click">False</property>
+                    <signal handler="on_digit_clicked" name="clicked"/>
+                    <accelerator key="s" modifiers="" signal="clicked"/>
+                    <accelerator key="r" modifiers="" signal="clicked"/>
+                    <accelerator key="q" modifiers="" signal="clicked"/>
+                    <accelerator key="p" modifiers="" signal="clicked"/>
+                    <accelerator key="7" modifiers="" signal="clicked"/>
+                  </object>
+                  <packing>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="digit8">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">8 TUV</property>
+                    <property name="focus_on_click">False</property>
+                    <signal handler="on_digit_clicked" name="clicked"/>
+                    <accelerator key="v" modifiers="" signal="clicked"/>
+                    <accelerator key="u" modifiers="" signal="clicked"/>
+                    <accelerator key="t" modifiers="" signal="clicked"/>
+                    <accelerator key="8" modifiers="" signal="clicked"/>
+                  </object>
+                  <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>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="digit9">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">9 WXYZ</property>
+                    <property name="focus_on_click">False</property>
+                    <signal handler="on_digit_clicked" name="clicked"/>
+                    <accelerator key="z" modifiers="" signal="clicked"/>
+                    <accelerator key="y" modifiers="" signal="clicked"/>
+                    <accelerator key="x" modifiers="" signal="clicked"/>
+                    <accelerator key="w" modifiers="" signal="clicked"/>
+                    <accelerator key="9" modifiers="" signal="clicked"/>
+                  </object>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="back">
+                    <property name="visible">True</property>
+                    <property name="focus_on_click">False</property>
+                    <signal handler="on_back_clicked" name="clicked"/>
+                    <accelerator key="BackSpace" modifiers="" signal="clicked"/>
+                    <child>
+                      <object class="GtkHBox" id="hbox2">
+                        <property name="visible">True</property>
+                        <child>
+                          <object class="GtkImage" id="image2">
+                            <property name="visible">True</property>
+                            <property name="xalign">1</property>
+                            <property name="stock">gtk-no</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkLabel" id="label9">
+                            <property name="visible">True</property>
+                            <property name="xalign">0</property>
+                            <property name="xpad">5</property>
+                            <property name="label" translatable="yes">Back</property>
+                          </object>
+                          <packing>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="top_attach">3</property>
+                    <property name="bottom_attach">4</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="digit0">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">0</property>
+                    <property name="focus_on_click">False</property>
+                    <signal handler="on_digit_clicked" name="clicked"/>
+                    <accelerator key="0" modifiers="" signal="clicked"/>
+                  </object>
+                  <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>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="dial">
+                    <property name="visible">True</property>
+                    <property name="focus_on_click">False</property>
+                    <signal handler="on_dial_clicked" name="clicked"/>
+                    <accelerator key="Return" modifiers="" signal="clicked"/>
+                    <child>
+                      <object class="GtkHBox" id="hbox1">
+                        <property name="visible">True</property>
+                        <child>
+                          <object class="GtkImage" id="image1">
+                            <property name="visible">True</property>
+                            <property name="xalign">1</property>
+                            <property name="stock">gtk-yes</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkLabel" id="label8">
+                            <property name="visible">True</property>
+                            <property name="xalign">0</property>
+                            <property name="xpad">5</property>
+                            <property name="label" translatable="yes">Dial</property>
+                          </object>
+                          <packing>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">3</property>
+                    <property name="bottom_attach">4</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="tab_expand">True</property>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+        <child type="tab">
+          <object class="GtkLabel" id="keypad">
+            <property name="height_request">30</property>
+            <property name="visible">True</property>
+            <property name="label" translatable="yes">Keypad</property>
+          </object>
+          <packing>
+            <property name="tab_expand">True</property>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkScrolledWindow" id="scrolledwindow1">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+            <child>
+              <object class="GtkTreeView" id="recentview">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="headers_visible">False</property>
+                <property name="enable_search">False</property>
+                <property name="fixed_height_mode">True</property>
+                <property name="enable_grid_lines">GTK_TREE_VIEW_GRID_LINES_HORIZONTAL</property>
+                <property name="enable_tree_lines">True</property>
+                <signal handler="on_recentview_row_activated" name="row_activated"/>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="position">1</property>
+            <property name="tab_expand">True</property>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+        <child type="tab">
+          <object class="GtkLabel" id="recent">
+            <property name="height_request">30</property>
+            <property name="visible">True</property>
+            <property name="label" translatable="yes">Recent</property>
+          </object>
+          <packing>
+            <property name="position">1</property>
+            <property name="tab_expand">True</property>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkTable" id="accountview">
+            <property name="visible">True</property>
+            <property name="border_width">11</property>
+            <property name="n_rows">3</property>
+            <property name="n_columns">2</property>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <object class="GtkComboBoxEntry" id="callbackcombo">
+                <property name="visible">True</property>
+                <child internal-child="entry">
+                  <object class="GtkEntry" id="callbackentry">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <signal handler="on_callbackentry_changed" name="changed"/>
+                  </object>
+                </child>
+              </object>
+              <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="x_options">GTK_FILL</property>
+                <property name="y_options"/>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label3">
+                <property name="visible">True</property>
+                <property name="xalign">1</property>
+                <property name="xpad">5</property>
+                <property name="label" translatable="yes">Callback Number:</property>
+              </object>
+              <packing>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">3</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="clearcookies">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="label" translatable="yes">Clear Account Information
+must reauthenticate</property>
+                <signal handler="on_clearcookies_clicked" name="clicked"/>
+              </object>
+              <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"/>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="gcnumberlabel">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">&lt;span size="15000" weight="bold"&gt;(518) 555-1212&lt;/span&gt;</property>
+                <property name="use_markup">True</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="y_options"/>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label1">
+                <property name="visible">True</property>
+                <property name="xalign">1</property>
+                <property name="xpad">5</property>
+                <property name="label" translatable="yes">GrandCentral
+Number:</property>
+                <property name="justify">GTK_JUSTIFY_RIGHT</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="position">2</property>
+            <property name="tab_expand">True</property>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+        <child type="tab">
+          <object class="GtkLabel" id="account">
+            <property name="height_request">30</property>
+            <property name="visible">True</property>
+            <property name="label" translatable="yes">Account</property>
+          </object>
+          <packing>
+            <property name="position">2</property>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkVBox" id="vbox1">
+            <property name="visible">True</property>
+            <child>
+              <object class="GtkLabel" id="label2">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">&lt;span size="20000" weight="bold"&gt;GrandCentral Dialer&lt;/span&gt;
+Copyright 2008</property>
+                <property name="use_markup">True</property>
+                <property name="justify">GTK_JUSTIFY_CENTER</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label4">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">GUI front-end to initiate outbound call from Grandcentral.com, typically with Grancentral configured to connect the outbound call to a VOIP number accessible via Gizmo on the Internet Tablet.</property>
+                <property name="wrap">True</property>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label7">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Authors: Mark Bergman &lt;bergman@merctech.com&gt;, Eric Warnke &lt;ericew@gmail.com&gt;</property>
+                <property name="wrap">True</property>
+              </object>
+              <packing>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">3</property>
+            <property name="tab_expand">True</property>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+        <child type="tab">
+          <object class="GtkLabel" id="about">
+            <property name="height_request">30</property>
+            <property name="visible">True</property>
+            <property name="label" translatable="yes">About</property>
+          </object>
+          <packing>
+            <property name="position">3</property>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="GtkDialog" id="login_dialog">
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">Grandcentral Login</property>
+    <property name="resizable">False</property>
+    <property name="modal">True</property>
+    <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+    <property name="destroy_with_parent">True</property>
+    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+    <property name="skip_taskbar_hint">True</property>
+    <property name="skip_pager_hint">True</property>
+    <property name="deletable">False</property>
+    <property name="transient_for">Dialpad</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <object class="GtkVBox" id="dialog-vbox1">
+        <property name="visible">True</property>
+        <property name="spacing">2</property>
+        <child>
+          <object class="GtkTable" id="table1">
+            <property name="visible">True</property>
+            <property name="n_rows">2</property>
+            <property name="n_columns">2</property>
+            <child>
+              <object class="GtkLabel" id="label5">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Username</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label6">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Password</property>
+              </object>
+              <packing>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkEntry" id="usernameentry">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkEntry" id="passwordentry">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="visibility">False</property>
+              </object>
+              <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>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <object class="GtkHButtonBox" id="dialog-action_area1">
+            <property name="visible">True</property>
+            <property name="layout_style">GTK_BUTTONBOX_END</property>
+            <child>
+              <object class="GtkButton" id="loginbutton">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="label" translatable="yes">Login</property>
+                <signal handler="on_loginbutton_clicked" name="clicked"/>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">GTK_PACK_END</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="0">loginbutton</action-widget>
+    </action-widgets>
+  </object>
+</interface>
diff --git a/gc_dialer/gc_dialer256.png b/gc_dialer/gc_dialer256.png
deleted file mode 100644 (file)
index 4fdc910..0000000
Binary files a/gc_dialer/gc_dialer256.png and /dev/null differ
diff --git a/gc_dialer/gc_dialer_256.png b/gc_dialer/gc_dialer_256.png
new file mode 100644 (file)
index 0000000..a875350
Binary files /dev/null and b/gc_dialer/gc_dialer_256.png differ
index b14447b..df50c66 100644 (file)
Binary files a/gc_dialer/gc_dialer_26.png and b/gc_dialer/gc_dialer_26.png differ
index da88173..8d98390 100644 (file)
Binary files a/gc_dialer/gc_dialer_64.png and b/gc_dialer/gc_dialer_64.png differ
diff --git a/gc_dialer/gc_dialer_new.glade b/gc_dialer/gc_dialer_new.glade
new file mode 100644 (file)
index 0000000..41b6e07
--- /dev/null
@@ -0,0 +1,589 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--Generated with glade3 3.4.4 on Sat Jun 28 00:31:26 2008 -->
+<glade-interface>
+  <widget class="GtkWindow" id="Dialpad">
+    <property name="width_request">400</property>
+    <property name="height_request">300</property>
+    <property name="title" translatable="yes">Dialer</property>
+    <child>
+      <widget class="GtkNotebook" id="notebook">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="tab_pos">GTK_POS_BOTTOM</property>
+        <property name="show_border">False</property>
+        <property name="homogeneous">True</property>
+        <signal name="switch_page" handler="on_notebook_switch_page"/>
+        <child>
+          <widget class="GtkVBox" id="vbox2">
+            <property name="visible">True</property>
+            <child>
+              <widget class="GtkLabel" id="numberdisplay">
+                <property name="height_request">50</property>
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">&lt;span size="30000" weight="bold"&gt;(518) 555-1212&lt;/span&gt;</property>
+                <property name="use_markup">True</property>
+                <property name="justify">GTK_JUSTIFY_CENTER</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkTable" id="keypadview">
+                <property name="visible">True</property>
+                <property name="n_rows">4</property>
+                <property name="n_columns">3</property>
+                <property name="homogeneous">True</property>
+                <child>
+                  <widget class="GtkButton" id="digit1">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">1</property>
+                    <property name="focus_on_click">False</property>
+                    <property name="response_id">0</property>
+                    <signal name="clicked" handler="on_digit_clicked"/>
+                    <accelerator key="1" modifiers="" signal="clicked"/>
+                  </widget>
+                </child>
+                <child>
+                  <widget class="GtkButton" id="digit2">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">2 ABC</property>
+                    <property name="focus_on_click">False</property>
+                    <property name="response_id">0</property>
+                    <signal name="clicked" handler="on_digit_clicked"/>
+                    <accelerator key="c" modifiers="" signal="clicked"/>
+                    <accelerator key="b" modifiers="" signal="clicked"/>
+                    <accelerator key="a" modifiers="" signal="clicked"/>
+                    <accelerator key="2" modifiers="" signal="clicked"/>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkButton" id="digit3">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">3 DEF</property>
+                    <property name="focus_on_click">False</property>
+                    <property name="response_id">0</property>
+                    <signal name="clicked" handler="on_digit_clicked"/>
+                    <accelerator key="f" modifiers="" signal="clicked"/>
+                    <accelerator key="e" modifiers="" signal="clicked"/>
+                    <accelerator key="d" modifiers="" signal="clicked"/>
+                    <accelerator key="3" modifiers="" signal="clicked"/>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkButton" id="digit4">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">4 GHI</property>
+                    <property name="focus_on_click">False</property>
+                    <property name="response_id">0</property>
+                    <signal name="clicked" handler="on_digit_clicked"/>
+                    <accelerator key="i" modifiers="" signal="clicked"/>
+                    <accelerator key="h" modifiers="" signal="clicked"/>
+                    <accelerator key="g" modifiers="" signal="clicked"/>
+                    <accelerator key="4" modifiers="" signal="clicked"/>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkButton" id="digit5">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">5 JKL</property>
+                    <property name="focus_on_click">False</property>
+                    <property name="response_id">0</property>
+                    <signal name="clicked" handler="on_digit_clicked"/>
+                    <accelerator key="l" modifiers="" signal="clicked"/>
+                    <accelerator key="k" modifiers="" signal="clicked"/>
+                    <accelerator key="j" modifiers="" signal="clicked"/>
+                    <accelerator key="5" modifiers="" signal="clicked"/>
+                  </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>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkButton" id="digit6">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">6 MNO</property>
+                    <property name="focus_on_click">False</property>
+                    <property name="response_id">0</property>
+                    <signal name="clicked" handler="on_digit_clicked"/>
+                    <accelerator key="o" modifiers="" signal="clicked"/>
+                    <accelerator key="n" modifiers="" signal="clicked"/>
+                    <accelerator key="m" modifiers="" signal="clicked"/>
+                    <accelerator key="6" modifiers="" signal="clicked"/>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkButton" id="digit7">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">7 PQRS</property>
+                    <property name="focus_on_click">False</property>
+                    <property name="response_id">0</property>
+                    <signal name="clicked" handler="on_digit_clicked"/>
+                    <accelerator key="s" modifiers="" signal="clicked"/>
+                    <accelerator key="r" modifiers="" signal="clicked"/>
+                    <accelerator key="q" modifiers="" signal="clicked"/>
+                    <accelerator key="p" modifiers="" signal="clicked"/>
+                    <accelerator key="7" modifiers="" signal="clicked"/>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkButton" id="digit8">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">8 TUV</property>
+                    <property name="focus_on_click">False</property>
+                    <property name="response_id">0</property>
+                    <signal name="clicked" handler="on_digit_clicked"/>
+                    <accelerator key="v" modifiers="" signal="clicked"/>
+                    <accelerator key="u" modifiers="" signal="clicked"/>
+                    <accelerator key="t" modifiers="" signal="clicked"/>
+                    <accelerator key="8" modifiers="" signal="clicked"/>
+                  </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>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkButton" id="digit9">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">9 WXYZ</property>
+                    <property name="focus_on_click">False</property>
+                    <property name="response_id">0</property>
+                    <signal name="clicked" handler="on_digit_clicked"/>
+                    <accelerator key="z" modifiers="" signal="clicked"/>
+                    <accelerator key="y" modifiers="" signal="clicked"/>
+                    <accelerator key="x" modifiers="" signal="clicked"/>
+                    <accelerator key="w" modifiers="" signal="clicked"/>
+                    <accelerator key="9" modifiers="" signal="clicked"/>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">2</property>
+                    <property name="bottom_attach">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkButton" id="back">
+                    <property name="visible">True</property>
+                    <property name="focus_on_click">False</property>
+                    <property name="response_id">0</property>
+                    <signal name="clicked" handler="on_back_clicked"/>
+                    <accelerator key="BackSpace" modifiers="" signal="clicked"/>
+                    <child>
+                      <widget class="GtkHBox" id="hbox2">
+                        <property name="visible">True</property>
+                        <child>
+                          <widget class="GtkImage" id="image2">
+                            <property name="visible">True</property>
+                            <property name="xalign">1</property>
+                            <property name="stock">gtk-no</property>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkLabel" id="label9">
+                            <property name="visible">True</property>
+                            <property name="xalign">0</property>
+                            <property name="xpad">5</property>
+                            <property name="label" translatable="yes">Back</property>
+                          </widget>
+                          <packing>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </widget>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="top_attach">3</property>
+                    <property name="bottom_attach">4</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkButton" id="digit0">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">0</property>
+                    <property name="focus_on_click">False</property>
+                    <property name="response_id">0</property>
+                    <signal name="clicked" handler="on_digit_clicked"/>
+                    <accelerator key="0" modifiers="" signal="clicked"/>
+                  </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>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkButton" id="dial">
+                    <property name="visible">True</property>
+                    <property name="has_default">True</property>
+                    <property name="focus_on_click">False</property>
+                    <property name="response_id">0</property>
+                    <signal name="clicked" handler="on_dial_clicked"/>
+                    <accelerator key="Return" modifiers="" signal="clicked"/>
+                    <child>
+                      <widget class="GtkHBox" id="hbox1">
+                        <property name="visible">True</property>
+                        <child>
+                          <widget class="GtkImage" id="image1">
+                            <property name="visible">True</property>
+                            <property name="xalign">1</property>
+                            <property name="stock">gtk-yes</property>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkLabel" id="label8">
+                            <property name="visible">True</property>
+                            <property name="xalign">0</property>
+                            <property name="xpad">5</property>
+                            <property name="label" translatable="yes">Dial</property>
+                          </widget>
+                          <packing>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </widget>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">3</property>
+                    <property name="bottom_attach">4</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="tab_expand">True</property>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkLabel" id="keypad">
+            <property name="height_request">30</property>
+            <property name="visible">True</property>
+            <property name="label" translatable="yes">Keypad</property>
+          </widget>
+          <packing>
+            <property name="type">tab</property>
+            <property name="tab_expand">True</property>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkScrolledWindow" id="scrolledwindow1">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+            <child>
+              <widget class="GtkTreeView" id="recentview">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="headers_visible">False</property>
+                <property name="enable_search">False</property>
+                <property name="fixed_height_mode">True</property>
+                <property name="enable_grid_lines">GTK_TREE_VIEW_GRID_LINES_HORIZONTAL</property>
+                <property name="enable_tree_lines">True</property>
+                <signal name="row_activated" handler="on_recentview_row_activated"/>
+              </widget>
+            </child>
+          </widget>
+          <packing>
+            <property name="position">1</property>
+            <property name="tab_expand">True</property>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkLabel" id="recent">
+            <property name="height_request">30</property>
+            <property name="visible">True</property>
+            <property name="label" translatable="yes">Recent</property>
+          </widget>
+          <packing>
+            <property name="type">tab</property>
+            <property name="position">1</property>
+            <property name="tab_expand">True</property>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkTable" id="accountview">
+            <property name="visible">True</property>
+            <property name="border_width">11</property>
+            <property name="n_rows">3</property>
+            <property name="n_columns">2</property>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <widget class="GtkComboBoxEntry" id="callbackcombo">
+                <property name="visible">True</property>
+                <child internal-child="entry">
+                  <widget class="GtkEntry" id="callbackentry">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <signal name="changed" handler="on_callbackentry_changed"/>
+                  </widget>
+                </child>
+              </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="x_options">GTK_FILL</property>
+                <property name="y_options"></property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label3">
+                <property name="visible">True</property>
+                <property name="xalign">1</property>
+                <property name="xpad">5</property>
+                <property name="label" translatable="yes">Callback Number:</property>
+              </widget>
+              <packing>
+                <property name="top_attach">2</property>
+                <property name="bottom_attach">3</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkButton" id="clearcookies">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="label" translatable="yes">Clear Account Information
+must reauthenticate</property>
+                <property name="response_id">0</property>
+                <signal name="clicked" handler="on_clearcookies_clicked"/>
+              </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"></property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="gcnumberlabel">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">&lt;span size="15000" weight="bold"&gt;(518) 555-1212&lt;/span&gt;</property>
+                <property name="use_markup">True</property>
+              </widget>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="y_options"></property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label1">
+                <property name="visible">True</property>
+                <property name="xalign">1</property>
+                <property name="xpad">5</property>
+                <property name="label" translatable="yes">GrandCentral
+Number:</property>
+                <property name="justify">GTK_JUSTIFY_RIGHT</property>
+              </widget>
+            </child>
+          </widget>
+          <packing>
+            <property name="position">2</property>
+            <property name="tab_expand">True</property>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkLabel" id="account">
+            <property name="height_request">30</property>
+            <property name="visible">True</property>
+            <property name="label" translatable="yes">Account</property>
+          </widget>
+          <packing>
+            <property name="type">tab</property>
+            <property name="position">2</property>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkVBox" id="vbox1">
+            <property name="visible">True</property>
+            <child>
+              <widget class="GtkLabel" id="label2">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">&lt;span size="20000" weight="bold"&gt;GrandCentral Dialer&lt;/span&gt;
+Copyright 2008</property>
+                <property name="use_markup">True</property>
+                <property name="justify">GTK_JUSTIFY_CENTER</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label4">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">GUI front-end to initiate outbound call from Grandcentral.com, typically with Grancentral configured to connect the outbound call to a VOIP number accessible via Gizmo on the Internet Tablet.</property>
+                <property name="wrap">True</property>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label7">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Authors: Mark Bergman &lt;bergman@merctech.com&gt;, Eric Warnke &lt;ericew@gmail.com&gt;</property>
+                <property name="wrap">True</property>
+              </widget>
+              <packing>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="position">3</property>
+            <property name="tab_expand">True</property>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkLabel" id="about">
+            <property name="height_request">30</property>
+            <property name="visible">True</property>
+            <property name="label" translatable="yes">About</property>
+          </widget>
+          <packing>
+            <property name="type">tab</property>
+            <property name="position">3</property>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+  <widget class="GtkDialog" id="login_dialog">
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">Grandcentral Login</property>
+    <property name="resizable">False</property>
+    <property name="modal">True</property>
+    <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+    <property name="destroy_with_parent">True</property>
+    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+    <property name="skip_taskbar_hint">True</property>
+    <property name="skip_pager_hint">True</property>
+    <property name="deletable">False</property>
+    <property name="transient_for">Dialpad</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox1">
+        <property name="visible">True</property>
+        <property name="spacing">2</property>
+        <child>
+          <widget class="GtkTable" id="table1">
+            <property name="visible">True</property>
+            <property name="n_rows">2</property>
+            <property name="n_columns">2</property>
+            <child>
+              <widget class="GtkLabel" id="label5">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Username</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label6">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Password</property>
+              </widget>
+              <packing>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkEntry" id="usernameentry">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+              </widget>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkEntry" id="passwordentry">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="visibility">False</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>
+              </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">GTK_BUTTONBOX_END</property>
+            <child>
+              <widget class="GtkButton" id="loginbutton">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="label" translatable="yes">Login</property>
+                <property name="response_id">0</property>
+                <signal name="clicked" handler="on_loginbutton_clicked"/>
+              </widget>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">GTK_PACK_END</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+</glade-interface>
diff --git a/gc_dialer/gcbackend.py b/gc_dialer/gcbackend.py
new file mode 100644 (file)
index 0000000..18c82fc
--- /dev/null
@@ -0,0 +1,181 @@
+#!/usr/bin/python 
+
+# Grandcentral Dialer backend code
+# Eric Warnke <ericew@gmail.com>
+# Copyright 2008 GPLv2
+
+
+import os
+import re
+import urllib
+
+from browser_emu import MozillaEmulator
+
+class GCDialer:
+       # This class encapsulates all of the knowledge necessary to interace with the grandcentral servers
+       # the functions include login, setting up a callback number, and initalting a callback
+
+       _wgetOKstrRe    = re.compile("This may take a few seconds", re.M)       # string from Grandcentral.com on successful dial 
+       _validateRe     = re.compile("^[0-9]{10,}$")
+       _accessTokenRe  = re.compile(r"""<input type="hidden" name="a_t" [^>]*value="(.*)"/>""")
+       _isLoginPageRe  = re.compile(r"""<form method="post" action="https://www.grandcentral.com/mobile/account/login">""")
+       _callbackRe     = re.compile(r"""name="default_number" value="(\d+)" />\s+(.*)\s$""", re.M)
+       _accountNumRe   = re.compile(r"""<img src="/images/mobile/inbox_logo.gif" alt="GrandCentral" />\s*(.{14})\s*&nbsp""", re.M)
+       _inboxRe        = re.compile(r"""<td>.*?(voicemail|received|missed|call return).*?</td>\s+<td>\s+<font size="2">\s+(.*?)\s+&nbsp;\|&nbsp;\s+<a href="/mobile/contacts/.*?">(.*?)\s?</a>\s+<br/>\s+(.*?)\s?<a href=""", re.S)
+
+       _forwardselectURL       = "http://www.grandcentral.com/mobile/settings/forwarding_select"
+       _loginURL               = "https://www.grandcentral.com/mobile/account/login"
+       _setforwardURL          = "http://www.grandcentral.com/mobile/settings/set_forwarding?from=settings"
+       _clicktocallURL         = "http://www.grandcentral.com/mobile/calls/click_to_call?a_t=%s&destno=%s"
+       _inboxallURL            = "http://www.grandcentral.com/mobile/messages/inbox?types=all"
+
+       def __init__(self, cookieFile = None):
+               # Important items in this function are the setup of the browser emulation and cookie file
+
+               self._msg = ""
+               if cookieFile == None:
+                       cookieFile = os.path.join(os.path.expanduser("~"),".gc_dialer_cookies.txt")
+               self._browser = MozillaEmulator(None,0)
+               self._browser.cookies.filename = cookieFile
+               if os.path.isfile(cookieFile):
+                       self._browser.cookies.load()
+               #else:
+               #       self._browser.cookies.save()
+               self._lastData = ""
+               self._accessToken = None
+               self._accountNum = None
+
+
+       def grabToken(self, data):
+               # Pull the magic cookie from the datastream
+
+               atGroup = GCDialer._accessTokenRe.search(data)
+               try:
+                       self._accessToken = atGroup.group(1)
+               except:
+                       pass
+
+               anGroup = GCDialer._accountNumRe.search(data)
+               try:
+                       self._accountNum = anGroup.group(1)
+               except:
+                       pass
+               
+
+       def getAccountNumber(self):
+               return self._accountNum
+       
+       def isAuthed(self):
+               # Attempts to detect a current session and pull the
+               # auth token ( a_t ) from the page
+
+               self._lastData = self._browser.download(GCDialer._forwardselectURL)
+               self._browser.cookies.save()
+               if GCDialer._isLoginPageRe.search(self._lastData) is None:
+                       self.grabToken(self._lastData)
+                       return True
+               return False
+
+       def login(self, username, password):
+               # Attempt to login to grandcentral
+
+               if self.isAuthed():
+                       return
+
+               loginPostData = urllib.urlencode( {'username' : username , 'password' : password } )
+               self._lastData = self._browser.download(GCDialer._loginURL, loginPostData)
+               return self.isAuthed()
+       
+       def setSaneCallback(self):
+               # Try to set a sane default callback number on these preferences
+               # 1) 1747 numbers ( Gizmo )
+               # 2) anything with gizmo in the name
+               # 3) anything with compueter in the name
+               # 4) the first value
+
+               numbers = self.getCallbackNumbers()
+
+               for k,v in numbers.iteritems():
+                       if not re.compile(r"""1747""").match(k) is None:
+                               self.setCallbackNumber(k)
+                               return
+
+               for k,v in numbers.iteritems():
+                       if not re.compile(r"""gizmo""",re.I).search(v) is None:
+                               self.setCallbackNumber(k)
+                               return
+
+               for k,v in numbers.iteritems():
+                       if not re.compile(r"""computer""",re.I).search(v) is None:
+                               self.setCallbackNumber(k)
+                               return
+
+               for k,v in numbers.iteritems():
+                       self.setCallbackNumber(k)
+                       return
+               
+       def getCallbackNumbers(self):
+
+               retval = {}
+                               
+               self._lastData = self._browser.download(GCDialer._forwardselectURL)
+               for match in GCDialer._callbackRe.finditer(self._lastData):
+                       retval[match.group(1)] = match.group(2)
+
+               return retval                                   
+
+
+       def setCallbackNumber(self, callbacknumber):
+               # set the number that grandcental calls
+               # this should be a proper 10 digit number
+
+               callbackPostData = urllib.urlencode({'a_t' : self._accessToken, 'default_number' : callbacknumber })
+               self._lastData = self._browser.download(GCDialer._setforwardURL, callbackPostData)
+               self._browser.cookies.save()
+
+       def getCallbackNumber(self):
+               for c in self._browser.cookies:
+                       if c.name == "pda_forwarding_number":
+                               return c.value
+               return None
+
+       def reset(self):
+               self._browser.cookies.clear()
+               self._browser.cookies.save()
+
+       def validate(self,number):
+               # Can this number be called ( syntax validation only )
+
+               return GCDialer._validateRe.match(number) != None
+
+       def dial(self,number):
+               # This is the main function responsible for initating the callback
+
+               # If the number is not valid throw exception
+               if self.validate(number) == False:
+                       raise ValueError, 'number is not valid'
+
+               # No point if we don't have the magic cookie
+               if not self.isAuthed:
+                       return False
+
+               # Strip leading 1 from 11 digit dialing
+               if len(number) == 11 and number[0] == 1:
+                       number = number[1:] 
+
+               self._lastData = self._browser.download(
+                       GCDialer._clicktocallURL % (self._accessToken, number), 
+                       None, {'Referer' : 'http://www.grandcentral.com/mobile/messages'} )
+
+               if GCDialer._wgetOKstrRe.search(self._lastData) != None:
+                       return True
+               else:
+                       return False
+
+       def get_recent(self):
+               retval = []
+               self._lastData = self._browser.download(GCDialer._inboxallURL)
+               for match in self._inboxRe.finditer(self._lastData):
+                       #print "type: %s date: %s person: %s number: %s" % (match.group(1), match.group(2), match.group(3), match.group(4))
+                       retval.append([match.group(4), "%s on %s from/to %s - %s" % (match.group(1).capitalize(), match.group(2), match.group(3), match.group(4))])
+               return retval