Merge branch 'master' of git+ssh://dev.laptop.org/git/sugar
This commit is contained in:
		
						commit
						d52860a764
					
				
							
								
								
									
										4
									
								
								NEWS
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								NEWS
									
									
									
									
									
								
							| @ -1,3 +1,7 @@ | ||||
| Snapshot d38cacfe2c | ||||
| 
 | ||||
| * Add icons to the activity ring palette. (danw) | ||||
| 
 | ||||
| Snapshot e83b98a8f6 | ||||
| 
 | ||||
| * #2912: Improvements to GlibURLDownloader API (dcbw) | ||||
|  | ||||
| @ -69,9 +69,7 @@ services/console/lib/Makefile | ||||
| services/console/lib/graphics/Makefile | ||||
| services/console/lib/procmem/Makefile | ||||
| services/console/lib/net/Makefile | ||||
| services/console/lib/gui/Makefile | ||||
| services/console/lib/purk/Makefile | ||||
| services/console/lib/purk/scripts/Makefile | ||||
| services/console/lib/ui/Makefile | ||||
| services/console/Makefile | ||||
| services/console/interface/Makefile | ||||
| services/console/interface/xo/Makefile | ||||
| @ -84,7 +82,6 @@ services/console/interface/memphis/Makefile | ||||
| services/console/interface/network/Makefile | ||||
| services/console/interface/logviewer/Makefile | ||||
| services/console/interface/terminal/Makefile | ||||
| services/console/interface/irc_client/Makefile | ||||
| sugar/Makefile | ||||
| sugar/activity/Makefile | ||||
| sugar/clipboard/Makefile | ||||
|  | ||||
| @ -30,7 +30,7 @@ CONSOLE_BUS = 'org.laptop.sugar.Console' | ||||
| CONSOLE_PATH = '/org/laptop/sugar/Console' | ||||
| CONSOLE_IFACE = 'org.laptop.sugar.Console' | ||||
| 
 | ||||
| class Console(object): | ||||
| class Console: | ||||
|      | ||||
|     def __init__(self): | ||||
|         # Main Window | ||||
| @ -54,7 +54,6 @@ class Console(object): | ||||
|         self._load_interface('memphis', 'Memphis') | ||||
|         self._load_interface('logviewer', 'Log Viewer') | ||||
|         self._load_interface('terminal', 'Terminal') | ||||
|     	self._load_interface('irc_client', 'IRC Client') | ||||
|         self._load_interface('ps_watcher', 'Presence') | ||||
|          | ||||
|         main_hbox = gtk.HBox() | ||||
| @ -91,4 +90,5 @@ bus = dbus.SessionBus() | ||||
| name = dbus.service.BusName(CONSOLE_BUS, bus) | ||||
| 
 | ||||
| obj = Service(name) | ||||
| 
 | ||||
| gtk.main() | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| SUBDIRS = irc_client memphis network logviewer terminal xo | ||||
| SUBDIRS = memphis network logviewer terminal xo | ||||
| 
 | ||||
| sugardir = $(pkgdatadir)/services/console/interface | ||||
| sugar_PYTHON =				\
 | ||||
|  | ||||
| @ -1,5 +0,0 @@ | ||||
| sugardir = $(pkgdatadir)/services/console/interface/irc_client | ||||
| sugar_PYTHON =				\
 | ||||
| 	__init__.py			\
 | ||||
| 	irc_client.py			 | ||||
| 
 | ||||
| @ -1,2 +0,0 @@ | ||||
| from irc_client import Interface | ||||
| 
 | ||||
| @ -1,28 +0,0 @@ | ||||
| import gtk | ||||
| import purk | ||||
| 
 | ||||
| class IRCGui(gtk.VBox): | ||||
|     _DEFAULT_SERVER = "irc.freenode.net" | ||||
|     _AUTO_JOIN_CHANNEL = "#olpc-help" | ||||
| 
 | ||||
|     def __init__(self): | ||||
|         gtk.VBox.__init__(self, False) | ||||
| 
 | ||||
|         connect_button = gtk.Button('Connect to OLPC Help Channel') | ||||
|         connect_button.connect('clicked', self._on_connect_clicked_cb) | ||||
| 
 | ||||
|         self._client = purk.Client() | ||||
|         self._client.add_channel(self._AUTO_JOIN_CHANNEL) | ||||
|         client_widget = self._client.get_widget() | ||||
| 
 | ||||
|         self.pack_start(connect_button, False, False, 1) | ||||
|         self.pack_start(client_widget) | ||||
|         self.show_all() | ||||
| 
 | ||||
|     def _on_connect_clicked_cb(self, widget): | ||||
|         self._client.join_server(self._DEFAULT_SERVER) | ||||
| 
 | ||||
| class Interface(object): | ||||
|     def __init__(self): | ||||
|         self.widget = IRCGui() | ||||
| 
 | ||||
| @ -17,7 +17,7 @@ | ||||
| 
 | ||||
| import gobject | ||||
| from net.device import Device | ||||
| from gui.treeview import TreeView | ||||
| from ui.treeview import TreeView | ||||
| 
 | ||||
| class NetworkView(TreeView): | ||||
|     def __init__(self): | ||||
|  | ||||
| @ -101,6 +101,7 @@ class XO_CPU(gtk.Frame): | ||||
|         gobject.timeout_add(self._DRW_CPU.frequency, self._update_cpu_usage) | ||||
| 
 | ||||
|     def _update_cpu_usage(self): | ||||
|         print "update XO CPU" | ||||
|         self._cpu = self._DRW_CPU._get_CPU_usage() | ||||
|         self.set_label('System CPU Usage: ' + str(self._cpu) + '%') | ||||
| 
 | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| SUBDIRS = procmem graphics net gui purk | ||||
| SUBDIRS = procmem graphics net ui | ||||
| 
 | ||||
| sugardir = $(pkgdatadir)/shell/console/lib | ||||
| sugar_PYTHON =				 | ||||
|  | ||||
| @ -1,5 +0,0 @@ | ||||
| sugardir = $(pkgdatadir)/services/console/lib/gui | ||||
| 
 | ||||
| sugar_PYTHON =				\
 | ||||
| 	__init__.py			\
 | ||||
| 	treeview.py			 | ||||
| @ -1,73 +0,0 @@ | ||||
| # Copyright (C) 2007, Eduardo Silva <edsiper@gmail.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 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., 59 Temple Place - Suite 330, | ||||
| # Boston, MA 02111-1307, USA. | ||||
| 
 | ||||
| import gtk | ||||
| 
 | ||||
| class TreeView(gtk.ScrolledWindow): | ||||
|     iters = [] # Iters index | ||||
| 
 | ||||
|     # Create a window with a treeview object | ||||
|     # | ||||
|     # cols      = List of dicts, ex: | ||||
|     # | ||||
|     #   cols = [] | ||||
|     #   cols.append({'index': integer_index_position, 'name': string_col_name}) | ||||
|     def __init__(self, cols_def, cols_name): | ||||
|         gtk.ScrolledWindow.__init__(self) | ||||
| 
 | ||||
|         self._iters = [] | ||||
|         self._treeview = gtk.TreeView() | ||||
| 
 | ||||
|         # Creating column data types | ||||
|         self._store = gtk.TreeStore(*cols_def) | ||||
| 
 | ||||
|         # Columns definition | ||||
|         cell = gtk.CellRendererText() | ||||
|         tv_cols = [] | ||||
| 
 | ||||
|         i=0 | ||||
|         for col in cols_name: | ||||
|             col_tv = gtk.TreeViewColumn(col['name'], cell, text=i) | ||||
|             col_tv.set_reorderable(True) | ||||
|             col_tv.set_resizable(True) | ||||
|             tv_cols.append(col_tv) | ||||
|             i+=1 | ||||
| 
 | ||||
|         # Setting treeview properties | ||||
|         self._treeview.set_model(self._store) | ||||
|         self._treeview.set_enable_search(True) | ||||
|         self._treeview.set_rules_hint(True) | ||||
|          | ||||
|         for col in tv_cols: | ||||
|             self._treeview.append_column(col) | ||||
|         self.add(self._treeview) | ||||
| 
 | ||||
|     def add_row(self, cols_data): | ||||
|         iter = self._store.insert_after(None, None) | ||||
|         for col in cols_data: | ||||
|             print col['index'],col['info'] | ||||
|             self._store.set_value(iter, int(col['index']) , col['info']) | ||||
| 
 | ||||
|         self.iters.append(iter) | ||||
|         return iter | ||||
| 
 | ||||
|     def update_row(self, iter, cols_data): | ||||
|         for col in cols_data: | ||||
|             self._store.set_value(iter, int(col['index']) , str(col['info'])) | ||||
| 
 | ||||
|     def remove_row(self, iter): | ||||
|         self._store.remove(iter) | ||||
| @ -1,7 +0,0 @@ | ||||
| Urk is a PyGTK IRC Client written by Vincent Povirk and Marc Liddell.  | ||||
| This current version has been modified in order to have an PyGTK  | ||||
| IRC Client Widget called 'Purk'. | ||||
| 
 | ||||
| Suggestions are welcome... | ||||
| 
 | ||||
| Eduardo Silva <edsiper@gmail.com> | ||||
| @ -1,340 +0,0 @@ | ||||
| 		    GNU GENERAL PUBLIC LICENSE | ||||
| 		       Version 2, June 1991 | ||||
| 
 | ||||
|  Copyright (C) 1989, 1991 Free Software Foundation, Inc. | ||||
|                        51 Franklin St, 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. | ||||
| 
 | ||||
| 			    Preamble | ||||
| 
 | ||||
|   The licenses for most software are designed to take away your | ||||
| freedom to share and change it.  By contrast, the GNU General Public | ||||
| License is intended to guarantee your freedom to share and change free | ||||
| software--to make sure the software is free for all its users.  This | ||||
| General Public License applies to most of the Free Software | ||||
| Foundation's software and to any other program whose authors commit to | ||||
| using it.  (Some other Free Software Foundation software is covered by | ||||
| the GNU Library General Public License instead.)  You can apply it to | ||||
| your programs, too. | ||||
| 
 | ||||
|   When we speak of free software, we are referring to freedom, 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 or use pieces of it | ||||
| in new free programs; and that you know you can do these things. | ||||
| 
 | ||||
|   To protect your rights, we need to make restrictions that forbid | ||||
| anyone to deny you these rights or to ask you to surrender the rights. | ||||
| These restrictions translate to certain responsibilities for you if you | ||||
| distribute copies of the software, or if you modify it. | ||||
| 
 | ||||
|   For example, if you distribute copies of such a program, whether | ||||
| gratis or for a fee, you must give the recipients all the rights that | ||||
| you have.  You must make sure that they, too, receive or can get the | ||||
| source code.  And you must show them these terms so they know their | ||||
| rights. | ||||
| 
 | ||||
|   We protect your rights with two steps: (1) copyright the software, and | ||||
| (2) offer you this license which gives you legal permission to copy, | ||||
| distribute and/or modify the software. | ||||
| 
 | ||||
|   Also, for each author's protection and ours, we want to make certain | ||||
| that everyone understands that there is no warranty for this free | ||||
| software.  If the software is modified by someone else and passed on, we | ||||
| want its recipients to know that what they have is not the original, so | ||||
| that any problems introduced by others will not reflect on the original | ||||
| authors' reputations. | ||||
| 
 | ||||
|   Finally, any free program is threatened constantly by software | ||||
| patents.  We wish to avoid the danger that redistributors of a free | ||||
| program will individually obtain patent licenses, in effect making the | ||||
| program proprietary.  To prevent this, we have made it clear that any | ||||
| patent must be licensed for everyone's free use or not licensed at all. | ||||
| 
 | ||||
|   The precise terms and conditions for copying, distribution and | ||||
| modification follow. | ||||
|  | ||||
| 		    GNU GENERAL PUBLIC LICENSE | ||||
|    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | ||||
| 
 | ||||
|   0. This License applies to any program or other work which contains | ||||
| a notice placed by the copyright holder saying it may be distributed | ||||
| under the terms of this General Public License.  The "Program", below, | ||||
| refers to any such program or work, and a "work based on the Program" | ||||
| means either the Program or any derivative work under copyright law: | ||||
| that is to say, a work containing the Program or a portion of it, | ||||
| either verbatim or with modifications and/or translated into another | ||||
| language.  (Hereinafter, translation is included without limitation in | ||||
| the term "modification".)  Each licensee is addressed as "you". | ||||
| 
 | ||||
| Activities other than copying, distribution and modification are not | ||||
| covered by this License; they are outside its scope.  The act of | ||||
| running the Program is not restricted, and the output from the Program | ||||
| is covered only if its contents constitute a work based on the | ||||
| Program (independent of having been made by running the Program). | ||||
| Whether that is true depends on what the Program does. | ||||
| 
 | ||||
|   1. You may copy and distribute verbatim copies of the Program's | ||||
| 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 give any other recipients of the Program a copy of this License | ||||
| along with the Program. | ||||
| 
 | ||||
| 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 Program or any portion | ||||
| of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices | ||||
|     stating that you changed the files and the date of any change. | ||||
| 
 | ||||
|     b) You must cause any work that you distribute or publish, that in | ||||
|     whole or in part contains or is derived from the Program or any | ||||
|     part thereof, to be licensed as a whole at no charge to all third | ||||
|     parties under the terms of this License. | ||||
| 
 | ||||
|     c) If the modified program normally reads commands interactively | ||||
|     when run, you must cause it, when started running for such | ||||
|     interactive use in the most ordinary way, to print or display an | ||||
|     announcement including an appropriate copyright notice and a | ||||
|     notice that there is no warranty (or else, saying that you provide | ||||
|     a warranty) and that users may redistribute the program under | ||||
|     these conditions, and telling the user how to view a copy of this | ||||
|     License.  (Exception: if the Program itself is interactive but | ||||
|     does not normally print such an announcement, your work based on | ||||
|     the Program is not required to print an announcement.) | ||||
|  | ||||
| These requirements apply to the modified work as a whole.  If | ||||
| identifiable sections of that work are not derived from the Program, | ||||
| 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 Program, 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 Program. | ||||
| 
 | ||||
| In addition, mere aggregation of another work not based on the Program | ||||
| with the Program (or with a work based on the Program) on a volume of | ||||
| a storage or distribution medium does not bring the other work under | ||||
| the scope of this License. | ||||
| 
 | ||||
|   3. You may copy and distribute the Program (or a work based on it, | ||||
| under Section 2) in object code or executable form under the terms of | ||||
| Sections 1 and 2 above provided that you also do one of the following: | ||||
| 
 | ||||
|     a) 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; or, | ||||
| 
 | ||||
|     b) Accompany it with a written offer, valid for at least three | ||||
|     years, to give any third party, for a charge no more than your | ||||
|     cost of physically performing source distribution, a complete | ||||
|     machine-readable copy of the corresponding source code, to be | ||||
|     distributed under the terms of Sections 1 and 2 above on a medium | ||||
|     customarily used for software interchange; or, | ||||
| 
 | ||||
|     c) Accompany it with the information you received as to the offer | ||||
|     to distribute corresponding source code.  (This alternative is | ||||
|     allowed only for noncommercial distribution and only if you | ||||
|     received the program in object code or executable form with such | ||||
|     an offer, in accord with Subsection b above.) | ||||
| 
 | ||||
| The source code for a work means the preferred form of the work for | ||||
| making modifications to it.  For an executable work, 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 executable.  However, as a | ||||
| special exception, the source code 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. | ||||
| 
 | ||||
| If distribution of executable or 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 counts as | ||||
| distribution of the source code, even though third parties are not | ||||
| compelled to copy the source along with the object code. | ||||
|  | ||||
|   4. You may not copy, modify, sublicense, or distribute the Program | ||||
| except as expressly provided under this License.  Any attempt | ||||
| otherwise to copy, modify, sublicense or distribute the Program 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. | ||||
| 
 | ||||
|   5. 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 Program or its derivative works.  These actions are | ||||
| prohibited by law if you do not accept this License.  Therefore, by | ||||
| modifying or distributing the Program (or any work based on the | ||||
| Program), you indicate your acceptance of this License to do so, and | ||||
| all its terms and conditions for copying, distributing or modifying | ||||
| the Program or works based on it. | ||||
| 
 | ||||
|   6. Each time you redistribute the Program (or any work based on the | ||||
| Program), the recipient automatically receives a license from the | ||||
| original licensor to copy, distribute or modify the Program 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 to | ||||
| this License. | ||||
| 
 | ||||
|   7. 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 Program at all.  For example, if a patent | ||||
| license would not permit royalty-free redistribution of the Program 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 Program. | ||||
| 
 | ||||
| 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. | ||||
|  | ||||
|   8. If the distribution and/or use of the Program is restricted in | ||||
| certain countries either by patents or by copyrighted interfaces, the | ||||
| original copyright holder who places the Program 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. | ||||
| 
 | ||||
|   9. The Free Software Foundation may publish revised and/or new versions | ||||
| of the 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 Program | ||||
| 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 Program does not specify a version number of | ||||
| this License, you may choose any version ever published by the Free Software | ||||
| Foundation. | ||||
| 
 | ||||
|   10. If you wish to incorporate parts of the Program into other free | ||||
| programs whose distribution conditions are different, 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 | ||||
| 
 | ||||
|   11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY | ||||
| FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN | ||||
| OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES | ||||
| PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU.  SHOULD THE | ||||
| PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, | ||||
| REPAIR OR CORRECTION. | ||||
| 
 | ||||
|   12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER | ||||
| PROGRAMS), 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 Programs | ||||
| 
 | ||||
|   If you develop a new program, and you want it to be of the greatest | ||||
| possible use to the public, the best way to achieve this is to make it | ||||
| free software which everyone can redistribute and change under these terms. | ||||
| 
 | ||||
|   To do so, attach the following notices to the program.  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. | ||||
| 
 | ||||
|     <one line to give the program's name and a brief idea of what it does.> | ||||
|     Copyright (C) <year>  <name of author> | ||||
| 
 | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program 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 General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License | ||||
|     along with this program; if not, write to the Free Software | ||||
|     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | ||||
| 
 | ||||
| 
 | ||||
| Also add information on how to contact you by electronic and paper mail. | ||||
| 
 | ||||
| If the program is interactive, make it output a short notice like this | ||||
| when it starts in an interactive mode: | ||||
| 
 | ||||
|     Gnomovision version 69, Copyright (C) year name of author | ||||
|     Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. | ||||
|     This is free software, and you are welcome to redistribute it | ||||
|     under certain conditions; type `show c' for details. | ||||
| 
 | ||||
| The hypothetical commands `show w' and `show c' should show the appropriate | ||||
| parts of the General Public License.  Of course, the commands you use may | ||||
| be called something other than `show w' and `show c'; they could even be | ||||
| mouse-clicks or menu items--whatever suits your program. | ||||
| 
 | ||||
| You should also get your employer (if you work as a programmer) or your | ||||
| school, if any, to sign a "copyright disclaimer" for the program, if | ||||
| necessary.  Here is a sample; alter the names: | ||||
| 
 | ||||
|   Yoyodyne, Inc., hereby disclaims all copyright interest in the program | ||||
|   `Gnomovision' (which makes passes at compilers) written by James Hacker. | ||||
| 
 | ||||
|   <signature of Ty Coon>, 1 April 1989 | ||||
|   Ty Coon, President of Vice | ||||
| 
 | ||||
| This General Public License does not permit incorporating your program into | ||||
| proprietary programs.  If your program is a subroutine library, you may | ||||
| consider it more useful to permit linking proprietary applications with the | ||||
| library.  If this is what you want to do, use the GNU Library General | ||||
| Public License instead of this License. | ||||
| @ -1,17 +0,0 @@ | ||||
| SUBDIRS = scripts | ||||
| sugardir = $(pkgdatadir)/services/console/lib/purk | ||||
| 
 | ||||
| sugar_PYTHON =				\
 | ||||
| 	__init__.py			\
 | ||||
| 	conf.py				\
 | ||||
| 	events.py			\
 | ||||
| 	info.py				\
 | ||||
| 	irc.py				\
 | ||||
| 	parse_mirc.py			\
 | ||||
| 	servers.py			\
 | ||||
| 	ui.py				\
 | ||||
| 	urk_trace.py			\
 | ||||
| 	widgets.py			\
 | ||||
| 	windows.py			\
 | ||||
| 	README				\
 | ||||
| 	ABOUT				 | ||||
| @ -1,186 +0,0 @@ | ||||
| urk 0.-1.cvs | ||||
| http://urk.sourceforge.net/ | ||||
| 
 | ||||
| 
 | ||||
| Overview: | ||||
| 
 | ||||
| urk is an IRC client written purely in python for linux/gnome. It has a powerful | ||||
| built-in scripting system (also python), which is used to implement much of the | ||||
| client. | ||||
| 
 | ||||
| 
 | ||||
| Requirements/User installation: | ||||
| 
 | ||||
| urk requires the following packages (and should run on any os that can provide  | ||||
| them): | ||||
| -python version 2.4 or greater (www.python.org) | ||||
| -pygtk 2.6 or greater (www.pygtk.org) | ||||
| 
 | ||||
| Most Linux (or at least GNOME) users should have these things already or be able | ||||
| to easily install them. | ||||
| 
 | ||||
| Because urk is pure python, no compilation of urk is required. Just extract the  | ||||
| source to somewhere and run 'python urk.py'. | ||||
| 
 | ||||
| Windows versions of the above should theoretically work but may not in practice. | ||||
| I am hoping someone will come along and actually support urk on windows. | ||||
| 
 | ||||
| 
 | ||||
| Optional requirements: | ||||
| 
 | ||||
| urk can also make use of these packages if you have them: | ||||
| -pygtksourceview, part of gnome-python-extras <=2.12 and gnome-python-desktop  | ||||
|  >=2.14, for source highlighting and undo in the internal script editor | ||||
| -python-dbus, for, well, not much (if dbus is available, urk only makes a single | ||||
|  instance per session, and commands can be executed remotely by calling urk.py) | ||||
| 
 | ||||
| 
 | ||||
| Getting started: | ||||
| 
 | ||||
| Make sure you have all the requirements, go to the directory where you have | ||||
| extracted the source, and type 'python urk.py'. | ||||
| 
 | ||||
| We don't have any preferences windows yet. You can change your nickname by | ||||
| typing '/nick nickname' (replacing nickname with the nick you want to use) | ||||
| or typing a new nick in the nick field on the lower right corner and pressing  | ||||
| enter. | ||||
| 
 | ||||
| To connect, type '/server irc.gamesurge.net' (replacing irc.gamesurge.net with  | ||||
| the server you want to connect to). | ||||
| 
 | ||||
| If you want to connect to another server (without disconnecting the current | ||||
| one), use the -m switch as in '/server -m irc.gamesurge.net'. | ||||
| 
 | ||||
| To join a channel when you're connected to a server, type '/join #channelname', | ||||
| replacing #channelname with the channel you want to join. | ||||
| 
 | ||||
| urk currently only supports the bare minimum commands and features you should | ||||
| need to connect and chat normally. On channels, you can send messages by | ||||
| typing them (if you want to send a message that starts with a /, use /say to | ||||
| send your message). You can send actions to channels with /me and send messages | ||||
| to arbitrary targets with /msg. If urk does not recognize a command, it will | ||||
| send it to the server. This works to implement most commands you would expect | ||||
| on an irc client. | ||||
| 
 | ||||
| 
 | ||||
| Configuration: | ||||
| 
 | ||||
| Most configuration has to be done manually. If urk is running, you can configure | ||||
| it using commands. The settings are stored in urk.conf on your profile | ||||
| directory, which you can locate by typing '/pyeval urk.userpath' in urk. | ||||
| 
 | ||||
| To set a value in urk, type | ||||
| 
 | ||||
| /pyexec conf.conf['setting'] = value | ||||
| 
 | ||||
| To see the current value, type | ||||
| 
 | ||||
| /pyeval conf.conf['setting'] | ||||
| 
 | ||||
| To unset a value (meaning urk will use the default), type | ||||
| 
 | ||||
| /pyexec del conf.conf['setting'] | ||||
| 
 | ||||
| Setting:            Description: | ||||
| 
 | ||||
| 'nick'                 The nickname urk should use. The default is to try to get | ||||
|     it from the os. Put this in quotes when you set it. | ||||
| 
 | ||||
| 'altnicks'             A list of alternative nicknames to use. The default is | ||||
|     an empty list. | ||||
| 
 | ||||
| 'quitmsg'              The message people see when you quit. The default is to | ||||
|     advertise urk with your current version; we have to promote it somehow. | ||||
|     This value needs to be in quotes. | ||||
| 
 | ||||
| 'autoreconnect'        If True, urk will try to reconnect when you're | ||||
|     disconnected from a network. Defaults to True. Set this to True or False. | ||||
| 
 | ||||
| 'highlight_words'      A list of words, in addition to your nick, that cause a | ||||
|     highlight event (normally the tab label turns blue and, if it's available, | ||||
|     the tray icon shows up). For example: ['python', 'whale', 'peanut butter'] | ||||
| 
 | ||||
| 'log_dir'              The place where logs are written. The default is a | ||||
|     directory called "logs" on your profile directory. | ||||
| 
 | ||||
| 'ui-gtk/tab-pos'       The side of the window where the tabs will reside | ||||
|     2 for top | ||||
|     0 for left | ||||
|     1 for right | ||||
|     3 for bottom (default) | ||||
| 
 | ||||
| 'ui-gtk/show-menubar'  If True, the menubar is shown. The default is True. Set | ||||
|     this to True or False. | ||||
| 
 | ||||
| 'command_prefix'       The prefix used to signal a command. Defaults to '/' | ||||
| 
 | ||||
| 'font'                 The font used for output. Defaults to "sans 8". | ||||
| 
 | ||||
| 'bg_color'             The background color ("black" or "#000000"). | ||||
| 
 | ||||
| 'fg_color'             The foreground color ("white" or "#ffffff"). | ||||
| 
 | ||||
| 'timestamp'            A timestamp that will show up before messages. The | ||||
|     default is no timestamp. A simple timestamp with hours and minutes is | ||||
|     "[%H:%M] ". See http://docs.python.org/lib/module-time.html#l2h-1955 for | ||||
|     a key to format this string. | ||||
| 
 | ||||
| 'start-console'        If True, urk will start up with a special console window | ||||
|     that shows debugging output (normally sent to a terminal) and accepts | ||||
|     python expressions. Defaults to False. | ||||
| 
 | ||||
| 'status'               If True, urk will be in status window mode. Each network | ||||
|     will ALWAYS have a status window. When not in status window mode, networks  | ||||
|     only have a status window when there are no channel windows. Defaults to  | ||||
|     False. | ||||
| 
 | ||||
| 'open-file-command'    The command used to open files and url's with your  | ||||
|     preferred application (such as "gnome-open"). This is ignored on Windows,  | ||||
|     and you should never need to mess with this, ever. | ||||
| 
 | ||||
| 
 | ||||
| System-wide installation: | ||||
| 
 | ||||
| Not yet implemented. | ||||
| 
 | ||||
| 
 | ||||
| About scripting: | ||||
| 
 | ||||
| urk scripts are python source files that contain definitions of functions with  | ||||
| certain "magic" names, like onText (for when someone sends a channel or private  | ||||
| message). See www.python.org for help on writing python code. The format for  | ||||
| onText is: | ||||
| 
 | ||||
| def onText(e): | ||||
|     code | ||||
| 
 | ||||
| e is an object used to pass on the various information relating to the event | ||||
| that triggered the function. The name is a convention we use, much like self. | ||||
| 
 | ||||
| e.source is the nickname of the person who sent the message. | ||||
| 
 | ||||
| e.target is the nickname or channel that received the message. | ||||
| 
 | ||||
| e.text is the text of the message. | ||||
| 
 | ||||
| e.network is an object representing the network where the message was sent. | ||||
| 
 | ||||
| e.window is a window that seems to be related to the event for some unspecified | ||||
| reason. It could be the status window, a channel window, a query, anything. | ||||
| 
 | ||||
| Complete documentation doesn't exist yet. Sorry. Ask us or look at the source. theme.py is good for finding event names. | ||||
| 
 | ||||
| 
 | ||||
| Bugs/Feedback: | ||||
| 
 | ||||
| Naturally, feedback of any sort is welcome. Of course we want to know about | ||||
| bugs. In particular, we'd also like to hear about any features you want or | ||||
| expect in an irc client that urk doesn't have. While we'd like to limit the | ||||
| things that go in the default client (a notify list, for example, is something | ||||
| we'd want to see as an external script, not as part of the default setup, and | ||||
| something we're not likely to implement soon), there are probably a lot of  | ||||
| little things that we may have skipped over because we don't use them or have  | ||||
| become used to urk not having them. | ||||
| 
 | ||||
| The best way to get in touch with us is by irc, at #urk on irc.gamesurge.net. | ||||
| Or send a message to the mailing list, urk-discussion@lists.sourceforge.net. | ||||
| @ -1,245 +0,0 @@ | ||||
| using System; | ||||
| using System.Collections; | ||||
| using System.IO; | ||||
| using System.Text; | ||||
| using System.Threading; | ||||
| 
 | ||||
| using Beagle.Daemon; | ||||
| using Beagle.Util; | ||||
| 
 | ||||
| namespace Beagle.Daemon.UrkLogQueryable { | ||||
| 
 | ||||
|     [QueryableFlavor (Name="UrkLog", Domain=QueryDomain.Local, RequireInotify=false)] | ||||
|     public class UrkLogQueryable : LuceneFileQueryable { | ||||
| 
 | ||||
|         private static Logger log = Logger.Get ("UrkLogQueryable"); | ||||
| 
 | ||||
|         private string config_dir, log_dir, icons_dir; | ||||
| 
 | ||||
|         private int polling_interval_in_seconds = 60; | ||||
|          | ||||
|         //private GaimBuddyListReader list = new GaimBuddyListReader (); | ||||
| 
 | ||||
|         public UrkLogQueryable () : base ("UrkLogIndex") | ||||
|         { | ||||
|             config_dir = Path.Combine (PathFinder.HomeDir, ".urk"); | ||||
|             log_dir = Path.Combine (config_dir, "logs"); | ||||
|             icons_dir = Path.Combine (config_dir, "icons"); | ||||
|         } | ||||
| 
 | ||||
|         ///////////////////////////////////////////////// | ||||
|                      | ||||
|         private void StartWorker()  | ||||
|         {     | ||||
|             if (! Directory.Exists (log_dir)) { | ||||
|                 GLib.Timeout.Add (60000, new GLib.TimeoutHandler (CheckForExistence)); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             log.Info ("Starting urk log backend"); | ||||
| 
 | ||||
|             Stopwatch stopwatch = new Stopwatch (); | ||||
|             stopwatch.Start (); | ||||
| 
 | ||||
|             State = QueryableState.Crawling; | ||||
|             Crawl (); | ||||
|             State = QueryableState.Idle; | ||||
| 
 | ||||
|             if (!Inotify.Enabled) { | ||||
|                 Scheduler.Task task = Scheduler.TaskFromHook (new Scheduler.TaskHook (CrawlHook)); | ||||
|                 task.Tag = "Crawling ~/.urk/logs to find new logfiles"; | ||||
|                 task.Source = this; | ||||
|                 ThisScheduler.Add (task); | ||||
|             } | ||||
| 
 | ||||
|             stopwatch.Stop (); | ||||
| 
 | ||||
|             log.Info ("urk log backend worker thread done in {0}", stopwatch);  | ||||
|         } | ||||
|          | ||||
|         public override void Start ()  | ||||
|         { | ||||
|             base.Start (); | ||||
|              | ||||
|             ExceptionHandlingThread.Start (new ThreadStart (StartWorker)); | ||||
|         } | ||||
| 
 | ||||
|         ///////////////////////////////////////////////// | ||||
| 
 | ||||
|         private void CrawlHook (Scheduler.Task task) | ||||
|         { | ||||
|             Crawl (); | ||||
|             task.Reschedule = true; | ||||
|             task.TriggerTime = DateTime.Now.AddSeconds (polling_interval_in_seconds); | ||||
|         } | ||||
| 
 | ||||
|         private void Crawl () | ||||
|         { | ||||
|             Inotify.Subscribe (log_dir, OnInotifyNewProtocol, Inotify.EventType.Create); | ||||
| 
 | ||||
|             // Walk through protocol subdirs | ||||
|             foreach (string proto_dir in DirectoryWalker.GetDirectories (log_dir)) | ||||
|                 CrawlProtocolDirectory (proto_dir); | ||||
|         } | ||||
| 
 | ||||
|         private void CrawlNetworkDirectory (string proto_dir) | ||||
|         { | ||||
|             Inotify.Subscribe (proto_dir, OnInotifyNewTarget, Inotify.EventType.Create); | ||||
| 
 | ||||
|             // Walk through accounts | ||||
|             foreach (string account_dir in DirectoryWalker.GetDirectories (proto_dir)) | ||||
|                 CrawlTargetDirectory (account_dir); | ||||
|         } | ||||
| 
 | ||||
|         private void CrawlTargetDirectory (string account_dir) | ||||
|         { | ||||
|             Inotify.Subscribe (account_dir, OnInotifyNewRemote, Inotify.EventType.Create); | ||||
| 
 | ||||
|             // Walk through remote user conversations | ||||
|             foreach (string remote_dir in DirectoryWalker.GetDirectories (account_dir)) | ||||
|                 CrawlRemoteDirectory (remote_dir); | ||||
|         } | ||||
| 
 | ||||
|         private void CrawlRemoteDirectory (string remote_dir) | ||||
|         { | ||||
|             Inotify.Subscribe (remote_dir, OnInotifyNewConversation, Inotify.EventType.CloseWrite); | ||||
| 
 | ||||
|             foreach (FileInfo file in DirectoryWalker.GetFileInfos (remote_dir)) | ||||
|                 if (FileIsInteresting (file.Name)) | ||||
|                     IndexLog (file.FullName, Scheduler.Priority.Delayed); | ||||
|         } | ||||
| 
 | ||||
|         ///////////////////////////////////////////////// | ||||
| 
 | ||||
|         private bool CheckForExistence () | ||||
|         { | ||||
|             if (!Directory.Exists (log_dir)) | ||||
|                 return true; | ||||
| 
 | ||||
|             this.Start (); | ||||
| 
 | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         private bool FileIsInteresting (string filename) | ||||
|         { | ||||
|             // Filename must be fixed length, see below | ||||
|             if (filename.Length < 21 || filename.Length > 22) | ||||
|                 return false; | ||||
| 
 | ||||
|             // Check match on regex: ^[0-9]{4}-[0-9]{2}-[0-9]{2}\\.[0-9]{6}\\.(txt|html)$ | ||||
|             // e.g. 2005-07-22.161521.txt | ||||
|             // We'd use System.Text.RegularExpressions if they werent so much more expensive | ||||
|             return Char.IsDigit (filename [0]) && Char.IsDigit (filename [1]) | ||||
|                 && Char.IsDigit (filename [2]) && Char.IsDigit (filename [3]) | ||||
|                 && filename [4] == '-' | ||||
|                 && Char.IsDigit (filename [5]) && Char.IsDigit (filename [6]) | ||||
|                 && filename [7] == '-' | ||||
|                 && Char.IsDigit (filename [8]) && Char.IsDigit (filename [9]) | ||||
|                 && filename [10] == '.' | ||||
|                 && Char.IsDigit (filename [11]) && Char.IsDigit (filename [12]) | ||||
|                 && Char.IsDigit (filename [13]) && Char.IsDigit (filename [14]) | ||||
|                 && Char.IsDigit (filename [15]) && Char.IsDigit (filename [16]) | ||||
|                 && filename [17] == '.' | ||||
|                 &&  (    (filename [18] == 't' && filename [19] == 'x' && filename [20] == 't') | ||||
|                     ||    (filename [18] == 'h' && filename [19] == 't' && filename [20] == 'm' && filename [21] == 'l') | ||||
|                     ); | ||||
|         } | ||||
| 
 | ||||
|         ///////////////////////////////////////////////// | ||||
| 
 | ||||
|         private void OnInotifyNewNetwork (Inotify.Watch watch, | ||||
|                         string path, string subitem, string srcpath, | ||||
|                         Inotify.EventType type) | ||||
|         { | ||||
|             if (subitem.Length == 0 || (type & Inotify.EventType.IsDirectory) == 0) | ||||
|                 return; | ||||
| 
 | ||||
|             CrawlNetworkDirectory (Path.Combine (path, subitem)); | ||||
|         } | ||||
| 
 | ||||
|         private void OnInotifyNewTarget (Inotify.Watch watch, | ||||
|                         string path, string subitem, string srcpath, | ||||
|                         Inotify.EventType type) | ||||
|         { | ||||
|             if (subitem.Length == 0 || (type & Inotify.EventType.IsDirectory) == 0) | ||||
|                 return; | ||||
| 
 | ||||
|             CrawlTargetDirectory (Path.Combine (path, subitem)); | ||||
|         } | ||||
| 
 | ||||
|         private void OnInotifyNewRemote (Inotify.Watch watch, | ||||
|                         string path, string subitem, string srcpath, | ||||
|                         Inotify.EventType type) | ||||
|         { | ||||
|             if (subitem.Length == 0 || (type & Inotify.EventType.IsDirectory) == 0) | ||||
|                 return; | ||||
| 
 | ||||
|             CrawlRemoteDirectory (Path.Combine (path, subitem)); | ||||
|         } | ||||
| 
 | ||||
|         private void OnInotifyNewConversation (Inotify.Watch watch, | ||||
|                         string path, string subitem, string srcpath, | ||||
|                         Inotify.EventType type) | ||||
|         { | ||||
|             if (subitem.Length == 0 || (type & Inotify.EventType.IsDirectory) != 0) | ||||
|                 return; | ||||
| 
 | ||||
|             if (FileIsInteresting (subitem)) | ||||
|                 IndexLog (Path.Combine (path, subitem), Scheduler.Priority.Immediate);             | ||||
|         } | ||||
| 
 | ||||
|         ///////////////////////////////////////////////// | ||||
|          | ||||
|         private static Indexable IRCLogToIndexable (string filename) | ||||
|         { | ||||
|             Uri uri = UriFu.PathToFileUri (filename); | ||||
|             Indexable indexable = new Indexable (uri); | ||||
|             indexable.ContentUri = uri; | ||||
|             indexable.Timestamp = File.GetLastWriteTimeUtc (filename); | ||||
|             indexable.MimeType = GaimLog.MimeType; // XXX | ||||
|             indexable.HitType = "IRCLog"; | ||||
|             indexable.CacheContent = false; | ||||
| 
 | ||||
|             return indexable; | ||||
|         } | ||||
| 
 | ||||
|         private void IndexLog (string filename, Scheduler.Priority priority) | ||||
|         { | ||||
|             if (! File.Exists (filename)) | ||||
|                 return; | ||||
| 
 | ||||
|             if (IsUpToDate (filename)) | ||||
|                 return; | ||||
| 
 | ||||
|             Indexable indexable = IRCLogToIndexable (filename); | ||||
|             Scheduler.Task task = NewAddTask (indexable); | ||||
|             task.Priority = priority; | ||||
|             task.SubPriority = 0; | ||||
|             ThisScheduler.Add (task); | ||||
|         } | ||||
| 
 | ||||
|         override protected double RelevancyMultiplier (Hit hit) | ||||
|         { | ||||
|             return HalfLifeMultiplierFromProperty (hit, 0.25, | ||||
|                                    "fixme:endtime", "fixme:starttime"); | ||||
|         } | ||||
| 
 | ||||
|         override protected bool HitFilter (Hit hit)  | ||||
|         { | ||||
|             /*ImBuddy buddy = list.Search (hit ["fixme:speakingto"]); | ||||
|              | ||||
|             if (buddy != null) { | ||||
|                 if (buddy.Alias != "") | ||||
|                     hit ["fixme:speakingto_alias"] = buddy.Alias; | ||||
|                  | ||||
|                 //if (buddy.BuddyIconLocation != "") | ||||
|                 //  hit ["fixme:speakingto_icon"] = Path.Combine (icons_dir, buddy.BuddyIconLocation); | ||||
|             }*/ | ||||
|              | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -1,101 +0,0 @@ | ||||
| import os | ||||
| import sys | ||||
| import traceback | ||||
| import events | ||||
| import windows | ||||
| 
 | ||||
| urkpath = os.path.abspath(os.path.dirname(__file__)) | ||||
| 
 | ||||
| if os.path.abspath(os.curdir) != os.path.join(urkpath): | ||||
|     sys.path[0] = os.path.join(urkpath)     | ||||
| 
 | ||||
| sys.path = [ | ||||
|     os.path.join(urkpath, "scripts"), | ||||
|     ] + sys.path | ||||
| 
 | ||||
| script_path = urkpath+"/scripts" | ||||
| 
 | ||||
| from ui import * | ||||
| 
 | ||||
| # Here I'm trying to handle the original URL IRC Client, urk don't use | ||||
| # normal classes . Let's try to get a urk widget: | ||||
| class Trigger(object): | ||||
|     def __init__(self): | ||||
|         self._mods = [] | ||||
|         self.events = events | ||||
|         self._load_scripts() | ||||
| 
 | ||||
|     def _load_scripts(self): | ||||
|         script_path = urkpath + "/scripts" | ||||
|         try: | ||||
|             suffix = os.extsep+"py" | ||||
|             for script in os.listdir(script_path): | ||||
|                 if script.endswith(suffix): | ||||
|                     try: | ||||
|                         mod = self.events.load(script) | ||||
|                         self._mods.append(mod) | ||||
|                     except: | ||||
|                         traceback.print_exc() | ||||
|                         print "Failed loading script %s." % script | ||||
|         except OSError: | ||||
|             pass | ||||
| 
 | ||||
|     def get_modules(self): | ||||
|         return self._mods | ||||
| 
 | ||||
| class Core(object): | ||||
|     def __init__(self): | ||||
|         self.window = None | ||||
|         self.trigger = Trigger() | ||||
|         self.events = self.trigger.events | ||||
|         self.manager = widgets.UrkUITabs(self) | ||||
|         self.channels = [] | ||||
| 
 | ||||
|         mods = self.trigger.get_modules() | ||||
|         for m in mods: | ||||
|             m.core = self | ||||
|             m.manager = self.manager | ||||
| 
 | ||||
|         if not self.window: | ||||
|            self.window = windows.new(windows.StatusWindow, irc.Network(self), "status", self) | ||||
|            self.window.activate() | ||||
| 
 | ||||
|         register_idle(self.trigger_start) | ||||
|         gtk.gdk.threads_enter() | ||||
| 
 | ||||
|     def run_command(self, command): | ||||
|         offset = 0 | ||||
|         if command[0] == '/': | ||||
|             offset = 1 | ||||
| 
 | ||||
|         self.events.run(command[offset:], self.manager.get_active(), self.window.network) | ||||
| 
 | ||||
|     def trigger_start(self): | ||||
|         self.events.trigger("Start") | ||||
| 
 | ||||
|     def _add_script(self, module): | ||||
|         return  | ||||
| 
 | ||||
| class Client(object): | ||||
|     def __init__(self): | ||||
|         self.core = Core() | ||||
|         self.widget = self.core.manager.box | ||||
|      | ||||
|     def run_command(self, command): | ||||
|         self.core.run_command(command) | ||||
| 
 | ||||
|     def join_server(self, network_name, port=6667): | ||||
|         command = 'server '+ network_name + ' ' + str(port) | ||||
|         self.run_command(command) | ||||
| 
 | ||||
|     def get_widget(self): | ||||
|         return self.widget | ||||
| 
 | ||||
|     def show(self): | ||||
|         self.widget.show_all() | ||||
| 
 | ||||
|     def add_channel(self, channel): | ||||
|         self.core.channels.append(channel) | ||||
| 
 | ||||
|     def clear_channels(self): | ||||
|         self.core.channels = [] | ||||
| @ -1,86 +0,0 @@ | ||||
| import os | ||||
| urkpath = os.path.dirname(__file__) | ||||
| 
 | ||||
| def path(filename=""): | ||||
|     if filename: | ||||
|         return os.path.join(urkpath, filename) | ||||
|     else: | ||||
|         return urkpath | ||||
| 
 | ||||
| if os.access(path('profile'),os.F_OK) or os.path.expanduser("~") == "~": | ||||
|     userpath = path('profile') | ||||
|     if not os.access(userpath,os.F_OK): | ||||
|         os.mkdir(userpath) | ||||
|     if not os.access(os.path.join(userpath,'scripts'),os.F_OK): | ||||
|         os.mkdir(os.path.join(userpath,'scripts')) | ||||
| else: | ||||
|     userpath = os.path.join(os.path.expanduser("~"), ".urk") | ||||
|     if not os.access(userpath,os.F_OK): | ||||
|         os.mkdir(userpath, 0700) | ||||
|     if not os.access(os.path.join(userpath,'scripts'),os.F_OK): | ||||
|         os.mkdir(os.path.join(userpath,'scripts'), 0700) | ||||
| 
 | ||||
| CONF_FILE = os.path.join(userpath,'urk.conf') | ||||
| 
 | ||||
| 
 | ||||
| def pprint(obj, depth=-2): | ||||
|     depth += 2 | ||||
|      | ||||
|     string = [] | ||||
| 
 | ||||
|     if isinstance(obj, dict): | ||||
|         if obj: | ||||
|             string.append('{\n') | ||||
|          | ||||
|             for key in obj: | ||||
|                 string.append('%s%s%s' % (' '*depth, repr(key), ': ')) | ||||
|                 string += pprint(obj[key], depth) | ||||
|                  | ||||
|             string.append('%s%s' % (' '*depth, '},\n')) | ||||
|              | ||||
|         else: | ||||
|             string.append('{},\n') | ||||
|          | ||||
|     elif isinstance(obj, list): | ||||
|         if obj: | ||||
|             string.append('[\n') | ||||
|          | ||||
|             for item in obj: | ||||
|                 string.append('%s' % (' '*depth)) | ||||
|                 string += pprint(item, depth) | ||||
|                  | ||||
|             string.append('%s%s' % (' '*depth, '],\n')) | ||||
|              | ||||
|         else: | ||||
|             string.append('[],\n') | ||||
|          | ||||
|     else: | ||||
|         string.append('%s,\n' % (repr(obj),)) | ||||
|          | ||||
|     if depth: | ||||
|         return string | ||||
|     else: | ||||
|         return ''.join(string)[:-2] | ||||
| 
 | ||||
| def save(*args): | ||||
|     new_file = not os.access(CONF_FILE,os.F_OK) | ||||
|     fd = file(CONF_FILE, "wb") | ||||
|     try: | ||||
|         if new_file: | ||||
|             os.chmod(CONF_FILE,0600) | ||||
|         fd.write(pprint(conf)) | ||||
|     finally: | ||||
|         fd.close() | ||||
| 
 | ||||
| #events.register('Exit', 'post', save) | ||||
| 
 | ||||
| try: | ||||
|     conf = eval(file(CONF_FILE, "U").read().strip()) | ||||
| except IOError, e: | ||||
|     if e.args[0] == 2: | ||||
|         conf = {} | ||||
|     else: | ||||
|         raise | ||||
|      | ||||
| if __name__ == '__main__': | ||||
|     print pprint(conf) | ||||
| @ -1,292 +0,0 @@ | ||||
| import sys | ||||
| import os | ||||
| import traceback | ||||
| 
 | ||||
| pyending = os.extsep + 'py' | ||||
| 
 | ||||
| class error(Exception): | ||||
|     pass | ||||
| 
 | ||||
| class EventStopError(error): | ||||
|     pass | ||||
| 
 | ||||
| class CommandError(error): | ||||
|     pass | ||||
| 
 | ||||
| class data(object): | ||||
|     done = False | ||||
|     quiet = False | ||||
|      | ||||
|     def __init__(self, **kwargs): | ||||
|         for attr in kwargs.items(): | ||||
|             setattr(self, *attr) | ||||
| 
 | ||||
| trigger_sequence = ("pre", "setup", "on", "setdown", "post") | ||||
| 
 | ||||
| all_events = {} | ||||
| loaded = {} | ||||
| 
 | ||||
| # An event has occurred, the e_name event! | ||||
| def trigger(e_name, e_data=None, **kwargs): | ||||
|     if e_data is None: | ||||
|         e_data = data(**kwargs) | ||||
|      | ||||
|     #print 'Event:', e_name, e_data | ||||
|      | ||||
|     failure = True | ||||
|     error = None | ||||
|      | ||||
|     if e_name in all_events: | ||||
|         for e_stage in trigger_sequence: | ||||
|             if e_stage in all_events[e_name]: | ||||
|                 for f_ref, s_name in all_events[e_name][e_stage]: | ||||
|                     try: | ||||
|                         f_ref(e_data) | ||||
|                     except EventStopError: | ||||
|                         return | ||||
|                     except CommandError, e: | ||||
|                         error = e.args | ||||
|                         continue | ||||
|                     except: | ||||
|                         traceback.print_exc() | ||||
|                     failure = False | ||||
|     if failure: | ||||
|         print "Error handling: " + e_name | ||||
|          | ||||
|         return error | ||||
| 
 | ||||
| # Stop all processing of the current event now! | ||||
| def halt(): | ||||
|     raise EventStopError | ||||
| 
 | ||||
| # Registers a specific function with an event at the given sequence stage. | ||||
| def register(e_name, e_stage, f_ref, s_name=""): | ||||
|     global all_events | ||||
|      | ||||
|     if e_name not in all_events: | ||||
|         all_events[e_name] = {} | ||||
|          | ||||
|     if e_stage not in all_events[e_name]: | ||||
|         all_events[e_name][e_stage] = [] | ||||
| 
 | ||||
|     all_events[e_name][e_stage] += [(f_ref, s_name)] | ||||
| 
 | ||||
| # turn a filename (or module name) and trim it to the name of the module | ||||
| def get_scriptname(name): | ||||
|     s_name = os.path.basename(name) | ||||
|     if s_name.endswith(pyending): | ||||
|         s_name = s_name[:-len(pyending)] | ||||
|     return s_name | ||||
| 
 | ||||
| #take a given script name and turn it into a filename | ||||
| def get_filename(name): | ||||
|     # split the directory and filename | ||||
|     dirname = os.path.dirname(name) | ||||
|     s_name = get_scriptname(name) | ||||
|      | ||||
|     for path in dirname and (dirname,) or sys.path: | ||||
|         filename = os.path.join(path, s_name + pyending) | ||||
|         if os.access(filename, os.R_OK): | ||||
|             return filename | ||||
| 
 | ||||
|     raise ImportError("No urk script %s found" % name)  | ||||
| 
 | ||||
| # register the events defined by obj | ||||
| def register_all(name, obj): | ||||
|     # we look through everything defined in the file     | ||||
|     for f in dir(obj): | ||||
|         # for each bit of the event sequence | ||||
|         for e_stage in trigger_sequence: | ||||
| 
 | ||||
|             # if the function is for this bit | ||||
|             if f.startswith(e_stage): | ||||
|                 # get a reference to a function | ||||
|                 f_ref = getattr(obj, f) | ||||
|                 #print f | ||||
|                 # normalise to the event name                 | ||||
|                 e_name = f.replace(e_stage, "", 1) | ||||
|                  | ||||
|                 # add our function to the right event section | ||||
|                 register(e_name, e_stage, f_ref, name) | ||||
|                  | ||||
|                 break | ||||
| 
 | ||||
| #load a .py file into a new module object without affecting sys.modules | ||||
| def load_pyfile(filename): | ||||
|     s_name = get_scriptname(filename) | ||||
|     return __import__(s_name) | ||||
| 
 | ||||
| # Load a python script and register | ||||
| # the functions defined in it for events. | ||||
| # Return True if we loaded the script, False if it was already loaded | ||||
| def load(name): | ||||
|     s_name = get_scriptname(name) | ||||
|     filename = get_filename(name) | ||||
| 
 | ||||
|     if s_name in loaded: | ||||
|         return False | ||||
|      | ||||
|     loaded[s_name] = None | ||||
|      | ||||
|     try: | ||||
|         module = load_pyfile(filename) | ||||
|         loaded[s_name] = module | ||||
|     except: | ||||
|         del loaded[s_name] | ||||
|         raise | ||||
|      | ||||
|     register_all(s_name, loaded[s_name]) | ||||
| 
 | ||||
|     return module | ||||
| 
 | ||||
| # Is the script with the given name loaded? | ||||
| def is_loaded(name): | ||||
|     return get_scriptname(name) in loaded | ||||
| 
 | ||||
| # Remove any function which was defined in the given script | ||||
| def unload(name): | ||||
|     s_name = get_scriptname(name) | ||||
|      | ||||
|     del loaded[s_name] | ||||
| 
 | ||||
|     for e_name in list(all_events): | ||||
|         for e_stage in list(all_events[e_name]): | ||||
|             to_check = all_events[e_name][e_stage] | ||||
| 
 | ||||
|             all_events[e_name][e_stage] = [(f, m) for f, m in to_check if m != s_name] | ||||
|              | ||||
|             if not all_events[e_name][e_stage]: | ||||
|                 del all_events[e_name][e_stage] | ||||
|          | ||||
|         if not all_events[e_name]: | ||||
|             del all_events[e_name] | ||||
| 
 | ||||
| def reload(name): | ||||
|     s_name = get_scriptname(name) | ||||
| 
 | ||||
|     if s_name not in loaded: | ||||
|         return False | ||||
|      | ||||
|     temp = loaded[s_name] | ||||
|      | ||||
|     unload(s_name) | ||||
| 
 | ||||
|     try: | ||||
|         load(name) | ||||
|         return True | ||||
|     except: | ||||
|         loaded[s_name] = temp | ||||
|         register_all(s_name, temp) | ||||
|         raise | ||||
| 
 | ||||
| def run(text, window, network): | ||||
|     split = text.split(' ') | ||||
| 
 | ||||
|     c_data = data(name=split.pop(0), text=text, window=window, network=network) | ||||
|      | ||||
|     if split and split[0].startswith('-'): | ||||
|         c_data.switches = set(split.pop(0)[1:]) | ||||
|     else: | ||||
|         c_data.switches = set() | ||||
|      | ||||
|     c_data.args = split | ||||
| 
 | ||||
|     event_name = "Command" + c_data.name.capitalize() | ||||
| 
 | ||||
|     if event_name in all_events: | ||||
|         result = trigger(event_name, c_data) | ||||
|          | ||||
|         if result: | ||||
|             c_data.window.write("* /%s: %s" % (c_data.name, result[0])) | ||||
|     else: | ||||
|         trigger("Command", c_data) | ||||
| 
 | ||||
|         if not c_data.done: | ||||
|             c_data.window.write("* /%s: No such command exists" % (c_data.name)) | ||||
| 
 | ||||
| def onCommandPyeval(e): | ||||
|     loc = sys.modules.copy() | ||||
|     loc.update(e.__dict__) | ||||
|     import pydoc #fix nonresponsive help() command | ||||
|     old_pager, pydoc.pager = pydoc.pager, pydoc.plainpager  | ||||
|     try: | ||||
|         result = repr(eval(' '.join(e.args), loc)) | ||||
|         if 's' in e.switches: | ||||
|             run( | ||||
|                 'say - %s => %s' % (' '.join(e.args),result), | ||||
|                 e.window, | ||||
|                 e.network | ||||
|                 ) | ||||
|         else: | ||||
|             e.window.write(result) | ||||
|     except: | ||||
|         for line in traceback.format_exc().split('\n'): | ||||
|             e.window.write(line) | ||||
|     pydoc.pager = old_pager | ||||
| 
 | ||||
| def onCommandPyexec(e): | ||||
|     loc = sys.modules.copy() | ||||
|     loc.update(e.__dict__) | ||||
|     import pydoc #fix nonresponsive help() command | ||||
|     old_pager, pydoc.pager = pydoc.pager, pydoc.plainpager  | ||||
|     try: | ||||
|         exec ' '.join(e.args) in loc | ||||
|     except: | ||||
|         for line in traceback.format_exc().split('\n'): | ||||
|             e.window.write(line) | ||||
|     pydoc.pager = old_pager | ||||
| 
 | ||||
| def onCommandLoad(e): | ||||
|     if e.args: | ||||
|         name = e.args[0] | ||||
|     else: | ||||
|         e.window.write('Usage: /load scriptname') | ||||
| 
 | ||||
|     try: | ||||
|         if load(name): | ||||
|             e.window.write("* The script '%s' has been loaded." % name) | ||||
|         else: | ||||
|             raise CommandError("The script is already loaded; use /reload instead") | ||||
|     except: | ||||
|         e.window.write(traceback.format_exc(), line_ending='') | ||||
|         raise CommandError("Error loading the script") | ||||
| 
 | ||||
| def onCommandUnload(e): | ||||
|     if e.args: | ||||
|         name = e.args[0] | ||||
|     else: | ||||
|         e.window.write('Usage: /unload scriptname') | ||||
| 
 | ||||
|     if is_loaded(name): | ||||
|         unload(name) | ||||
|         e.window.write("* The script '%s' has been unloaded." % name) | ||||
|     else: | ||||
|         raise CommandError("No such script is loaded") | ||||
| 
 | ||||
| def onCommandReload(e): | ||||
|     if e.args: | ||||
|         name = e.args[0] | ||||
|     else: | ||||
|         e.window.write('Usage: /reload scriptname') | ||||
| 
 | ||||
|     try: | ||||
|         if reload(name): | ||||
|             e.window.write("* The script '%s' has been reloaded." % name) | ||||
|         else: | ||||
|             raise CommandError("The script isn't loaded yet; use /load instead")  | ||||
|     except: | ||||
|         e.window.write(traceback.format_exc(), line_ending='') | ||||
| 
 | ||||
| def onCommandScripts(e): | ||||
|     e.window.write("Loaded scripts:") | ||||
|     for name in loaded: | ||||
|         e.window.write("* %s" % name) | ||||
| 
 | ||||
| def onCommandEcho(e): | ||||
|     e.window.write(' '.join(e.args)) | ||||
| 
 | ||||
| name = '' | ||||
| for name in globals(): | ||||
|     if name.startswith('onCommand'): | ||||
|         register(name[2:], "on", globals()[name], '_all_events') | ||||
| del name | ||||
| @ -1,8 +0,0 @@ | ||||
| name = "PUrk" | ||||
| long_name = "Purk IRC" | ||||
| version = 0, 1, "git" | ||||
| long_version = "%s v%s" % (long_name, ".".join(str(x) for x in version)) | ||||
| website = "http://dev.laptop.org/" | ||||
| authors = ["Vincent Povirk", "Marc Liddell"] | ||||
| copyright = "2005 %s" % ', '.join(authors) | ||||
| 
 | ||||
| @ -1,333 +0,0 @@ | ||||
| import socket | ||||
| import sys | ||||
| 
 | ||||
| from conf import conf | ||||
| import ui | ||||
| import windows | ||||
| import info | ||||
| 
 | ||||
| DISCONNECTED = 0 | ||||
| CONNECTING = 1 | ||||
| INITIALIZING = 2 | ||||
| CONNECTED = 3 | ||||
| 
 | ||||
| def parse_irc(msg, server): | ||||
|     msg = msg.split(' ') | ||||
|      | ||||
|     # if our very first character is : | ||||
|     # then this is the source,  | ||||
|     # otherwise insert the server as the source | ||||
|     if msg and msg[0].startswith(':'): | ||||
|         msg[0] = msg[0][1:] | ||||
|     else: | ||||
|         msg.insert(0, server) | ||||
|      | ||||
|     # loop through the msg until we find  | ||||
|     # something beginning with : | ||||
|     for i, token in enumerate(msg): | ||||
|         if token.startswith(':'): | ||||
|             # remove the : | ||||
|             msg[i] = msg[i][1:] | ||||
|              | ||||
|             # join up the rest | ||||
|             msg[i:] = [' '.join(msg[i:])] | ||||
|             break | ||||
|      | ||||
|     # filter out the empty pre-":" tokens and add on the text to the end | ||||
|     return [m for m in msg[:-1] if m] + msg[-1:] | ||||
|      | ||||
|     # note: this sucks and makes very little sense, but it matches the BNF | ||||
|     #       as far as we've tested, which seems to be the goal | ||||
| 
 | ||||
| def default_nicks(): | ||||
|     try: | ||||
|         nicks = [conf.get('nick')] + conf.get('altnicks',[]) | ||||
|         if not nicks[0]: | ||||
|             import getpass | ||||
|             nicks = [getpass.getuser()] | ||||
|     except: | ||||
|         nicks = ["mrurk"] | ||||
|     return nicks | ||||
| 
 | ||||
| class Network(object): | ||||
|     socket = None | ||||
|      | ||||
|     def __init__(self, core, server="irc.default.org", port=6667, nicks=[],  | ||||
|                     username="", fullname="", name=None, **kwargs): | ||||
|         self.core = core | ||||
|         self.manager = core.manager | ||||
|         self.server = server | ||||
|         self.port = port | ||||
|         self.events = core.events | ||||
| 
 | ||||
|         self.name = name or server | ||||
|          | ||||
|         self.nicks = nicks or default_nicks() | ||||
|         self.me = self.nicks[0] | ||||
|          | ||||
|         self.username = username or "urk" | ||||
|         self.fullname = fullname or conf.get("fullname", self.username) | ||||
|         self.password = '' | ||||
|          | ||||
|         self.isupport = { | ||||
|             'NETWORK': server,  | ||||
|             'PREFIX': '(ohv)@%+', | ||||
|             'CHANMODES': 'b,k,l,imnpstr', | ||||
|         } | ||||
|         self.prefixes = {'o':'@', 'h':'%', 'v':'+', '@':'o', '%':'h', '+':'v'} | ||||
|          | ||||
|         self.status = DISCONNECTED | ||||
|         self.failedhosts = [] #hosts we've tried and failed to connect to | ||||
|         self.channel_prefixes = '&#+$'   # from rfc2812 | ||||
|          | ||||
|         self.on_channels = set() | ||||
|         self.requested_joins = set() | ||||
|         self.requested_parts = set() | ||||
|          | ||||
|         self.buffer = '' | ||||
|      | ||||
|     #called when we get a result from the dns lookup | ||||
|     def on_dns(self, result, error): | ||||
|         if error: | ||||
|             self.disconnect(error=error[1]) | ||||
|         else: | ||||
|             #import os | ||||
|             #import random | ||||
|             #random.seed() | ||||
|             #random.shuffle(result) | ||||
|             if socket.has_ipv6: #prefer ipv6 | ||||
|                 result = [(f, t, p, c, a) for (f, t, p, c, a) in result if f == socket.AF_INET6]+result | ||||
|             elif hasattr(socket,"AF_INET6"): #ignore ipv6 | ||||
|                 result = [(f, t, p, c, a) for (f, t, p, c, a) in result if f != socket.AF_INET6] | ||||
|              | ||||
|             self.failedlasthost = False | ||||
|              | ||||
|             for f, t, p, c, a in result: | ||||
|                 if (f, t, p, c, a) not in self.failedhosts: | ||||
|                     try: | ||||
|                         self.socket = socket.socket(f, t, p) | ||||
|                     except: | ||||
|                         continue | ||||
|                     self.source = ui.fork(self.on_connect, self.socket.connect, a) | ||||
|                     self.failedhosts.append((f, t, p, c, a)) | ||||
|                     if set(self.failedhosts) >= set(result): | ||||
|                         self.failedlasthost = True | ||||
|                     break | ||||
|             else: | ||||
|                 self.failedlasthost = True | ||||
|                 if len(result): | ||||
|                     self.failedhosts[:] = (f, t, p, c, a), | ||||
|                     f, t, p, c, a = result[0] | ||||
|                     try: | ||||
|                         self.socket = socket.socket(f, t, p) | ||||
|                         self.source = ui.fork(self.on_connect, self.socket.connect, a) | ||||
|                     except: | ||||
|                         self.disconnect(error="Couldn't find a host we can connect to") | ||||
|                 else: | ||||
|                     self.disconnect(error="Couldn't find a host we can connect to") | ||||
|      | ||||
|     #called when socket.open() returns | ||||
|     def on_connect(self, result, error): | ||||
|         if error: | ||||
|             self.disconnect(error=error[1]) | ||||
|             #we should immediately retry if we failed to open the socket and there are hosts left | ||||
|             if self.status == DISCONNECTED and not self.failedlasthost: | ||||
|                 windows.get_default(self, self.core.manager).write("* Retrying with next available host") | ||||
|                 self.connect() | ||||
|         else: | ||||
|             self.source = source = ui.Source() | ||||
|             self.status = INITIALIZING | ||||
|             self.failedhosts[:] = () | ||||
|              | ||||
|             self.events.trigger('SocketConnect', network=self) | ||||
|              | ||||
|             if source.enabled: | ||||
|                 self.source = ui.fork(self.on_read, self.socket.recv, 8192) | ||||
|          | ||||
|         # Auto join channels on connect | ||||
|         for channel in self.core.channels: | ||||
|             self.core.run_command("/join %s" % channel) | ||||
| 
 | ||||
|     #called when we read data or failed to read data | ||||
|     def on_read(self, result, error): | ||||
|         if error: | ||||
|             self.disconnect(error=error[1]) | ||||
|         elif not result: | ||||
|             self.disconnect(error="Connection closed by remote host") | ||||
|         else: | ||||
|             self.source = source = ui.Source() | ||||
|              | ||||
|             self.buffer = (self.buffer + result).split("\r\n") | ||||
|              | ||||
|             for line in self.buffer[:-1]: | ||||
|                 self.got_msg(line) | ||||
|              | ||||
|             if self.buffer: | ||||
|                 self.buffer = self.buffer[-1] | ||||
|             else: | ||||
|                 self.buffer = '' | ||||
|              | ||||
|             if source.enabled: | ||||
|                 self.source = ui.fork(self.on_read, self.socket.recv, 8192)     | ||||
| 
 | ||||
|     def raw(self, msg): | ||||
|         self.events.trigger("OwnRaw", network=self, raw=msg) | ||||
|          | ||||
|         if self.status >= INITIALIZING: | ||||
|             self.socket.send(msg + "\r\n") | ||||
|          | ||||
|     def got_msg(self, msg): | ||||
|         pmsg = parse_irc(msg, self.server) | ||||
|      | ||||
|         e_data = self.events.data( | ||||
|                     raw=msg, | ||||
|                     msg=pmsg, | ||||
|                     text=pmsg[-1], | ||||
|                     network=self, | ||||
|                     window=windows.get_default(self, self.manager) | ||||
|                     ) | ||||
|          | ||||
|         if "!" in pmsg[0]: | ||||
|             e_data.source, e_data.address = pmsg[0].split('!',1) | ||||
|              | ||||
|         else: | ||||
|             e_data.source, e_data.address = pmsg[0], '' | ||||
|          | ||||
|         if len(pmsg) > 2: | ||||
|             e_data.target = pmsg[2] | ||||
|         else: | ||||
|             e_data.target = pmsg[-1] | ||||
|          | ||||
|         self.events.trigger('Raw', e_data) | ||||
|      | ||||
|     def connect(self): | ||||
|         if not self.status: | ||||
|             self.status = CONNECTING | ||||
|              | ||||
|             self.source = ui.fork(self.on_dns, socket.getaddrinfo, self.server, self.port, 0, socket.SOCK_STREAM) | ||||
|              | ||||
|             self.events.trigger('Connecting', network=self) | ||||
|      | ||||
|     def disconnect(self, error=None): | ||||
|         if self.socket: | ||||
|             self.socket.close() | ||||
|          | ||||
|         if self.source: | ||||
|             self.source.unregister() | ||||
|             self.source = None | ||||
|          | ||||
|         self.socket = None | ||||
|          | ||||
|         self.status = DISCONNECTED | ||||
|          | ||||
|         #note: connecting from onDisconnect is probably a Bad Thing | ||||
|         self.events.trigger('Disconnect', network=self, error=error) | ||||
|          | ||||
|         #trigger a nick change if the nick we want is different from the one we | ||||
|         # had. | ||||
|         if self.me != self.nicks[0]: | ||||
|             self.events.trigger( | ||||
|                 'Nick', network=self, window=windows.get_default(self), | ||||
|                 source=self.me, target=self.nicks[0], address='', | ||||
|                 text=self.nicks[0] | ||||
|                 ) | ||||
|             self.me = self.nicks[0] | ||||
|          | ||||
|     def norm_case(self, string): | ||||
|         return string.lower() | ||||
|      | ||||
|     def quit(self, msg=None): | ||||
|         if self.status: | ||||
|             try: | ||||
|                 if msg == None: | ||||
|                     msg = conf.get('quitmsg', "%s - %s" % (info.long_version, info.website)) | ||||
|                 self.raw("QUIT :%s" % msg) | ||||
|             except: | ||||
|                 pass | ||||
|             self.disconnect() | ||||
|          | ||||
|     def join(self, target, key='', requested=True): | ||||
|         if key: | ||||
|             key = ' '+key | ||||
|         self.raw("JOIN %s%s" % (target,key)) | ||||
|         if requested: | ||||
|             for chan in target.split(' ',1)[0].split(','): | ||||
|                 if chan == '0': | ||||
|                     self.requested_parts.update(self.on_channels) | ||||
|                 else: | ||||
|                     self.requested_joins.add(self.norm_case(chan)) | ||||
|          | ||||
|     def part(self, target, msg="", requested=True): | ||||
|         if msg: | ||||
|             msg = " :" + msg | ||||
|          | ||||
|         self.raw("PART %s%s" % (target, msg)) | ||||
|         if requested: | ||||
|             for chan in target.split(' ',1)[0].split(','): | ||||
|                 self.requested_parts.add(self.norm_case(target)) | ||||
|          | ||||
|     def msg(self, target, msg): | ||||
|         self.raw("PRIVMSG %s :%s" % (target, msg)) | ||||
|          | ||||
|         self.events.trigger( | ||||
|             'OwnText', source=self.me, target=str(target), text=msg, | ||||
|             network=self, window=windows.get_default(self, self.manager) | ||||
|             ) | ||||
| 
 | ||||
|     def notice(self, target, msg): | ||||
|         self.raw("NOTICE %s :%s" % (target, msg)) | ||||
|          | ||||
|         self.events.trigger( | ||||
|             'OwnNotice', source=self.me, target=str(target), text=msg, | ||||
|             network=self, window=windows.get_default(self) | ||||
|             ) | ||||
| 
 | ||||
| #a Network that is never connected | ||||
| class DummyNetwork(Network): | ||||
|     def __nonzero__(self): | ||||
|         return False     | ||||
|      | ||||
|     def __init__(self, core): | ||||
|         Network.__init__(self, core) | ||||
|          | ||||
|         self.name = self.server = self.isupport['NETWORK'] = "None" | ||||
|      | ||||
|     def connect(self): | ||||
|         raise NotImplementedError, "Cannot connect dummy network." | ||||
|      | ||||
|     def raw(self, msg): | ||||
|         raise NotImplementedError, "Cannot send %s over the dummy network." % repr(msg) | ||||
| 
 | ||||
| #dummy_network = DummyNetwork() | ||||
| 
 | ||||
| #this was ported from srvx's tools.c | ||||
| def match_glob(text, glob, t=0, g=0): | ||||
|     while g < len(glob): | ||||
|         if glob[g] in '?*': | ||||
|             star_p = q_cnt = 0 | ||||
|             while g < len(glob): | ||||
|                 if glob[g] == '*': | ||||
|                     star_p = True | ||||
|                 elif glob[g] == '?': | ||||
|                     q_cnt += 1 | ||||
|                 else: | ||||
|                     break | ||||
|                 g += 1 | ||||
|             t += q_cnt | ||||
|             if t > len(text): | ||||
|                 return False | ||||
|             if star_p: | ||||
|                 if g == len(glob): | ||||
|                     return True | ||||
|                 for i in xrange(t, len(text)): | ||||
|                     if text[i] == glob[g] and match_glob(text, glob, i+1, g+1): | ||||
|                         return True | ||||
|                 return False | ||||
|             else: | ||||
|                 if t == len(text) and g == len(glob): | ||||
|                     return True | ||||
|         if t == len(text) or g == len(glob) or text[t] != glob[g]: | ||||
|             return False | ||||
|         t += 1 | ||||
|         g += 1 | ||||
|     return t == len(text) | ||||
| @ -1,457 +0,0 @@ | ||||
| try: | ||||
|     from conf import conf | ||||
| except ImportError: | ||||
|     conf = {} | ||||
| 
 | ||||
| BOLD = '\x02' | ||||
| UNDERLINE = '\x1F' | ||||
| MIRC_COLOR = '\x03' | ||||
| MIRC_COLOR_BG = MIRC_COLOR, MIRC_COLOR | ||||
| BERS_COLOR = '\x04' | ||||
| RESET = '\x0F' | ||||
| 
 | ||||
| colors = ( | ||||
|   '#FFFFFF', '#000000', '#00007F', '#009300',  | ||||
|   '#FF0000', '#7F0000', '#9C009C', '#FF7F00', | ||||
|   '#FFFF00', '#00FF00', '#009393', '#00FFFF', | ||||
|   '#0000FF', '#FF00FF', '#7F7F7F', '#D2D2D2' | ||||
|   ) | ||||
| 
 | ||||
| def get_mirc_color(number): | ||||
|     if number == '99': | ||||
|         return None | ||||
| 
 | ||||
|     number = int(number) & 15 | ||||
|      | ||||
|     confcolors = conf.get('colors', colors) | ||||
|     try: | ||||
|         return confcolors[number] | ||||
|     except: | ||||
|         # someone edited their colors wrongly | ||||
|         return colors[number] | ||||
|      | ||||
| DEC_DIGITS, HEX_DIGITS = set('0123456789'), set('0123456789abcdefABCDEF') | ||||
| 
 | ||||
| def parse_mirc_color(string, pos, open_tags, tags): | ||||
|     color_chars = 1 | ||||
| 
 | ||||
|     if MIRC_COLOR in open_tags: | ||||
|         fgtag = open_tags.pop(MIRC_COLOR) | ||||
|         fgtag['to'] = pos | ||||
|         tags.append(fgtag) | ||||
|      | ||||
|     if MIRC_COLOR_BG in open_tags: | ||||
|         bgtag = open_tags.pop(MIRC_COLOR_BG) | ||||
|         bgtag['to'] = pos | ||||
|         tags.append(bgtag) | ||||
| 
 | ||||
|         bg = bgtag['data'][1] | ||||
|     else: | ||||
|         bg = None | ||||
| 
 | ||||
|     if string[0] in DEC_DIGITS:    | ||||
|         if string[1] in DEC_DIGITS: | ||||
|             fg = get_mirc_color(string[:2]) | ||||
|             string = string[1:] | ||||
|             color_chars += 2 | ||||
| 
 | ||||
|         else: | ||||
|             fg = get_mirc_color(string[0]) | ||||
|             color_chars += 1 | ||||
|          | ||||
|         if string[1] == "," and string[2] in DEC_DIGITS: | ||||
|             if string[3] in DEC_DIGITS: | ||||
|                 bg = get_mirc_color(string[2:4]) | ||||
|                 color_chars += 3 | ||||
| 
 | ||||
|             else: | ||||
|                 bg = get_mirc_color(string[2]) | ||||
|                 color_chars += 2 | ||||
|      | ||||
|     else: | ||||
|         fg = bg = None | ||||
|      | ||||
|     if fg: | ||||
|         open_tags[MIRC_COLOR] = {'data': ("foreground",fg), 'from': pos} | ||||
|     else: | ||||
|         open_tags.pop(MIRC_COLOR,None) | ||||
|      | ||||
|     if bg: | ||||
|         open_tags[MIRC_COLOR_BG] = {'data': ("background",bg), 'from': pos} | ||||
|     else: | ||||
|         open_tags.pop(MIRC_COLOR_BG,None) | ||||
|                | ||||
|     return color_chars | ||||
| 
 | ||||
| def parse_bersirc_color(string, pos, open_tags, tags): | ||||
|     bg = None | ||||
|     if MIRC_COLOR in open_tags: | ||||
|         tag = open_tags.pop(MIRC_COLOR) | ||||
|         tag['to'] = pos | ||||
|         tags.append(tag) | ||||
|          | ||||
|     if MIRC_COLOR_BG in open_tags: | ||||
|         bgtag = open_tags.pop(MIRC_COLOR_BG) | ||||
|         bgtag['to'] = pos | ||||
|         tags.append(bgtag) | ||||
| 
 | ||||
|         bg = bgtag['data'][1] | ||||
|      | ||||
|     for c in (0, 1, 2, 3, 4, 5): | ||||
|         if string[c] not in HEX_DIGITS: | ||||
|             return 1 | ||||
|     fg = '#' + string[:6].upper() | ||||
| 
 | ||||
|     color_chars = 7 | ||||
|     for c in (7, 8, 9, 10, 11, 12): | ||||
|         if string[c] not in HEX_DIGITS: | ||||
|             break | ||||
|     else: | ||||
|         if string[6] == ",": | ||||
|             bg = '#' + string[7:13].upper() | ||||
|             color_chars = 14 | ||||
|          | ||||
|     if fg: | ||||
|         open_tags[MIRC_COLOR] = {'data': ("foreground",fg), 'from': pos} | ||||
|     else: | ||||
|         open_tags.pop(MIRC_COLOR,None) | ||||
|      | ||||
|     if bg: | ||||
|         open_tags[MIRC_COLOR_BG] = {'data': ("background",bg), 'from': pos} | ||||
|     else: | ||||
|         open_tags.pop(MIRC_COLOR_BG,None) | ||||
|      | ||||
|     return color_chars | ||||
|    | ||||
| def parse_bold(string, pos, open_tags, tags): | ||||
|     if BOLD in open_tags: | ||||
|         tag = open_tags.pop(BOLD) | ||||
|         tag['to'] = pos | ||||
|         tags.append(tag) | ||||
|          | ||||
|     else: | ||||
|         open_tags[BOLD] = {'data': ('weight', BOLD), 'from': pos} | ||||
| 
 | ||||
|     return 1 | ||||
| 
 | ||||
| def parse_underline(string, pos, open_tags, tags): | ||||
|     if UNDERLINE in open_tags: | ||||
|         tag = open_tags.pop(UNDERLINE) | ||||
|         tag['to'] = pos | ||||
|         tags.append(tag) | ||||
|          | ||||
|     else: | ||||
|         open_tags[UNDERLINE] = {'data': ('underline', UNDERLINE), 'from': pos} | ||||
|          | ||||
|     return 1 | ||||
| 
 | ||||
| def parse_reset(string, pos, open_tags, tags): | ||||
|     for t in open_tags: | ||||
|         tag = open_tags[t] | ||||
|         tag['to'] = pos | ||||
|         tags.append(tag) | ||||
| 
 | ||||
|     open_tags.clear() | ||||
|      | ||||
|     return 1 | ||||
| 
 | ||||
| tag_parser = { | ||||
|     MIRC_COLOR: parse_mirc_color, | ||||
|     BERS_COLOR: parse_bersirc_color, | ||||
|     BOLD: parse_bold, | ||||
|     UNDERLINE: parse_underline, | ||||
|     RESET: parse_reset | ||||
|     } | ||||
| 
 | ||||
| def parse_mirc(string): | ||||
|     string += RESET | ||||
| 
 | ||||
|     out = '' | ||||
|     open_tags = {} | ||||
|     tags = [] | ||||
|     text_i = outtext_i = 0 | ||||
| 
 | ||||
|     for tag_i, char in enumerate(string): | ||||
|         if char in tag_parser: | ||||
|             out += string[text_i:tag_i] | ||||
| 
 | ||||
|             outtext_i += tag_i - text_i | ||||
| 
 | ||||
|             text_i = tag_i + tag_parser[char]( | ||||
|                                 string[tag_i+1:],  | ||||
|                                 outtext_i,  | ||||
|                                 open_tags, | ||||
|                                 tags | ||||
|                                 ) | ||||
| 
 | ||||
|     return tags, out | ||||
| 
 | ||||
| #transforms for unparse_mirc | ||||
| 
 | ||||
| #^O | ||||
| def transform_reset(start, end): | ||||
|     return RESET, '', {} | ||||
| 
 | ||||
| #^K | ||||
| def transform_color_reset(start, end): | ||||
|     if ('foreground' in start and 'foreground' not in end) or \ | ||||
|           ('background' in start and 'background' not in end): | ||||
|         result = start.copy() | ||||
|         result.pop("foreground",None) | ||||
|         result.pop("background",None) | ||||
|         return MIRC_COLOR, DEC_DIGITS, result | ||||
|     else: | ||||
|         return '','',start | ||||
| 
 | ||||
| #^KXX | ||||
| def transform_color(start, end): | ||||
|     if (start.get('foreground',99) != end.get('foreground',99)): | ||||
|         confcolors = conf.get('colors', colors) | ||||
|         result = start.copy() | ||||
|         if 'foreground' in end: | ||||
|             try: | ||||
|                 index = list(confcolors).index(end['foreground'].upper()) | ||||
|             except ValueError: | ||||
|                 return '','',start | ||||
|             result['foreground'] = end['foreground'] | ||||
|         else: | ||||
|             index = 99 | ||||
|             del result['foreground'] | ||||
|         return '\x03%02i' % index, ',', result | ||||
|     else: | ||||
|         return '','',start | ||||
| 
 | ||||
| #^KXX,YY | ||||
| def transform_bcolor(start, end): | ||||
|     if (start.get('background',99) != end.get('background',99)): | ||||
|         confcolors = conf.get('colors', colors) | ||||
|         result = start.copy() | ||||
|         if 'foreground' in end: | ||||
|             try: | ||||
|                 fg_index = list(confcolors).index(end['foreground'].upper()) | ||||
|             except ValueError: | ||||
|                 return '','',start | ||||
|             result['foreground'] = end['foreground'] | ||||
|         else: | ||||
|             fg_index = 99 | ||||
|             result.pop('foreground',None) | ||||
|         if 'background' in end: | ||||
|             try: | ||||
|                 bg_index = list(confcolors).index(end['background'].upper()) | ||||
|             except ValueError: | ||||
|                 return '','',start | ||||
|             result['background'] = end['background'] | ||||
|         else: | ||||
|             bg_index = 99 | ||||
|             del result['background'] | ||||
|         return '\x03%02i,%02i' % (fg_index, bg_index), ',', result | ||||
|     else: | ||||
|         return '','',start | ||||
| 
 | ||||
| #^LXXXXXX | ||||
| def transform_bersirc(start, end): | ||||
|     if 'foreground' in end and end['foreground'] != start.get('foreground'): | ||||
|         result = start.copy() | ||||
|         result['foreground'] = end['foreground'] | ||||
|         return "\x04%s" % end['foreground'][1:], ',', result | ||||
|     else: | ||||
|         return '','',start | ||||
| 
 | ||||
| #^LXXXXXX,YYYYYY | ||||
| def transform_bbersirc(start, end): | ||||
|     if 'foreground' in end and 'background' in end and ( | ||||
|           end['foreground'] != start.get('foreground') or  | ||||
|           end['background'] != start.get('background')): | ||||
|         result = start.copy() | ||||
|         result['foreground'] = end['foreground'] | ||||
|         result['background'] = end['background'] | ||||
|         return "\x04%s,%s" % (end['foreground'][1:], end['background'][1:]), ',', result | ||||
|     else: | ||||
|         return '','',start | ||||
| 
 | ||||
| 
 | ||||
| #^B | ||||
| def transform_underline(start, end): | ||||
|     if ('underline' in start) != ('underline' in end): | ||||
|         result = start.copy() | ||||
|         if 'underline' in start: | ||||
|             del result['underline'] | ||||
|         else: | ||||
|             result['underline'] = UNDERLINE | ||||
|         return UNDERLINE, '', result | ||||
|     else: | ||||
|         return '','',start | ||||
| 
 | ||||
| #^U | ||||
| def transform_bold(start, end): | ||||
|     if ('weight' in start) != ('weight' in end): | ||||
|         result = start.copy() | ||||
|         if 'weight' in start: | ||||
|             del result['weight'] | ||||
|         else: | ||||
|             result['weight'] = BOLD | ||||
|         return BOLD, '', result | ||||
|     else: | ||||
|         return '','',start | ||||
| 
 | ||||
| #^B^B | ||||
| #In some rare circumstances, we HAVE to do this to generate a working string | ||||
| def transform_dbold(start, end): | ||||
|     return BOLD*2, '', start | ||||
| 
 | ||||
| #return the formatting needed to transform one set of format tags to another | ||||
| def transform(start, end, nextchar=" "): | ||||
|     transform_functions = ( | ||||
|         transform_reset, transform_color_reset, transform_color, transform_bcolor, | ||||
|         transform_bersirc, transform_bbersirc, transform_underline,  | ||||
|         transform_bold, transform_dbold, | ||||
|         ) | ||||
|      | ||||
|     candidates = [('','',start)] | ||||
|     result = None | ||||
|      | ||||
|     for f in transform_functions: | ||||
|         for string, badchars, s in candidates[:]: | ||||
|             newstring, badchars, s = f(s, end) | ||||
|             string += newstring | ||||
|             if newstring and (result == None or len(string) < len(result)): | ||||
|                 if nextchar not in badchars and s == end: | ||||
|                     result = string | ||||
|                 else: | ||||
|                     candidates.append((string, badchars, s)) | ||||
|     return result | ||||
| 
 | ||||
| def unparse_mirc(tagsandtext): | ||||
|     lasttags, lastchar = {}, '' | ||||
|      | ||||
|     string = [] | ||||
|     for tags, char in tagsandtext: | ||||
|         if tags != lasttags: | ||||
|             string.append(transform(lasttags, tags, char[0])) | ||||
|         string.append(char) | ||||
|         lasttags, lastchar = tags, char | ||||
|     return ''.join(string) | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     tests = [ | ||||
|         'not\x02bold\x02not', | ||||
|         'not\x1Funderline\x1Fnot', | ||||
|          | ||||
|         "\x02\x1FHi\x0F", | ||||
|          | ||||
|         'not\x030,17white-on-black\x0304red-on-black\x03nothing', | ||||
|          | ||||
|         "\x040000CC<\x04nick\x040000CC>\x04 text", | ||||
|          | ||||
|         '\x04770077,FFFFFFbersirc color with background! \x04000077setting foreground! \x04reset!', | ||||
|          | ||||
|         '\x047700,FFFFbersirc', | ||||
|          | ||||
|         "\x03123Hello", | ||||
|          | ||||
|         "\x0312,Hello", | ||||
|          | ||||
|         "\x034Hello", | ||||
|          | ||||
|         "Bo\x02ld", | ||||
|          | ||||
|         "\x034,5Hello\x036Goodbye", | ||||
|          | ||||
|         "\x04ff0000,00ff00Hello\x040000ffGoodbye", | ||||
|          | ||||
|         "\x04777777(\x0400CCCCstuff\x04777777)\x04", | ||||
|          | ||||
|         '\x0307orange\x04CCCCCCgrey\x0307orange', | ||||
|          | ||||
|         '\x04CCCCCC,444444sdf\x0304jkl', | ||||
|          | ||||
|         '\x0403\x02\x02,trixy', | ||||
|          | ||||
|         '\x04FFFFFF\x02\x02,000000trixy for bersirc', | ||||
|         ] | ||||
|          | ||||
|     results = [ | ||||
|         ([{'from': 3, 'data': ('weight', '\x02'), 'to': 7}], 'notboldnot'), | ||||
| 
 | ||||
|         ([{'from': 3, 'data': ('underline', '\x1f'), 'to': 12}], 'notunderlinenot'), | ||||
| 
 | ||||
|         ([{'from': 0, 'data': ('weight', '\x02'), 'to': 2}, {'from': 0, 'data': ('underline', '\x1f'), 'to': 2}], 'Hi'), | ||||
| 
 | ||||
|         ([{'from': 3, 'data': ('foreground', '#FFFFFF'), 'to': 17}, {'from': 3, 'data': ('background', '#000000'), 'to': 17}, {'from': 17, 'data': ('foreground', '#FF0000'), 'to': 29}, {'from': 17, 'data': ('background', '#000000'), 'to': 29}], 'notwhite-on-blackred-on-blacknothing'), | ||||
| 
 | ||||
|         ([{'from': 0, 'data': ('foreground', '#0000CC'), 'to': 1}, {'from': 5, 'data': ('foreground', '#0000CC'), 'to': 6}], '<nick> text'), | ||||
| 
 | ||||
|         ([{'from': 0, 'data': ('foreground', '#770077'), 'to': 31}, {'from': 0, 'data': ('background', '#FFFFFF'), 'to': 31}, {'from': 31, 'data': ('foreground', '#000077'), 'to': 51}, {'from': 31, 'data': ('background', '#FFFFFF'), 'to': 51}], 'bersirc color with background! setting foreground! reset!'), | ||||
| 
 | ||||
|         ([], '7700,FFFFbersirc'), | ||||
|          | ||||
|         ([{'from': 0, 'data': ('foreground', '#0000FF'), 'to': 6}], '3Hello'), | ||||
| 
 | ||||
|         ([{'from': 0, 'data': ('foreground', '#0000FF'), 'to': 6}], ',Hello'), | ||||
| 
 | ||||
|         ([{'from': 0, 'data': ('foreground', '#FF0000'), 'to': 5}], 'Hello'), | ||||
| 
 | ||||
|         ([{'from': 2, 'data': ('weight', '\x02'), 'to': 4}], 'Bold'), | ||||
| 
 | ||||
|         ([{'from': 0, 'data': ('foreground', '#FF0000'), 'to': 5}, {'from': 0, 'data': ('background', '#7F0000'), 'to': 5}, {'from': 5, 'data': ('foreground', '#9C009C'), 'to': 12}, {'from': 5, 'data': ('background', '#7F0000'), 'to': 12}], 'HelloGoodbye'), | ||||
| 
 | ||||
|         ([{'from': 0, 'data': ('foreground', '#FF0000'), 'to': 5}, {'from': 0, 'data': ('background', '#00FF00'), 'to': 5}, {'from': 5, 'data': ('foreground', '#0000FF'), 'to': 12}, {'from': 5, 'data': ('background', '#00FF00'), 'to': 12}], 'HelloGoodbye'), | ||||
| 
 | ||||
|         ([{'from': 0, 'data': ('foreground', '#777777'), 'to': 1}, {'from': 1, 'data': ('foreground', '#00CCCC'), 'to': 6}, {'from': 6, 'data': ('foreground', '#777777'), 'to': 7}], '(stuff)'), | ||||
|          | ||||
|         ([{'from': 0, 'data': ('foreground', '#FF7F00'), 'to': 6}, {'from': 6, 'data': ('foreground', '#CCCCCC'), 'to': 10}, {'from': 10, 'data': ('foreground', '#FF7F00'), 'to': 16}], 'orangegreyorange'), | ||||
|          | ||||
|         ([{'from': 0, 'data': ('foreground', '#CCCCCC'), 'to': 3}, {'from': 0, 'data': ('background', '#444444'), 'to': 3}, {'from': 3, 'data': ('foreground', '#FF0000'), 'to': 6}, {'from': 3, 'data': ('background', '#444444'), 'to': 6}], 'sdfjkl'), | ||||
|          | ||||
|         ([{'from': 2, 'data': ('weight', '\x02'), 'to': 2}], '03,trixy'), | ||||
|          | ||||
|         ([{'from': 0, 'data': ('weight', '\x02'), 'to': 0}, {'from': 0, 'data': ('foreground', '#FFFFFF'), 'to': 24}], ',000000trixy for bersirc'), | ||||
|         ] | ||||
|      | ||||
|     #""" | ||||
| 
 | ||||
|     #r = range(20000)     | ||||
|     #for i in r: | ||||
|     #    for test in tests: | ||||
|     #        parse_mirc(test) | ||||
|              | ||||
|     """ | ||||
|      | ||||
|     lines = [eval(line.strip()) for line in file("parse_mirc_torture_test.txt")] | ||||
|      | ||||
|     for r in range(100): | ||||
|         for line in lines: | ||||
|             parse_mirc(line) | ||||
|              | ||||
|     #"""  | ||||
|      | ||||
|     def setify_tags(tags): | ||||
|         return set(frozenset(tag.iteritems()) for tag in tags if tag['from'] != tag['to']) | ||||
|      | ||||
|     def parsed_eq((tags1, text1), (tags2, text2)): | ||||
|         return setify_tags(tags1) == setify_tags(tags2) and text1 == text2 | ||||
|      | ||||
|     def parsed_to_unparsed((tags, text)): | ||||
|         result = [] | ||||
|         for i, char in enumerate(text): | ||||
|             result.append(( | ||||
|                 dict(tag['data'] for tag in tags if tag['from'] <= i < tag['to']), | ||||
|                 char)) | ||||
|         return result | ||||
|      | ||||
|     for i, (test, result) in enumerate(zip(tests, results)): | ||||
|         if not parsed_eq(parse_mirc(test), result): | ||||
|             print "parse_mirc failed test %s:" % i | ||||
|             print repr(test) | ||||
|             print parse_mirc(test) | ||||
|             print result | ||||
|             print | ||||
|          | ||||
|         elif not parsed_eq(parse_mirc(unparse_mirc(parsed_to_unparsed(result))), result): | ||||
|             print "unparse_mirc failed test %s:" % i | ||||
|             print repr(test) | ||||
|             print unparse_mirc(test) | ||||
|             print | ||||
| 
 | ||||
| #import dis | ||||
| #dis.dis(parse_mirc) | ||||
| @ -1,16 +0,0 @@ | ||||
| sugardir = $(pkgdatadir)/services/console/lib/purk/scripts | ||||
| 
 | ||||
| sugar_PYTHON =				\
 | ||||
| 	alias.py			\
 | ||||
| 	chaninfo.py			\
 | ||||
| 	clicks.py			\
 | ||||
| 	completion.py			\
 | ||||
| 	console.py			\
 | ||||
| 	history.py			\
 | ||||
| 	ignore.py			\
 | ||||
| 	irc_script.py			\
 | ||||
| 	keys.py				\
 | ||||
| 	theme.py			\
 | ||||
| 	timeout.py			\
 | ||||
| 	ui_script.py | ||||
| 
 | ||||
| @ -1,60 +0,0 @@ | ||||
| import sys | ||||
| import os | ||||
| 
 | ||||
| from conf import conf | ||||
| 
 | ||||
| aliases = conf.get("aliases",{ | ||||
|     'op':'"mode "+window.id+" +"+"o"*len(args)+" "+" ".join(args)', | ||||
|     'deop':'"mode "+window.id+" -"+"o"*len(args)+" "+" ".join(args)', | ||||
|     'voice':'"mode "+window.id+" +"+"v"*len(args)+" "+" ".join(args)', | ||||
|     'devoice':'"mode "+window.id+" -"+"v"*len(args)+" "+" ".join(args)', | ||||
|     'umode':'"mode "+network.me+" "+" ".join(args)', | ||||
|     'clear':'window.output.clear()', | ||||
|     }) | ||||
| 
 | ||||
| class CommandHandler: | ||||
|     __slots__ = ["command"] | ||||
|     def __init__(self, command): | ||||
|         self.command = command | ||||
|     def __call__(self, e): | ||||
|         loc = sys.modules.copy() | ||||
|         loc.update(e.__dict__) | ||||
|         result = eval(self.command,loc) | ||||
|         if isinstance(result,basestring): | ||||
|             core.events.run(result,e.window,e.network) | ||||
| 
 | ||||
| for name in aliases: | ||||
|     globals()['onCommand'+name.capitalize()] = CommandHandler(aliases[name]) | ||||
| 
 | ||||
| def onCommandAlias(e): | ||||
|     if e.args and 'r' in e.switches: | ||||
|         name = e.args[0].lower() | ||||
|         command = aliases[name] | ||||
|         del aliases[name] | ||||
|         conf['aliases'] = aliases | ||||
|         e.window.write("* Deleted alias %s%s (was %s)" % (conf.get('command-prefix','/'),name,command)) | ||||
|         core.events.load(__name__,reloading=True) | ||||
|     elif 'l' in e.switches: | ||||
|         e.window.write("* Current aliases:") | ||||
|         for i in aliases: | ||||
|             e.window.write("*  %s%s: %s" % (conf.get('command-prefix','/'),i,aliases[i])) | ||||
|     elif len(e.args) >= 2: | ||||
|         name = e.args[0].lower() | ||||
|         command = ' '.join(e.args[1:]) | ||||
|         aliases[name] = command | ||||
|         conf['aliases'] = aliases | ||||
|         e.window.write("* Created an alias %s%s to %s" % (conf.get('command-prefix','/'),name,command)) | ||||
|         core.events.reload(__name__) | ||||
|     elif len(e.args) == 1: | ||||
|         name = e.args[0].lower() | ||||
|         if name in aliases: | ||||
|             e.window.write("* %s%s is an alias to %s" % (conf.get('command-prefix','/'),name,aliases[name])) | ||||
|         else: | ||||
|             e.window.write("* There is no alias %s%s" % (conf.get('command-prefix','/'),name)) | ||||
|     else: | ||||
|         e.window.write( | ||||
| """Usage: | ||||
|  /alias \x02name\x02 \x02expression\x02 to create or replace an alias | ||||
|  /alias \x02name\x02 to look at an alias | ||||
|  /alias -r \x02name\x02 to remove an alias | ||||
|  /alias -l to see a list of aliases""") | ||||
| @ -1,320 +0,0 @@ | ||||
| import windows | ||||
| 
 | ||||
| def _justprefix(network, channel, nick): | ||||
|     fr, to = network.isupport["PREFIX"][1:].split(")") | ||||
| 
 | ||||
|     for mode, prefix in zip(fr, to): | ||||
|         if mode in channel.nicks.get(nick, ''): | ||||
|             return prefix | ||||
| 
 | ||||
|     return '' | ||||
| 
 | ||||
| def prefix(network, channelname, nick): | ||||
|     channel = getchan(network, channelname) | ||||
|      | ||||
|     if channel: | ||||
|         nick = '%s%s' % (_justprefix(network, channel, nick), nick) | ||||
|      | ||||
|     return nick | ||||
| 
 | ||||
| def escape(string): | ||||
|     for escapes in (('&','&'), ('<','<'), ('>','>')): | ||||
|         string = string.replace(*escapes) | ||||
|     return string | ||||
| 
 | ||||
| def sortkey(network, channelname, nick): | ||||
|     chanmodes, dummy = network.isupport["PREFIX"][1:].split(")") | ||||
|     nickmodes = mode(network, channelname, nick) | ||||
|      | ||||
|     return '%s%s' % (''.join(str(int(mode not in nickmodes)) for mode in chanmodes), network.norm_case(nick)) | ||||
| 
 | ||||
| def nicklist_add(network, channel, nick): | ||||
|     window = windows.get(windows.ChannelWindow, network, channel.name, core) | ||||
|     #window = core.window | ||||
|     if window: | ||||
|         window.nicklist.append(nick, escape(prefix(network, channel.name, nick)), sortkey(network, channel.name, nick)) | ||||
| 
 | ||||
| def nicklist_del(network, channel, nick): | ||||
|     window = windows.get(windows.ChannelWindow, network, channel.name, core) | ||||
|     #window = core.window | ||||
|     if window: | ||||
|         try: | ||||
|             window.nicklist.remove(nick) | ||||
|         except ValueError: | ||||
|             pass | ||||
| 
 | ||||
| def setupListRightClick(e): | ||||
|     if isinstance(e.window, windows.ChannelWindow): | ||||
|     #if isinstance(core.window, windows.ChannelWindow): | ||||
|         #if e.data[0] in e.window.network.isupport["PREFIX"].split(")")[1]: | ||||
|         if e.data[0] in core.window.network.isupport["PREFIX"].split(")")[1]:         | ||||
|             e.nick = e.data[1:] | ||||
|         else: | ||||
|             e.nick = e.data | ||||
| 
 | ||||
| def setupSocketConnect(e): | ||||
|     e.network.channels = {} | ||||
| 
 | ||||
| def setdownDisconnect(e): | ||||
|     e.network.channels = {} | ||||
| 
 | ||||
| class Channel(object): | ||||
|     def __init__(self, name): | ||||
|         self.name = name | ||||
|         self.nicks = {} | ||||
|         self.normal_nicks = {} # mapping of normal nicks to actual nicks | ||||
|         self.getting_names = False #are we between lines in a /names reply? | ||||
|         self.mode = '' | ||||
|         self.special_mode = {} #for limits, keys, and anything similar | ||||
|         self.topic = '' | ||||
|         self.got_mode = False   #did we get at least one mode reply? | ||||
|         self.got_names = False  #did we get at least one names reply? | ||||
| 
 | ||||
| def getchan(network, channel): | ||||
|     return hasattr(network, 'channels') and network.channels.get(network.norm_case(channel)) | ||||
| 
 | ||||
| #return a list of channels you're on on the given network | ||||
| def channels(network): | ||||
|     if not hasattr(network, 'channels'): | ||||
|         network.channels = {} | ||||
| 
 | ||||
|     return list(network.channels) | ||||
| 
 | ||||
| #return True if you're on the channel | ||||
| def ischan(network, channel): | ||||
|     return bool(getchan(network, channel)) | ||||
| 
 | ||||
| #return True if the nick is on the channel | ||||
| def ison(network, channel, nickname): | ||||
|     channel = getchan(network, channel) | ||||
|     return channel and network.norm_case(nickname) in channel.normal_nicks | ||||
| 
 | ||||
| #return a list of nicks on the given channel | ||||
| def nicks(network, channel): | ||||
|     channel = getchan(network, channel) | ||||
|      | ||||
|     if channel: | ||||
|         return channel.nicks | ||||
|     else: | ||||
|         return {} | ||||
| 
 | ||||
| #return the mode on the given channel | ||||
| def mode(network, channel, nickname=''): | ||||
|     channel = getchan(network, channel) | ||||
|      | ||||
|     if channel: | ||||
|         if nickname: | ||||
|             realnick = channel.normal_nicks.get(network.norm_case(nickname)) | ||||
|             if realnick: | ||||
|                 return channel.nicks[realnick] | ||||
| 
 | ||||
|         else: | ||||
|             result = channel.mode | ||||
|             for m in channel.mode: | ||||
|                 if m in channel.special_mode: | ||||
|                     result += ' '+channel.special_mode[m] | ||||
|             return result | ||||
|          | ||||
|     return '' | ||||
| 
 | ||||
| #return the topic on the given channel | ||||
| def topic(network, channel): | ||||
|     channel = getchan(network, channel) | ||||
|      | ||||
|     if channel: | ||||
|         return channel.topic | ||||
|     else: | ||||
|         return '' | ||||
| 
 | ||||
| def setupJoin(e): | ||||
|     print e | ||||
|     if e.source == e.network.me: | ||||
|         e.network.channels[e.network.norm_case(e.target)] = Channel(e.target) | ||||
|         e.network.raw('MODE '+e.target) | ||||
| 
 | ||||
|     #if we wanted to be paranoid, we'd account for not being on the channel | ||||
|     channel = getchan(e.network,e.target) | ||||
|     channel.nicks[e.source] = '' | ||||
|     channel.normal_nicks[e.network.norm_case(e.source)] = e.source | ||||
|      | ||||
|     if e.source == e.network.me: | ||||
|         #If the channel window already existed, and we're joining, then we  | ||||
|         #didn't clear out the nicklist when we left. That means we have to clear | ||||
|         #it out now. | ||||
|         window = windows.get(windows.ChannelWindow, e.network, e.target, core) | ||||
|         #window = core.window | ||||
|         #print core | ||||
|         if window: | ||||
|             window.nicklist.clear() | ||||
| 
 | ||||
|     nicklist_add(e.network, channel, e.source) | ||||
| 
 | ||||
| def setdownPart(e): | ||||
|     if e.source == e.network.me: | ||||
|         del e.network.channels[e.network.norm_case(e.target)] | ||||
|     else: | ||||
|         channel = getchan(e.network,e.target) | ||||
|         nicklist_del(e.network, channel, e.source) | ||||
|         del channel.nicks[e.source] | ||||
|         del channel.normal_nicks[e.network.norm_case(e.source)] | ||||
| 
 | ||||
| def setdownKick(e): | ||||
|     if e.target == e.network.me: | ||||
|         del e.network.channels[e.network.norm_case(e.channel)] | ||||
|     else: | ||||
|         channel = getchan(e.network,e.channel) | ||||
|         nicklist_del(e.network, channel, e.target) | ||||
|         del channel.nicks[e.target] | ||||
|         del channel.normal_nicks[e.network.norm_case(e.target)] | ||||
| 
 | ||||
| def setdownQuit(e): | ||||
|     #if paranoid: check if e.source is me | ||||
|     for channame in channels(e.network): | ||||
|         channel = getchan(e.network,channame) | ||||
|         if e.source in channel.nicks: | ||||
|             nicklist_del(e.network, channel, e.source) | ||||
|             del channel.nicks[e.source] | ||||
|             del channel.normal_nicks[e.network.norm_case(e.source)] | ||||
| 
 | ||||
| def setupMode(e): | ||||
|     channel = getchan(e.network,e.channel) | ||||
|     if channel: | ||||
|         user_modes = e.network.isupport['PREFIX'].split(')')[0][1:] | ||||
|          | ||||
|         (list_modes, | ||||
|          always_parm_modes, | ||||
|          set_parm_modes, | ||||
|          normal_modes) = e.network.isupport['CHANMODES'].split(',') | ||||
| 
 | ||||
|         list_modes += user_modes | ||||
|          | ||||
|         mode_on = True #are we reading a + section or a - section? | ||||
|         params = e.text.split(' ') | ||||
| 
 | ||||
|         for char in params.pop(0): | ||||
|             if char == '+': | ||||
|                 mode_on = True | ||||
| 
 | ||||
|             elif char == '-': | ||||
|                 mode_on = False | ||||
| 
 | ||||
|             else: | ||||
|                 if char in user_modes: | ||||
|                     #these are modes like op and voice | ||||
|                     nickname = params.pop(0) | ||||
|                     nicklist_del(e.network, channel, nickname) | ||||
|                     if mode_on: | ||||
|                         channel.nicks[nickname] += char | ||||
|                     else: | ||||
|                         channel.nicks[nickname] = channel.nicks[nickname].replace(char, '') | ||||
|                     nicklist_add(e.network, channel, nickname) | ||||
|                  | ||||
|                 elif char in list_modes: | ||||
|                     #things like ban/unban | ||||
|                     #FIXME: We don't keep track of those lists here, but we know | ||||
|                     # when they're changed and how. Scriptors should be able to | ||||
|                     # take advantage of this | ||||
|                     params.pop(0) | ||||
|                  | ||||
|                 elif char in always_parm_modes: | ||||
|                     #these always have a parameter | ||||
|                     param = params.pop(0) | ||||
|                      | ||||
|                     if mode_on: | ||||
|                         channel.special_mode[char] = param | ||||
|                     else: | ||||
|                         #account for unsetting modes that aren't set | ||||
|                         channel.special_mode.pop(char, None) | ||||
| 
 | ||||
|                 elif char in set_parm_modes: | ||||
|                     #these have a parameter only if they're being set | ||||
|                     if mode_on: | ||||
|                         channel.special_mode[char] = params.pop(0) | ||||
|                     else: | ||||
|                         #account for unsetting modes that aren't set | ||||
|                         channel.special_mode.pop(char, None) | ||||
|                  | ||||
|                 if char not in list_modes: | ||||
|                     if mode_on: | ||||
|                         channel.mode = channel.mode.replace(char, '')+char | ||||
|                     else: | ||||
|                         channel.mode = channel.mode.replace(char, '') | ||||
| 
 | ||||
| def setdownNick(e): | ||||
|     for channame in channels(e.network): | ||||
|         channel = getchan(e.network,channame) | ||||
|         if e.source in channel.nicks: | ||||
|             nicklist_del(e.network, channel, e.source) | ||||
|             del channel.normal_nicks[e.network.norm_case(e.source)] | ||||
|             channel.nicks[e.target] = channel.nicks[e.source] | ||||
|             del channel.nicks[e.source] | ||||
|             channel.normal_nicks[e.network.norm_case(e.target)] = e.target | ||||
|             nicklist_add(e.network, channel, e.target) | ||||
| 
 | ||||
| def setupTopic(e): | ||||
|     channel = getchan(e.network, e.target) | ||||
|     if channel: | ||||
|         channel.topic = e.text | ||||
| 
 | ||||
| def setupRaw(e): | ||||
|     if e.msg[1] == '353': #names reply | ||||
|         channel = getchan(e.network,e.msg[4]) | ||||
|         if channel: | ||||
|             if not channel.getting_names: | ||||
|                 channel.nicks.clear() | ||||
|                 channel.normal_nicks.clear() | ||||
|                 channel.getting_names = True | ||||
|             if not channel.got_names: | ||||
|                 e.quiet = True | ||||
|             for nickname in e.msg[5].split(' '): | ||||
|                 if nickname: | ||||
|                     if not nickname[0].isalpha() and nickname[0] in e.network.prefixes: | ||||
|                         n = nickname[1:] | ||||
|                         channel.nicks[n] = e.network.prefixes[nickname[0]] | ||||
|                         channel.normal_nicks[e.network.norm_case(n)] = n | ||||
|                     else: | ||||
|                         channel.nicks[nickname] = '' | ||||
|                         channel.normal_nicks[e.network.norm_case(nickname)] = nickname | ||||
| 
 | ||||
|     elif e.msg[1] == '366': #end of names reply | ||||
|         channel = getchan(e.network,e.msg[3]) | ||||
|         if channel: | ||||
|             if not channel.got_names: | ||||
|                 e.quiet = True | ||||
|                 channel.got_names = True | ||||
|             channel.getting_names = False | ||||
|              | ||||
|             window = windows.get(windows.ChannelWindow, e.network, e.msg[3], core) | ||||
|             if window: | ||||
|                 window.nicklist.replace( | ||||
|                     (nick, escape(prefix(e.network, channel.name, nick)), sortkey(e.network, channel.name, nick)) for nick in channel.nicks | ||||
|                     ) | ||||
|          | ||||
|     elif e.msg[1] == '324': #channel mode is | ||||
|         channel = getchan(e.network,e.msg[3]) | ||||
|         if channel: | ||||
|             if not channel.got_mode: | ||||
|                 e.quiet = True | ||||
|                 channel.got_mode = True | ||||
|             mode = e.msg[4] | ||||
|             params = e.msg[:4:-1] | ||||
|             list_modes, always_parm_modes, set_parm_modes, normal_modes = \ | ||||
|                 e.network.isupport['CHANMODES'].split(',') | ||||
|             parm_modes = always_parm_modes + set_parm_modes | ||||
|             channel.mode = e.msg[4] | ||||
|             channel.special_mode.clear() | ||||
|             for char in channel.mode: | ||||
|                 if char in parm_modes: | ||||
|                     channel.special_mode[char] = params.pop() | ||||
|          | ||||
|     elif e.msg[1] == '331': #no topic | ||||
|         channel = getchan(e.network,e.msg[3]) | ||||
|         if channel: | ||||
|             channel.topic = '' | ||||
| 
 | ||||
|     elif e.msg[1] == '332': #channel topic is | ||||
|         channel = getchan(e.network,e.msg[3]) | ||||
|         if channel: | ||||
|             channel.topic = e.text | ||||
| 
 | ||||
| #core.events.load(__name__) | ||||
| @ -1,146 +0,0 @@ | ||||
| import ui | ||||
| import windows | ||||
| import chaninfo | ||||
| from conf import conf | ||||
| 
 | ||||
| def set_target(e): | ||||
|     target_l = e.target.lstrip('@+%.(<') | ||||
|     e._target_fr = e.target_fr + len(e.target) - len(target_l) | ||||
|      | ||||
|     target_r = e.target.rstrip('>:,') | ||||
|     e._target_to = e.target_to - len(e.target) + len(target_r) | ||||
|      | ||||
|     if target_r.endswith(')'): | ||||
|         e._target = e.text[e._target_fr:e._target_to] | ||||
|         open_parens = e._target.count('(') - e._target.count(')') | ||||
|         while open_parens < 0 and e.text[e._target_to-1] == ')': | ||||
|             e._target_to -= 1 | ||||
|             open_parens += 1 | ||||
|      | ||||
|     e._target = e.text[e._target_fr:e._target_to] | ||||
| 
 | ||||
| def is_nick(e): | ||||
|     return isinstance(e.window, windows.ChannelWindow) and \ | ||||
|             chaninfo.ison(e.window.network, e.window.id, e._target) | ||||
|      | ||||
| def is_url(e): | ||||
|     def starts(prefix, mindots=1): | ||||
|         def prefix_url(target): | ||||
|             return target.startswith(prefix) and target.count('.') >= mindots | ||||
|              | ||||
|         return prefix_url | ||||
|      | ||||
|     to_check = [starts(*x) for x in [ | ||||
|         ('http://', 1), | ||||
|         ('https://', 1), | ||||
|         ('ftp://', 1), | ||||
|         ('www', 2), | ||||
|         ]] | ||||
| 
 | ||||
|     for check_url in to_check: | ||||
|         if check_url(e._target): | ||||
|             return True | ||||
|      | ||||
|     return False | ||||
|      | ||||
| def is_chan(e): | ||||
|     # click on a #channel | ||||
|     return e.window.network and e._target and \ | ||||
|             e._target[0] in e.window.network.isupport.get('CHANTYPES', '&#$+') | ||||
| 
 | ||||
| def get_autojoin_list(network): | ||||
|     perform = conf.get('networks',{}).get(network.name,{}).get('perform',()) | ||||
|     channels = set() | ||||
|     for line in perform: | ||||
|         if line.startswith('join ') and ' ' not in line[5:]: | ||||
|             channels.update(line[5:].split(',')) | ||||
|     return channels | ||||
| 
 | ||||
| def add_autojoin(network, channel): | ||||
|     if 'networks' not in conf: | ||||
|         conf['networks'] = {} | ||||
|     if network.name not in conf['networks']: | ||||
|         conf['networks'][network.name] = {'server': network.server} | ||||
|         conf['start_networks'] = conf.get('start_networks',[]) + [network.name] | ||||
|     if 'perform' in conf['networks'][network.name]: | ||||
|         perform = conf['networks'][network.name]['perform'] | ||||
|     else: | ||||
|         perform = conf['networks'][network.name]['perform'] = [] | ||||
|      | ||||
|     for n, line in enumerate(perform): | ||||
|         if line.startswith('join ') and ' ' not in line[5:]: | ||||
|             perform[n] = "%s,%s" % (line, channel) | ||||
|             break | ||||
|     else: | ||||
|         perform.append('join %s' % channel) | ||||
| 
 | ||||
| def make_nick_menu(e, target): | ||||
|     def query(): | ||||
|         core.events.run('query %s' % target, e.window, e.window.network) | ||||
|      | ||||
|     def whois(): | ||||
|         core.events.run('whois %s' % target, e.window, e.window.network) | ||||
|      | ||||
|     e.menu += [ | ||||
|         ('Query', query), | ||||
|         ('Whois', whois), | ||||
|         (), | ||||
|         ] | ||||
| 
 | ||||
| def onHover(e): | ||||
|     set_target(e) | ||||
| 
 | ||||
|     for is_check in (is_nick, is_url, is_chan): | ||||
|         if is_check(e): | ||||
|             e.tolink.add((e._target_fr, e._target_to)) | ||||
|             break | ||||
| 
 | ||||
| def onClick(e): | ||||
|     set_target(e) | ||||
| 
 | ||||
|     if is_nick(e): | ||||
|         core.events.run('query %s' % e._target, e.window, e.window.network) | ||||
|      | ||||
|     # url of the form http://xxx.xxx or www.xxx.xxx        | ||||
|     elif is_url(e): | ||||
|         if e._target.startswith('www'): | ||||
|             e._target = 'http://%s' % e._target | ||||
|         ui.open_file(e._target) | ||||
|      | ||||
|     # click on a #channel | ||||
|     elif is_chan(e): | ||||
|         if not chaninfo.ischan(e.window.network, e._target): | ||||
|             e.window.network.join(e._target) | ||||
|         window = windows.get(windows.ChannelWindow, e.window.network, e._target) | ||||
|         if window: | ||||
|             window.activate() | ||||
| 
 | ||||
| def onRightClick(e): | ||||
|     set_target(e) | ||||
| 
 | ||||
|     # nick on this channel | ||||
|     if is_nick(e): | ||||
|         make_nick_menu(e, e._target) | ||||
| 
 | ||||
|     elif is_url(e): | ||||
|         if e._target.startswith('www'): | ||||
|             e._target = 'http://%s' % e._target | ||||
|      | ||||
|         def copy_to(): | ||||
|             # copy to clipboard | ||||
|             ui.set_clipboard(e._target) | ||||
|              | ||||
|         e.menu += [('Copy', copy_to)] | ||||
|          | ||||
|     elif is_chan(e): | ||||
|         e.channel = e._target | ||||
|         e.network = e.window.network | ||||
|         core.events.trigger('ChannelMenu', e)  | ||||
| 
 | ||||
| def onListRightClick(e): | ||||
|     if isinstance(e.window, windows.ChannelWindow): | ||||
|         make_nick_menu(e, e.nick) | ||||
| 
 | ||||
| def onListDoubleClick(e): | ||||
|     if isinstance(e.window, windows.ChannelWindow): | ||||
|         core.events.run("query %s" % e.target, e.window, e.window.network) | ||||
| @ -1,135 +0,0 @@ | ||||
| import windows | ||||
| import chaninfo | ||||
| from conf import conf | ||||
| 
 | ||||
| def channel_completer(window, left, right, text): | ||||
|     if isinstance(window, windows.ChannelWindow): | ||||
|         yield window.id | ||||
| 
 | ||||
|     for w in windows.get_with(wclass=windows.ChannelWindow, network=window.network): | ||||
|         if w is not window: | ||||
|             yield w.id | ||||
| 
 | ||||
|     for w in windows.get_with(wclass=windows.ChannelWindow): | ||||
|         if w.network is not window.network: | ||||
|             yield w.id | ||||
| 
 | ||||
| # normal server commands | ||||
| srv_commands = ('ping', 'join', 'part', 'mode', 'server', 'kick', | ||||
|                 'quit', 'nick', 'privmsg', 'notice', 'topic') | ||||
|              | ||||
| def command_completer(window, left, right, text): | ||||
|     for c in srv_commands: | ||||
|         yield '/%s' % c | ||||
|      | ||||
|     if 'CMDS' in window.network.isupport: | ||||
|         for c in window.network.isupport['CMDS'].split(','): | ||||
|             yield '/%s' % c.lower() | ||||
|      | ||||
|     for c in core.events.all_events: | ||||
|         if c.startswith('Command') and c != 'Command': | ||||
|             yield '/%s' % c[7:].lower() | ||||
| 
 | ||||
| def nick_completer(window, left, right, text):   | ||||
|     if type(window) == windows.QueryWindow: | ||||
|         yield window.id | ||||
|      | ||||
|     recent_speakers = getattr(window, 'recent_speakers', ()) | ||||
|      | ||||
|     for nick in recent_speakers: | ||||
|         if chaninfo.ison(window.network, window.id, nick): | ||||
|             yield nick | ||||
| 
 | ||||
|     for nick in chaninfo.nicks(window.network, window.id): | ||||
|         if nick not in recent_speakers: | ||||
|             yield nick | ||||
|      | ||||
| def script_completer(window, left, right, text): | ||||
|     return core.events.loaded.iterkeys() | ||||
|      | ||||
| def network_completer(window, left, right, text): | ||||
|     return conf.get('networks', {}).iterkeys() | ||||
| 
 | ||||
| def get_completer_for(window): | ||||
|     input = window.input | ||||
| 
 | ||||
|     left, right = input.text[:input.cursor], input.text[input.cursor:] | ||||
|              | ||||
|     text = left.split(' ')[-1] | ||||
| 
 | ||||
|     while True: | ||||
|         suffix = '' | ||||
|         if text and text[0] in window.network.isupport.get('CHANTYPES', '#&+'): | ||||
|             candidates = channel_completer(window, left, right, text) | ||||
|              | ||||
|         elif input.text.startswith('/reload '): | ||||
|             candidates = script_completer(window, left, right, text) | ||||
|          | ||||
|         elif input.text.startswith('/edit '): | ||||
|             candidates = script_completer(window, left, right, text) | ||||
|              | ||||
|         elif input.text.startswith('/server '): | ||||
|             candidates = network_completer(window, left, right, text) | ||||
|              | ||||
|         elif text.startswith('/'): | ||||
|             candidates = command_completer(window, left, right, text) | ||||
|             suffix = ' ' | ||||
|              | ||||
|         else: | ||||
|             candidates = nick_completer(window, left, right, text) | ||||
|              | ||||
|             if left == text: | ||||
|                 suffix = ': ' | ||||
|             else: | ||||
|                 suffix = ' ' | ||||
|                  | ||||
|         if text: | ||||
|             before = left[:-len(text)] | ||||
|         else: | ||||
|             before = left | ||||
|              | ||||
|         insert_text = '%s%s%s%s' % (before, '%s', suffix, right) | ||||
|         cursor_pos = len(before + suffix) | ||||
| 
 | ||||
|         original = window.input.text, window.input.cursor | ||||
| 
 | ||||
|         for cand in candidates: | ||||
|             if cand.lower().startswith(text.lower()): | ||||
|                 window.input.text, window.input.cursor = insert_text % cand, cursor_pos + len(cand) | ||||
|                 yield None | ||||
|                  | ||||
|         window.input.text, window.input.cursor = original | ||||
|         yield None | ||||
|       | ||||
| # generator--use recent_completer.next() to continue cycling through whatever | ||||
| recent_completer = None | ||||
| 
 | ||||
| def onKeyPress(e): | ||||
|     global recent_completer | ||||
| 
 | ||||
|     if e.key == 'Tab': | ||||
|         if not recent_completer: | ||||
|             recent_completer = get_completer_for(e.window) | ||||
| 
 | ||||
|         recent_completer.next() | ||||
|      | ||||
|     else: | ||||
|         recent_completer = None | ||||
| 
 | ||||
| def onActive(e): | ||||
|     global recent_completer | ||||
|      | ||||
|     recent_completer = None | ||||
| 
 | ||||
| def onText(e): | ||||
|     if chaninfo.ischan(e.network, e.target): | ||||
|         if not hasattr(e.window, 'recent_speakers'): | ||||
|             e.window.recent_speakers = [] | ||||
| 
 | ||||
|         for nick in e.window.recent_speakers: | ||||
|             if nick == e.source or not chaninfo.ison(e.network, e.target, nick): | ||||
|                 e.window.recent_speakers.remove(nick) | ||||
| 
 | ||||
|         e.window.recent_speakers.insert(0, e.source) | ||||
| 
 | ||||
| onAction = onText | ||||
| @ -1,68 +0,0 @@ | ||||
| import sys | ||||
| import traceback | ||||
| import windows | ||||
| from conf import conf | ||||
| 
 | ||||
| class ConsoleWriter: | ||||
|     __slots__ = ['window'] | ||||
|     def __init__(self, window): | ||||
|         self.window = window | ||||
|     def write(self, text): | ||||
|         try: | ||||
|             self.window.write(text, line_ending='') | ||||
|         except: | ||||
|             self.window.write(traceback.format_exc()) | ||||
| 
 | ||||
| class ConsoleWindow(windows.SimpleWindow): | ||||
|     def __init__(self, network, id): | ||||
|         windows.SimpleWindow.__init__(self, network, id) | ||||
|      | ||||
|         writer = ConsoleWriter(self) | ||||
|          | ||||
|         sys.stdout = writer | ||||
|         sys.stderr = writer | ||||
|          | ||||
|         self.globals = {'window': self} | ||||
|         self.locals = {} | ||||
| 
 | ||||
| #this prevents problems (and updates an open console window) on reload | ||||
| #window = None | ||||
| #for window in manager: | ||||
| #    if type(window).__name__ == "ConsoleWindow": | ||||
| #        window.mutate(ConsoleWindow, window.network, window.id) | ||||
| #del window | ||||
| 
 | ||||
| def onClose(e): | ||||
|     if isinstance(e.window, ConsoleWindow): | ||||
|         sys.stdout = sys.__stdout__ | ||||
|         sys.stderr = sys.__stderr__ | ||||
| 
 | ||||
| def onCommandConsole(e): | ||||
|     windows.new(ConsoleWindow, None, "console").activate()  | ||||
| 
 | ||||
| def onCommandSay(e): | ||||
|     if isinstance(e.window, ConsoleWindow): | ||||
|         import pydoc #fix nonresponsive help() command | ||||
|         old_pager, pydoc.pager = pydoc.pager, pydoc.plainpager  | ||||
|         e.window.globals.update(sys.modules) | ||||
|         text = ' '.join(e.args) | ||||
|         try: | ||||
|             e.window.write(">>> %s" % text)  | ||||
|             result = eval(text, e.window.globals, e.window.locals) | ||||
|             if result is not None: | ||||
|                 e.window.write(repr(result)) | ||||
|             e.window.globals['_'] = result | ||||
|         except SyntaxError: | ||||
|             try: | ||||
|                 exec text in e.window.globals, e.window.locals | ||||
|             except: | ||||
|                 traceback.print_exc() | ||||
|         except: | ||||
|             traceback.print_exc() | ||||
|         pydoc.pager = old_pager | ||||
|     else: | ||||
|         raise core.events.CommandError("There's no one here to speak to.") | ||||
| 
 | ||||
| def onStart(e): | ||||
|     if conf.get('start-console'): | ||||
|         windows.new(ConsoleWindow, None, "console") | ||||
| @ -1,45 +0,0 @@ | ||||
| def onKeyPress(e): | ||||
|     if not hasattr(e.window, 'history'): | ||||
|         e.window.history = [], -1 | ||||
|          | ||||
|     if e.key in ('Up', 'Down'): | ||||
|         h, i = e.window.history | ||||
|          | ||||
|         if i == -1 and e.window.input.text: | ||||
|             h.insert(0, e.window.input.text) | ||||
|             i = 0 | ||||
|              | ||||
|         if e.key == 'Up': | ||||
|             i += 1 | ||||
|              | ||||
|             if i < len(h):                 | ||||
|                 e.window.history = h, i | ||||
| 
 | ||||
|                 e.window.input.text = h[i] | ||||
|                 e.window.input.cursor = -1 | ||||
| 
 | ||||
|         else: # e.key == 'Up' | ||||
|             i -= 1 | ||||
|          | ||||
|             if i > -1: | ||||
|                 e.window.history = h, i | ||||
| 
 | ||||
|                 e.window.input.text = h[i] | ||||
|                 e.window.input.cursor = -1 | ||||
|                  | ||||
|             elif i == -1: | ||||
|                 e.window.history = h, i | ||||
|              | ||||
|                 e.window.input.text = '' | ||||
|                 e.window.input.cursor = -1 | ||||
| 
 | ||||
| def onInput(e): | ||||
|     if not hasattr(e.window, 'history'): | ||||
|         e.window.history = [], -1 | ||||
| 
 | ||||
|     if e.text: | ||||
|         h, i = e.window.history | ||||
|      | ||||
|         h.insert(0, e.text) | ||||
|          | ||||
|         e.window.history = h, -1 | ||||
| @ -1,43 +0,0 @@ | ||||
| from conf import conf | ||||
| import irc | ||||
| 
 | ||||
| def preRaw(e): | ||||
|     if e.msg[1] in ('PRIVMSG','NOTICE'): | ||||
|         address = e.network.norm_case('%s!%s' % (e.source, e.address)) | ||||
|         for mask in conf.get('ignore_masks',()): | ||||
|             if irc.match_glob(address, e.network.norm_case(mask)): | ||||
|                 core.events.halt() | ||||
| 
 | ||||
| def onCommandIgnore(e): | ||||
|     if 'ignore_masks' not in conf: | ||||
|         conf['ignore_masks'] = [] | ||||
|     if 'l' in e.switches: | ||||
|         for i in conf['ignore_masks']: | ||||
|             e.window.write('* %s' % i) | ||||
|     elif 'c' in e.switches: | ||||
|         del conf['ignore_masks'] | ||||
|         e.window.write('* Cleared the ignore list.')   | ||||
|     elif e.args: | ||||
|         if '!' in e.args[0] or '*' in e.args[0] or '?' in e.args[0]: | ||||
|             mask = e.args[0] | ||||
|         else: | ||||
|             mask = '%s!*' % e.args[0] | ||||
|         if 'r' in e.switches: | ||||
|             if mask in conf['ignore_masks']: | ||||
|                 conf['ignore_masks'].remove(mask) | ||||
|                 e.window.write('* Removed %s from the ignore list' % e.args[0]) | ||||
|             else: | ||||
|                 raise core.events.CommandError("Couldn't find %s in the ignore list" % e.args[0]) | ||||
|         else: | ||||
|             if mask in conf['ignore_masks']: | ||||
|                 e.window.write('* %s is already ignored' % e.args[0]) | ||||
|             else: | ||||
|                 conf['ignore_masks'].append(mask) | ||||
|                 e.window.write('* Ignoring messages from %s' % e.args[0]) | ||||
|     else: | ||||
|         e.window.write( | ||||
| """Usage: | ||||
|  /ignore \x02nick/mask\x02 to ignore a nickname or mask | ||||
|  /ignore -r \x02nick/mask\x02 to stop ignoring a nickname or mask | ||||
|  /ignore -l to view the ignore list | ||||
|  /ignore -c to clear the ignore list""") | ||||
| @ -1,588 +0,0 @@ | ||||
| import time | ||||
| 
 | ||||
| from conf import conf | ||||
| import ui | ||||
| import windows | ||||
| import irc | ||||
| 
 | ||||
| COMMAND_PREFIX = conf.get('command_prefix', '/') | ||||
| 
 | ||||
| NICK_SUFFIX = r"`_-\|0123456789" | ||||
| 
 | ||||
| _hextochr = dict(('%02x' % i, chr(i)) for i in range(256)) | ||||
| def unquote(url, rurl=""): | ||||
| 
 | ||||
|     while '%' in url: | ||||
|         url, char = url.rsplit('%', 1) | ||||
|          | ||||
|         chars = char[:2].lower() | ||||
| 
 | ||||
|         if chars in _hextochr: | ||||
|             rurl = '%s%s%s' % (_hextochr[chars], char[2:], rurl) | ||||
|         else: | ||||
|             rurl = "%s%s%s" % ('%', char, rurl) | ||||
|              | ||||
|     return url + rurl | ||||
| 
 | ||||
| #for getting a list of alternative nicks to try on a network | ||||
| def _nick_generator(network): | ||||
|     for nick in network.nicks[1:]: | ||||
|         yield nick | ||||
|     if network._nick_error: | ||||
|         nick = 'ircperson' | ||||
|     else: | ||||
|         nick = network.nicks[0] | ||||
|     import itertools | ||||
|     for i in itertools.count(1): | ||||
|         for j in xrange(len(NICK_SUFFIX)**i): | ||||
|             suffix = ''.join(NICK_SUFFIX[(j/(len(NICK_SUFFIX)**x))%len(NICK_SUFFIX)] for x in xrange(i)) | ||||
|             if network._nick_max_length: | ||||
|                 yield nick[0:network._nick_max_length-i]+suffix | ||||
|             else: | ||||
|                 yield nick+suffix | ||||
| 
 | ||||
| def setdownRaw(e): | ||||
|     if not e.done: | ||||
|         if not e.network.got_nick: | ||||
|             if e.msg[1] in ('432','433','436','437'): #nickname unavailable | ||||
|                 failednick = e.msg[3] | ||||
|                 nicks = list(e.network.nicks) | ||||
|                  | ||||
|                 if hasattr(e.network,'_nick_generator'): | ||||
|                     if len(failednick) < len(e.network._next_nick): | ||||
|                         e.network._nick_max_length = len(failednick) | ||||
|                     e.network._next_nick = e.network._nick_generator.next() | ||||
|                     e.network.raw('NICK %s' % e.network._next_nick) | ||||
|                     e.network._nick_error |= (e.msg[1] == '432') | ||||
|                 else: | ||||
|                     e.network._nick_error = (e.msg[1] == '432') | ||||
|                     if len(failednick) < len(e.network.nicks[0]): | ||||
|                         e.network._nick_max_length = len(failednick) | ||||
|                     else: | ||||
|                         e.network._nick_max_length = 0 | ||||
|                     e.network._nick_generator = _nick_generator(e.network) | ||||
|                     e.network._next_nick = e.network._nick_generator.next() | ||||
|                     e.network.raw('NICK %s' % e.network._next_nick) | ||||
|              | ||||
|             elif e.msg[1] == '431': #no nickname given--this shouldn't happen | ||||
|                 pass | ||||
|              | ||||
|             elif e.msg[1] == '001': | ||||
|                 e.network.got_nick = True | ||||
|                 if e.network.me != e.msg[2]: | ||||
|                     core.events.trigger( | ||||
|                         'Nick', network=e.network, window=e.window, | ||||
|                         source=e.network.me, target=e.msg[2], address='', | ||||
|                         text=e.msg[2] | ||||
|                         ) | ||||
|                     e.network.me = e.msg[2] | ||||
|                 if hasattr(e.network,'_nick_generator'): | ||||
|                     del e.network._nick_generator, e.network._nick_max_length, e.network._next_nick | ||||
|                  | ||||
|         if e.msg[1] == "PING": | ||||
|             e.network.raw("PONG :%s" % e.msg[-1]) | ||||
|             e.done = True | ||||
|          | ||||
|         elif e.msg[1] == "JOIN": | ||||
|             e.channel = e.target | ||||
|             e.requested = e.network.norm_case(e.channel) in e.network.requested_joins | ||||
|             core.events.trigger("Join", e) | ||||
|             e.done = True | ||||
|          | ||||
|         elif e.msg[1] == "PART": | ||||
|             e.channel = e.target | ||||
|             e.requested = e.network.norm_case(e.channel) in e.network.requested_parts | ||||
|             e.text = ' '.join(e.msg[3:]) | ||||
|             core.events.trigger("Part", e) | ||||
|             e.done = True | ||||
|          | ||||
|         elif e.msg[1] in "MODE": | ||||
|             e.channel = e.target | ||||
|             e.text = ' '.join(e.msg[3:]) | ||||
|             core.events.trigger("Mode", e) | ||||
|             e.done = True | ||||
|              | ||||
|         elif e.msg[1] == "QUIT": | ||||
|             core.events.trigger('Quit', e) | ||||
|             e.done = True | ||||
|              | ||||
|         elif e.msg[1] == "KICK": | ||||
|             e.channel = e.msg[2] | ||||
|             e.target = e.msg[3] | ||||
|             core.events.trigger('Kick', e) | ||||
|             e.done = True | ||||
|              | ||||
|         elif e.msg[1] == "NICK": | ||||
|             core.events.trigger('Nick', e) | ||||
|             if e.network.me == e.source: | ||||
|                 e.network.me = e.target | ||||
| 
 | ||||
|             e.done = True | ||||
|              | ||||
|         elif e.msg[1] == "PRIVMSG": | ||||
|             core.events.trigger('Text', e) | ||||
|             e.done = True | ||||
|          | ||||
|         elif e.msg[1] == "NOTICE": | ||||
|             core.events.trigger('Notice', e) | ||||
|             e.done = True | ||||
|          | ||||
|         elif e.msg[1] == "TOPIC": | ||||
|             core.events.trigger('Topic', e) | ||||
|             e.done = True | ||||
|          | ||||
|         elif e.msg[1] in ("376", "422"): #RPL_ENDOFMOTD | ||||
|             if e.network.status == irc.INITIALIZING: | ||||
|                 e.network.status = irc.CONNECTED | ||||
|                 core.events.trigger('Connect', e) | ||||
|             e.done = True | ||||
|          | ||||
|         elif e.msg[1] == "470": #forwarded from channel X to channel Y | ||||
|             if e.network.norm_case(e.msg[3]) in e.network.requested_joins: | ||||
|                 e.network.requested_joins.discard(e.network.norm_case(e.msg[3])) | ||||
|                 e.network.requested_joins.add(e.network.norm_case(e.msg[4])) | ||||
|          | ||||
|         elif e.msg[1] == "005": #RPL_ISUPPORT | ||||
|             for arg in e.msg[3:]: | ||||
|                 if ' ' not in arg: #ignore "are supported by this server" | ||||
|                     if '=' in arg: | ||||
|                         name, value = arg.split('=', 1) | ||||
|                         if value.isdigit(): | ||||
|                             value = int(value) | ||||
|                     else: | ||||
|                         name, value = arg, '' | ||||
| 
 | ||||
|                     #Workaround for broken servers (bahamut on EnterTheGame) | ||||
|                     if name == 'PREFIX' and value[0] != '(': | ||||
|                         continue | ||||
| 
 | ||||
|                     #in theory, we're supposed to replace \xHH with the | ||||
|                     # corresponding ascii character, but I don't think anyone | ||||
|                     # really does this | ||||
|                     e.network.isupport[name] = value | ||||
|                      | ||||
|                     if name == 'PREFIX': | ||||
|                         new_prefixes = {} | ||||
|                         modes, prefixes = value[1:].split(')') | ||||
|                         for mode, prefix in zip(modes, prefixes): | ||||
|                             new_prefixes[mode] = prefix | ||||
|                             new_prefixes[prefix] = mode | ||||
|                         e.network.prefixes = new_prefixes | ||||
| 
 | ||||
| def setupSocketConnect(e): | ||||
|     e.network.got_nick = False | ||||
|     e.network.isupport = { | ||||
|         'NETWORK': e.network.server,  | ||||
|         'PREFIX': '(ohv)@%+', | ||||
|         'CHANMODES': 'b,k,l,imnpstr', | ||||
|     } | ||||
|     e.network.prefixes = {'o':'@', 'h':'%', 'v':'+', '@':'o', '%':'h', '+':'v'} | ||||
|     e.network.connect_timestamp = time.time() | ||||
|     e.network.requested_joins.clear() | ||||
|     e.network.requested_parts.clear() | ||||
|     e.network.on_channels.clear() | ||||
|     if hasattr(e.network,'_nick_generator'): | ||||
|         del e.network._nick_generator, e.network._nick_max_length, e.network._next_nick | ||||
|     if not e.done: | ||||
|         #this needs to be tested--anyone have a server that uses PASS? | ||||
|         if e.network.password: | ||||
|             e.network.raw("PASS :%s" % e.network.password) | ||||
|         e.network.raw("NICK %s" % e.network.nicks[0]) | ||||
|         e.network.raw("USER %s %s %s :%s" % | ||||
|               (e.network.username, "8", "*", e.network.fullname)) | ||||
|               #per rfc2812 these are username, user mode flags, unused, realname | ||||
|          | ||||
|         #e.network.me = None | ||||
|         e.done = True | ||||
| 
 | ||||
| def onDisconnect(e): | ||||
|     if hasattr(e.network,'_reconnect_source'): | ||||
|         e.network._reconnect_source.unregister() | ||||
|         del e.network._reconnect_source | ||||
|     if hasattr(e.network,'connect_timestamp'): | ||||
|         if e.error and conf.get('autoreconnect',True): | ||||
|             delay = time.time() - e.network.connect_timestamp > 30 and 30 or 120 | ||||
|             def do_reconnect(): | ||||
|                 if not e.network.status: | ||||
|                     server(network=e.network) | ||||
|             def do_announce_reconnect(): | ||||
|                 if not e.network.status: | ||||
|                     windows.get_default(e.network).write("* Will reconnect in %s seconds.." % delay) | ||||
|                     e.network._reconnect_source = ui.register_timer(delay*1000,do_reconnect) | ||||
|             e.network._reconnect_source = ui.register_idle(do_announce_reconnect) | ||||
| 
 | ||||
| def onCloseNetwork(e): | ||||
|     e.network.quit() | ||||
|     if hasattr(e.network,'_reconnect_source'): | ||||
|         e.network._reconnect_source.unregister() | ||||
|         del e.network._reconnect_source | ||||
| 
 | ||||
| def setdownDisconnect(e): | ||||
|     if hasattr(e.network,'connect_timestamp'): | ||||
|         del e.network.connect_timestamp | ||||
| 
 | ||||
| def setupInput(e): | ||||
|     if not e.done: | ||||
|         if e.text.startswith(COMMAND_PREFIX) and not e.ctrl: | ||||
|             command = e.text[len(COMMAND_PREFIX):] | ||||
|         else: | ||||
|             command = 'say - %s' % e.text | ||||
| 
 | ||||
|         core.events.run(command, e.window, e.network) | ||||
|          | ||||
|         e.done = True | ||||
| 
 | ||||
| def onCommandSay(e): | ||||
|     if isinstance(e.window, windows.ChannelWindow) or isinstance(e.window, windows.QueryWindow): | ||||
|         e.network.msg(e.window.id, ' '.join(e.args)) | ||||
|     else: | ||||
|         raise core.events.CommandError("There's no one here to speak to.") | ||||
| 
 | ||||
| def onCommandMsg(e): | ||||
|     e.network.msg(e.args[0], ' '.join(e.args[1:])) | ||||
| 
 | ||||
| def onCommandNotice(e): | ||||
|     e.network.notice(e.args[0], ' '.join(e.args[1:])) | ||||
| 
 | ||||
| def onCommandQuery(e): | ||||
|     windows.new(windows.QueryWindow, e.network, e.args[0], core).activate() | ||||
|     if len(e.args) > 1: | ||||
|         message = ' '.join(e.args[1:]) | ||||
|         if message: #this is false if you do "/query nickname "  | ||||
|             e.network.msg(e.args[0], ' '.join(e.args[1:])) | ||||
| 
 | ||||
| def setupJoin(e): | ||||
|     if e.source == e.network.me: | ||||
|         chan = e.network.norm_case(e.channel) | ||||
|         e.network.on_channels.add(chan) | ||||
|         e.network.requested_joins.discard(chan) | ||||
| 
 | ||||
| def setdownPart(e): | ||||
|     if e.source == e.network.me: | ||||
|         chan = e.network.norm_case(e.channel) | ||||
|         e.network.on_channels.discard(chan) | ||||
|         e.network.requested_parts.discard(chan) | ||||
| 
 | ||||
| def setdownKick(e): | ||||
|     if e.target == e.network.me: | ||||
|         chan = e.network.norm_case(e.channel) | ||||
|         e.network.on_channels.discard(chan) | ||||
| 
 | ||||
| def ischan(network, channel): | ||||
|     return network.norm_case(channel) in network.on_channels | ||||
| 
 | ||||
| # make /nick work offline | ||||
| def change_nick(network, nick): | ||||
|     if not network.status: | ||||
|         core.events.trigger( | ||||
|             'Nick', | ||||
|             network=network, window=windows.get_default(network), | ||||
|             source=network.me, target=nick, address='', text=nick | ||||
|             ) | ||||
|         network.nicks[0] = nick | ||||
|         network.me = nick | ||||
|     else: | ||||
|         network.raw('NICK :%s' % nick) | ||||
| 
 | ||||
| def onCommandNick(e): | ||||
|     default_nick = irc.default_nicks()[0] | ||||
|     if 't' not in e.switches and e.network.me == default_nick: | ||||
|         conf['nick'] = e.args[0] | ||||
|         import conf as _conf | ||||
|         _conf.save() | ||||
|         for network in set(w.network for w in core.manager): | ||||
|             if network.me == default_nick: | ||||
|                 change_nick(network, e.args[0]) | ||||
|     else: | ||||
|         change_nick(e.network, e.args[0]) | ||||
| 
 | ||||
| def setdownNick(e): | ||||
|     if e.source != e.network.me: | ||||
|         window = windows.get(windows.QueryWindow, e.network, e.source) | ||||
|         if window: | ||||
|             window.id = e.target | ||||
| 
 | ||||
| # make /quit always disconnect us | ||||
| def onCommandQuit(e): | ||||
|     if e.network.status: | ||||
|         e.network.quit(' '.join(e.args)) | ||||
|     else: | ||||
|         raise core.events.CommandError("We're not connected to a network.") | ||||
| 
 | ||||
| def onCommandRaw(e): | ||||
|     if e.network.status >= irc.INITIALIZING: | ||||
|         e.network.raw(' '.join(e.args)) | ||||
|     else: | ||||
|         raise core.events.CommandError("We're not connected to a network.") | ||||
| 
 | ||||
| onCommandQuote = onCommandRaw | ||||
| 
 | ||||
| def onCommandJoin(e): | ||||
|     if e.args: | ||||
|         if e.network.status >= irc.INITIALIZING: | ||||
|             e.network.join(' '.join(e.args), requested = 'n' not in e.switches) | ||||
|         else: | ||||
|             raise core.events.CommandError("We're not connected.") | ||||
|     elif isinstance(e.window, windows.ChannelWindow): | ||||
|         e.window.network.join(e.window.id, requested = 'n' not in e.switches) | ||||
|     else: | ||||
|         raise core.events.CommandError("You must supply a channel.") | ||||
| 
 | ||||
| def onCommandPart(e): | ||||
|     if e.args: | ||||
|         if e.network.status >= irc.INITIALIZING: | ||||
|             e.network.part(' '.join(e.args), requested = 'n' not in e.switches) | ||||
|         else: | ||||
|             raise core.events.CommandError("We're not connected.") | ||||
|     elif isinstance(e.window, windows.ChannelWindow): | ||||
|         e.window.network.part(e.window.id, requested = 'n' not in e.switches) | ||||
|     else: | ||||
|         raise core.events.CommandError("You must supply a channel.") | ||||
| 
 | ||||
| def onCommandHop(e): | ||||
|     if e.args: | ||||
|         if e.network.status >= irc.INITIALIZING: | ||||
|             e.network.part(e.args[0], requested = False) | ||||
|             e.network.join(' '.join(e.args), requested = False) | ||||
|         else: | ||||
|             raise core.events.CommandError("We're not connected.") | ||||
|     elif isinstance(e.window, windows.ChannelWindow): | ||||
|         e.window.network.part(e.window.id, requested = False) | ||||
|         e.window.network.join(e.window.id, requested = False) | ||||
|     else: | ||||
|         raise core.events.CommandError("You must supply a channel.") | ||||
| 
 | ||||
| #this should be used whereever a new irc.Network may need to be created | ||||
| def server(server=None,port=6667,network=None,connect=True): | ||||
|     network_info = {} | ||||
|      | ||||
|     if server: | ||||
|         network_info["name"] = server | ||||
|         network_info["server"] = server | ||||
|         if port: | ||||
|             network_info["port"] = port | ||||
|         get_network_info(server, network_info) | ||||
|      | ||||
|     if not network: | ||||
|         network = irc.Network(**network_info) | ||||
|         windows.new(windows.StatusWindow, network, "status").activate() | ||||
|     else: | ||||
|         if "server" in network_info: | ||||
|             network.name = network_info['name'] | ||||
|             network.server = network_info['server'] | ||||
|             if not network.status: | ||||
|                 #window = windows.get_default(network) | ||||
|                 window = core.window | ||||
|                 if window: | ||||
|                     window.update() | ||||
|         if "port" in network_info: | ||||
|             network.port = network_info["port"] | ||||
|      | ||||
|     if network.status: | ||||
|         network.quit() | ||||
|     if connect: | ||||
|         network.connect() | ||||
|         core.window.write("* Connecting to %s on port %s" % (network.server, network.port)) | ||||
|         #windows.get_default(network).write( | ||||
|         #            "* Connecting to %s on port %s" % (network.server, network.port) | ||||
|         #    ) | ||||
|      | ||||
|     return network | ||||
| 
 | ||||
| def onCommandServer(e): | ||||
|     host = port = None | ||||
|      | ||||
|     if e.args: | ||||
|         host = e.args[0] | ||||
| 
 | ||||
|         if ':' in host: | ||||
|             host, port = host.rsplit(':', 1) | ||||
|             port = int(port) | ||||
|              | ||||
|         elif len(e.args) > 1: | ||||
|             port = int(e.args[1]) | ||||
| 
 | ||||
|         else: | ||||
|             port = 6667 | ||||
|      | ||||
|     if 'm' in e.switches:     | ||||
|         network = None | ||||
|     else: | ||||
|         network = e.network | ||||
|      | ||||
|     server(server=host, port=port, network=network, connect='o' not in e.switches) | ||||
| 
 | ||||
| #see http://www.w3.org/Addressing/draft-mirashi-url-irc-01.txt | ||||
| def onCommandIrcurl(e): | ||||
|     url = e.args[0] | ||||
|      | ||||
|     if url.startswith('irc://'): | ||||
|         url = url[6:] | ||||
|          | ||||
|         if not url.startswith('/'): | ||||
|             host, target = url.rsplit('/',1) | ||||
|             if ':' in host: | ||||
|                 host, port = host.rsplit(':',1) | ||||
|             else: | ||||
|                 port = 6667 | ||||
|         else: | ||||
|             host = None | ||||
|             port = 6667 | ||||
|             target = url | ||||
|          | ||||
|         if host: | ||||
|             if e.network and e.network.server == host: | ||||
|                 network = e.network | ||||
|             else: | ||||
|                 for w in list(windows.manager): | ||||
|                     if w.network and w.network.server == host: | ||||
|                         network = w.network | ||||
|                         break | ||||
|                 else: | ||||
|                     for w in list(windows.manager): | ||||
|                         if w.network and w.network.server == 'irc.default.org': | ||||
|                             network = server(host,port,w.network) | ||||
|                             break | ||||
|                     else: | ||||
|                         network = server(host,port) | ||||
|          | ||||
|         if ',' in target: | ||||
|             target, modifiers = target.split(',',1) | ||||
|             action = '' | ||||
|         else: | ||||
|             target = unquote(target) | ||||
|             if target[0] not in '#&+': | ||||
|                 target = '#'+target | ||||
|             action = 'join %s' % target | ||||
|          | ||||
|         if network.status == irc.CONNECTED: | ||||
|             core.events.run(action, windows.get_default(network), network) | ||||
|         else: | ||||
|             if not hasattr(network,'temp_perform'): | ||||
|                 network.temp_perform = [action] | ||||
|             else: | ||||
|                 network.temp_perform.append(action) | ||||
| 
 | ||||
| #commands that we need to add a : to but otherwise can send unchanged | ||||
| #the dictionary contains the number of arguments we take without adding the : | ||||
| trailing = { | ||||
|     'away':0, | ||||
|     'cnotice':2, | ||||
|     'cprivmsg':2, | ||||
|     'kick':2, | ||||
|     'kill':1, | ||||
|     'part':1, | ||||
|     'squery':1, | ||||
|     'squit':1, | ||||
|     'topic':1, | ||||
|     'wallops':0, | ||||
|     } | ||||
| 
 | ||||
| needschan = { | ||||
|     'topic':0, | ||||
|     'invite':1, | ||||
|     'kick':0, | ||||
| #    'mode':0, #this is commonly used for channels, but can apply to users | ||||
| #    'names':0, #with no parameters, this is supposed to give a list of all users; we may be able to safely ignore that. | ||||
|     } | ||||
|      | ||||
| def setupCommand(e): | ||||
|     if not e.done:  | ||||
|         if e.name in needschan and isinstance(e.window, windows.ChannelWindow): | ||||
|             valid_chan_prefixes = e.network.isupport.get('CHANTYPES', '#&+') | ||||
|             chan_pos = needschan[e.name] | ||||
|              | ||||
|             if len(e.args) > chan_pos: | ||||
|                 if not e.args[chan_pos] or e.args[chan_pos][0] not in valid_chan_prefixes: | ||||
|                     e.args.insert(chan_pos, e.window.id) | ||||
|             else: | ||||
|                 e.args.append(e.window.id) | ||||
|          | ||||
|         if e.name in trailing: | ||||
|             trailing_pos = trailing[e.name] | ||||
|          | ||||
|             if len(e.args) > trailing_pos: | ||||
|                 e.args[trailing_pos] = ':%s' % e.args[trailing_pos] | ||||
|          | ||||
|         e.text = '%s %s' % (e.name, ' '.join(e.args)) | ||||
| 
 | ||||
| def setdownCommand(e): | ||||
|     if not e.done and e.network.status >= irc.INITIALIZING: | ||||
|         e.network.raw(e.text) | ||||
|         e.done = True | ||||
|          | ||||
| def get_network_info(name, network_info): | ||||
|     conf_info = conf.get('networks', {}).get(name) | ||||
| 
 | ||||
|     if conf_info: | ||||
|         network_info['server'] = name | ||||
|         network_info.update(conf_info) | ||||
| 
 | ||||
| def onStart(e): | ||||
|     for network in conf.get('start_networks', []): | ||||
|         server(server=network) | ||||
| 
 | ||||
| def onConnect(e): | ||||
|     network_info = conf.get('networks', {}).get(e.network.name, {}) | ||||
| 
 | ||||
|     for command in network_info.get('perform', []): | ||||
|         while command.startswith(COMMAND_PREFIX): | ||||
|             command = command[len(COMMAND_PREFIX):] | ||||
|         core.events.run(command, e.window, e.network) | ||||
|      | ||||
|     tojoin = ','.join(network_info.get('join', [])) | ||||
|     if tojoin: | ||||
|         core.events.run('join -n %s' % tojoin, e.window, e.network) | ||||
|      | ||||
|     if hasattr(e.network,'temp_perform'): | ||||
|         for command in e.network.temp_perform: | ||||
|             core.events.run(command, e.window, e.network) | ||||
|         del e.network.temp_perform | ||||
| 
 | ||||
| def isautojoin(network, channel): | ||||
|     try: | ||||
|         joinlist = conf['networks'][network.name]['join'] | ||||
|     except KeyError: | ||||
|         return False | ||||
|     normchannel = network.norm_case(channel) | ||||
|     for chan in joinlist: | ||||
|         if normchannel == network.norm_case(chan): | ||||
|             return True | ||||
|     return False | ||||
| 
 | ||||
| def setautojoin(network, channel): | ||||
|     if 'networks' not in conf: | ||||
|         conf['networks'] = networks = {} | ||||
|     else: | ||||
|         networks = conf['networks'] | ||||
|     if network.name not in networks: | ||||
|         networks[network.name] = network_settings = {} | ||||
|         if 'start_networks' not in conf: | ||||
|             conf['start_networks'] = [] | ||||
|         conf['start_networks'].append(network.name) | ||||
|     else: | ||||
|         network_settings = networks[network.name] | ||||
|      | ||||
|     if 'join' not in network_settings: | ||||
|         network_settings['join'] = [channel] | ||||
|     else: | ||||
|         network_settings['join'].append(channel) | ||||
| 
 | ||||
| def unsetautojoin(network, channel): | ||||
|     try: | ||||
|         joinlist = conf['networks'][network.name]['join'] | ||||
|     except KeyError: | ||||
|         return False | ||||
|     normchannel = network.norm_case(channel) | ||||
|     for i, chan in enumerate(joinlist[:]): | ||||
|         if normchannel == network.norm_case(chan): | ||||
|             joinlist.pop(i) | ||||
| 
 | ||||
| def onChannelMenu(e): | ||||
|     def toggle_join(): | ||||
|         if isautojoin(e.network, e.channel): | ||||
|             unsetautojoin(e.network, e.channel) | ||||
|         else: | ||||
|             setautojoin(e.network, e.channel) | ||||
|      | ||||
|     e.menu.append(('Autojoin', isautojoin(e.network, e.channel), toggle_join)) | ||||
| @ -1,70 +0,0 @@ | ||||
| import windows | ||||
| import widgets | ||||
| import irc | ||||
| 
 | ||||
| shortcuts = { | ||||
|     '^b': '\x02', | ||||
|     '^u': '\x1F', | ||||
|     '^r': '\x16', | ||||
|     '^k': '\x03', | ||||
|     '^l': '\x04', | ||||
|     '^o': '\x0F', | ||||
|     } | ||||
| 
 | ||||
| def onKeyPress(e): | ||||
|     if e.key in shortcuts: | ||||
|         e.window.input.insert(shortcuts[e.key]) | ||||
| 
 | ||||
|     elif e.key == '!c': | ||||
|         e.window.output.copy() | ||||
| 
 | ||||
|     elif e.key == 'Page_Up': | ||||
|         e.window.output.y = e.window.output.y - e.window.output.height / 2 | ||||
|      | ||||
|     elif e.key == 'Page_Down': | ||||
|         e.window.output.y = e.window.output.y + e.window.output.height / 2 | ||||
| 
 | ||||
|     elif e.key == '^Home': | ||||
|         e.window.output.y = 0 | ||||
|      | ||||
|     elif e.key == '^End': | ||||
|         e.window.output.y = e.window.output.ymax | ||||
| 
 | ||||
|     elif e.key in ('^Page_Up', '^Page_Down'): | ||||
|         winlist = list(windows.manager) | ||||
|         index = winlist.index(e.window) + ((e.key == '^Page_Down') and 1 or -1) | ||||
|         if 0 <= index < len(winlist): | ||||
|             winlist[index].activate() | ||||
| 
 | ||||
|     elif e.key == '!a': | ||||
|         winlist = list(windows.manager) | ||||
|         winlist = winlist[winlist.index(e.window):]+winlist | ||||
|         w = [w for w in winlist if widgets.HILIT in w.activity] | ||||
|          | ||||
|         if not w: | ||||
|             w = [w for w in winlist if widgets.TEXT in w.activity] | ||||
|          | ||||
|         if w: | ||||
|             windows.manager.set_active(w[0]) | ||||
| 
 | ||||
|     # tabbed browsing | ||||
|     elif e.key == '^t': | ||||
|         windows.new(windows.StatusWindow, irc.Network(), 'status').activate() | ||||
| 
 | ||||
|     elif e.key == '^w': | ||||
|         windows.manager.get_active().close() | ||||
|          | ||||
|     elif e.key == '^f': | ||||
|         window = windows.manager.get_active() | ||||
|          | ||||
|         find = widgets.FindBox(window) | ||||
|          | ||||
|         window.pack_start(find, expand=False) | ||||
|          | ||||
|         find.textbox.grab_focus() | ||||
|      | ||||
|     elif len(e.key) == 2 and e.key.startswith('!') and e.key[1].isdigit(): | ||||
|         n = int(e.key[1]) | ||||
|         if n and n <= len(core.manager): | ||||
|              list(core.manager)[n-1].activate() | ||||
|         #else e.key == "!0" | ||||
| @ -1,366 +0,0 @@ | ||||
| import time | ||||
| 
 | ||||
| import windows | ||||
| import widgets | ||||
| import chaninfo | ||||
| 
 | ||||
| from conf import conf | ||||
| 
 | ||||
| textareas = {} | ||||
| if 'font' in conf: | ||||
|     textareas['font'] = conf['font'] | ||||
| if 'bg_color' in conf: | ||||
|     textareas['bg'] = conf['bg_color'] | ||||
| if 'fg_color' in conf: | ||||
|     textareas['fg'] = conf['fg_color'] | ||||
| 
 | ||||
| widgets.set_style("view", textareas) | ||||
| widgets.set_style("nicklist", textareas) | ||||
| 
 | ||||
| #copied pretty directly from something that was probably copied from wine sources | ||||
| def RGBtoHSL(r, g, b): | ||||
|     maxval = max(r, g, b) | ||||
|     minval = min(r, g, b) | ||||
|      | ||||
|     luminosity = ((maxval + minval) * 240 + 255) // 510 | ||||
|      | ||||
|     if maxval == minval: | ||||
|         saturation = 0 | ||||
|         hue = 160 | ||||
|     else: | ||||
|         delta = maxval - minval | ||||
|          | ||||
|         if luminosity <= 120: | ||||
|             saturation = ((maxval+minval)//2 + delta*240) // (maxval + minval) | ||||
|         else: | ||||
|             saturation = ((150-maxval-minval)//2 + delta*240) // (150-maxval-minval) | ||||
|          | ||||
|         #sigh.. | ||||
|         rnorm = (delta//2 + maxval*40 - r*40)//delta | ||||
|         gnorm = (delta//2 + maxval*40 - g*40)//delta | ||||
|         bnorm = (delta//2 + maxval*40 - b*40)//delta | ||||
|          | ||||
|         if r == maxval: | ||||
|             hue = bnorm-gnorm | ||||
|         elif g == maxval: | ||||
|             hue = 80+rnorm-bnorm | ||||
|         else: | ||||
|             hue = 160+gnorm-rnorm | ||||
|         hue = hue % 240 | ||||
|     return hue, saturation, luminosity | ||||
| 
 | ||||
| #copied from the same place | ||||
| def huetoRGB(hue, mid1, mid2): | ||||
|     hue = hue % 240 | ||||
|      | ||||
|     if hue > 160: | ||||
|         return mid1 | ||||
|     elif hue > 120: | ||||
|         hue = 160 - hue | ||||
|     elif hue > 40: | ||||
|         return mid2 | ||||
|     return ((hue * (mid2 - mid1) + 20) // 40) + mid1 | ||||
| 
 | ||||
| #this too | ||||
| def HSLtoRGB(hue, saturation, luminosity): | ||||
|     if saturation != 0: | ||||
|         if luminosity > 120: | ||||
|             mid2 = saturation + luminosity - (saturation * luminosity + 120)//240 | ||||
|         else: | ||||
|             mid2 = ((saturation + 240) * luminosity + 120)//240 | ||||
|          | ||||
|         mid1 = luminosity * 2 - mid2 | ||||
|          | ||||
|         return tuple((huetoRGB(hue+x, mid1, mid2) * 255 + 120) // 240 for x in (80,0,-80)) | ||||
|     else: | ||||
|         value = luminosity * 255 // 240 | ||||
|         return value, value, value | ||||
| 
 | ||||
| def gethashcolor(string): | ||||
|     h = hash(string) | ||||
|     rgb = HSLtoRGB(h%241, 100-h//241%61, 90) | ||||
|     return "%02x%02x%02x" % rgb | ||||
| 
 | ||||
| #take an event e and trigger the highlight event if necessary | ||||
| def hilight_text(e): | ||||
|     if not hasattr(e, 'Highlight'): | ||||
|         e.Highlight = [] | ||||
|         core.events.trigger('Highlight', e) | ||||
| 
 | ||||
| #hilight own nick | ||||
| def onHighlight(e): | ||||
|     lowertext = e.text.lower() | ||||
|     for word in conf.get('highlight_words', []) + [e.network.me] + e.network.nicks: | ||||
|         lowerword = word.lower() | ||||
|         pos = lowertext.find(lowerword, 0) | ||||
|         while pos != -1: | ||||
|             e.Highlight.append((pos, pos+len(word))) | ||||
|             pos = lowertext.find(lowerword, pos+1) | ||||
| 
 | ||||
| def prefix(e): | ||||
|     return time.strftime(conf.get('timestamp', '')) | ||||
| 
 | ||||
| def getsourcecolor(e): | ||||
|     address = getattr(e, "address", "") | ||||
|     if address: | ||||
|         if e.network.me == e.source: | ||||
|             e.network._my_address = address | ||||
|     elif e.network.me == e.source: | ||||
|         address = getattr(e.network, "_my_address", "") | ||||
|     if '@' in address: | ||||
|         address = address.split('@')[1] | ||||
|     if not address: | ||||
|         address = e.source | ||||
|     return "\x04%s" % gethashcolor(address) | ||||
| 
 | ||||
| def format_source(e): | ||||
|     highlight = getattr(e, "Highlight", "") and '\x02' or '' | ||||
|     return "%s\x04%s%s" % (highlight, getsourcecolor(e), e.source) | ||||
| 
 | ||||
| def format_info_source(e): | ||||
|     if e.source == e.network.me: | ||||
|         return "\x04%sYou" % (getsourcecolor(e)) | ||||
|     else: | ||||
|         return "\x04%s%s" % (getsourcecolor(e), e.source) | ||||
| 
 | ||||
| def address(e): | ||||
|     #if e.source != e.network.me: | ||||
|     #    return "%s " % info_in_brackets(e.address) | ||||
|     #else: | ||||
|     #    return "" | ||||
|     return "" | ||||
| 
 | ||||
| def text(e): | ||||
|     if e.text: | ||||
|         #return " %s" % info_in_brackets(e.text) | ||||
|         return ": \x0F%s" % e.text | ||||
|     else: | ||||
|         return "" | ||||
|          | ||||
| def info_in_brackets(text): | ||||
|     return "(\x044881b6%s\x0F)" % text | ||||
| 
 | ||||
| def pretty_time(secs): | ||||
|     times = ( | ||||
|         #("years", "year", 31556952), | ||||
|         ("weeks", "week", 604800), | ||||
|         ("days", "day", 86400), | ||||
|         ("hours", "hour", 3600), | ||||
|         ("minutes", "minute", 60), | ||||
|         ("seconds", "second", 1), | ||||
|         ) | ||||
|     if secs == 0: | ||||
|         return "0 seconds" | ||||
|     result = "" | ||||
|     for plural, singular, amount in times: | ||||
|         n, secs = divmod(secs, amount) | ||||
|         if n == 1: | ||||
|             result = result + " %s %s" % (n, singular) | ||||
|         elif n: | ||||
|             result = result + " %s %s" % (n, plural) | ||||
|     return result[1:] | ||||
| 
 | ||||
| def onText(e): | ||||
|     hilight_text(e) | ||||
|     color = getsourcecolor(e) | ||||
|     to_write = prefix(e) | ||||
|     if e.network.me == e.target:    # this is a pm | ||||
|         if e.window.id == e.network.norm_case(e.source): | ||||
|             to_write += "\x02<\x0F%s\x0F\x02>\x0F " % (format_source(e)) | ||||
|         else: | ||||
|             to_write += "\x02*\x0F%s\x0F\x02*\x0F " % (format_source(e)) | ||||
|     else: | ||||
|         if e.window.id == e.network.norm_case(e.target): | ||||
|             to_write += "\x02<\x0F%s\x0F\x02>\x0F " % (format_source(e)) | ||||
|         else: | ||||
|             to_write += "\x02<\x0F%s:%s\x0F\x02>\x0F " % (format_source(e), e.target) | ||||
|     to_write += e.text | ||||
|      | ||||
|     if e.Highlight: | ||||
|         e.window.write(to_write, widgets.HILIT) | ||||
|     else: | ||||
|         e.window.write(to_write, widgets.TEXT) | ||||
|      | ||||
| def onOwnText(e): | ||||
|     color = getsourcecolor(e) | ||||
|     to_write = prefix(e) | ||||
|     if e.window.id == e.network.norm_case(e.target): | ||||
|         to_write += "\x02<\x0F%s\x0F\x02>\x0F %s" % (format_source(e), e.text) | ||||
|     else: | ||||
|         to_write += "%s->\x0F \x02*\x0F%s\x0F\x02*\x0F %s" % (color, e.target, e.text) | ||||
|      | ||||
|     e.window.write(to_write) | ||||
|      | ||||
| def onAction(e): | ||||
|     hilight_text(e) | ||||
|     color = color = getsourcecolor(e) | ||||
|     to_write = "%s\x02*\x0F %s\x0F %s" % (prefix(e), format_source(e), e.text) | ||||
|      | ||||
|     if e.Highlight: | ||||
|         e.window.write(to_write, widgets.HILIT) | ||||
|     else: | ||||
|         e.window.write(to_write, widgets.TEXT) | ||||
|      | ||||
| def onOwnAction(e): | ||||
|     color = getsourcecolor(e) | ||||
|     to_write = "%s\x02*\x0F %s\x0F %s" % (prefix(e), format_source(e), e.text) | ||||
|      | ||||
|     e.window.write(to_write) | ||||
| 
 | ||||
| def onNotice(e): | ||||
|     hilight_text(e) | ||||
|     color = getsourcecolor(e) | ||||
|     to_write = prefix(e) | ||||
|     if e.network.me == e.target:    # this is a pm | ||||
|         to_write += "\x02-\x0F%s\x0F\x02-\x0F " % (format_source(e)) | ||||
|     else: | ||||
|         to_write += "\x02-\x0F%s:%s\x0F\x02-\x0F " % (format_source(e), e.target) | ||||
|     to_write += e.text | ||||
|      | ||||
|     e.window.write(to_write, (e.Highlight and widgets.HILIT) or widgets.TEXT) | ||||
| 
 | ||||
| def onOwnNotice(e): | ||||
|     color = getsourcecolor(e) | ||||
|     to_write = "%s-> \x02-\x02%s\x0F\x02-\x0F %s" % (prefix(e), e.target, e.text) | ||||
|      | ||||
|     e.window.write(to_write) | ||||
| 
 | ||||
| def onCtcp(e): | ||||
|     color = getsourcecolor(e) | ||||
|     to_write = "%s\x02[\x02%s\x0F\x02]\x0F %s" % (prefix(e), format_source(e), e.text) | ||||
|      | ||||
|     if not e.quiet: | ||||
|         e.window.write(to_write) | ||||
| 
 | ||||
| def onCtcpReply(e): | ||||
|     color = getsourcecolor(e) | ||||
|     to_write = "%s%s--- %s reply from %s:\x0F %s" % (prefix(e), color, e.name.capitalize(), format_source(e), ' '.join(e.args)) | ||||
|      | ||||
|     window = windows.manager.get_active() | ||||
|     if window.network != e.network: | ||||
|         window = windows.get_default(e.network) | ||||
|     window.write(to_write, widgets.TEXT) | ||||
| 
 | ||||
| def onJoin(e): | ||||
|     if e.source == e.network.me: | ||||
|         to_write = "%s%s %sjoin %s" % (prefix(e), format_info_source(e), address(e), e.target) | ||||
|     else: | ||||
|         to_write = "%s%s %sjoins %s" % (prefix(e), format_info_source(e), address(e), e.target) | ||||
| 
 | ||||
|     e.window.write(to_write) | ||||
|          | ||||
| def onPart(e): | ||||
|     if e.source == e.network.me: | ||||
|         to_write = "%s%s leave %s%s" % (prefix(e), format_info_source(e), e.target, text(e)) | ||||
|     else: | ||||
|         to_write = "%s%s leaves %s%s" % (prefix(e), format_info_source(e), e.target, text(e)) | ||||
|      | ||||
|     e.window.write(to_write) | ||||
| 
 | ||||
| def onKick(e): | ||||
|     if e.source == e.network.me: | ||||
|         to_write = "%s%s kick %s%s" % (prefix(e), format_info_source(e), e.target, text(e)) | ||||
|     else: | ||||
|         to_write = "%s%s kicks %s%s" % (prefix(e), format_info_source(e), e.target, text(e)) | ||||
|      | ||||
|     e.window.write(to_write, (e.target == e.network.me and widgets.HILIT) or widgets.EVENT) | ||||
|          | ||||
| def onMode(e): | ||||
|     if e.source == e.network.me: | ||||
|         to_write = "%s%s set mode:\x0F %s" % (prefix(e), format_info_source(e), e.text) | ||||
|     else: | ||||
|         to_write = "%s%s sets mode:\x0F %s" % (prefix(e), format_info_source(e), e.text) | ||||
|      | ||||
|     e.window.write(to_write) | ||||
|          | ||||
| def onQuit(e): | ||||
|     to_write = "%s%s leaves%s" % (prefix(e), format_info_source(e), text(e)) | ||||
|      | ||||
|     for channame in chaninfo.channels(e.network): | ||||
|         if chaninfo.ison(e.network, channame, e.source): | ||||
|             window = windows.get(windows.ChannelWindow, e.network, channame, core) | ||||
|             if window: | ||||
|                 window.write(to_write) | ||||
| 
 | ||||
| def onNick(e): | ||||
|     color = getsourcecolor(e) | ||||
|     if e.source == e.network.me: | ||||
|         to_write = "%s%sYou are now known as %s" % (prefix(e), color, e.target) | ||||
|     else: | ||||
|         to_write = "%s%s%s is now known as %s" % (prefix(e), color, e.source, e.target) | ||||
|      | ||||
|     if e.source == e.network.me: | ||||
|         for window in windows.get_with(core.manager, network=e.network): | ||||
|             window.write(to_write) | ||||
|     else: | ||||
|         for channame in chaninfo.channels(e.network): | ||||
|             if chaninfo.ison(e.network,channame,e.source): | ||||
|                 window = windows.get(windows.ChannelWindow, e.network, channame) | ||||
|                 if window: | ||||
|                     window.write(to_write) | ||||
| 
 | ||||
| def onTopic(e): | ||||
|     if e.source == e.network.me: | ||||
|         to_write = "%s%s set topic:\x0F %s" % (prefix(e), format_info_source(e), e.text) | ||||
|     else: | ||||
|         to_write = "%s%s sets topic:\x0F %s" % (prefix(e), format_info_source(e), e.text) | ||||
|      | ||||
|     e.window.write(to_write) | ||||
| 
 | ||||
| def onRaw(e): | ||||
|     if not e.quiet: | ||||
|         if e.msg[1].isdigit(): | ||||
|             if e.msg[1] == '332': | ||||
|                 window = windows.get(windows.ChannelWindow, e.network, e.msg[3], core) or e.window | ||||
|                 window.write( | ||||
|                     "%sTopic on %s is: %s" %  | ||||
|                         (prefix(e), e.msg[3], e.text) | ||||
|                         ) | ||||
|                  | ||||
|             elif e.msg[1] == '333': | ||||
|                 window = windows.get(windows.ChannelWindow, e.network, e.msg[3], core) or e.window | ||||
|                 window.write( | ||||
|                     "%sTopic on %s set by %s at time %s" %  | ||||
|                         (prefix(e), e.msg[3], e.msg[4], time.ctime(int(e.msg[5]))) | ||||
|                         ) | ||||
|              | ||||
|             elif e.msg[1] == '329': #RPL_CREATIONTIME | ||||
|                 pass | ||||
|              | ||||
|             elif e.msg[1] == '311': #RPL_WHOISUSER | ||||
|                 e.window.write("* %s is %s@%s * %s" % (e.msg[3], e.msg[4], e.msg[5], e.msg[7])) | ||||
|              | ||||
|             elif e.msg[1] == '312': #RPL_WHOISSERVER | ||||
|                 e.window.write("* %s on %s (%s)" % (e.msg[3], e.msg[4], e.msg[5])) | ||||
|              | ||||
|             elif e.msg[1] == '317': #RPL_WHOISIDLE | ||||
|                 e.window.write("* %s has been idle for %s" % (e.msg[3], pretty_time(int(e.msg[4])))) | ||||
|                 if e.msg[5].isdigit(): | ||||
|                     e.window.write("* %s signed on %s" % (e.msg[3], time.ctime(int(e.msg[5])))) | ||||
|              | ||||
|             elif e.msg[1] == '319': #RPL_WHOISCHANNELS | ||||
|                 e.window.write("* %s on channels: %s" % (e.msg[3], e.msg[4])) | ||||
|              | ||||
|             elif e.msg[1] == '330': #RPL_WHOISACCOUNT | ||||
|                 #this appears to conflict with another raw, so if there's anything weird about it, | ||||
|                 # we fall back on the default | ||||
|                 if len(e.msg) == 6 and not e.msg[4].isdigit() and not e.msg[5].isdigit(): | ||||
|                     e.window.write("* %s %s %s" % (e.msg[3], e.msg[5], e.msg[4])) | ||||
|                 else: | ||||
|                     e.window.write("* %s" % ' '.join(e.msg[3:])) | ||||
|              | ||||
|             else: | ||||
|                 e.window.write("* %s" % ' '.join(e.msg[3:])) | ||||
|         elif e.msg[1] == 'ERROR': | ||||
|             e.window.write("Error: %s" % e.text) | ||||
| 
 | ||||
| def onDisconnect(e): | ||||
|     to_write = '%s* Disconnected' % prefix(e) | ||||
|     if e.error: | ||||
|         to_write += ' (%s)' % e.error | ||||
| 
 | ||||
|     for window in windows.get_with(network=e.network): | ||||
|         if isinstance(window, windows.StatusWindow): | ||||
|             window.write(to_write, widgets.TEXT) | ||||
|         else: | ||||
|             window.write(to_write, widgets.EVENT) | ||||
| @ -1,45 +0,0 @@ | ||||
| import time | ||||
| 
 | ||||
| import ui | ||||
| from conf import conf | ||||
| 
 | ||||
| def setupRaw(e): | ||||
|     e.network._message_timeout = False | ||||
| 
 | ||||
| def onSocketConnect(e): | ||||
|     timeout = conf.get("server_traffic_timeout", 120)*1000 | ||||
|     e.network._message_timeout = False | ||||
|     if timeout: | ||||
|         e.network._message_timeout_source = ui.register_timer(timeout, check_timeout, e.network) | ||||
|     else: | ||||
|         e.network._message_timeout_source = None | ||||
| 
 | ||||
| def check_timeout(network): | ||||
|     if network._message_timeout: | ||||
|         network.raw("PING %s" % network.me) | ||||
|         timeout = conf.get("server_death_timeout", 240)*1000 | ||||
|         network._message_timeout_source = ui.register_timer(timeout, check_death_timeout, network) | ||||
|         return False | ||||
|     else: | ||||
|         network._message_timeout = True | ||||
|         return True # call this function again | ||||
| 
 | ||||
| def check_death_timeout(network): | ||||
|     if network._message_timeout: | ||||
|         network.raw("QUIT :Server missing, presumed dead") | ||||
|         network.disconnect(error="The server seems to have stopped talking to us") | ||||
|     else: | ||||
|         network._message_timeout = False | ||||
|         timeout = conf.get("server_traffic_timeout", 120)*1000 | ||||
|         if timeout: | ||||
|             network._message_timeout_source = ui.register_timer(timeout, check_timeout, network) | ||||
|         else: | ||||
|             network._message_timeout_source = None | ||||
| 
 | ||||
| def onDisconnect(e): | ||||
|     try: | ||||
|         if e.network._message_timeout_source: | ||||
|             e.network._message_timeout_source.unregister() | ||||
|             e.network._message_timeout_source = None | ||||
|     except AttributeError: | ||||
|         pass | ||||
| @ -1,132 +0,0 @@ | ||||
| import irc | ||||
| import ui | ||||
| import windows | ||||
| import irc_script | ||||
| from conf import conf | ||||
| 
 | ||||
| # FIXME: meh still might want rid of these, I'm not sure yet | ||||
| 
 | ||||
| def onActive(e): | ||||
|     e.window.activity = None | ||||
| 
 | ||||
|     ui.register_idle(windows.manager.set_title) | ||||
| 
 | ||||
| def setupNick(e): | ||||
|     if e.source == e.network.me: | ||||
|         for w in windows.get_with(core.manager, network=e.network): | ||||
|             try: | ||||
|                 w.nick_label.update(e.target) | ||||
|             except AttributeError: | ||||
|                 pass     | ||||
| 
 | ||||
| def onExit(e): | ||||
|     for n in set(w.network for w in windows.manager): | ||||
|         if n: | ||||
|             n.quit() | ||||
| 
 | ||||
| def setupJoin(e): | ||||
|     if e.source == e.network.me: | ||||
|         window = windows.get(windows.StatusWindow, e.network, 'status', core) | ||||
|          | ||||
|         if window and not conf.get('status'): | ||||
|             window.mutate(windows.ChannelWindow, e.network, e.target) | ||||
|         else: | ||||
|             window = windows.new(windows.ChannelWindow, e.network, e.target, core) | ||||
|          | ||||
|         if e.requested: | ||||
|             window.activate() | ||||
| 
 | ||||
|     e.window = windows.get(windows.ChannelWindow, e.network, e.target, core) or e.window | ||||
| 
 | ||||
| def setupText(e): | ||||
|     if e.target == e.network.me: | ||||
|         e.window = windows.new(windows.QueryWindow, e.network, e.source, core) | ||||
|     else: | ||||
|         e.window = \ | ||||
|             windows.get(windows.ChannelWindow, e.network, e.target, core) or \ | ||||
|             windows.get(windows.QueryWindow, e.network, e.source, core) or \ | ||||
|             e.window | ||||
| 
 | ||||
| setupAction = setupText | ||||
| 
 | ||||
| def setupNotice(e): | ||||
|     if e.target != e.network.me: | ||||
|         e.window = \ | ||||
|             windows.get(windows.ChannelWindow, e.network, e.target, core) or e.window | ||||
| 
 | ||||
| def setupOwnText(e): | ||||
|     e.window = \ | ||||
|         windows.get(windows.ChannelWindow, e.network, e.target, core) or \ | ||||
|         windows.get(windows.QueryWindow, e.network, e.target, core) or \ | ||||
|         e.window | ||||
| 
 | ||||
| setupOwnAction = setupOwnText | ||||
| 
 | ||||
| def setdownPart(e): | ||||
|     if e.source == e.network.me: | ||||
|         window = windows.get(windows.ChannelWindow, e.network, e.target, core)         | ||||
|          | ||||
|         if window: | ||||
|             cwindows = list(windows.get_with( | ||||
|                                 network=window.network, | ||||
|                                 wclass=windows.ChannelWindow | ||||
|                                 )) | ||||
|                              | ||||
|             if len(cwindows) == 1 and not list(windows.get_with(network=window.network, wclass=windows.StatusWindow)): | ||||
|                 window.mutate(windows.StatusWindow, e.network, 'status') | ||||
|                 if e.requested: | ||||
|                     window.activate() | ||||
|             elif e.requested: | ||||
|                 window.close() | ||||
| 
 | ||||
| def onClose(e): | ||||
|     nwindows = list(windows.get_with(core.manager, network=e.window.network)) | ||||
|      | ||||
|     if isinstance(e.window, windows.ChannelWindow):  | ||||
|         cwindows = list(windows.get_with(core.manager, | ||||
|                             network=e.window.network, | ||||
|                             wclass=windows.ChannelWindow | ||||
|                             )) | ||||
|          | ||||
|         #if we only have one window for the network, don't bother to part as | ||||
|         # we'll soon be quitting anyway | ||||
|         if len(nwindows) != 1 and irc_script.ischan(e.window.network, e.window.id): | ||||
|             e.window.network.part(e.window.id)  | ||||
|      | ||||
|     if len(nwindows) == 1: | ||||
|         core.events.trigger("CloseNetwork", window=e.window, network=e.window.network) | ||||
|      | ||||
|     elif isinstance(e.window, windows.StatusWindow) and conf.get('status'): | ||||
|         core.events.trigger("CloseNetwork", window=e.window, network=e.window.network) | ||||
|         for window in nwindows: | ||||
|             if window != e.window: | ||||
|                 window.close() | ||||
|          | ||||
|     if len(core.manager) == 1: | ||||
|         windows.new(windows.StatusWindow, irc.Network(), "status", core) | ||||
| 
 | ||||
| def onConnecting(e): | ||||
|     return | ||||
|     window = windows.get_default(e.network) | ||||
|     if window: | ||||
|         window.update() | ||||
| 
 | ||||
| onDisconnect = onConnecting | ||||
| 
 | ||||
| def setupPart(e): | ||||
|     e.window = windows.get(windows.ChannelWindow, e.network, e.target, core) or e.window | ||||
| 
 | ||||
| setupTopic = setupPart | ||||
| 
 | ||||
| def setupKick(e): | ||||
|     e.window = windows.get(windows.ChannelWindow, e.network, e.channel, core) or e.window | ||||
| 
 | ||||
| def setupMode(e): | ||||
|     if e.target != e.network.me: | ||||
|         e.window = windows.get(windows.ChannelWindow, e.network, e.target, core) or e.window | ||||
| 
 | ||||
| def onWindowMenu(e): | ||||
|     if isinstance(e.window, windows.ChannelWindow): | ||||
|         e.channel = e.window.id | ||||
|         e.network = e.window.network | ||||
|         core.events.trigger('ChannelMenu', e) | ||||
| @ -1,51 +0,0 @@ | ||||
| import gtk | ||||
| 
 | ||||
| import windows | ||||
| from conf import conf | ||||
| 
 | ||||
| if 'networks' not in conf: | ||||
|     conf['networks'] = {} | ||||
| 
 | ||||
| def server_get_data(network_info): | ||||
|     if 'port' in network_info: | ||||
|         return "%s:%s" % ( | ||||
|             network_info.get('server', '') , network_info.get('port') | ||||
|             ) | ||||
|     else: | ||||
|         return network_info.get('server', '') | ||||
|          | ||||
| def server_set_data(text, network_info): | ||||
|     if ':' in text: | ||||
|         network_info['server'], port = text.rsplit(':',1) | ||||
|         network_info['port'] = int(port) | ||||
|     else: | ||||
|         network_info['server'] = text | ||||
|         network_info.pop('port', None) | ||||
|              | ||||
| def channels_get_data(network_info): | ||||
|     return '\n'.join(network_info.get('join', ())) | ||||
|              | ||||
| def channels_set_data(text, network_info): | ||||
|     network_info['join'] = [] | ||||
|      | ||||
|     for line in text.split('\n'): | ||||
|         for chan in line.split(','): | ||||
|             if chan: | ||||
|                 network_info['join'].append(chan.strip()) | ||||
|      | ||||
| def perform_get_data(network_info): | ||||
|     return '\n'.join(network_info.get('perform', ())) | ||||
|              | ||||
| def perform_set_data(text, network_info): | ||||
|     network_info['perform'] = [line for line in text.split('\n') if line] | ||||
|      | ||||
| def autoconnect_set_data(do_autoconnect, network):  | ||||
|     if 'start_networks' not in conf: | ||||
|         conf['start_networks'] = [] | ||||
| 
 | ||||
|     # note (n in C) != w | ||||
|     if (network in conf.get('start_networks')) != do_autoconnect: | ||||
|         if do_autoconnect: | ||||
|             conf.get('start_networks').append(network) | ||||
|         else: | ||||
|             conf.get('start_networks').remove(network) | ||||
| @ -1,105 +0,0 @@ | ||||
| import sys  | ||||
| import os | ||||
| import thread | ||||
| import socket | ||||
| import signal | ||||
| import traceback | ||||
| 
 | ||||
| import commands | ||||
| 
 | ||||
| import gobject | ||||
| 
 | ||||
| __sys_path = list(sys.path) | ||||
| import gtk | ||||
| sys.path = __sys_path | ||||
| 
 | ||||
| import irc | ||||
| from conf import conf | ||||
| 
 | ||||
| import widgets | ||||
| import windows | ||||
| 
 | ||||
| # Running from same package dir | ||||
| urkpath = os.path.dirname(__file__) | ||||
| 
 | ||||
| def path(filename=""): | ||||
|     if filename: | ||||
|         return os.path.join(urkpath, filename) | ||||
|     else: | ||||
|         return urkpath | ||||
| 
 | ||||
| # Priority Constants | ||||
| PRIORITY_HIGH = gobject.PRIORITY_HIGH | ||||
| PRIORITY_DEFAULT = gobject.PRIORITY_DEFAULT | ||||
| PRIORITY_HIGH_IDLE = gobject.PRIORITY_HIGH_IDLE | ||||
| PRIORITY_DEFAULT_IDLE = gobject.PRIORITY_DEFAULT_IDLE | ||||
| PRIORITY_LOW = gobject.PRIORITY_LOW | ||||
| 
 | ||||
| 
 | ||||
| if os.access(path('profile'),os.F_OK) or os.path.expanduser("~") == "~": | ||||
|     userpath = path('profile') | ||||
|     if not os.access(userpath,os.F_OK): | ||||
|         os.mkdir(userpath) | ||||
|     if not os.access(os.path.join(userpath,'scripts'),os.F_OK): | ||||
|         os.mkdir(os.path.join(userpath,'scripts')) | ||||
| else: | ||||
|     userpath = os.path.join(os.path.expanduser("~"), ".urk") | ||||
|     if not os.access(userpath,os.F_OK): | ||||
|         os.mkdir(userpath, 0700) | ||||
|     if not os.access(os.path.join(userpath,'scripts'),os.F_OK): | ||||
|         os.mkdir(os.path.join(userpath,'scripts'), 0700) | ||||
| 
 | ||||
| 
 | ||||
| def set_clipboard(text): | ||||
|     gtk.clipboard_get(gtk.gdk.SELECTION_CLIPBOARD).set_text(text) | ||||
|     gtk.clipboard_get(gtk.gdk.SELECTION_PRIMARY).set_text(text) | ||||
| 
 | ||||
| class Source(object): | ||||
|     __slots__ = ['enabled'] | ||||
|     def __init__(self): | ||||
|         self.enabled = True | ||||
|     def unregister(self): | ||||
|         self.enabled = False | ||||
| 
 | ||||
| class GtkSource(object): | ||||
|     __slots__ = ['tag'] | ||||
|     def __init__(self, tag): | ||||
|         self.tag = tag | ||||
|     def unregister(self): | ||||
|         gobject.source_remove(self.tag) | ||||
| 
 | ||||
| def register_idle(f, *args, **kwargs): | ||||
|     priority = kwargs.pop("priority",PRIORITY_DEFAULT_IDLE) | ||||
|     def callback(): | ||||
|         return f(*args, **kwargs) | ||||
|     return GtkSource(gobject.idle_add(callback, priority=priority)) | ||||
| 
 | ||||
| def register_timer(time, f, *args, **kwargs): | ||||
|     priority = kwargs.pop("priority",PRIORITY_DEFAULT_IDLE) | ||||
|     def callback(): | ||||
|         return f(*args, **kwargs) | ||||
|     return GtkSource(gobject.timeout_add(time, callback, priority=priority)) | ||||
| 
 | ||||
| def fork(cb, f, *args, **kwargs): | ||||
|     is_stopped = Source() | ||||
|     def thread_func(): | ||||
|         try: | ||||
|             result, error = f(*args, **kwargs), None | ||||
|         except Exception, e: | ||||
|             result, error = None, e | ||||
| 
 | ||||
|         if is_stopped.enabled: | ||||
|             def callback():            | ||||
|                 if is_stopped.enabled: | ||||
|                     cb(result, error) | ||||
| 
 | ||||
|             gobject.idle_add(callback) | ||||
| 
 | ||||
|     thread.start_new_thread(thread_func, ()) | ||||
|     return is_stopped | ||||
| 
 | ||||
| set_style = widgets.set_style | ||||
| 
 | ||||
| def we_get_signal(*what): | ||||
|     gobject.idle_add(windows.manager.exit) | ||||
| 
 | ||||
| @ -1,70 +0,0 @@ | ||||
| import sys | ||||
| import commands | ||||
| import linecache | ||||
| 
 | ||||
| import time | ||||
| 
 | ||||
| last_mem = [0] | ||||
| 
 | ||||
| def traceit_memory(frame, event, arg): | ||||
|     if event == "line": | ||||
|         mem = int(" " + commands.getoutput( | ||||
|                 "ps -eo cmd,rss | grep urk_trace.py | grep -v grep" | ||||
|                 ).split(" ")[-1]) | ||||
|                  | ||||
|         if mem > last_mem[0]: | ||||
|             last_mem[0] = mem | ||||
|              | ||||
|             mem = str(mem) | ||||
|          | ||||
|             filename = frame.f_globals["__file__"] | ||||
|              | ||||
|             if filename.endswith(".pyc") or filename.endswith(".pyo"): | ||||
|                 filename = filename[:-1] | ||||
|                  | ||||
|             name = frame.f_globals["__name__"] | ||||
|          | ||||
|             lineno = frame.f_lineno | ||||
|             line = linecache.getline(filename,lineno).rstrip() | ||||
| 
 | ||||
|             data = "%s:%i: %s" % (name, lineno, line) | ||||
| 
 | ||||
|             print "%s%s" % (data, mem.rjust(80 - len(data))) | ||||
| 
 | ||||
|     return traceit_memory | ||||
|      | ||||
| lines = {} | ||||
| 
 | ||||
| def traceit(frame, event, arg): | ||||
|     if event == "line": | ||||
|         try: | ||||
|             filename = frame.f_globals["__file__"] | ||||
|              | ||||
|             if filename.endswith(".pyc") or filename.endswith(".pyo"): | ||||
|                 filename = filename[:-1] | ||||
|                  | ||||
|             name = frame.f_globals["__name__"] | ||||
|          | ||||
|             lineno = frame.f_lineno | ||||
|             line = linecache.getline(filename,lineno).rstrip() | ||||
| 
 | ||||
|             data = "%s:%i: %s" % (name, lineno, line) | ||||
|              | ||||
|             print time.time(), data | ||||
|              | ||||
|             #if data in lines: | ||||
|             #    lines[data] += 1 | ||||
|             #else: | ||||
|             #    lines[data] = 1 | ||||
| 
 | ||||
|         except Exception, e: | ||||
|             print e | ||||
| 
 | ||||
|     return traceit | ||||
|      | ||||
| def main(): | ||||
|     import urk | ||||
|     urk.main() | ||||
| 
 | ||||
| sys.settrace(traceit) | ||||
| main() | ||||
| @ -1,811 +0,0 @@ | ||||
| import codecs | ||||
| 
 | ||||
| import gobject | ||||
| import gtk | ||||
| import gtk.gdk | ||||
| import pango | ||||
| 
 | ||||
| from conf import conf | ||||
| import parse_mirc | ||||
| import windows | ||||
| 
 | ||||
| import servers | ||||
| 
 | ||||
| # Window activity Constants | ||||
| HILIT = 'h' | ||||
| TEXT ='t' | ||||
| EVENT = 'e' | ||||
| 
 | ||||
| ACTIVITY_MARKUP = { | ||||
|     HILIT: "<span style='italic' foreground='#00F'>%s</span>", | ||||
|     TEXT: "<span foreground='#ca0000'>%s</span>", | ||||
|     EVENT: "<span foreground='#363'>%s</span>", | ||||
|     } | ||||
| 
 | ||||
| # This holds all tags for all windows ever | ||||
| tag_table = gtk.TextTagTable() | ||||
| 
 | ||||
| link_tag = gtk.TextTag('link') | ||||
| link_tag.set_property('underline', pango.UNDERLINE_SINGLE) | ||||
| 
 | ||||
| indent_tag = gtk.TextTag('indent') | ||||
| indent_tag.set_property('indent', -20) | ||||
| 
 | ||||
| tag_table.add(link_tag) | ||||
| tag_table.add(indent_tag) | ||||
| 
 | ||||
| #FIXME: MEH hates dictionaries, they remind him of the bad words | ||||
| styles = {} | ||||
| 
 | ||||
| def style_me(widget, style): | ||||
|     widget.set_style(styles.get(style)) | ||||
| 
 | ||||
| def set_style(widget_name, style): | ||||
|     if style: | ||||
|         # FIXME: find a better way... | ||||
|         dummy = gtk.Label() | ||||
|         dummy.set_style(None) | ||||
|      | ||||
|         def apply_style_fg(value): | ||||
|             dummy.modify_text(gtk.STATE_NORMAL, gtk.gdk.color_parse(value)) | ||||
| 
 | ||||
|         def apply_style_bg(value): | ||||
|             dummy.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(value)) | ||||
| 
 | ||||
|         def apply_style_font(value): | ||||
|             dummy.modify_font(pango.FontDescription(value)) | ||||
|      | ||||
|         style_functions = ( | ||||
|             ('fg', apply_style_fg), | ||||
|             ('bg', apply_style_bg), | ||||
|             ('font', apply_style_font), | ||||
|             ) | ||||
| 
 | ||||
|         for name, f in style_functions: | ||||
|             if name in style: | ||||
|                 f(style[name]) | ||||
| 
 | ||||
|         style = dummy.rc_get_style() | ||||
|     else: | ||||
|         style = None | ||||
|      | ||||
|     styles[widget_name] = style | ||||
|      | ||||
| def menu_from_list(alist): | ||||
|     while alist and not alist[-1]: | ||||
|         alist.pop(-1) | ||||
| 
 | ||||
|     last = None | ||||
|     for item in alist: | ||||
|         if item != last: | ||||
|             if item: | ||||
|                 if len(item) == 2: | ||||
|                     name, function = item | ||||
|                      | ||||
|                     menuitem = gtk.ImageMenuItem(name) | ||||
|                      | ||||
|                 elif len(item) == 3: | ||||
|                     name, stock_id, function = item | ||||
|                      | ||||
|                     if isinstance(stock_id, bool): | ||||
|                         menuitem = gtk.CheckMenuItem(name) | ||||
|                         menuitem.set_active(stock_id) | ||||
|                     else: | ||||
|                         menuitem = gtk.ImageMenuItem(stock_id) | ||||
|                      | ||||
|                 if isinstance(function, list): | ||||
|                     submenu = gtk.Menu() | ||||
|                     for subitem in menu_from_list(function): | ||||
|                         submenu.append(subitem) | ||||
|                     menuitem.set_submenu(submenu) | ||||
| 
 | ||||
|                 else: | ||||
|                     menuitem.connect("activate", lambda a, f: f(), function) | ||||
| 
 | ||||
|                 yield menuitem | ||||
| 
 | ||||
|             else: | ||||
|                 yield gtk.SeparatorMenuItem() | ||||
|                  | ||||
|         last = item | ||||
| 
 | ||||
| class Nicklist(gtk.TreeView): | ||||
|     def click(self, event): | ||||
|         if event.button == 3: | ||||
|             x, y = event.get_coords() | ||||
|      | ||||
|             (data,), path, x, y = self.get_path_at_pos(int(x), int(y)) | ||||
|          | ||||
|             c_data = self.events.data(window=self.win, data=self[data], menu=[]) | ||||
|          | ||||
|             self.events.trigger("ListRightClick", c_data) | ||||
|              | ||||
|             if c_data.menu: | ||||
|                 menu = gtk.Menu() | ||||
|                 for item in menu_from_list(c_data.menu): | ||||
|                     menu.append(item) | ||||
|                 menu.show_all() | ||||
|                 menu.popup(None, None, None, event.button, event.time) | ||||
|          | ||||
|         elif event.button == 1 and event.type == gtk.gdk._2BUTTON_PRESS: | ||||
|             x, y = event.get_coords() | ||||
|      | ||||
|             (data,), path, x, y = self.get_path_at_pos(int(x), int(y)) | ||||
|          | ||||
|             self.events.trigger("ListDoubleClick", window=self.win, target=self[data]) | ||||
|          | ||||
|     def __getitem__(self, pos): | ||||
|         return self.get_model()[pos][0] | ||||
|          | ||||
|     def __setitem__(self, pos, name_markup): | ||||
|         realname, markedupname, sortkey = name_markup | ||||
|      | ||||
|         self.get_model()[pos] = realname, markedupname, sortkey | ||||
| 
 | ||||
|     def __len__(self): | ||||
|         return len(self.get_model()) | ||||
|      | ||||
|     def index(self, item): | ||||
|         for i, x in enumerate(self): | ||||
|             if x == item: | ||||
|                 return i | ||||
|                  | ||||
|         return -1 | ||||
|          | ||||
|     def append(self, realname, markedupname, sortkey): | ||||
|         self.get_model().append((realname, markedupname, sortkey)) | ||||
|   | ||||
|     def insert(self, pos, realname, markedupname, sortkey): | ||||
|         self.get_model().insert(pos, (realname, markedupname, sortkey)) | ||||
|          | ||||
|     def replace(self, names): | ||||
|         self.set_model(gtk.ListStore(str, str, str)) | ||||
|          | ||||
|         self.insert_column_with_attributes( | ||||
|             0, '', gtk.CellRendererText(), markup=1 | ||||
|             ).set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) | ||||
| 
 | ||||
|         for name in names: | ||||
|             self.append(*name) | ||||
|          | ||||
|         self.get_model().set_sort_column_id(2, gtk.SORT_ASCENDING) | ||||
| 
 | ||||
|     def remove(self, realname): | ||||
|         index = self.index(realname) | ||||
|          | ||||
|         if index == -1: | ||||
|             raise ValueError | ||||
| 
 | ||||
|         self.get_model().remove(self.get_model().iter_nth_child(None, index)) | ||||
|      | ||||
|     def clear(self): | ||||
|         self.get_model().clear() | ||||
|          | ||||
|     def __iter__(self): | ||||
|         return (r[0] for r in self.get_model()) | ||||
| 
 | ||||
|     def __init__(self, window, core): | ||||
|         self.win = window | ||||
|         self.core = core | ||||
|         self.events = core.events | ||||
| 
 | ||||
|         gtk.TreeView.__init__(self) | ||||
|          | ||||
|         self.replace(()) | ||||
| 
 | ||||
|         self.set_headers_visible(False) | ||||
|         self.set_property("fixed-height-mode", True) | ||||
|         self.connect("button-press-event", Nicklist.click) | ||||
|         self.connect_after("button-release-event", lambda *a: True) | ||||
|          | ||||
|         style_me(self, "nicklist") | ||||
| 
 | ||||
| # Label used to display/edit your current nick on a network | ||||
| class NickEditor(gtk.EventBox): | ||||
|     def nick_change(self, entry): | ||||
|         oldnick, newnick = self.label.get_text(), entry.get_text() | ||||
|          | ||||
|         if newnick and newnick != oldnick: | ||||
|             self.events.run('nick %s' % newnick, self.win, self.win.network) | ||||
| 
 | ||||
|         self.win.input.grab_focus() | ||||
| 
 | ||||
|     def update(self, nick=None): | ||||
|         self.label.set_text(nick or self.win.network.me) | ||||
|      | ||||
|     def to_edit_mode(self, widget, event): | ||||
|         if self.label not in self.get_children(): | ||||
|             return | ||||
| 
 | ||||
|         if getattr(event, 'button', None) == 3: | ||||
|             c_data = self.events.data(window=self.win, menu=[]) | ||||
|             self.events.trigger("NickEditMenu", c_data) | ||||
| 
 | ||||
|             if c_data.menu: | ||||
|                 menu = gtk.Menu() | ||||
|                 for item in menu_from_list(c_data.menu): | ||||
|                     menu.append(item) | ||||
|                 menu.show_all() | ||||
|                 menu.popup(None, None, None, event.button, event.time) | ||||
|          | ||||
|         else: | ||||
|             entry = gtk.Entry() | ||||
|             entry.set_text(self.label.get_text()) | ||||
|             entry.connect('activate', self.nick_change) | ||||
|             entry.connect('focus-out-event', self.to_show_mode) | ||||
| 
 | ||||
|             self.remove(self.label) | ||||
|             self.add(entry) | ||||
|             self.window.set_cursor(None) | ||||
|                  | ||||
|             entry.show() | ||||
|             entry.grab_focus() | ||||
|      | ||||
|     def to_show_mode(self, widget, event): | ||||
|         self.remove(widget) | ||||
|         self.add(self.label) | ||||
|         self.win.input.grab_focus() | ||||
|         self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.XTERM)) | ||||
| 
 | ||||
|     def __init__(self, window, core): | ||||
|         gtk.EventBox.__init__(self) | ||||
|         self.events = core.events | ||||
|         self.win = window | ||||
| 
 | ||||
|         self.label = gtk.Label() | ||||
|         self.label.set_padding(5, 0) | ||||
|         self.add(self.label) | ||||
| 
 | ||||
|         self.connect("button-press-event", self.to_edit_mode) | ||||
|          | ||||
|         self.update() | ||||
| 
 | ||||
|         self.connect( | ||||
|             "realize",  | ||||
|             lambda *a: self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.XTERM)) | ||||
|             ) | ||||
| 
 | ||||
| # The entry which you type in to send messages         | ||||
| class TextInput(gtk.Entry): | ||||
|     # Generates an input event | ||||
|     def entered_text(self, ctrl): | ||||
|         #for a in globals(): | ||||
|         #    print a | ||||
|         #print events.__file__ | ||||
|         #self.core.run_command(self.text) | ||||
|         for line in self.text.splitlines(): | ||||
|             if line: | ||||
|                 e_data = self.events.data( | ||||
|                             window=self.win, network=self.win.network, | ||||
|                             text=line, ctrl=ctrl | ||||
|                             ) | ||||
|                 self.events.trigger('Input', e_data) | ||||
|                 if not e_data.done: | ||||
|                     self.events.run(line, self.win, self.win.network) | ||||
| 
 | ||||
|         self.text = '' | ||||
|      | ||||
|     def _set_selection(self, s): | ||||
|         if s: | ||||
|             self.select_region(*s) | ||||
|         else: | ||||
|             self.select_region(self.cursor, self.cursor) | ||||
| 
 | ||||
|     #some nice toys for the scriptors | ||||
|     text = property(gtk.Entry.get_text, gtk.Entry.set_text) | ||||
|     cursor = property(gtk.Entry.get_position, gtk.Entry.set_position) | ||||
|     selection = property(gtk.Entry.get_selection_bounds, _set_selection) | ||||
|      | ||||
|     def insert(self, text): | ||||
|         self.do_insert_at_cursor(self, text) | ||||
|      | ||||
|     #hack to stop it selecting the text when we focus | ||||
|     def do_grab_focus(self): | ||||
|         temp = self.text, (self.selection or (self.cursor,)*2) | ||||
|         self.text = '' | ||||
|         gtk.Entry.do_grab_focus(self) | ||||
|         self.text, self.selection = temp | ||||
| 
 | ||||
|     def keypress(self, event): | ||||
|         keychar = ( | ||||
|             (gtk.gdk.CONTROL_MASK, '^'), | ||||
|             (gtk.gdk.SHIFT_MASK, '+'), | ||||
|             (gtk.gdk.MOD1_MASK, '!') | ||||
|             ) | ||||
| 
 | ||||
|         key = '' | ||||
|         for keymod, char in keychar: | ||||
|             # we make this an int, because otherwise it leaks | ||||
|             if int(event.state) & keymod: | ||||
|                 key += char | ||||
|         key += gtk.gdk.keyval_name(event.keyval) | ||||
| 
 | ||||
|         self.events.trigger('KeyPress', key=key, string=event.string, window=self.win) | ||||
| 
 | ||||
|         if key == "^Return": | ||||
|             self.entered_text(True) | ||||
|          | ||||
|         up = gtk.gdk.keyval_from_name("Up") | ||||
|         down = gtk.gdk.keyval_from_name("Down") | ||||
|         tab = gtk.gdk.keyval_from_name("Tab") | ||||
| 
 | ||||
|         return event.keyval in (up, down, tab) | ||||
|      | ||||
|     def __init__(self, window, core): | ||||
|         gtk.Entry.__init__(self) | ||||
|         self.events = core.events | ||||
|         self.core = core | ||||
|         self.win = window | ||||
| 
 | ||||
|         # we don't want key events to propogate so we stop them in connect_after | ||||
|         self.connect('key-press-event', TextInput.keypress) | ||||
|         self.connect_after('key-press-event', lambda *a: True) | ||||
|          | ||||
|         self.connect('activate', TextInput.entered_text, False) | ||||
| 
 | ||||
| gobject.type_register(TextInput) | ||||
| 
 | ||||
| def prop_to_gtk(textview, (prop, val)): | ||||
|     if val == parse_mirc.BOLD: | ||||
|         val = pango.WEIGHT_BOLD | ||||
| 
 | ||||
|     elif val == parse_mirc.UNDERLINE: | ||||
|         val = pango.UNDERLINE_SINGLE | ||||
|          | ||||
|     return {prop: val} | ||||
|          | ||||
| def word_from_pos(text, pos): | ||||
|     if text[pos] == ' ': | ||||
|         return ' ', pos, pos+1 | ||||
| 
 | ||||
|     else: | ||||
|         fr = text[:pos].split(" ")[-1] | ||||
|         to = text[pos:].split(" ")[0] | ||||
| 
 | ||||
|         return fr + to, pos - len(fr), pos + len(to) | ||||
|   | ||||
| def get_iter_at_coords(view, x, y): | ||||
|     return view.get_iter_at_location( | ||||
|         *view.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, int(x), int(y)) | ||||
|         ) | ||||
| 
 | ||||
| def get_event_at_iter(view, iter, core): | ||||
|     buffer = view.get_buffer() | ||||
|          | ||||
|     line_strt = buffer.get_iter_at_line(iter.get_line()) | ||||
|     line_end = line_strt.copy() | ||||
|     line_end.forward_lines(1) | ||||
|      | ||||
|     pos = iter.get_line_offset() | ||||
|      | ||||
|     #Caveat: text must be a unicode string, not utf-8 encoded; otherwise our | ||||
|     # offsets will be off when we use anything outside 7-bit ascii | ||||
|     #gtk.TextIter.get_text returns unicode but gtk.TextBuffer.get_text does not | ||||
|     text = line_strt.get_text(line_end).rstrip("\n") | ||||
|      | ||||
|     word, fr, to = word_from_pos(text, pos) | ||||
|      | ||||
|     return core.events.data( | ||||
|                 window=view.win, pos=pos, text=text, | ||||
|                 target=word, target_fr=fr, target_to=to, | ||||
|                 ) | ||||
| 
 | ||||
| class TextOutput(gtk.TextView): | ||||
|     def copy(self): | ||||
|         startend = self.get_buffer().get_selection_bounds() | ||||
| 
 | ||||
|         tagsandtext = [] | ||||
|         if startend: | ||||
|             start, end = startend | ||||
|              | ||||
|             while not start.equal(end): | ||||
|                 tags_at_iter = {} | ||||
|                 for tag in start.get_tags():         | ||||
|                     try: | ||||
|                         tagname, tagval = eval(tag.get_property('name')) | ||||
|                         tags_at_iter[tagname] = tagval | ||||
|                     except NameError: | ||||
|                         continue | ||||
| 
 | ||||
|                 tagsandtext.append((dict(tags_at_iter), start.get_char())) | ||||
|                 start.forward_char() | ||||
| 
 | ||||
|         text = parse_mirc.unparse_mirc(tagsandtext) | ||||
|          | ||||
|         gtk.clipboard_get(gtk.gdk.SELECTION_CLIPBOARD).set_text(text) | ||||
|         gtk.clipboard_get(gtk.gdk.SELECTION_PRIMARY).set_text(text) | ||||
| 
 | ||||
|         return text | ||||
| 
 | ||||
|     def clear(self): | ||||
|         self.get_buffer().set_text('') | ||||
|      | ||||
|     def get_y(self): | ||||
|         rect = self.get_visible_rect() | ||||
|         return rect.y | ||||
|      | ||||
|     def set_y(self,y): | ||||
|         iter = self.get_iter_at_location(0, y) | ||||
|         if self.get_iter_location(iter).y < y: | ||||
|             self.forward_display_line(iter) | ||||
|         yalign = float(self.get_iter_location(iter).y-y)/self.height | ||||
|         self.scroll_to_iter(iter, 0, True, 0, yalign) | ||||
|          | ||||
|         self.check_autoscroll() | ||||
|      | ||||
|     def get_ymax(self): | ||||
|         buffer = self.get_buffer() | ||||
|         return sum(self.get_line_yrange(buffer.get_end_iter())) - self.height | ||||
|      | ||||
|     def get_height(self): | ||||
|         return self.get_visible_rect().height | ||||
|      | ||||
|     y = property(get_y, set_y) | ||||
|     ymax = property(get_ymax) | ||||
|     height = property(get_height) | ||||
|      | ||||
|     # the unknowing print weird things to our text widget function | ||||
|     def write(self, text, line_ending='\n', fg=None): | ||||
|         if not isinstance(text, unicode): | ||||
|             try: | ||||
|                 text = codecs.utf_8_decode(text)[0] | ||||
|             except: | ||||
|                 text = codecs.latin_1_decode(text)[0] | ||||
|         tags, text = parse_mirc.parse_mirc(text) | ||||
| 
 | ||||
|         if fg: | ||||
|             tags.append({'data': ("foreground", isinstance(fg, basestring) and ('#%s'%fg) or parse_mirc.get_mirc_color(fg)), 'from': 0, 'to': len(text)}) | ||||
| 
 | ||||
|         buffer = self.get_buffer() | ||||
|          | ||||
|         cc = buffer.get_char_count() | ||||
| 
 | ||||
|         buffer.insert_with_tags_by_name( | ||||
|             buffer.get_end_iter(), | ||||
|             text + line_ending, | ||||
|             'indent' | ||||
|             ) | ||||
| 
 | ||||
|         for tag in tags: | ||||
|             tag_name = str(tag['data']) | ||||
|     | ||||
|             if not tag_table.lookup(tag_name): | ||||
|                 buffer.create_tag(tag_name, **prop_to_gtk(self, tag['data'])) | ||||
| 
 | ||||
|             buffer.apply_tag_by_name( | ||||
|                 tag_name,  | ||||
|                 buffer.get_iter_at_offset(tag['from'] + cc), | ||||
|                 buffer.get_iter_at_offset(tag['to'] + cc) | ||||
|                 ) | ||||
|      | ||||
|     def popup(self, menu):     | ||||
|         hover_iter = get_iter_at_coords(self, *self.hover_coords) | ||||
|         | ||||
|         menuitems = [] | ||||
|         if not hover_iter.ends_line(): | ||||
|             c_data = get_event_at_iter(self, hover_iter) | ||||
|             c_data.menu = [] | ||||
|              | ||||
|             self.events.trigger("RightClick", c_data) | ||||
|              | ||||
|             menuitems = c_data.menu | ||||
|              | ||||
|         if not menuitems: | ||||
|             c_data = self.events.data(menu=[]) | ||||
|             self.events.trigger("MainMenu", c_data) | ||||
|              | ||||
|             menuitems = c_data.menu | ||||
| 
 | ||||
|         for child in menu.get_children(): | ||||
|             menu.remove(child) | ||||
|          | ||||
|         for item in menu_from_list(menuitems): | ||||
|             menu.append(item) | ||||
|              | ||||
|         menu.show_all() | ||||
|      | ||||
|     def mousedown(self, event): | ||||
|         if event.button == 3: | ||||
|             self.hover_coords = event.get_coords() | ||||
|      | ||||
|     def mouseup(self, event): | ||||
|         if not self.get_buffer().get_selection_bounds(): | ||||
|             if event.button == 1: | ||||
|                 hover_iter = get_iter_at_coords(self, event.x, event.y) | ||||
|              | ||||
|                 if not hover_iter.ends_line(): | ||||
|                     c_data = get_event_at_iter(self, hover_iter, self.core) | ||||
| 
 | ||||
|                     self.events.trigger("Click", c_data) | ||||
|                  | ||||
|             if self.is_focus(): | ||||
|                 self.win.focus() | ||||
| 
 | ||||
|     def clear_hover(self, _event=None): | ||||
|         buffer = self.get_buffer() | ||||
|      | ||||
|         for fr, to in self.linking: | ||||
|             buffer.remove_tag_by_name( | ||||
|                 "link",  | ||||
|                 buffer.get_iter_at_mark(fr),  | ||||
|                 buffer.get_iter_at_mark(to) | ||||
|                 ) | ||||
|          | ||||
|         self.linking = set() | ||||
|         self.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(None) | ||||
| 
 | ||||
|     def hover(self, event): | ||||
|         if self.linking: | ||||
|             self.clear_hover() | ||||
| 
 | ||||
|         hover_iter = get_iter_at_coords(self, event.x, event.y) | ||||
| 
 | ||||
|         if not hover_iter.ends_line():         | ||||
|             h_data = get_event_at_iter(self, hover_iter, self.core) | ||||
|             h_data.tolink = set() | ||||
| 
 | ||||
|             self.events.trigger("Hover", h_data) | ||||
|              | ||||
|             if h_data.tolink: | ||||
|                 buffer = self.get_buffer() | ||||
|              | ||||
|                 offset = buffer.get_iter_at_line( | ||||
|                             hover_iter.get_line() | ||||
|                             ).get_offset() | ||||
|                                    | ||||
|                 for fr, to in h_data.tolink: | ||||
|                     fr = buffer.get_iter_at_offset(offset + fr) | ||||
|                     to = buffer.get_iter_at_offset(offset + to) | ||||
|                      | ||||
|                     buffer.apply_tag_by_name("link", fr, to) | ||||
|                      | ||||
|                     self.linking.add( | ||||
|                         (buffer.create_mark(None, fr),  | ||||
|                             buffer.create_mark(None, to)) | ||||
|                         ) | ||||
|                          | ||||
|                     self.get_window( | ||||
|                         gtk.TEXT_WINDOW_TEXT | ||||
|                         ).set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND2)) | ||||
|          | ||||
|         self.get_pointer() | ||||
| 
 | ||||
|     def scroll(self, _allocation=None): | ||||
|         if self.autoscroll: | ||||
|             def do_scroll(): | ||||
|                 self.scroller.value = self.scroller.upper - self.scroller.page_size | ||||
|                 self._scrolling = False | ||||
|              | ||||
|             if not self._scrolling: | ||||
|                 self._scrolling = gobject.idle_add(do_scroll) | ||||
|      | ||||
|     def check_autoscroll(self, *args): | ||||
|         def set_to_scroll(): | ||||
|             self.autoscroll = self.scroller.value + self.scroller.page_size >= self.scroller.upper | ||||
|              | ||||
|         gobject.idle_add(set_to_scroll) | ||||
| 
 | ||||
|     def __init__(self, core, window, buffer=None): | ||||
|         if not buffer: | ||||
|             buffer = gtk.TextBuffer(tag_table) | ||||
|          | ||||
|         gtk.TextView.__init__(self, buffer) | ||||
|         self.core = core | ||||
|         self.events = core.events | ||||
|         self.win = window | ||||
|          | ||||
|         self.set_size_request(0, -1) | ||||
|          | ||||
|         self.set_wrap_mode(gtk.WRAP_WORD_CHAR) | ||||
|         self.set_editable(False) | ||||
|         self.set_cursor_visible(False) | ||||
| 
 | ||||
|         self.set_property("left-margin", 3) | ||||
|         self.set_property("right-margin", 3) | ||||
| 
 | ||||
|         self.linking = set() | ||||
| 
 | ||||
|         self.add_events(gtk.gdk.POINTER_MOTION_HINT_MASK) | ||||
|         self.add_events(gtk.gdk.LEAVE_NOTIFY_MASK) | ||||
| 
 | ||||
|         self.connect('populate-popup', TextOutput.popup) | ||||
|         self.connect('motion-notify-event', TextOutput.hover) | ||||
|         self.connect('button-press-event', TextOutput.mousedown) | ||||
|         self.connect('button-release-event', TextOutput.mouseup) | ||||
|         self.connect_after('button-release-event', lambda *a: True) | ||||
|         self.connect('leave-notify-event', TextOutput.clear_hover) | ||||
|            | ||||
|         self.hover_coords = 0, 0 | ||||
| 
 | ||||
|         self.autoscroll = True | ||||
|         self._scrolling = False | ||||
|         self.scroller = gtk.Adjustment() | ||||
| 
 | ||||
|         def setup_scroll(self, _adj, vadj): | ||||
|             self.scroller = vadj | ||||
| 
 | ||||
|             if vadj: | ||||
|                 def set_scroll(adj): | ||||
|                     self.autoscroll = adj.value + adj.page_size >= adj.upper | ||||
|      | ||||
|                 vadj.connect("value-changed", set_scroll) | ||||
| 
 | ||||
|         self.connect("set-scroll-adjustments", setup_scroll) | ||||
|         self.connect("size-allocate", TextOutput.scroll) | ||||
| 
 | ||||
|         def set_cursor(widget): | ||||
|             self.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(None)       | ||||
| 
 | ||||
|         self.connect("realize", set_cursor) | ||||
|          | ||||
|         style_me(self, "view") | ||||
| 
 | ||||
| class WindowLabel(gtk.EventBox): | ||||
|     def update(self): | ||||
|         title = self.win.get_title() | ||||
|          | ||||
|         for escapes in (('&','&'), ('<','<'), ('>','>')): | ||||
|             title = title.replace(*escapes) | ||||
| 
 | ||||
|         for a_type in (HILIT, TEXT, EVENT): | ||||
|             if a_type in self.win.activity: | ||||
|                 title = ACTIVITY_MARKUP[a_type] % title | ||||
|                 break | ||||
|              | ||||
|         self.label.set_markup(title) | ||||
| 
 | ||||
|     def tab_popup(self, event): | ||||
|         if event.button == 3: # right click | ||||
|             c_data = self.events.data(window=self.win, menu=[]) | ||||
|             self.events.trigger("WindowMenu", c_data) | ||||
| 
 | ||||
|             c_data.menu += [ | ||||
|                 None, | ||||
|                 ("Close", gtk.STOCK_CLOSE, self.win.close), | ||||
|                 ] | ||||
| 
 | ||||
|             menu = gtk.Menu() | ||||
|             for item in menu_from_list(c_data.menu): | ||||
|                 menu.append(item) | ||||
|             menu.show_all() | ||||
|             menu.popup(None, None, None, event.button, event.time) | ||||
| 
 | ||||
|     def __init__(self, window, core): | ||||
|         gtk.EventBox.__init__(self) | ||||
|         self.core = core | ||||
|         self.events = core.events | ||||
| 
 | ||||
|         self.win = window | ||||
|         self.connect("button-press-event", WindowLabel.tab_popup) | ||||
|          | ||||
|         self.label = gtk.Label()         | ||||
|         self.add(self.label) | ||||
| 
 | ||||
|         self.update() | ||||
|         self.show_all() | ||||
|          | ||||
| class FindBox(gtk.HBox): | ||||
|     def remove(self, *args): | ||||
|         self.parent.remove(self) | ||||
|         self.win.focus() | ||||
| 
 | ||||
|     def clicked(self, button, search_down=False): | ||||
|         text = self.textbox.get_text() | ||||
|          | ||||
|         if not text: | ||||
|             return | ||||
|          | ||||
|         buffer = self.win.output.get_buffer() | ||||
|          | ||||
|         if buffer.get_selection_bounds(): | ||||
|             if button == self.down: | ||||
|                 _, cursor_iter = buffer.get_selection_bounds() | ||||
|             else: | ||||
|                 cursor_iter, _ = buffer.get_selection_bounds() | ||||
|         else: | ||||
|            cursor_iter = buffer.get_end_iter() | ||||
|      | ||||
|         if search_down: | ||||
|             cursor = cursor_iter.forward_search( | ||||
|                 text, gtk.TEXT_SEARCH_VISIBLE_ONLY | ||||
|                 ) | ||||
|         else: | ||||
|             cursor = cursor_iter.backward_search( | ||||
|                 text, gtk.TEXT_SEARCH_VISIBLE_ONLY | ||||
|                 ) | ||||
|          | ||||
|         if not cursor: | ||||
|             return | ||||
|          | ||||
|         fr, to = cursor | ||||
|          | ||||
|         if button == self.up: | ||||
|             buffer.place_cursor(fr) | ||||
|             self.win.output.scroll_to_iter(fr, 0) | ||||
|         elif button == self.down: | ||||
|             buffer.place_cursor(to) | ||||
|             self.win.output.scroll_to_iter(to, 0) | ||||
|              | ||||
|         buffer.select_range(*cursor) | ||||
|          | ||||
|         cursor_iter = buffer.get_iter_at_mark(buffer.get_insert()) | ||||
| 
 | ||||
|     def __init__(self, window): | ||||
|         gtk.HBox.__init__(self) | ||||
|          | ||||
|         self.win = window | ||||
| 
 | ||||
|         self.up = gtk.Button(stock='gtk-go-up') | ||||
|         self.down = gtk.Button(stock='gtk-go-down') | ||||
|          | ||||
|         self.up.connect('clicked', self.clicked) | ||||
|         self.down.connect('clicked', self.clicked, True) | ||||
|          | ||||
|         self.up.set_property('can_focus', False) | ||||
|         self.down.set_property('can_focus', False) | ||||
|          | ||||
|         self.textbox = gtk.Entry() | ||||
|          | ||||
|         self.textbox.connect('focus-out-event', self.remove) | ||||
|         self.textbox.connect('activate', self.clicked) | ||||
|                  | ||||
|         self.pack_start(gtk.Label('Find:'), expand=False) | ||||
|         self.pack_start(self.textbox) | ||||
| 
 | ||||
|         self.pack_start(self.up, expand=False) | ||||
|         self.pack_start(self.down, expand=False) | ||||
| 
 | ||||
|         self.show_all() | ||||
| 
 | ||||
| #class UrkUITabs(gtk.Window): | ||||
| class UrkUITabs(object): | ||||
|     def __init__(self, core): | ||||
|         # threading stuff | ||||
|         gtk.gdk.threads_init() | ||||
|         self.core = core | ||||
|         self.events = core.events | ||||
|         self.tabs = gtk.Notebook() | ||||
|         self.tabs.set_property( | ||||
|             "tab-pos",  | ||||
|             conf.get("ui-gtk/tab-pos", gtk.POS_BOTTOM) | ||||
|             ) | ||||
| 
 | ||||
|         self.tabs.set_scrollable(True) | ||||
|         self.tabs.set_property("can-focus", False) | ||||
| 
 | ||||
|         self.box = gtk.VBox(False) | ||||
|         self.box.pack_end(self.tabs) | ||||
| 
 | ||||
|     def __iter__(self): | ||||
|         return iter(self.tabs.get_children()) | ||||
| 
 | ||||
|     def __len__(self): | ||||
|         return self.tabs.get_n_pages() | ||||
|      | ||||
|     def exit(self, *args): | ||||
|         self.events.trigger("Exit") | ||||
|         gtk.main_level() and gtk.main_quit() | ||||
|          | ||||
|     def get_active(self): | ||||
|         return self.tabs.get_nth_page(self.tabs.get_current_page()) | ||||
| 
 | ||||
|     def set_active(self, window): | ||||
|         self.tabs.set_current_page(self.tabs.page_num(window)) | ||||
| 
 | ||||
|     def add(self, window): | ||||
|         for pos in reversed(range(self.tabs.get_n_pages())): | ||||
|             if self.tabs.get_nth_page(pos).network == window.network: | ||||
|                 break | ||||
|         else: | ||||
|             pos = self.tabs.get_n_pages() - 1 | ||||
|   | ||||
|         self.tabs.insert_page(window, WindowLabel(window, self.core), pos+1)    | ||||
| 
 | ||||
|     def remove(self, window): | ||||
|         self.tabs.remove_page(self.tabs.page_num(window)) | ||||
| 
 | ||||
|     def update(self, window): | ||||
|         self.tabs.get_tab_label(window).update() | ||||
| 
 | ||||
|     def show_all(self): | ||||
|         self.box.show_all() | ||||
| @ -1,298 +0,0 @@ | ||||
| import gtk | ||||
| 
 | ||||
| import irc | ||||
| from conf import conf | ||||
| import widgets | ||||
| 
 | ||||
| #manager = widgets.UrkUITabs() | ||||
| 
 | ||||
| def append(window, manager): | ||||
|     manager.add(window) | ||||
| 
 | ||||
| def remove(window, manager): | ||||
|     manager.remove(window) | ||||
|      | ||||
|     # i don't want to have to call this | ||||
|     window.destroy() | ||||
|      | ||||
| def new(wclass, network, id, core): | ||||
|     if network is None: | ||||
|         network = irc.dummy_network | ||||
|      | ||||
|     w = get(wclass, network, id, core) | ||||
|     if not w: | ||||
|         w = wclass(network, id, core) | ||||
|         append(w, core.manager) | ||||
| 
 | ||||
|     return w | ||||
|      | ||||
| def get(windowclass, network, id, core): | ||||
|     if network: | ||||
|         id = network.norm_case(id) | ||||
| 
 | ||||
|     for w in core.manager: | ||||
|         if (type(w), w.network, w.id) == (windowclass, network, id): | ||||
|             return w | ||||
|    | ||||
| def get_with(manager, wclass=None, network=None, id=None): | ||||
|     if network and id: | ||||
|         id = network.norm_case(id) | ||||
| 
 | ||||
|     for w in list(manager): | ||||
|         for to_find, found in ((wclass, type(w)), (network, w.network), (id, w.id)): | ||||
|             # to_find might be False but not None (if it's a DummyNetwork) | ||||
|             if to_find is not None and to_find != found: | ||||
|                 break | ||||
|         else: | ||||
|             yield w | ||||
|              | ||||
| def get_default(network, manager): | ||||
| 
 | ||||
|     window = manager.get_active() | ||||
|     if window.network == network: | ||||
|         return window | ||||
| 
 | ||||
|     # There can be only one... | ||||
|     for window in get_with(network=network): | ||||
|         return window | ||||
| 
 | ||||
| class Window(gtk.VBox): | ||||
|     need_vbox_init = True | ||||
|      | ||||
|     def mutate(self, newclass, network, id): | ||||
|         isactive = self == manager.get_active() | ||||
|         self.hide() | ||||
|          | ||||
|         for child in self.get_children(): | ||||
|             self.remove(child) | ||||
| 
 | ||||
|         self.__class__ = newclass | ||||
|         self.__init__(network, id) | ||||
|         self.update() | ||||
|         if isactive: | ||||
|             self.activate() | ||||
|          | ||||
|     def transfer_text(self, _widget, event): | ||||
|         if event.string and not self.input.is_focus(): | ||||
|             self.input.grab_focus() | ||||
|             self.input.set_position(-1) | ||||
|             self.input.event(event) | ||||
|          | ||||
|     def write(self, text, activity_type=widgets.EVENT, line_ending='\n', fg=None): | ||||
|         if self.manager.get_active() != self: | ||||
|             self.activity = activity_type | ||||
|         self.output.write(text, line_ending, fg) | ||||
| 
 | ||||
|     def get_id(self): | ||||
|         if self.network: | ||||
|             return self.network.norm_case(self.rawid) | ||||
|         else: | ||||
|             return self.rawid | ||||
|              | ||||
|     def set_id(self, id): | ||||
|         self.rawid = id | ||||
|         self.update() | ||||
| 
 | ||||
|     id = property(get_id, set_id) | ||||
|      | ||||
|     def get_toplevel_title(self): | ||||
|         return self.rawid | ||||
|      | ||||
|     def get_title(self): | ||||
|         return self.rawid | ||||
| 
 | ||||
|     def get_activity(self): | ||||
|         return self.__activity | ||||
|      | ||||
|     def set_activity(self, value): | ||||
|         if value: | ||||
|             self.__activity.add(value) | ||||
|         else: | ||||
|             self.__activity = set() | ||||
|         self.update() | ||||
|          | ||||
|     activity = property(get_activity, set_activity) | ||||
|      | ||||
|     def focus(self): | ||||
|         pass | ||||
|      | ||||
|     def activate(self): | ||||
|         self.manager.set_active(self) | ||||
|         self.focus() | ||||
|      | ||||
|     def close(self): | ||||
|         self.events.trigger("Close", window=self) | ||||
|         remove(self, self.manager) | ||||
|          | ||||
|     def update(self): | ||||
|         self.manager.update(self) | ||||
| 
 | ||||
|     def __init__(self, network, id, core): | ||||
|         self.manager = core.manager | ||||
|         self.events = core.events | ||||
| 
 | ||||
|         if self.need_vbox_init: | ||||
|             #make sure we don't call this an extra time when mutating | ||||
|             gtk.VBox.__init__(self, False) | ||||
|             self.need_vbox_init = False | ||||
|          | ||||
|         if hasattr(self, "buffer"): | ||||
|             self.output = widgets.TextOutput(core, self, self.buffer) | ||||
|         else: | ||||
|             self.output = widgets.TextOutput(core, self) | ||||
|             self.buffer = self.output.get_buffer() | ||||
|              | ||||
|         if hasattr(self, "input"): | ||||
|             if self.input.parent: | ||||
|                 self.input.parent.remove(self.input) | ||||
|         else: | ||||
|             self.input = widgets.TextInput(self, core) | ||||
|          | ||||
|         self.network = network | ||||
|         self.rawid = id | ||||
|          | ||||
|         self.__activity = set() | ||||
|      | ||||
| class SimpleWindow(Window): | ||||
|     def __init__(self, network, id, core):     | ||||
|         Window.__init__(self, network, id) | ||||
| 
 | ||||
|         self.focus = self.input.grab_focus | ||||
|         self.connect("key-press-event", self.transfer_text) | ||||
| 
 | ||||
|         self.pack_end(self.input, expand=False) | ||||
|          | ||||
|         topbox = gtk.ScrolledWindow() | ||||
|         topbox.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) | ||||
|         topbox.add(self.output) | ||||
| 
 | ||||
|         self.pack_end(topbox) | ||||
| 
 | ||||
|         self.show_all() | ||||
| 
 | ||||
| class StatusWindow(Window): | ||||
|     def get_toplevel_title(self): | ||||
|         return '%s - %s' % (self.network.me, self.get_title()) | ||||
| 
 | ||||
|     def get_title(self): | ||||
|         # Something about self.network.isupport | ||||
|         if self.network.status: | ||||
|             return "%s" % self.network.server | ||||
|         else: | ||||
|             return "[%s]" % self.network.server | ||||
| 
 | ||||
|     def __init__(self, network, id, core): | ||||
|         Window.__init__(self, network, id, core) | ||||
| 
 | ||||
|         self.nick_label = widgets.NickEditor(self, core) | ||||
| 
 | ||||
|         self.focus = self.input.grab_focus | ||||
|         self.connect("key-press-event", self.transfer_text) | ||||
|         self.manager = core.manager | ||||
|         botbox = gtk.HBox() | ||||
|         botbox.pack_start(self.input) | ||||
|         botbox.pack_end(self.nick_label, expand=False) | ||||
| 
 | ||||
|         self.pack_end(botbox, expand=False) | ||||
|          | ||||
|         topbox = gtk.ScrolledWindow() | ||||
|         topbox.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) | ||||
|         topbox.add(self.output) | ||||
| 
 | ||||
|         self.pack_end(topbox) | ||||
| 
 | ||||
|         self.show_all() | ||||
| 
 | ||||
| class QueryWindow(Window): | ||||
|     def __init__(self, network, id, core):     | ||||
|         Window.__init__(self, network, id, core) | ||||
| 
 | ||||
|         self.nick_label = widgets.NickEditor(self, core) | ||||
| 
 | ||||
|         self.focus = self.input.grab_focus | ||||
|         self.connect("key-press-event", self.transfer_text) | ||||
| 
 | ||||
|         botbox = gtk.HBox() | ||||
|         botbox.pack_start(self.input) | ||||
|         botbox.pack_end(self.nick_label, expand=False) | ||||
| 
 | ||||
|         self.pack_end(botbox, expand=False) | ||||
|          | ||||
|         topbox = gtk.ScrolledWindow() | ||||
|         topbox.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) | ||||
|         topbox.add(self.output) | ||||
| 
 | ||||
|         self.pack_end(topbox) | ||||
| 
 | ||||
|         self.show_all() | ||||
| 
 | ||||
| def move_nicklist(paned, event): | ||||
|     paned._moving = ( | ||||
|         event.type == gtk.gdk._2BUTTON_PRESS, | ||||
|         paned.get_position() | ||||
|         ) | ||||
|          | ||||
| def drop_nicklist(paned, event): | ||||
|     width = paned.allocation.width | ||||
|     pos = paned.get_position() | ||||
|      | ||||
|     double_click, nicklist_pos = paned._moving | ||||
| 
 | ||||
|     if double_click: | ||||
|         # if we're "hidden", then we want to unhide | ||||
|         if width - pos <= 10: | ||||
|             # get the normal nicklist width | ||||
|             conf_nicklist = conf.get("ui-gtk/nicklist-width", 200) | ||||
| 
 | ||||
|             # if the normal nicklist width is "hidden", then ignore it | ||||
|             if conf_nicklist <= 10: | ||||
|                 paned.set_position(width - 200) | ||||
|             else: | ||||
|                 paned.set_position(width - conf_nicklist) | ||||
| 
 | ||||
|         # else we hide | ||||
|         else: | ||||
|             paned.set_position(width) | ||||
|          | ||||
|     else:     | ||||
|         if pos != nicklist_pos: | ||||
|             conf["ui-gtk/nicklist-width"] = width - pos - 6 | ||||
| 
 | ||||
| class ChannelWindow(Window): | ||||
|     def __init__(self, network, id, core): | ||||
|         Window.__init__(self, network, id, core) | ||||
| 
 | ||||
|         self.nicklist = widgets.Nicklist(self, core) | ||||
|         self.nick_label = widgets.NickEditor(self, core) | ||||
| 
 | ||||
|         self.focus = self.input.grab_focus | ||||
|         self.connect("key-press-event", self.transfer_text) | ||||
|          | ||||
|         topbox = gtk.ScrolledWindow() | ||||
|         topbox.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) | ||||
|         topbox.add(self.output) | ||||
|          | ||||
|         nlbox = gtk.ScrolledWindow() | ||||
|         nlbox.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)    | ||||
|         nlbox.add(self.nicklist) | ||||
| 
 | ||||
|         nlbox.set_size_request(conf.get("ui-gtk/nicklist-width", 112), -1) | ||||
| 
 | ||||
|         botbox = gtk.HBox() | ||||
|         botbox.pack_start(self.input) | ||||
|         botbox.pack_end(self.nick_label, expand=False) | ||||
|          | ||||
|         self.pack_end(botbox, expand=False) | ||||
|          | ||||
|         pane = gtk.HPaned() | ||||
|         pane.pack1(topbox, resize=True, shrink=False) | ||||
|         pane.pack2(nlbox, resize=False, shrink=True) | ||||
|          | ||||
|         self.nicklist.pos = None | ||||
|   | ||||
|         pane.connect("button-press-event", move_nicklist) | ||||
|         pane.connect("button-release-event", drop_nicklist) | ||||
|          | ||||
|         self.pack_end(pane) | ||||
| 
 | ||||
|         self.show_all() | ||||
| @ -51,6 +51,14 @@ class RadioToolButton(gtk.RadioToolButton): | ||||
|         if self._palette and self._palette.is_up(): | ||||
|             invoker = self._palette.props.invoker | ||||
|             invoker.draw_rectangle(event, self._palette) | ||||
|         elif self.child.state == gtk.STATE_PRELIGHT: | ||||
|             self.child.style.paint_box(event.window, gtk.STATE_PRELIGHT, | ||||
|                                        gtk.SHADOW_NONE, event.area, | ||||
|                                        self.child, "toolbutton-prelight", | ||||
|                                        self.allocation.x, | ||||
|                                        self.allocation.y, | ||||
|                                        self.allocation.width, | ||||
|                                        self.allocation.height) | ||||
| 
 | ||||
|         gtk.RadioToolButton.do_expose_event(self, event) | ||||
|      | ||||
|  | ||||
| @ -47,6 +47,14 @@ class ToggleToolButton(gtk.ToggleToolButton): | ||||
|         if self._palette and self._palette.is_up(): | ||||
|             invoker = self._palette.props.invoker | ||||
|             invoker.draw_rectangle(event, self._palette) | ||||
|         elif self.child.state == gtk.STATE_PRELIGHT: | ||||
|             self.child.style.paint_box(event.window, gtk.STATE_PRELIGHT, | ||||
|                                        gtk.SHADOW_NONE, event.area, | ||||
|                                        self.child, "toolbutton-prelight", | ||||
|                                        self.allocation.x, | ||||
|                                        self.allocation.y, | ||||
|                                        self.allocation.width, | ||||
|                                        self.allocation.height) | ||||
| 
 | ||||
|         gtk.ToggleToolButton.do_expose_event(self, event) | ||||
|      | ||||
|  | ||||
| @ -51,6 +51,14 @@ class ToolButton(gtk.ToolButton): | ||||
|         if self._palette and self._palette.is_up(): | ||||
|             invoker = self._palette.props.invoker | ||||
|             invoker.draw_rectangle(event, self._palette) | ||||
|         elif self.child.state == gtk.STATE_PRELIGHT: | ||||
|             self.child.style.paint_box(event.window, gtk.STATE_PRELIGHT, | ||||
|                                        gtk.SHADOW_NONE, event.area, | ||||
|                                        self.child, "toolbutton-prelight", | ||||
|                                        self.allocation.x, | ||||
|                                        self.allocation.y, | ||||
|                                        self.allocation.width, | ||||
|                                        self.allocation.height) | ||||
| 
 | ||||
|         gtk.ToolButton.do_expose_event(self, event) | ||||
|      | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Morgan Collett
						Morgan Collett