From 7e080345fe90941ba88d50eba32ff78c77896ba0 Mon Sep 17 00:00:00 2001 From: epage Date: Thu, 2 Oct 2008 23:46:15 +0000 Subject: [PATCH] Clearing out the old version to bring in the new git-svn-id: file:///svnroot/gc-dialer/trunk@157 c39d3808-3fe2-4d86-a59f-b7f623ee9f21 --- LICENSE | 504 ------------------- Makefile | 148 ------ README | 37 -- TODO | 30 -- src/browser_emu.py | 152 ------ src/evo_backend.py | 117 ----- src/gc_backend.py | 315 ------------ src/gc_dialer.desktop | 7 - src/gc_dialer.glade | 893 --------------------------------- src/gc_dialer.py | 784 ----------------------------- src/gc_dialer_256.png | Bin 32182 -> 0 bytes src/gc_dialer_26.png | Bin 1671 -> 0 bytes src/gc_dialer_64.png | Bin 6411 -> 0 bytes support/DEBIAN/control | 50 -- support/DEBIAN/postinst | 3 - support/GrandcentralDialer.pypackager | 25 - support/pylint.rc | 305 ----------- 17 files changed, 3370 deletions(-) delete mode 100644 LICENSE delete mode 100644 Makefile delete mode 100644 README delete mode 100644 TODO delete mode 100644 src/__init__.py delete mode 100644 src/browser_emu.py delete mode 100644 src/evo_backend.py delete mode 100644 src/gc_backend.py delete mode 100644 src/gc_dialer.desktop delete mode 100644 src/gc_dialer.glade delete mode 100755 src/gc_dialer.py delete mode 100644 src/gc_dialer_256.png delete mode 100644 src/gc_dialer_26.png delete mode 100644 src/gc_dialer_64.png delete mode 100644 support/DEBIAN/control delete mode 100755 support/DEBIAN/postinst delete mode 100644 support/GrandcentralDialer.pypackager delete mode 100644 support/pylint.rc diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 5ab7695..0000000 --- a/LICENSE +++ /dev/null @@ -1,504 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - - diff --git a/Makefile b/Makefile deleted file mode 100644 index 9933c6a..0000000 --- a/Makefile +++ /dev/null @@ -1,148 +0,0 @@ -PROJECT_NAME=gc_dialer -PROJECT_VERSION=0.8.0 -SOURCE_PATH=src -SOURCE=$(SOURCE_PATH)/gc_dialer.py $(SOURCE_PATH)/evo_backend.py $(SOURCE_PATH)/gc_backend.py $(SOURCE_PATH)/browser_emu.py -OBJ=$(SOURCE:.py=.pyc) -LINT_STATS_PATH=~/.pylint.d -LINT_STATS=$(foreach file, $(addsuffix 1.stats,$(subst /,.,$(basename $(SOURCE)))), $(LINT_STATS_PATH)/$(file) ) -TEST_PATH=./tests -TAG_FILE=~/.ctags/$(PROJECT_NAME).tags -PYPACKAGE_FILE=./support/GrandcentralDialer.pypackager -DEB_METADATA=./support/DEBIAN -SDK_DISPLAY=:2 - -PY_FAST_LAUNCH=1 -PLATFORM=desktop -ifeq ($(PLATFORM),os2007) - LEGACY_GLADE=1 -else - LEGACY_GLADE=0 -endif -PRE_PACKAGE_PATH=./pkg-$(PLATFORM) -PACKAGE_PATH=./deb-$(PLATFORM) -BUILD_PATH=./build-$(PLATFORM) -BUILD_BIN=$(BUILD_PATH)/gc_dialer.py -DEB_PACKAGE=$(PACKAGE_PATH)/$(PROJECT_NAME)-$(PROJECT_VERSION)_$(PLATFORM).deb - -DEBUGGER=winpdb -UNIT_TEST=nosetests -w $(TEST_PATH) -STYLE_TEST=../../Python/tools/pep8.py --ignore=W191 -LINT_RC=./support/pylint.rc -LINT=pylint --rcfile=$(LINT_RC) -COVERAGE_TEST=figleaf -PROFILER=pyprofiler -CTAGS=ctags-exuberant - -.PHONY: all run debug test lint tags build package clean - -all: test package - -run: $(SOURCE) - cd $(SOURCE_PATH) ; ./gc_dialer.py - -debug: $(SOURCE) - cd $(SOURCE_PATH) ; $(DEBUGGER) ./gc_dialer.py - -test: $(SOURCE) - cd $(SOURCE_PATH) ; ./gc_dialer.py -t - -xephyr: - Xephyr $(SDK_DISPLAY) -host-cursor -screen 800x480x16 -dpi 96 -ac - -sdk_start: - export DISPLAY=$(SDK_DISPLAY) - af-sb-ini.sh start - -sdk_stop: - af-sb-ini.sh stop - -lint: $(LINT_STATS) - -tags: $(TAG_FILE) - -build: $(BUILD_PATH) - -package: $(DEB_PACKAGE) - -$(BUILD_PATH): $(BUILD_BIN) - mkdir -p $(BUILD_PATH) - - cp $(SOURCE_PATH)/gc_dialer_256.png $(BUILD_PATH) - cp $(SOURCE_PATH)/gc_dialer_64.png $(BUILD_PATH) - cp $(SOURCE_PATH)/gc_dialer_26.png $(BUILD_PATH) - - cp $(SOURCE_PATH)/gc_dialer.desktop $(BUILD_PATH) - - cp $(SOURCE_PATH)/gc_dialer.glade $(BUILD_PATH) -ifneq ($(PLATFORM),desktop) - sed -i 's/^[ \t]*//;s/GtkWindow/HildonWindow/' $(BUILD_PATH)/gc_dialer.glade -endif - -$(PRE_PACKAGE_PATH): $(BUILD_PATH) - mkdir -p $(PRE_PACKAGE_PATH)/build/usr/share/icons/hicolor/scalable/hildon - mkdir -p $(PRE_PACKAGE_PATH)/build/usr/share/icons/hicolor/26x26/hildon - mkdir -p $(PRE_PACKAGE_PATH)/build/usr/share/icons/hicolor/64x64/hildon - mkdir -p $(PRE_PACKAGE_PATH)/build/usr/share/applications/hildon - mkdir -p $(PRE_PACKAGE_PATH)/build/usr/local/lib - mkdir -p $(PRE_PACKAGE_PATH)/build/usr/local/bin - - cp $(BUILD_PATH)/gc_dialer_256.png $(PRE_PACKAGE_PATH)/build/usr/share/icons/hicolor/scalable/hildon/gc_dialer.png - cp $(BUILD_PATH)/gc_dialer_64.png $(PRE_PACKAGE_PATH)/build/usr/share/icons/hicolor/64x64/hildon/gc_dialer.png - cp $(BUILD_PATH)/gc_dialer_26.png $(PRE_PACKAGE_PATH)/build/usr/share/icons/hicolor/26x26/hildon/gc_dialer.png - - cp $(BUILD_PATH)/gc_dialer.desktop $(PRE_PACKAGE_PATH)/build/usr/share/applications/hildon - - cp $(BUILD_PATH)/gc_dialer.glade $(PRE_PACKAGE_PATH)/build/usr/local/lib - - cp $(BUILD_BIN) $(PRE_PACKAGE_PATH)/build/usr/local/bin - - cp $(PYPACKAGE_FILE) $(PRE_PACKAGE_PATH) - cp -R $(DEB_METADATA) $(PRE_PACKAGE_PATH)/build/ -ifeq ($(PLATFORM),desktop) - #sed -i 's/, python2.5-hildon//' $(PRE_PACKAGE_PATH)/build/DEBIAN/control -endif - sed -i 's/Version: 0.0.0/Version: $(PROJECT_VERSION)/' $(PRE_PACKAGE_PATH)/build/DEBIAN/control - #Autoguess install size - #du -hs pkg-desktop/build/ | sed 's/K *.*//' - sed -i 's/Installed-Size: 0/Installed-Size: 196/' $(PRE_PACKAGE_PATH)/build/DEBIAN/control - cp LICENSE $(PRE_PACKAGE_PATH)/build/DEBIAN/copyright - -$(DEB_PACKAGE): $(PRE_PACKAGE_PATH) - mkdir -p $(PACKAGE_PATH) - dpkg-deb -b $(PRE_PACKAGE_PATH)/build/ $(DEB_PACKAGE) - -clean: - rm -Rf $(PRE_PACKAGE_PATH) $(PACKAGE_PATH) $(BUILD_PATH) - rm -Rf $(DEB_PACKAGE) - rm -Rf $(OBJ) - rm -Rf $(LINT_STATS_PATH)/* - -$(BUILD_BIN): $(SOURCE) - mkdir -p $(dir $(BUILD_BIN)) - - #Construct the program by cat-ing all the python files together -ifeq ($(PY_FAST_LAUNCH),1) - echo "#!/usr/bin/python" > $(BUILD_BIN) -else - echo "#!/usr/bin/python2.5" > $(BUILD_BIN) -endif - #echo "from __future__ import with_statement" >> $(PRE_PACKAGE_PATH)/usr/local/bin/gc_dialer.py - cat $(SOURCE_PATH)/gc_dialer.py $(SOURCE_PATH)/evo_backend.py $(SOURCE_PATH)/gc_backend.py $(SOURCE_PATH)/browser_emu.py | grep -e '^import ' | sort -u >> $(BUILD_BIN) - cat $(SOURCE_PATH)/browser_emu.py $(SOURCE_PATH)/evo_backend.py $(SOURCE_PATH)/gc_backend.py $(SOURCE_PATH)/gc_dialer.py | grep -v 'browser_emu' | grep -v 'gc_backend' | grep -v "evo_backend"| grep -v "#!" >> $(BUILD_BIN) - chmod 755 $(BUILD_BIN) - -$(TAG_FILE): $(SOURCE) - mkdir -p $(dir $(TAG_FILE)) - $(CTAGS) -o $(TAG_FILE) $(SOURCE) - -%1.stats: $(SOURCE) $(LINT_RC) - @ #DESIRED DEPENDENCY: $(subst .,/,$(notdir $*)).py - @ #DESIRED COMMAND: $(LINT) $< - @ $(LINT) $(subst .,/,$(notdir $*)).py - @# echo $* - @# echo $? - -#Makefile Debugging -#Target to print any variable, can be added to the dependencies of any other target -#Userfule flags for make, -d, -p, -n -print-%: ; @$(error $* is $($*) ($(value $*)) (from $(origin $*))) diff --git a/README b/README deleted file mode 100644 index b87ce9c..0000000 --- a/README +++ /dev/null @@ -1,37 +0,0 @@ -Building a package -=================== -Run - make PLATFORM=... package -which will create a "./pkg-.../..." heirarchy. Move this structure to somewhere on the tablet, then run pypackager. - -Supported PLATFORMs include - desktop - os2007 - os2008 - -SDK Enviroment -=================== - -Native - -Follow install instructions - Ubuntu: http://www.linuxuk.org/node/38 -Install Nokia stuff (for each target) - fakeroot apt-get install maemo-explicit - -Userful commands -Login - /scratchbox/login -Change targets - sb-conf select DIABLO_ARMEL - sb-conf select DIABLO_X86 -Fixing it - fakeroot apt-get -f install - -Starting scratchbox - Xephyr :2 -host-cursor -screen 800x480x16 -dpi 96 -ac -extension Composite - scratchbox - export DISPLAY=:2 - af-sb-init.sh start -Then running a command in the "Maemo" terminal will launch it in the Xephyr session - Tip: run with "run-standalone.sh" for niceness? diff --git a/TODO b/TODO deleted file mode 100644 index 711515c..0000000 --- a/TODO +++ /dev/null @@ -1,30 +0,0 @@ -Ideas -================= -User Contacts - It seems the evolution contact API used is specific to the desktop. evolution.ebook combined with abook is what is needed for Maemo. - http://maemo.org/maemo_release_documentation/maemo4.1.x/node8.html#SECTION00870000000000000000 - https://garage.maemo.org/svn/pymaemo/packages/python-abook/trunk/tests/ especially contact_get_iter amd filter_model - http://pymaemo.garage.maemo.org/documentation/api/abook/index.html - - Other possible addressbooks - GMail http://libgmail.sourceforge.net/ - GPE - -Internet Connection - Look into being a bit more advanced, beyond just enabling/disabling the GUI - Possible Approach: - Defer login - While not logged in or device is offline, disable the GUI - Don't attempt to login if not online - -Keep callbacks to a minimum amount of blocking I/O - -Re-examine all use of add_idle - I dont think its a thread but idle processing in mainloop, so it could block for long execution - -Notes -================= -General Python/Maemo stuff - http://pymaemo.garage.maemo.org/documentation.html -DBus - http://dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html diff --git a/src/__init__.py b/src/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/browser_emu.py b/src/browser_emu.py deleted file mode 100644 index 3083a1c..0000000 --- a/src/browser_emu.py +++ /dev/null @@ -1,152 +0,0 @@ -""" -@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 -""" - -import urllib2 -import cookielib -import warnings - - -class MozillaEmulator(object): - - def __init__(self, cacher=None, 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.""" - if cacher is None: - cacher = {} - self.cacher = cacher - self.cookies = cookielib.LWPCookieJar() - self.debug = False - self.trycount = trycount - - def build_opener(self, url, postdata=None, extraheaders=None, forbid_redirect=False): - if extraheaders is None: - extraheaders = {} - - 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-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7', - } - 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=None, 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. - """ - warnings.warn("Performing download of %s" % url, UserWarning, 2) - - if extraheaders is None: - extraheaders = {} - 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 - return openerdirector.read() - except urllib2.URLError: - cnt += 1 - if (trycount > -1) and (trycount < cnt): - raise - # Retry :-) - if self.debug: - print "MozillaEmulator: urllib2.URLError, retryting ", cnt - - -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 diff --git a/src/evo_backend.py b/src/evo_backend.py deleted file mode 100644 index c094c2e..0000000 --- a/src/evo_backend.py +++ /dev/null @@ -1,117 +0,0 @@ -#!/usr/bin/python - -# GC Dialer - Front end for Google's Grand Central service. -# Copyright (C) 2008 Eric Warnke ericew AT gmail DOT com -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - - -""" -Evolution Contact Support -""" - - -try: - import evolution -except ImportError: - evolution = None - - -class EvolutionAddressBook(object): - """ - @note Combined the factory and the addressbook for "simplicity" and "cutting down" the number of allocations/deallocations - """ - - def __init__(self, bookId = None): - if not self.is_supported(): - return - - self._phoneTypes = None - self._bookId = bookId if bookId is not None else self.get_addressbooks().next()[1] - self._book = evolution.ebook.open_addressbook(self._bookId) - - @classmethod - def is_supported(cls): - return evolution is not None - - def get_addressbooks(self): - """ - @returns Iterable of (Address Book Factory, Book Id, Book Name) - """ - if not self.is_supported(): - return - - if len(evolution.ebook.list_addressbooks()) == 0 and evolution.ebook.open_addressbook('default') is not None: - # It appears that Maemo's e-d-s does not always list the default addressbook, so we're faking it being listed - yield self, "default", "Maemo" - - for bookId in evolution.ebook.list_addressbooks(): - yield self, bookId[1], bookId[0] - - def open_addressbook(self, bookId): - self._bookId = bookId - self._book = evolution.ebook.open_addressbook(self._bookId) - return self - - @staticmethod - def factory_short_name(): - return "Evo" - - @staticmethod - def factory_name(): - return "Evolution" - - def get_contacts(self): - """ - @returns Iterable of (contact id, contact name) - """ - if not self.is_supported(): - return - - for contact in self._book.get_all_contacts(): - yield contact.get_uid(), contact.props.full_name - - def get_contact_details(self, contactId): - """ - @returns Iterable of (Phone Type, Phone Number) - """ - contact = self._book.get_contact(contactId) - - if self._phoneTypes is None and contact is not None: - self._phoneTypes = [pt for pt in dir(contact.props) if "phone" in pt.lower()] - - for phoneType in self._phoneTypes: - phoneNumber = getattr(contact.props, phoneType) - if isinstance(phoneNumber, str): - yield phoneType, phoneNumber - -def print_addressbooks(): - """ - Included here for debugging. - - Either insert it into the code or launch python with the "-i" flag - """ - if not EvolutionAddressBook.is_supported(): - print "No Evolution Support" - return - - eab = EvolutionAddressBook() - for book in eab.get_addressbooks(): - eab = eab.open_addressbook(book[1]) - print book - for contact in eab.get_contacts(): - print "\t", contact - for details in eab.get_contact_details(contact[0]): - print "\t\t", details diff --git a/src/gc_backend.py b/src/gc_backend.py deleted file mode 100644 index d1641de..0000000 --- a/src/gc_backend.py +++ /dev/null @@ -1,315 +0,0 @@ -#!/usr/bin/python - -# GC Dialer - Front end for Google's Grand Central service. -# Copyright (C) 2008 Eric Warnke ericew AT gmail DOT com -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -""" -Grandcentral Dialer backend code -""" - - -import os -import re -import urllib -import urllib2 -import time -import warnings - -from browser_emu import MozillaEmulator - - -class GCDialer(object): - """ - 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 - """ - - _gcDialingStrRe = re.compile("This may take a few seconds", re.M) - _accessTokenRe = re.compile(r"""]*value="(.*)"/>""") - _isLoginPageRe = re.compile(r"""
""") - _callbackRe = re.compile(r"""name="default_number" value="(\d+)" />\s+(.*)\s$""", re.M) - _accountNumRe = re.compile(r"""GrandCentral\s*(.{14})\s* """, re.M) - _inboxRe = re.compile(r""".*?(voicemail|received|missed|call return).*?\s+\s+\s+(.*?)\s+ \| \s+(.*?)\s?\s+
\s+(.*?)\s?(.*?)""", re.S) - _contactsNextRe = re.compile(r""".*Next""", re.S) - _contactDetailGroupRe = re.compile(r"""Group:\s*(\w*)""", re.S) - _contactDetailPhoneRe = re.compile(r"""(\w+):[0-9\-\(\) \t]*?call""", re.S) - - _validateRe = re.compile("^[0-9]{10,}$") - - _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" - _contactsURL = "http://www.grandcentral.com/mobile/contacts" - _contactDetailURL = "http://www.grandcentral.com/mobile/contacts/detail" - - def __init__(self, cookieFile = None): - # Important items in this function are the setup of the browser emulation and cookie file - self._msg = "" - - self._browser = MozillaEmulator(None, 0) - if cookieFile is None: - cookieFile = os.path.join(os.path.expanduser("~"), ".gc_dialer_cookies.txt") - self._browser.cookies.filename = cookieFile - if os.path.isfile(cookieFile): - self._browser.cookies.load() - - self._accessToken = None - self._accountNum = None - self._callbackNumbers = {} - self._lastAuthed = 0.0 - - def is_authed(self, force = False): - """ - Attempts to detect a current session and pull the auth token ( a_t ) from the page. - @note Once logged in try not to reauth more than once a minute. - @returns If authenticated - """ - - if (time.time() - self._lastAuthed) < 60 and not force: - return True - - try: - forwardSelectionPage = self._browser.download(GCDialer._forwardselectURL) - except urllib2.URLError, e: - warnings.warn("%s is not accesible" % GCDialer._forwardselectURL, UserWarning, 2) - return False - - self._browser.cookies.save() - if GCDialer._isLoginPageRe.search(forwardSelectionPage) is None: - self._grab_token(forwardSelectionPage) - self._lastAuthed = time.time() - return True - - return False - - def login(self, username, password): - """ - Attempt to login to grandcentral - @returns Whether login was successful or not - """ - if self.is_authed(): - return True - - loginPostData = urllib.urlencode( {'username' : username , 'password' : password } ) - - try: - loginSuccessOrFailurePage = self._browser.download(GCDialer._loginURL, loginPostData) - except urllib2.URLError, e: - warnings.warn("%s is not accesible" % GCDialer._loginURL, UserWarning, 2) - return False - - return self.is_authed() - - def logout(self): - self._lastAuthed = 0.0 - self._browser.cookies.clear() - self._browser.cookies.save() - - def dial(self, number): - """ - This is the main function responsible for initating the callback - """ - self._msg = "" - - # If the number is not valid throw exception - if not self.is_valid_syntax(number): - raise ValueError('number is not valid') - - # No point if we don't have the magic cookie - if not self.is_authed(): - self._msg = "Not authenticated" - return False - - # Strip leading 1 from 11 digit dialing - if len(number) == 11 and number[0] == 1: - number = number[1:] - - try: - callSuccessPage = self._browser.download( - GCDialer._clicktocallURL % (self._accessToken, number), - None, - {'Referer' : 'http://www.grandcentral.com/mobile/messages'} - ) - except urllib2.URLError, e: - warnings.warn("%s is not accesible" % GCDialer._clicktocallURL, UserWarning, 2) - return False - - if GCDialer._gcDialingStrRe.search(callSuccessPage) is not None: - return True - else: - self._msg = "Grand Central returned an error" - return False - - self._msg = "Unknown Error" - return False - - def clear_caches(self): - pass - - def is_valid_syntax(self, number): - """ - @returns If This number be called ( syntax validation only ) - """ - return self._validateRe.match(number) is not None - - def get_account_number(self): - """ - @returns The grand central phone number - """ - return self._accountNum - - def set_sane_callback(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 computer in the name - 4) the first value - """ - numbers = self.get_callback_numbers() - - for number, description in numbers.iteritems(): - if not re.compile(r"""1747""").match(number) is None: - self.set_callback_number(number) - return - - for number, description in numbers.iteritems(): - if not re.compile(r"""gizmo""", re.I).search(description) is None: - self.set_callback_number(number) - return - - for number, description in numbers.iteritems(): - if not re.compile(r"""computer""", re.I).search(description) is None: - self.set_callback_number(number) - return - - for number, description in numbers.iteritems(): - self.set_callback_number(number) - return - - def get_callback_numbers(self): - """ - @returns a dictionary mapping call back numbers to descriptions - @note These results are cached for 30 minutes. - """ - if time.time() - self._lastAuthed < 1800 or self.is_authed(): - return self._callbackNumbers - - return {} - - def set_callback_number(self, callbacknumber): - """ - Set the number that grandcental calls - @param callbacknumber should be a proper 10 digit number - """ - callbackPostData = urllib.urlencode({ - 'a_t': self._accessToken, - 'default_number': callbacknumber - }) - try: - callbackSetPage = self._browser.download(GCDialer._setforwardURL, callbackPostData) - except urllib2.URLError, e: - warnings.warn("%s is not accesible" % GCDialer._setforwardURL, UserWarning, 2) - return False - - self._browser.cookies.save() - return True - - def get_callback_number(self): - """ - @returns Current callback number or None - """ - for c in self._browser.cookies: - if c.name == "pda_forwarding_number": - return c.value - return None - - def get_recent(self): - """ - @returns Iterable of (personsName, phoneNumber, date, action) - """ - try: - recentCallsPage = self._browser.download(GCDialer._inboxallURL) - except urllib2.URLError, e: - warnings.warn("%s is not accesible" % GCDialer._inboxallURL, UserWarning, 2) - return - - for match in self._inboxRe.finditer(recentCallsPage): - phoneNumber = match.group(4) - action = match.group(1) - date = match.group(2) - personsName = match.group(3) - yield personsName, phoneNumber, date, action - - def get_addressbooks(self): - """ - @returns Iterable of (Address Book Factory, Book Id, Book Name) - """ - yield self, "", "" - - def open_addressbook(self, bookId): - return self - - @staticmethod - def factory_short_name(): - return "GC" - - @staticmethod - def factory_name(): - return "Grand Central" - - def get_contacts(self): - """ - @returns Iterable of (contact id, contact name) - """ - contactsPagesUrls = [GCDialer._contactsURL] - for contactsPageUrl in contactsPagesUrls: - contactsPage = self._browser.download(contactsPageUrl) - for contact_match in self._contactsRe.finditer(contactsPage): - contactId = contact_match.group(1) - contactName = contact_match.group(2) - yield contactId, contactName - - next_match = self._contactsNextRe.match(contactsPage) - if next_match is not None: - newContactsPageUrl = self._contactsURL + next_match.group(1) - contactsPagesUrls.append(newContactsPageUrl) - - def get_contact_details(self, contactId): - """ - @returns Iterable of (Phone Type, Phone Number) - """ - detailPage = self._browser.download(GCDialer._contactDetailURL + '/' + contactId) - for detail_match in self._contactDetailPhoneRe.finditer(detailPage): - phoneType = detail_match.group(1) - phoneNumber = detail_match.group(2) - yield (phoneType, phoneNumber) - - def _grab_token(self, data): - "Pull the magic cookie from the datastream" - atGroup = GCDialer._accessTokenRe.search(data) - self._accessToken = atGroup.group(1) - - anGroup = GCDialer._accountNumRe.search(data) - self._accountNum = anGroup.group(1) - - self._callbackNumbers = {} - for match in GCDialer._callbackRe.finditer(data): - self._callbackNumbers[match.group(1)] = match.group(2) diff --git a/src/gc_dialer.desktop b/src/gc_dialer.desktop deleted file mode 100644 index f3ebe19..0000000 --- a/src/gc_dialer.desktop +++ /dev/null @@ -1,7 +0,0 @@ -[Desktop Entry] -Encoding=UTF-8 -Version=1.0 -Type=Application -Name=Grandcentral Dialer -Exec=run-standalone.sh /usr/local/bin/gc_dialer.py -Icon=gc_dialer diff --git a/src/gc_dialer.glade b/src/gc_dialer.glade deleted file mode 100644 index f406750..0000000 --- a/src/gc_dialer.glade +++ /dev/null @@ -1,893 +0,0 @@ - - - - - - 400 - 350 - Dialer - - - True - - - True - - - True - _File - True - - - True - - - True - _New Login - True - - - - gtk-new - - - - - - - True - - - - - True - gtk-quit - True - True - - - - - - - - - - True - _Edit - True - - - True - - - True - gtk-paste - True - True - - - - - - True - gtk-delete - True - True - - - - - - - - - - False - - - - - True - True - GTK_POS_BOTTOM - False - True - - - - True - - - 50 - True - <span size="35000" weight="bold">(518) 555-1212</span> - True - GTK_JUSTIFY_CENTER - - - False - False - - - - - True - 4 - 3 - True - - - True - False - 0 - - - - - True - - - True - 1 - gtk-yes - - - - - True - 0 - 5 - <span size="17000" weight="bold">Dial</span> - True - - - 1 - - - - - - - 2 - 3 - 3 - 4 - - - - - True - False - 0 - - - - - True - <span size="33000" weight="bold">0</span> -<span size="9000"></span> - True - GTK_JUSTIFY_CENTER - - - - - 1 - 2 - 3 - 4 - - - - - True - False - 0 - - - - - True - - - True - 1 - gtk-no - - - - - True - 0 - 5 - <span size="17000" weight="Bold">Back</span> - True - - - 1 - - - - - - - 3 - 4 - - - - - True - False - 0 - - - - - - - - - True - <span size="30000" weight="bold">9</span> -<span size="12000">WXYZ</span> - True - GTK_JUSTIFY_CENTER - - - - - 2 - 3 - 2 - 3 - - - - - True - False - 0 - - - - - - - - True - <span size="30000" weight="bold">8</span> -<span size="12000">TUV</span> - True - GTK_JUSTIFY_CENTER - - - - - 1 - 2 - 2 - 3 - - - - - True - False - 0 - - - - - - - - - True - <span size="30000" weight="bold">7</span> -<span size="12000">PQRS</span> - True - GTK_JUSTIFY_CENTER - - - - - 2 - 3 - - - - - True - False - 0 - - - - - - - - True - <span size="30000" weight="bold">6</span> -<span size="12000">MNO</span> - True - GTK_JUSTIFY_CENTER - - - - - 2 - 3 - 1 - 2 - - - - - True - False - 0 - - - - - - - - True - <span size="30000" weight="bold">5</span> -<span size="12000">JKL</span> - True - GTK_JUSTIFY_CENTER - - - - - 1 - 2 - 1 - 2 - - - - - True - False - 0 - - - - - - - - True - <span size="30000" weight="bold">4</span> -<span size="12000">GHI</span> - True - GTK_JUSTIFY_CENTER - - - - - 1 - 2 - - - - - True - False - 0 - - - - - - - - True - <span size="30000" weight="bold" stretch="ultraexpanded">3</span> -<span size="12000">DEF</span> - True - GTK_JUSTIFY_CENTER - - - - - 2 - 3 - - - - - True - False - 0 - - - - - - - - True - <span size="30000" weight="bold">2</span> -<span size="12000">ABC</span> - True - GTK_JUSTIFY_CENTER - - - - - 1 - 2 - - - - - True - False - 0 - - - - - True - <span size="33000" weight="bold">1</span> -<span size="9000"> </span> - True - - - - - - - 1 - - - - - True - False - - - - - 30 - True - Keypad - - - tab - True - False - - - - - True - 2 - 1 - - - True - - - True - True - False - - - - - - GTK_FILL - GTK_FILL - - - - - True - True - GTK_POLICY_NEVER - - - True - True - False - False - True - GTK_TREE_VIEW_GRID_LINES_HORIZONTAL - True - - - - - - 1 - 2 - - - - - 1 - True - False - - - - - True - Contacts - - - tab - 1 - True - False - - - - - True - True - GTK_POLICY_NEVER - - - True - True - False - False - True - GTK_TREE_VIEW_GRID_LINES_HORIZONTAL - True - - - - - - 1 - True - False - - - - - 30 - True - Recent - - - tab - 1 - True - False - - - - - True - 11 - 3 - 2 - - - - - - True - 1 - 5 - GrandCentral -Number: - GTK_JUSTIFY_RIGHT - - - - - True - <span size="15000" weight="bold">(518) 555-1212</span> - True - - - 1 - 2 - - - - - - True - True - True - Clear Account Information -must reauthenticate - 0 - - - - 1 - 2 - 1 - 2 - - - - - - True - 1 - 5 - Callback Number: - - - 2 - 3 - - - - - True - - - True - True - - - - - - 1 - 2 - 2 - 3 - GTK_FILL - - - - - - 2 - True - False - - - - - 30 - True - Account - - - tab - 2 - False - - - - - True - - - True - <span size="20000" weight="bold">GrandCentral Dialer</span> -Copyright 2008 - True - GTK_JUSTIFY_CENTER - - - - - True - 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. - True - - - 1 - - - - - True - Authors: Mark Bergman <bergman@merctech.com>, Eric Warnke <ericew@gmail.com> - True - - - 2 - - - - - 3 - True - False - - - - - 30 - True - About - - - tab - 3 - False - - - - - 1 - - - - - - - 5 - Grandcentral Login - False - True - GTK_WIN_POS_CENTER_ON_PARENT - True - GDK_WINDOW_TYPE_HINT_DIALOG - True - True - False - Dialpad - False - - - True - 2 - - - True - 2 - 2 - - - True - Username - - - - - True - Password - - - 1 - 2 - - - - - True - True - - - 1 - 2 - - - - - True - True - False - - - 1 - 2 - 1 - 2 - - - - - 1 - - - - - True - GTK_BUTTONBOX_END - - - True - True - True - Login - 0 - - - - - - True - True - Close - 0 - - - - 1 - - - - - False - GTK_PACK_END - - - - - - - 5 - Select Phone Type - False - True - GTK_WIN_POS_CENTER_ON_PARENT - True - GDK_WINDOW_TYPE_HINT_DIALOG - True - True - False - Dialpad - False - - - True - 2 - - - True - True - True - - - - 1 - - - - - True - GTK_BUTTONBOX_END - - - True - True - Select - 0 - - - - - - True - True - True - Cancel - 0 - - - - 1 - - - - - False - GTK_PACK_END - - - - - - diff --git a/src/gc_dialer.py b/src/gc_dialer.py deleted file mode 100755 index e4d5ced..0000000 --- a/src/gc_dialer.py +++ /dev/null @@ -1,784 +0,0 @@ -#!/usr/bin/python - -# GC Dialer - Front end for Google's Grand Central service. -# Copyright (C) 2008 Mark Bergman bergman AT merctech DOT com -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - - -""" -Grandcentral Dialer -""" - - -import sys -import gc -import os -import threading -import time -import re -import warnings - -import gobject -import gtk -import gtk.glade - -try: - import hildon -except ImportError: - hildon = None - -try: - import osso -except ImportError: - osso = None - -try: - import conic -except ImportError: - conic = None - -try: - import doctest - import optparse -except ImportError: - doctest = None - optparse = None - -from gc_backend import GCDialer -from evo_backend import EvolutionAddressBook - -import socket - - -socket.setdefaulttimeout(5) - - -def make_ugly(prettynumber): - """ - function to take a phone number and strip out all non-numeric - characters - - >>> make_ugly("+012-(345)-678-90") - '01234567890' - """ - uglynumber = re.sub('\D', '', prettynumber) - return uglynumber - - -def make_pretty(phonenumber): - """ - Function to take a phone number and return the pretty version - pretty numbers: - if phonenumber begins with 0: - ...-(...)-...-.... - if phonenumber begins with 1: ( for gizmo callback numbers ) - 1 (...)-...-.... - if phonenumber is 13 digits: - (...)-...-.... - if phonenumber is 10 digits: - ...-.... - >>> make_pretty("12") - '12' - >>> make_pretty("1234567") - '123-4567' - >>> make_pretty("2345678901") - '(234)-567-8901' - >>> make_pretty("12345678901") - '1 (234)-567-8901' - >>> make_pretty("01234567890") - '+012-(345)-678-90' - """ - if phonenumber is None: - return "" - - if len(phonenumber) < 3: - return phonenumber - - if phonenumber[0] == "0": - prettynumber = "" - prettynumber += "+%s" % phonenumber[0:3] - if 3 < len(phonenumber): - prettynumber += "-(%s)" % phonenumber[3:6] - if 6 < len(phonenumber): - prettynumber += "-%s" % phonenumber[6:9] - if 9 < len(phonenumber): - prettynumber += "-%s" % phonenumber[9:] - return prettynumber - elif len(phonenumber) <= 7: - prettynumber = "%s-%s" % (phonenumber[0:3], phonenumber[3:]) - elif len(phonenumber) > 8 and phonenumber[0] == "1": - prettynumber = "1 (%s)-%s-%s" % (phonenumber[1:4], phonenumber[4:7], phonenumber[7:]) - elif len(phonenumber) > 7: - prettynumber = "(%s)-%s-%s" % (phonenumber[0:3], phonenumber[3:6], phonenumber[6:]) - return prettynumber - - -def make_idler(func): - """ - Decorator that makes a generator-function into a function that will continue execution on next call - """ - a = [] - - def decorated_func(*args, **kwds): - if not a: - a.append(func(*args, **kwds)) - try: - a[0].next() - return True - except StopIteration: - del a[:] - return False - - decorated_func.__name__ = func.__name__ - decorated_func.__doc__ = func.__doc__ - decorated_func.__dict__.update(func.__dict__) - - return decorated_func - - -class DummyAddressBook(object): - """ - Minimal example of both an addressbook factory and an addressbook - """ - - def get_addressbooks(self): - """ - @returns Iterable of (Address Book Factory, Book Id, Book Name) - """ - yield self, "", "None" - - def open_addressbook(self, bookId): - return self - - @staticmethod - def factory_short_name(): - return "" - - @staticmethod - def factory_name(): - return "" - - @staticmethod - def get_contacts(): - """ - @returns Iterable of (contact id, contact name) - """ - return [] - - @staticmethod - def get_contact_details(contactId): - """ - @returns Iterable of (Phone Type, Phone Number) - """ - return [] - - -class PhoneTypeSelector(object): - - def __init__(self, widgetTree, gcBackend): - self._gcBackend = gcBackend - self._widgetTree = widgetTree - self._dialog = self._widgetTree.get_widget("phonetype_dialog") - - self._selectButton = self._widgetTree.get_widget("select_button") - self._selectButton.connect("clicked", self._on_phonetype_select) - - self._cancelButton = self._widgetTree.get_widget("cancel_button") - self._cancelButton.connect("clicked", self._on_phonetype_cancel) - - self._typemodel = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING) - self._typeviewselection = None - - typeview = self._widgetTree.get_widget("phonetypes") - typeview.connect("row-activated", self._on_phonetype_select) - typeview.set_model(self._typemodel) - textrenderer = gtk.CellRendererText() - - # Add the column to the treeview - column = gtk.TreeViewColumn("Phone Numbers", textrenderer, text=1) - column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) - - typeview.append_column(column) - - self._typeviewselection = typeview.get_selection() - self._typeviewselection.set_mode(gtk.SELECTION_SINGLE) - - def run(self, contactDetails): - self._typemodel.clear() - - for phoneType, phoneNumber in contactDetails: - self._typemodel.append((phoneNumber, "%s - %s" % (make_pretty(phoneNumber), phoneType))) - - userResponse = self._dialog.run() - - if userResponse == gtk.RESPONSE_OK: - model, itr = self._typeviewselection.get_selected() - if itr: - phoneNumber = self._typemodel.get_value(itr, 0) - else: - phoneNumber = "" - - self._typeviewselection.unselect_all() - self._dialog.hide() - return phoneNumber - - def _on_phonetype_select(self, *args): - self._dialog.response(gtk.RESPONSE_OK) - - def _on_phonetype_cancel(self, *args): - self._dialog.response(gtk.RESPONSE_CANCEL) - - -class Dialpad(object): - - __pretty_app_name__ = "Dialer" - __app_name__ = "gc_dialer" - __version__ = "0.8.0" - __app_magic__ = 0xdeadbeef - - _glade_files = [ - './gc_dialer.glade', - '../lib/gc_dialer.glade', - '/usr/local/lib/gc_dialer.glade', - ] - - def __init__(self): - self._phonenumber = "" - self._prettynumber = "" - self._areacode = "518" - - self._clipboard = gtk.clipboard_get() - - self._deviceIsOnline = True - self._callbackList = None - self._callbackNeedsSetup = True - - self._recenttime = 0.0 - self._recentmodel = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING) - self._recentviewselection = None - - self._contactstime = 0.0 - self._contactsmodel = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING) - self._contactsviewselection = None - - for path in Dialpad._glade_files: - if os.path.isfile(path): - self._widgetTree = gtk.glade.XML(path) - break - else: - self.display_error_message("Cannot find gc_dialer.glade") - gtk.main_quit() - return - - aboutHeader = self._widgetTree.get_widget("about_title") - aboutHeader.set_label("%s\nVersion %s" % (aboutHeader.get_label(), Dialpad.__version__)) - - #Get the buffer associated with the number display - self._numberdisplay = self._widgetTree.get_widget("numberdisplay") - self.set_number("") - self._notebook = self._widgetTree.get_widget("notebook") - - self._window = self._widgetTree.get_widget("Dialpad") - - global hildon - self._app = None - self._isFullScreen = False - if hildon is not None and self._window is gtk.Window: - warnings.warn("Hildon installed but glade file not updated to work with hildon", UserWarning, 2) - hildon = None - elif hildon is not None: - self._app = hildon.Program() - self._app.add_window(self._window) - self._widgetTree.get_widget("callbackcombo").get_child().set_property('hildon-input-mode', (1 << 4)) - self._widgetTree.get_widget("usernameentry").set_property('hildon-input-mode', 7) - self._widgetTree.get_widget("passwordentry").set_property('hildon-input-mode', 7|(1 << 29)) - - gtkMenu = self._widgetTree.get_widget("dialpad_menubar") - menu = gtk.Menu() - for child in gtkMenu.get_children(): - child.reparent(menu) - self._window.set_menu(menu) - gtkMenu.destroy() - - self._window.connect("key-press-event", self._on_key_press) - self._window.connect("window-state-event", self._on_window_state_change) - else: - warnings.warn("No Hildon", UserWarning, 2) - - if hildon is not None: - self._window.set_title("Keypad") - else: - self._window.set_title("%s - Keypad" % self.__pretty_app_name__) - - self._osso = None - if osso is not None: - self._osso = osso.Context(Dialpad.__app_name__, Dialpad.__version__, False) - device = osso.DeviceState(self._osso) - device.set_device_state_callback(self._on_device_state_change, 0) - else: - warnings.warn("No OSSO", UserWarning, 2) - - self._connection = None - if conic is not None: - self._connection = conic.Connection() - self._connection.connect("connection-event", self._on_connection_change, Dialpad.__app_magic__) - self._connection.request_connection(conic.CONNECT_FLAG_NONE) - else: - warnings.warn("No Internet Connectivity API ", UserWarning, 2) - - callbackMapping = { - # Process signals from buttons - "on_loginbutton_clicked": self._on_loginbutton_clicked, - "on_loginclose_clicked": self._on_loginclose_clicked, - - "on_dialpad_quit": self._on_close, - "on_paste": self._on_paste, - "on_clear_number": self._on_clear_number, - - "on_clearcookies_clicked": self._on_clearcookies_clicked, - "on_notebook_switch_page": self._on_notebook_switch_page, - "on_recentview_row_activated": self._on_recentview_row_activated, - "on_contactsview_row_activated" : self._on_contactsview_row_activated, - - "on_digit_clicked": self._on_digit_clicked, - "on_back_clicked": self._on_backspace, - "on_dial_clicked": self._on_dial_clicked, - } - self._widgetTree.signal_autoconnect(callbackMapping) - self._widgetTree.get_widget("callbackcombo").get_child().connect("changed", self._on_callbackentry_changed) - self._widgetTree.get_widget("addressbook_combo").get_child().connect("changed", self._on_addressbook_entry_changed) - - if self._window: - self._window.connect("destroy", gtk.main_quit) - self._window.show_all() - - self._gcBackend = GCDialer() - - self._addressBookFactories = [ - DummyAddressBook(), - EvolutionAddressBook(), - self._gcBackend, - ] - self._addressBook = None - self.open_addressbook(*self.get_addressbooks().next()[0][0:2]) - - self._booksList = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING) - for (factoryId, bookId), (factoryName, bookName) in self.get_addressbooks(): - if factoryName and bookName: - entryName = "%s: %s" % (factoryName, bookName) - elif factoryName: - entryName = factoryName - elif bookName: - entryName = bookName - else: - entryName = "Bad name (%d)" % factoryId - row = (str(factoryId), bookId, entryName) - self._booksList.append(row) - - combobox = self._widgetTree.get_widget("addressbook_combo") - combobox.set_model(self._booksList) - combobox.set_text_column(2) - combobox.set_active(0) - - self._phoneTypeSelector = PhoneTypeSelector(self._widgetTree, self._gcBackend) - - self._init_recent_view() - self._init_contacts_view() - if self._gcBackend.is_authed(): - self.set_account_number() - else: - self.attempt_login(2) - - def _init_recent_view(self): - recentview = self._widgetTree.get_widget("recentview") - recentview.set_model(self._recentmodel) - textrenderer = gtk.CellRendererText() - - # Add the column to the treeview - column = gtk.TreeViewColumn("Calls", textrenderer, text=1) - column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) - - recentview.append_column(column) - - self._recentviewselection = recentview.get_selection() - self._recentviewselection.set_mode(gtk.SELECTION_SINGLE) - - return False - - def _init_contacts_view(self): - contactsview = self._widgetTree.get_widget("contactsview") - contactsview.set_model(self._contactsmodel) - - # Add the column to the treeview - column = gtk.TreeViewColumn("Contact") - - textrenderer = gtk.CellRendererText() - column.pack_start(textrenderer, expand=True) - column.add_attribute(textrenderer, 'text', 1) - - textrenderer = gtk.CellRendererText() - column.pack_start(textrenderer, expand=True) - column.add_attribute(textrenderer, 'text', 4) - - column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) - column.set_sort_column_id(1) - column.set_visible(True) - contactsview.append_column(column) - - #textrenderer = gtk.CellRendererText() - #column = gtk.TreeViewColumn("Location", textrenderer, text=2) - #column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) - #column.set_sort_column_id(2) - #column.set_visible(True) - #contactsview.append_column(column) - - #textrenderer = gtk.CellRendererText() - #column = gtk.TreeViewColumn("Phone", textrenderer, text=3) - #column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) - #column.set_sort_column_id(3) - #column.set_visible(True) - #contactsview.append_column(column) - - self._contactsviewselection = contactsview.get_selection() - self._contactsviewselection.set_mode(gtk.SELECTION_SINGLE) - - return False - - def _idly_populate_callback_combo(self): - self._callbackList = gtk.ListStore(gobject.TYPE_STRING) - for number, description in self._gcBackend.get_callback_numbers().iteritems(): - self._callbackList.append((make_pretty(number),)) - - combobox = self._widgetTree.get_widget("callbackcombo") - combobox.set_model(self._callbackList) - combobox.set_text_column(0) - - combobox.get_child().set_text(make_pretty(self._gcBackend.get_callback_number())) - self._callbackNeedsSetup = False - - def _idly_populate_recentview(self): - self._recentmodel.clear() - - for personsName, phoneNumber, date, action in self._gcBackend.get_recent(): - description = "%s on %s from/to %s - %s" % (action.capitalize(), date, personsName, phoneNumber) - item = (phoneNumber, description) - self._recentmodel.append(item) - - self._recenttime = time.time() - return False - - @make_idler - def _idly_populate_contactsview(self): - self._contactsmodel.clear() - - # completely disable updating the treeview while we populate the data - contactsview = self._widgetTree.get_widget("contactsview") - contactsview.freeze_child_notify() - contactsview.set_model(None) - - contactType = (self._addressBook.factory_short_name(),) - for contactId, contactName in self._addressBook.get_contacts(): - self._contactsmodel.append(contactType + (contactName, "", contactId) + ("",)) - yield - - # restart the treeview data rendering - contactsview.set_model(self._contactsmodel) - contactsview.thaw_child_notify() - - self._contactstime = time.time() - - def attempt_login(self, numOfAttempts = 1): - """ - @note Assumes that you are already logged in - """ - assert 0 < numOfAttempts, "That was pointless having 0 or less login attempts" - - if not self._deviceIsOnline: - warnings.warn("Attempted to login while device was offline", UserWarning, 2) - return False - - dialog = self._widgetTree.get_widget("login_dialog") - for i in range(numOfAttempts): - dialog.run() - - username = self._widgetTree.get_widget("usernameentry").get_text() - password = self._widgetTree.get_widget("passwordentry").get_text() - self._widgetTree.get_widget("passwordentry").set_text("") - - loggedIn = self._gcBackend.login(username, password) - dialog.hide() - if loggedIn: - if self._gcBackend.get_callback_number() is None: - self._gcBackend.set_sane_callback() - self.set_account_number() - return True - - return False - - def display_error_message(self, msg): - error_dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, msg) - - def close(dialog, response, editor): - editor.about_dialog = None - dialog.destroy() - error_dialog.connect("response", close, self) - error_dialog.run() - - def get_addressbooks(self): - """ - @returns Iterable of ((Factory Id, Book Id), (Factory Name, Book Name)) - """ - for i, factory in enumerate(self._addressBookFactories): - for bookFactory, bookId, bookName in factory.get_addressbooks(): - yield (i, bookId), (factory.factory_name(), bookName) - - def open_addressbook(self, bookFactoryId, bookId): - self._addressBook = self._addressBookFactories[bookFactoryId].open_addressbook(bookId) - self._contactstime = 0 - gobject.idle_add(self._idly_populate_contactsview) - - def set_number(self, number): - """ - Set the callback phonenumber - """ - self._phonenumber = make_ugly(number) - self._prettynumber = make_pretty(self._phonenumber) - self._numberdisplay.set_label("%s" % (self._prettynumber)) - - def set_account_number(self): - """ - Displays current account number - """ - accountnumber = self._gcBackend.get_account_number() - self._widgetTree.get_widget("gcnumber_display").set_label("%s" % (accountnumber)) - - @staticmethod - def _on_close(*args, **kwds): - gtk.main_quit() - - def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData): - """ - For shutdown or save_unsaved_data, our only state is cookies and I think the cookie manager handles that for us. - For system_inactivity, we have no background tasks to pause - - @note Hildon specific - """ - if memory_low: - self._gcBackend.clear_caches() - re.purge() - gc.collect() - - def _on_connection_change(self, connection, event, magicIdentifier): - """ - @note Hildon specific - """ - status = event.get_status() - error = event.get_error() - iap_id = event.get_iap_id() - bearer = event.get_bearer_type() - - if status == conic.STATUS_CONNECTED: - self._window.set_sensitive(True) - self._deviceIsOnline = True - if not self._gcBackend.is_authed(): - self.attempt_login(2) - elif status == conic.STATUS_DISCONNECTED: - self._window.set_sensitive(False) - self._deviceIsOnline = False - - def _on_window_state_change(self, widget, event, *args): - """ - @note Hildon specific - """ - if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN: - self._isFullScreen = True - else: - self._isFullScreen = False - - def _on_key_press(self, widget, event, *args): - """ - @note Hildon specific - """ - if event.keyval == gtk.keysyms.F6: - if self._isFullScreen: - self._window.unfullscreen() - else: - self._window.fullscreen() - - def _on_loginbutton_clicked(self, *args): - self._widgetTree.get_widget("login_dialog").response(gtk.RESPONSE_OK) - - def _on_loginclose_clicked(self, *args): - self._on_close() - sys.exit(0) - - def _on_clearcookies_clicked(self, *args): - self._gcBackend.logout() - self._callbackNeedsSetup = True - self._recenttime = 0.0 - self._contactstime = 0.0 - self._recentmodel.clear() - self._widgetTree.get_widget("callbackcombo").get_child().set_text("") - - # re-run the inital grandcentral setup - self.attempt_login(2) - gobject.idle_add(self._idly_populate_callback_combo) - - def _on_callbackentry_changed(self, *args): - """ - @todo Potential blocking on web access, maybe we should defer this or put up a dialog? - """ - text = make_ugly(self._widgetTree.get_widget("callbackcombo").get_child().get_text()) - if not self._gcBackend.is_valid_syntax(text): - warnings.warn("%s is not a valid callback number" % text, UserWarning, 2) - elif text == self._gcBackend.get_callback_number(): - warnings.warn("Callback number already is %s" % self._gcBackend.get_callback_number(), UserWarning, 2) - else: - self._gcBackend.set_callback_number(text) - - def _on_recentview_row_activated(self, treeview, path, view_column): - model, itr = self._recentviewselection.get_selected() - if not itr: - return - - self.set_number(self._recentmodel.get_value(itr, 0)) - self._notebook.set_current_page(0) - self._recentviewselection.unselect_all() - - def _on_addressbook_entry_changed(self, *args, **kwds): - combobox = self._widgetTree.get_widget("addressbook_combo") - itr = combobox.get_active_iter() - - factoryId = int(self._booksList.get_value(itr, 0)) - bookId = self._booksList.get_value(itr, 1) - self.open_addressbook(factoryId, bookId) - - def _on_contactsview_row_activated(self, treeview, path, view_column): - model, itr = self._contactsviewselection.get_selected() - if not itr: - return - - contactId = self._contactsmodel.get_value(itr, 3) - contactDetails = self._addressBook.get_contact_details(contactId) - contactDetails = [phoneNumber for phoneNumber in contactDetails] - - if len(contactDetails) == 0: - phoneNumber = "" - elif len(contactDetails) == 1: - phoneNumber = contactDetails[0][1] - else: - phoneNumber = self._phoneTypeSelector.run(contactDetails) - - if 0 < len(phoneNumber): - self.set_number(phoneNumber) - self._notebook.set_current_page(0) - - self._contactsviewselection.unselect_all() - - def _on_notebook_switch_page(self, notebook, page, page_num): - if page_num == 1 and 300 < (time.time() - self._contactstime): - gobject.idle_add(self._idly_populate_contactsview) - elif page_num == 2 and 300 < (time.time() - self._recenttime): - gobject.idle_add(self._idly_populate_recentview) - elif page_num == 3 and self._callbackNeedsSetup: - gobject.idle_add(self._idly_populate_callback_combo) - - tabTitle = self._notebook.get_tab_label(self._notebook.get_nth_page(page_num)).get_text() - if hildon is not None: - self._window.set_title(tabTitle) - else: - self._window.set_title("%s - %s" % (self.__pretty_app_name__, tabTitle)) - - def _on_dial_clicked(self, widget): - """ - @todo Potential blocking on web access, maybe we should defer parts of this or put up a dialog? - """ - loggedIn = self._gcBackend.is_authed() - if not loggedIn: - loggedIn = self.attempt_login(2) - - if not loggedIn or not self._gcBackend.is_authed() or self._gcBackend.get_callback_number() == "": - self.display_error_message("Backend link with grandcentral is not working, please try again") - warnings.warn("Backend Status: Logged in? %s, Authenticated? %s, Callback=%s" % (loggedIn, self._gcBackend.is_authed(), self._gcBackend.get_callback_number()), UserWarning, 2) - return - - try: - callSuccess = self._gcBackend.dial(self._phonenumber) - except ValueError, e: - self._gcBackend._msg = e.message - callSuccess = False - - if not callSuccess: - self.display_error_message(self._gcBackend._msg) - else: - self.set_number("") - - self._recentmodel.clear() - self._recenttime = 0.0 - - def _on_paste(self, *args): - contents = self._clipboard.wait_for_text() - phoneNumber = re.sub('\D', '', contents) - self.set_number(phoneNumber) - - def _on_clear_number(self, *args): - self.set_number("") - - def _on_digit_clicked(self, widget): - self.set_number(self._phonenumber + widget.get_name()[5]) - - def _on_backspace(self, widget): - self.set_number(self._phonenumber[:-1]) - - -def run_doctest(): - failureCount, testCount = doctest.testmod() - if not failureCount: - print "Tests Successful" - sys.exit(0) - else: - sys.exit(1) - - -def run_dialpad(): - gtk.gdk.threads_init() - title = 'Dialpad' - handle = Dialpad() - gtk.main() - - -class DummyOptions(object): - - def __init__(self): - self.test = False - - -if __name__ == "__main__": - if hildon is not None: - gtk.set_application_name(Dialpad.__pretty_app_name__) - - if optparse is not None: - parser = optparse.OptionParser() - parser.add_option("-t", "--test", action="store_true", dest="test", help="Run tests") - (commandOptions, commandArgs) = parser.parse_args() - else: - commandOptions = DummyOptions() - commandArgs = [] - - if commandOptions.test: - run_doctest() - else: - run_dialpad() diff --git a/src/gc_dialer_256.png b/src/gc_dialer_256.png deleted file mode 100644 index a8753506901fcff1ea8d827774b21ac938bf3026..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32182 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 OilMHV&O>ddsQ&|#ca-%2 diff --git a/src/gc_dialer_26.png b/src/gc_dialer_26.png deleted file mode 100644 index df50c66807a4ac5a11657771901db51b56e2d2c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1671 zcmV;226*|2P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01ejw01ejxLMWSf00007bV*G`2iOK2 z4=f7;iQIkw00sw1L_t(Y$EB5hOk39(#(($v+P*dcyI^C8Nj%6$p;`i>sX$$pc7bjJ zG?0L-iNX?ziH=B1rfHfckt{@!l0UXcQ=(`}qNtsS@}aV}sEUXpmu|2qDM^{MFJ(<# zzy=x^5DZ+dudlD~-5)Df)}~9RJ^!2|o%25LqxXHzdjvki<;$0?{{DVdlB7TT{r=v~ zn>R0On&wzNR}=-)G|_b(!!R&S6Gc(bb)7^afv)Qaf>0a?1YSLT`t*7L0bp)!?s#5a z-Y-^(PM$nTS63HLgQ6%zA`v2y2on<%BoYZKDk^AbXb>{wmStIw2X%FIQBqPuZ*MQ@ zbQ(bru-okjf`H9t%d9q=4M~!?diCmmBt3roxZd5}?UbIRXlZE)S(d4*s{_aot?nHT z2a+UVv)Q=)!3WI6<2?H3BD$_qy1tmBM~^zjJ{hZeB6%W_VB^M(w6(Q8wKQ(OpS0IY zfy0Yzfb?0Ho-KH`K?2Zq9f!k#VHoJTPBNLKy1JUFsVQb>XOSccQ4|qH5lz!F&2?sF z;r9E9#Z6u)ucEjMewBNIx4duQ`PdT?0mjG2Gv&}Ujg^%Z0D{3FB_$;k6%}EcCfBZA zBQGxxpU;QO<-%n*S-&=mg4Uf78(}>8A-m?k!}FyTq>Pj+qZ}ZaOrokPsZDcGQ8IhxXHL;3SN_66Qxy48HT~~@-l{DuxcJr z6p4ESXfvfnM|T-8iF7&FfhP{3m2H4o@Q`xkexesQdn3>b8|C0cI@Eb!5>jt zD6#Jc2Pu8|+idt+A?42nxO?UX+iSkX>zChNCzY3%+m@D=SYBR6QIyQ1si~>q)~#EZ zrU^hG5TL24iTn5OqiGs#-`|Z#8|T)9os(}%WO+?m>)~fxHj|a@!Z3`aB#L6tvMf|p zC7n)Z5&%RZ5ze1K55Tcw$B0IwbaZt5d#=pK2*26SyHPK5??jms99(+PK;5nfeEPsd z)3mjs)9JLSs+v)rOeWEFo&NrQ_Uze1XJ;p>s&eAQ2?`1dICA6&_wL=})0s!SQu`t$ z*^dx%mXZ8An{vm=T9b{IR8bVgA^Cj1(CFwWs;aWGvO+SM#4rr%>+2aA8DaPC-2m+0 zzaO{TO?P)U#Vw88&R#Kwn=UilT7p)G0Jg2!LqtgMV^G>WEa#N%;NsT79}9pcKBE9B(lke{DVRaF%;GczA^B|8XM}7<$T;OH#r^;Nm1Zx?qto{6GjmP77yFate%>h`bjhz-FoNFoxV5HlA@AtupscLyE8qV@SuD%?HSi4ZF)$Cb0pIwN z_@4qM;0MZq9}9x;$(Lrco`A*0MQd?!(Tc@l)>H7t<6K|(ps!!Qe)sIzv)))N_QzBz z^>Ev^ZKV!}qg9q=(&;p^ER#y5n4h1Igu~&!O`A4h7)B@*3bnSkx1V~P>t9a#+JnYl RY6buR002ovPDHLkV1gVhDY*au diff --git a/src/gc_dialer_64.png b/src/gc_dialer_64.png deleted file mode 100644 index 8d98390d0478e4bbef85641af67982cdd29ab00c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6411 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 LUFGt}R$>1G8dR*- diff --git a/support/DEBIAN/control b/support/DEBIAN/control deleted file mode 100644 index f1185d9..0000000 --- a/support/DEBIAN/control +++ /dev/null @@ -1,50 +0,0 @@ -Maintainer: Mark Bergman -Package: Dialer -Section: user/Communication -Priority: Optional -Depends: python2.5, python2.5-gtk2 -Recommends: python2.5-hildon, python2.5-evolution -Version: 0.0.0 -Architecture: all -Description: Grand Central frontend written in Python - . - Features - * Dialing of custom numbers. - * GrandCentral contact support. - * Hildon integration where supported. - . - Requirements - * An account with Grandcentral. -Maemo-Icon-26: - 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 - 2oPGX2oGAAAAAElFTkSuQmCC - -Installed-Size: 0 diff --git a/support/DEBIAN/postinst b/support/DEBIAN/postinst deleted file mode 100755 index 1fe6997..0000000 --- a/support/DEBIAN/postinst +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -gtk-update-icon-cache -f /usr/share/icons/hicolor \ No newline at end of file diff --git a/support/GrandcentralDialer.pypackager b/support/GrandcentralDialer.pypackager deleted file mode 100644 index 3bdcce0..0000000 --- a/support/GrandcentralDialer.pypackager +++ /dev/null @@ -1,25 +0,0 @@ -(S'Grandcentral_Dialer' -S'0.6' -S'user/Communication' -S'all' -S'Optional' -S'Mark Bergman ' -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/support/pylint.rc b/support/pylint.rc deleted file mode 100644 index b9a1464..0000000 --- a/support/pylint.rc +++ /dev/null @@ -1,305 +0,0 @@ -# lint Python modules using external checkers. -# -# This is the main checker controling the other ones and the reports -# generation. It is itself both a raw checker and an astng checker in order -# to: -# * handle message activation / deactivation at the module level -# * handle some basic but necessary stats'data (number of classes, methods...) -# -[MASTER] - -# Specify a configuration file. -#rcfile= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -#init-hook= - -# Profiled execution. -profile=no - -# Add to the black list. It should be a base name, not a -# path. You may set this option multiple times. -ignore=CVS - -# Pickle collected data for later comparisons. -persistent=yes - -# Set the cache size for astng objects. -cache-size=500 - -# List of plugins (as comma separated values of python modules names) to load, -# usually to register additional checkers. -load-plugins= - - -[MESSAGES CONTROL] - -# Enable only checker(s) with the given id(s). This option conflicts with the -# disable-checker option -#enable-checker= - -# Enable all checker(s) except those with the given id(s). This option -# conflicts with the enable-checker option -#disable-checker= - -# Enable all messages in the listed categories. -#enable-msg-cat= - -# Disable all messages in the listed categories. -#disable-msg-cat= - -# Enable the message(s) with the given id(s). -#enable-msg= - -# Disable the message(s) with the given id(s). -disable-msg=W0403,W0612,W0613,C0103,C0111,C0301 - -[REPORTS] - -# set the output format. Available formats are text, parseable, colorized, msvs -# (visual studio) and html -output-format=text - -# Include message's id in output -include-ids=no - -# Put messages in a separate file for each module / package specified on the -# command line instead of printing them on stdout. Reports (if any) will be -# written in a file name "pylint_global.[txt|html]". -files-output=no - -# Tells wether to display a full report or only the messages -reports=yes - -# Python expression which should return a note less than 10 (10 is the highest -# note).You have access to the variables errors warning, statement which -# respectivly contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (R0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Add a comment according to your evaluation note. This is used by the global -# evaluation report (R0004). -comment=no - -# Enable the report(s) with the given id(s). -#enable-report= - -# Disable the report(s) with the given id(s). -#disable-report= - - -# checks for -# * unused variables / imports -# * undefined variables -# * redefinition of variable from builtins or from an outer scope -# * use of variable before assigment -# -[VARIABLES] - -# Tells wether we should check for unused import in __init__ files. -init-import=no - -# A regular expression matching names used for dummy variables (i.e. not used). -dummy-variables-rgx=_|dummy - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -additional-builtins= - - -# checks for : -# * doc strings -# * modules / classes / functions / methods / arguments / variables name -# * number of arguments, local variables, branchs, returns and statements in -# functions, methods -# * required module attributes -# * dangerous default values as arguments -# * redefinition of function / method / class -# * uses of the global statement -# -[BASIC] - -# Required attributes for module, separated by a comma -required-attributes= - -# Regular expression which should only match functions or classes name which do -# not require a docstring -no-docstring-rgx=__.*__ - -# Regular expression which should only match correct module names -module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - -# Regular expression which should only match correct module level names -const-rgx=(([A-Z_][A-Z1-9_]*)|(__.*__))$ - -# Regular expression which should only match correct class names -class-rgx=[A-Z_][a-zA-Z0-9]+$ - -# Regular expression which should only match correct function names -function-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct method names -method-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct instance attribute names -attr-rgx=[a-z_][a-zA-Z0-9_]{2,30}$ - -# Regular expression which should only match correct argument names -argument-rgx=[a-z_][a-zA-Z0-9_]{2,30}$ - -# Regular expression which should only match correct variable names -variable-rgx=[a-z_][a-zA-Z0-9_]{2,30}$ - -# Regular expression which should only match correct list comprehension / -# generator expression variable names -inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ - -# Good variable names which should always be accepted, separated by a comma -good-names=i,j,k,ex,Run,_ - -# Bad variable names which should always be refused, separated by a comma -bad-names=foo,bar,baz,toto,tutu,tata - -# List of builtins function names that should not be used, separated by a comma -bad-functions=map,filter,apply,input - - -# try to find bugs in the code using type inference -# -[TYPECHECK] - -# Tells wether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# When zope mode is activated, consider the acquired-members option to ignore -# access to some undefined attributes. -zope=no - -# List of members which are usually get through zope's acquisition mecanism and -# so shouldn't trigger E0201 when accessed (need zope=yes to be considered). -acquired-members=REQUEST,acl_users,aq_parent - - -# checks for sign of poor/misdesign: -# * number of methods, attributes, local variables... -# * size, complexity of functions, methods -# -[DESIGN] - -# Maximum number of arguments for function / method -max-args=5 - -# Maximum number of locals for function / method body -max-locals=15 - -# Maximum number of return / yield for function / method body -max-returns=6 - -# Maximum number of branch for function / method body -max-branchs=12 - -# Maximum number of statements in function / method body -max-statements=50 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of attributes for a class (see R0902). -max-attributes=7 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=1 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=20 - - -# checks for : -# * methods without self as first argument -# * overridden methods signature -# * access only to existant members via self -# * attributes not defined in the __init__ method -# * supported interfaces implementation -# * unreachable code -# -[CLASSES] - -# List of interface methods to ignore, separated by a comma. This is used for -# instance to not check methods defines in Zope's Interface base class. -ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__,__new__,setUp - - -# checks for -# * external modules dependencies -# * relative / wildcard imports -# * cyclic imports -# * uses of deprecated modules -# -[IMPORTS] - -# Deprecated modules which should not be used, separated by a comma -deprecated-modules=regsub,string,TERMIOS,Bastion,rexec - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report R0402 must not be disabled) -import-graph= - -# Create a graph of external dependencies in the given file (report R0402 must -# not be disabled) -ext-import-graph= - -# Create a graph of internal dependencies in the given file (report R0402 must -# not be disabled) -int-import-graph= - - -# checks for similarities and duplicated code. This computation may be -# memory / CPU intensive, so you should disable it if you experiments some -# problems. -# -[SIMILARITIES] - -# Minimum lines number of a similarity. -min-similarity-lines=4 - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - - -# checks for: -# * warning notes in the code like FIXME, XXX -# * PEP 263: source code with non ascii character but no encoding declaration -# -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=FIXME,XXX,TODO - - -# checks for : -# * unauthorized constructions -# * strict indentation -# * line length -# * use of <> instead of != -# -[FORMAT] - -# Maximum number of characters on a single line. -# @note Limiting this to the most extreme cases -max-line-length=100 - -# Maximum number of lines in a module -max-module-lines=1000 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string='\t' -- 1.7.9.5