tritium-0.3.8/0000755000175000017500000000000011026361435011746 5ustar stewstewtritium-0.3.8/docs/0000755000175000017500000000000011026361435012676 5ustar stewstewtritium-0.3.8/docs/tritium.10000644000175000017500000000063210630111703014445 0ustar stewstew.\"-*- nroff -*- .TH TRITIUM 1 "June 1, 2007" .SH NAME tritium \- a tabbed/tiling window manager for the X Window System .SH SYNOPSIS .B tritium .RI [ options ] .SH DESCRIPTION .B tritium is a tabbed / tiling window manager geared towards keyboard users. .SH OPTIONS .B \-c, \-\config-file filename use .RI filename as the tritium config file. .SH AUTHOR tritium was written by Mike O'Connor tritium-0.3.8/TODO0000644000175000017500000000442511026326655012450 0ustar stewstew-*- mode:outline;mode:auto-fill -*- note: MORE_TODO_ITEMS=$(grep -r TODO *) * restart if there was a way to restart tritium (reread config, (redo layout?)) it would often be helpful ** ok, now we have wm.restart(), however its not ideal since it will move windows around on you * Add completion to query.py for instance, runCommand should show completions from expanding PATH, runSSH should show completions from known_hosts * help screen a way to pop up a window showing the current keybindings * Config reload we really should have a way to reload the config files. * floating frames ** need to decide how to integrate floting frames. here are some options: *** make a "floating frame" tab in the tab bar which contains all the floating windows *** make a "floating workspace" containing nothing but the floating frame * should move the fonts / colors to .Xresources * Dragging tabs to other workspaces When you start dragging a tab, the hit a key to switch to another workspace, you should be able to drop the tab on that workspace. It seems like currently, when you are dragging, workspace switching keys are ignored. * Get menus working we at least have a debian menu now when you hit f12, but we should have menus for stuff like "close window" that are available when right clicking on the tabs * fullscreen current window hit a key and make the current window zoom to fullscreen, hit the same key to restore the previous layout * xinerama support looking through the archives of the PLWM ml, it looks like support for xinerama under plwm has been worked on, but afaik, no code has yet been checked in. * find application by name ** I need to be able to hit 'C-j e' to switch to emacs or launch one if its not running ** plpwm in the plwm examples has such a function * dock on one workspace only ** might not be an insignificant change, since right now we are using a plwm function which changes the apparant screen size. I don't think there is a way to 'undo' it currnetly in plwm (which means, setting and unsetting of the doc when changeing workspaces is not currently an option) * hide titlebars with only one frame * Extended Window Manager Hints (EWMH) http://standards.freedesktop.org/wm-spec/wm-spec-1.4.html tritium-0.3.8/extras/0000755000175000017500000000000011026361435013254 5ustar stewstewtritium-0.3.8/extras/tritium.desktop0000644000175000017500000000014310760174372016350 0ustar stewstew[Desktop Entry] Name=Tritium Comment=Tritium window manager Exec=/usr/bin/tritium Type=Application tritium-0.3.8/setup.py0000644000175000017500000000402611024475506013465 0ustar stewstew""" Setup script for tritium Just run "python setup.py install" to install tritium Iron setup script Copyright (C) 2007,2008 Mike O'Connor 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 can find the full GNU General Public Licence at http://www.gnu.org/licenses/gpl.html or included with your favourite Linux Distribution. """ #from setuptools import setup from distutils.core import setup import tritium # Call the setup() routine which does most of the work setup( name = tritium.__distname__, version = tritium.__version__, description = tritium.__description__, long_description = tritium.__long_description__, author = tritium.__author__, author_email = tritium.__email__, url = tritium.__url__, license = tritium.__license__, packages = ['tritium', 'tritium.frame'], scripts = ['bin/tritium'], data_files = [ ('/etc/X11/tritium/', ['etc/keys.py', 'etc/layout.py']), ('share/applications', ['extras/tritium.desktop']), ], # why, oh why, can't i make this work # entry_points=""" # [distutils.commands] # install_config = tritium.install_config:install_config # """, # entry_points = { # "distutils.commands": [ # "install_config = install_config:install_config", # ], # "distutils.setup_keywords": [ # "config_files = install_config:install_config", # ], # }, ) tritium-0.3.8/README0000644000175000017500000000471311024473021012624 0ustar stewstewtritium -- a tabbed/tiling window manager. tritium is a tiling/tabbed window manager for the X Window System inspired by the ion3 window manager. It was written completely from scratch in python and shares no actual code with ion3. tritium is implemented in python using the plwm ("pointless window manager") library, which is available here: http://plwm.sourceforge.net INSTALLATION ------------ run "python setup.py install" to install tritium. CONFIGURATION ------------- Configuration for tritium is curretly done in two files, layout.py and keys.py. By default, these files are installed to /etc/X11/tritium. When tritium starts it will search the following directories for these files: $HOME/.config/tritum/ $HOME/.tritium/ /usr/local/etc/tritium /usr/local/ext/X11/tritium /etc/tritium /etc/X11/tritium It is recommended that if you are going to customize the configuration (which is recommended!) that you do it by first copying layout.py and keys.py to $HOME/.config/tritum/ or $HOME/.tritium. keys.py ------- This file should define a python class named TritiumKeys which will provide the keybindings for the window manager. Binding a key is done by declaring a function with the name of the keysym you are binding. A function named 'k' will be called when a key event is received for the 'k' key. Modifier keys can preceed the keysym, separated by underscores (_). s means shift c means control m means meta (modifier 1) m1,m2,m3,m4,m5 mean Mod1,Mod2,Mod3,Mod4 any means any comination of modifiers or no modifiers So, for example a function named s_c_F1 would create a key binding for the key combination Shift + Control + F1. layout.py --------- This file should define a python class named TritiumLayout, which is responsible for initial setup of the workspaces / frames, and is responsible for deciding on the placement of new windows. When each screen is initialized, a function named screen_N will be called, where N is the screen number. This function will be able to do the initial layout of the screen. I use this function to create a couple of workspaces, some with a split pane, and to allocate a dock area at the bottom where I run a gnome-panel. When new windows mapped, a function named "which_frame" is called with the new window, and it is expected that the function will return a frame which should contain the window, or None to indicate that the window be placed into the current frame. -- Mike O'Connor tritium-0.3.8/bin/0000755000175000017500000000000011026361435012516 5ustar stewstewtritium-0.3.8/bin/tritium0000755000175000017500000001372611026360572014153 0ustar stewstew#!/usr/bin/python # -*-python-*- # # tritium.py -- a keyboard driven window manager # # Copyright 2007,2008 Mike O'Connor # # Portions of code plagarized from plwm's panes.py which is # Copyright (C) 2001 Mike Meyer # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import sys from plwm import wmanager, keys, inspect, \ border, color, font, menu, cfilter,outline, focus import logging import tritium import sys,os from tritium import workspace,dock from tritium.query import * from tritium.submap import SubMap import tritium.frame from tritium.frame import title,frame,tab from Xlib import XK log = logging.getLogger() class TritiumMenuHandler(menu.MenuCharHandler): Any_Escape = C_g = menu.MenuKeyHandler._abort Any_Return = menu.MenuKeyHandler._do Any_space = Any_Down = C_n = menu.MenuKeyHandler._down Any_BackSpace = Any_Up = C_p = menu.MenuKeyHandler._up class TritiumClient( wmanager.Client, tritium.frame.FrameClient, tritium.frame.tab.TabClient, workspace.WorkspaceClient, border.BorderClient, tritium.frame.title.TitleClient, tritium.dock.DockClient, ): "Put the clients in a frame, with a border." border_default_width = 1 border_color_name = 'White' border_focuscolor_name = 'White' def __init__(self, screen, window, maprequest): self.transient=False self.dockapp=False tritium.frame.FrameClient.__init__( self, screen, window, maprequest ) wmanager.Client.__init__( self, screen, window, maprequest ) class LayoutScreen(object): def __screen_client_init__( self ): import layout self.layout = layout.TritiumLayout() method = "screen_%d" % self.number if layout.TritiumLayout.__dict__.has_key( method ): layout.TritiumLayout.__dict__[method]( self.layout, self ) class TritiumScreen( tritium.dock.DockScreen, wmanager.Screen, color.Color, tritium.frame.title.TitleScreen, LayoutScreen, message.screenMessage, tritium.tritiumScreen, menu.screenMenu, ): "And panes on the screen, and I'm going to want some menus." menu_handler = TritiumMenuHandler menu_bordercolor = "Red" message_bordercolor = "Blue" class TritiumConfig(object): def __wm_screen_init__( self ): log.debug( "TritiumConfig.__wm_screen_init__" ) # the directories we searce for keys.py and layout.py in reverse order: sys.path.insert( 0, "/etc/X11/tritium" ) sys.path.insert( 0, "/etc/tritium/config" ) sys.path.insert( 0, "/usr/local/etc/X11/tritium/config" ) sys.path.insert( 0, "/usr/local/etc/tritium/config" ) sys.path.insert( 0, os.path.join( os.getenv('HOME'), ".tritium" ), ) sys.path.insert( 0, os.path.join( os.getenv('HOME'), ".config/tritium/config" ) ) def __wm_init__( self ): "install the tritium key map" import keys keys.TritiumKeys(self) # TODO: this should be moved into a patch in debian/patches menufile='/var/lib/tritium/debian-menu.py' if os.path.isfile( menufile ): try: log.info( "loading menus from %s" % menufile ) menus = open( menufile ) exec menus in {'wm': self} except: log.error( "error loading menu file: %s: %s, %s" % (menufile, sys.exc_info()[0],sys.exc_info()[1] )) class Tritium( wmanager.WindowManager, focus.SloppyFocus, font.Font, workspace.WorkspaceWindowManager, tritium.frame.FrameWindowManager, TritiumConfig, tritium.tritiumWindowManager, inspect.InspectServer, ): client_class = TritiumClient screen_class = TritiumScreen def restart( self ): """ replace the current tritium process with a new one, this will allow us to reread the config files without having to restart X, but will have the annoying side-effect of repositioning winodws. so there is obviosly room for improvement """ executable = sys.argv[0] if sys.argv[0].find( '/' ): # it is a relative path, so run it from pwd executable = os.path.join( os.getcwd(), executable ) log.info( "going to try to restart tritium by running execvp( %s )" % executable ) os.execvp( executable, sys.argv[1:] ) if __name__ == '__main__': os.environ[ "DISPLAY" ] = ":0" level=logging.INFO # right now all we understand is -d for debug if len( sys.argv ) > 1: if( len( sys.argv ) == 2 ): o = sys.argv.pop() if o == '-d': import contract contract.checkmod(tab) level=logging.DEBUG elif o == '-dd': import contract contract.checkmod(tab) level=logging.DEBUG wmanager.debug = wmanager.do_debug else: usage() else: usage() logging.basicConfig( level=level, format='%(asctime)s %(levelname)s %(message)s', stream = sys.stderr ) wmanager.main(Tritium) tritium-0.3.8/changelog0000644000175000017500000000111511026360712013613 0ustar stewstewtritium (0.3.8) * get rid of lots of useless debugging debugging * add wm.restart() using execvp to restart tritium (Thanks Clint) * handle ConfigureNotify for dockapps -- Mike O'Connor Wed, Jun 18 2008 10:04:48 -0400 tritium (0.3.7) * oops, the 0.3.6 fix for transients got reverted when doing dockapp stuff -- Mike O'Connor Wed, Jun 18 2008 09:11:48 -0400 tritium (0.3.6) * Don't force transient windows to frame size * Fixes for dockapp to make conky work -- Mike O'Connor Sun, 15 Jun 2008 14:15:42 -0400 tritium-0.3.8/COPYING0000644000175000017500000004313311024475114013003 0ustar stewstew 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. Copyright (C) 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. , 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. tritium-0.3.8/etc/0000755000175000017500000000000011026361435012521 5ustar stewstewtritium-0.3.8/etc/layout.py0000644000175000017500000000542511026047073014415 0ustar stewstewimport re import logging from tritium import workspace,dock from Xlib import X log = logging.getLogger() class TritiumLayout(object): def screen_0( self, screen ): """ screen_0 is called when the screen is first initialized. (screen_1, screen_2, etc will be called for subsequent screens). This gives us a chance to setup workspaces / frames """ wm = screen.wm # the following allocates a 25 pixel area at the bottom of the # screen where clients marked with "dockapp=True" will be placed. # I use this to run a gnome-panel screen.set_dock_area( dock.BOTTOM, 25 ) wm.new_workspace( screen, workspace.TABBED, "shell" ) wm.new_workspace( screen, workspace.TABBED, "client" ) wm.new_workspace( screen, workspace.TABBED, "emacs" ) wm.new_workspace( screen, workspace.TABBED, "web" ) screen.wm.set_current_workspace( 0 ) wm.current_frame().split_horizontally() gnome_panel_re = re.compile( 'Gnome-panel',re.IGNORECASE ) def which_frame( self, client ): """ which_frame is called for each new client that is created. It gives us a chance to decide where the new client should be placed """ (appname, appclass) = client.window.get_wm_class() if( "Conky" == appclass or self.gnome_panel_re.match( appclass ) ): client.dockapp = True return None # By default, we return None, which causes the window to show # up in the current frame return None # Here is the which_frame implementation I use, which shows how you # can map new clients to particular workspaces or frames # # xterm_re = re.compile( 'xterm',re.IGNORECASE ) # firefox_re = re.compile( 'firefox',re.IGNORECASE ) # emacs_re = re.compile( 'emacs',re.IGNORECASE ) # music_re = re.compile( 'music',re.IGNORECASE ) # def which_frame( self, client ): # """ # which_frame is called for each new client that is created. It # gives us a chance to decide where the new client should be placed # """ # (appname, appclass) = client.window.get_wm_class() # if( self.gnome_panel_re.match( appclass ) ): # client.dockapp = True # if( self.firefox_re.match( appclass ) ): # ws = client.wm.get_workspace_by_name( "web" ) # if ws: # return ws.current_frame # elif( self.emacs_re.match( appclass ) ): # ws = client.wm.get_workspace_by_name( "emacs" ) # if ws: # return ws.current_frame # elif( self.music_re.match( appname ) ): # ws = client.wm.get_workspace_by_name( "client" ) # if ws: # return ws.current_frame tritium-0.3.8/etc/keys.py0000644000175000017500000001704311026327504014052 0ustar stewstew# -*-python-*- import sys from Xlib import XK from plwm import keys from tritium.submap import SubMap import logging log = logging.getLogger() from tritium.identify import IdentifyWindow from tritium.workspace import Workspace #from tritium.frame import SplitFrame from tritium.frame.split import SplitFrame """ This class MUST be named "TritiumKeys" it will define the keybindings for tritium. Binding a key is done by declaring a function with the name of the keysym you are binding. A function named 'k' will be called when a key event is received for the 'k' key. Modifier keys can preceed the keysym, separated by underscores (_). s means shift c means control m means meta (modifier 1) m1,m2,m3,m4,m5 mean Mod1,Mod2,Mod3,Mod4 The case and order of the modifiers is not important Return C_Right C_M_F1 """ def find_split( frame, vertical ): """ find a vertical or horizontal split that can be resized if none are found, return None """ if isinstance( frame, Workspace): return None if isinstance( frame, SplitFrame) and frame.vertical == vertical: return frame else: return find_split( frame.tritium_parent, vertical ) class TritiumKeys(keys.KeyHandler): def __init__( self, a ): keys.KeyHandler.__init__( self, a ) self.wii = None class ResizeMap( SubMap ): """ use the home row keys to resize a SplitFrame """ def __init__( self, wm, time, vertical ): SubMap.__init__( self, wm, time ) self.vertical = vertical pct_map = { XK.XK_a : .1, XK.XK_s : .2, XK.XK_d : .3, XK.XK_f : .4, XK.XK_g : .5, XK.XK_h : .6, XK.XK_j : .7, XK.XK_k : .8, XK.XK_l : .9, } def Any_a( self, event ): f = find_split( self.wm.current_frame(), self.vertical ) if f: f.resize_fraction( self.pct_map[ self.wm.display.keycode_to_keysym(event.detail, 0) ] ) Any_s = Any_d = Any_f = Any_g = Any_h = Any_j = Any_k = Any_l = Any_a class CtrlJ( SubMap ): def Any_c( self, event ): self.wm.current_frame().screen.system( "x-terminal-emulator &") def Any_n( self, event ): self.wm.current_frame().next() def Any_p( self, event ): self.wm.current_frame().prev() def Any_c( self, event ): self.wm.current_frame().windows.current().delete() def Any_k( self, event ): self.wm.current_frame().windows.current().destroy() def Any_x( self, event ): self.wm.current_frame().remove_split() def _1( self, event ): self.wm.current_frame().set_current( self.wm.display.keycode_to_keysym(event.detail, 0) - XK.XK_1 ) Any_1 = Any_2 = Any_3 = Any_4 = Any_5 = Any_6 = Any_7 = Any_9 = _1 class NoKeys( keys.KeyGrabKeyboard ): """ Ungrab all keys except one so that the window manager 'gets out of your way'. """ F11 = keys.KeyGrabKeyboard._timeout def F1( self, event ): self.wm.runMan.query( self.wm.current_frame() ) def F2( self, event ): self.wm.current_frame().screen.system( "x-terminal-emulator &") def F3( self, event ): self.wm.runCommand.query( self.wm.current_frame() ) def C_F3( self, event ): self.wm.runPython.query( self.wm.current_frame() ) def S_F3( self, event ): self.wm.runCommandInXterm.query( self.wm.current_frame() ) def F4( self, event ): self.wm.runSSH.query( self.wm.current_frame() ) def F9( self, event ): self.wm.new_workspace( self.wm.current_frame().screen ) def S_F9( self, event ): self.wm.new_workspace( self.wm.current_frame().screen, True ) def M4_F1( self, event ): self.wm.set_current_workspace( self.wm.display.keycode_to_keysym(event.detail, 0) - XK.XK_F1 ) M4_F2 = M4_F3 = M4_F4 = M4_F5 = M4_F6 = M4_F7 = M4_F8 = M4_F9 = M4_F1 def M4_w( self, event ): self.wm.current_frame().windows.current().delete() def M4_n( self, event ): self.wm.current_frame().next() def M4_p( self, event ): self.wm.current_frame().prev() def M4_s( self, event ): self.wm.current_frame().split_vertically() def M4_S_s( self, event ): self.wm.current_frame().split_horizontally() def M4_h( self, event ): self.wm.current_frame().prev() def M4_l( self, event ): self.wm.current_frame().next() def C_j( self, event ): self.CtrlJ( self.wm, event.time ) def M4_r( self, event ): self.ResizeMap( self.wm, event.time, True ) def M4_e( self, event ): self.ResizeMap( self.wm, event.time, False ) def M4_Right( self, event ): f = find_split( self.wm.current_frame(), False ) if( f ): f.resize_incr( 5 ) def M4_Left( self, event ): f = find_split( self.wm.current_frame(), False ) if( f ): f.resize_incr( -5 ) def M4_Down( self, event ): f = find_split( self.wm.current_frame(), True ) if( f ): f.resize_incr( 5 ) def M4_Up( self, event ): f = find_split( self.wm.current_frame(), True ) if( f ): f.resize_incr( -5 ) def M4_k( self, event ): old = self.wm.current_frame() new = old.tritium_parent.find_frame_above( old ) old.deactivate() new.activate(); def M4_j( self, event ): old = self.wm.current_frame() new = old.tritium_parent.find_frame_below( old ) old.deactivate() new.activate(); def M4_semicolon( self, event ): old = self.wm.current_frame() new = old.tritium_parent.find_frame_right( old ) old.deactivate() new.activate(); def M4_g( self, event ): old = self.wm.current_frame() new = old.tritium_parent.find_frame_left( old ) old.deactivate() new.activate(); def S_M4_h( self, event ): cw = self.wm.current_frame().windows.current() f = cw.frame.tritium_parent.find_frame_left( cw.frame ) if f: cw.move_to_frame( cw.frame.tritium_parent.find_frame_left( cw.frame ) ) def S_M4_l( self, event ): cw = self.wm.current_frame().windows.current() f = cw.frame.tritium_parent.find_frame_right( cw.frame ) if f: cw.move_to_frame( f ) def S_M4_j( self, event ): cw = self.wm.current_frame().windows.current() f = cw.frame.tritium_parent.find_frame_below( cw.frame ) if f: cw.move_to_frame( f ) def S_M4_k( self, event ): cw = self.wm.current_frame().windows.current() f = cw.frame.tritium_parent.find_frame_above( cw.frame ) if f: cw.move_to_frame( f ) def M4_Tab( self, event ): self.wm.workspaces.current().next_frame() def M4_i( self, event ): IdentifyWindow( self.wm.current_frame().windows.current(), event.time ) def F11(self, evt): wmanager.debug('keys', 'dropping keygrabs temporarily') # First release all our grabs. They will be reinstalled # when BypassHandler exits self._ungrab() BypassHandler(self) def F12( self, event ): self.wm.Debian_menu() def F13( self, event ): self.wm.set_current_workspace( self.wm.workspaces.index - 1 ) def F14( self, event ): self.wm.set_current_workspace( self.wm.workspaces.index + 1 ) tritium-0.3.8/PKG-INFO0000644000175000017500000000115111026361435013041 0ustar stewstewMetadata-Version: 1.0 Name: tritium Version: 0.3.8 Summary: a tabbed/tiling window manager Home-page: http://tritium.vireo.org/ Author: Mike O'Connor Author-email: stew@vireo.org License: GPL Description: tritium is a tiling/tabbed window manager for the X Window System inspired by the ion window manager. It was written completely from scratch in Python and shares no actual code with ion. tritium is implemented in Python using the plwm ("pointless window manager") library, which is available here: http://plwm.sourceforge.net Platform: UNKNOWN tritium-0.3.8/tritium/0000755000175000017500000000000011026361435013443 5ustar stewstewtritium-0.3.8/tritium/frame/0000755000175000017500000000000011026361435014535 5ustar stewstewtritium-0.3.8/tritium/frame/frame.py0000644000175000017500000001176111026336372016211 0ustar stewstew# frame.py -- a container for clients # # Copyright 2007,2008 Mike O'Connor # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import logging log = logging.getLogger() from plwm import wmevents from tritium.cycle import Cycle import tritium.workspace class Frame( object ): """ a container for [fullscreen] windows inv: self.tritium_parent != self """ def __init__( self, screen, x, y, width, height ): log.debug( "Frame.__init__" ) self.screen = screen self.x = x self.y = y self.width = width self.height = height self.wm = screen.wm self.shown = True self.tritium_parent = None self.windows = Cycle() def workspace( self ): "return the workspace this frame is part of" if not isinstance( self.tritium_parent, Frame ): return self.tritium_parent else: return self.tritium_parent.workspace() def find_frame( self, x, y ): if ( self.x <= x ) and \ ( self.y <= y ) and \ ((self.x+self.width) >= x ) and \ ((self.y+self.height) >= y): return self def append( self, window ): self.place_window( window ) if self.visible(): self.deactivate() window.dispatch.add_handler( wmevents.RemoveClient, self.remove_client_event ) self.windows.insert_after_current( window ) if self.visible(): self.activate() window.frame = self def remove_client_event( self, event ): self.remove( event.client ) def remove( self, window ): cur = self.windows.current() try: self.windows.remove( window ) except ValueError: log.warning( "window wasn't in list" ) if cur == window: self.deactivate() if self.wm.current_frame() == self: self.windows.prev() self.activate() def visible( self ): return self.tritium_parent.visible() def next( self ): "Move to the next window in this pane." self.deactivate() self.windows.next() self.activate() def prev( self ): "Move to the prev window in this pane." self.deactivate() self.windows.prev() self.activate() def set_current( self, index ): "set the current window to index." self.deactivate() self.windows.index = index self.activate() def set_current_window( self, window ): self.windows.set_current( window ) def show( self ): if not self.shown: for window in self.windows: window.show() self.shown = True def hide( self ): # if self.shown: # for window in self.windows: # window.hide() # self.shown = False pass def deactivate(self): if self.windows.current() and not self.windows.current().withdrawn: #self.windows.current().panes_pointer_pos = self.windows.current().pointer_position() if self.wm.current_frame() == self: self.wm.set_current_client( None ) def activate(self): "Dummy function, reset to _activate after all windows are opened." def _activate(self): "Activate whatever is currently self.windows.current()." self.wm.current_screen = self.screen self.wm.set_current_frame( self ) if self.windows.current() and not self.windows.current().withdrawn: # Will raise window and give focus self.windows.current().activate() #pos = self.windows.current().panes_pointer_pos #if pos: #self.windows.current().warppointer(pos[0], pos[1]) def next_frame( self ): if self.tritium_parent: return self.tritium_parent.next_sibling_frame( self ) else: return self def first_child_frame( self ): return self def identity( self ): return self def __str__( self ): return "Frame: (%d,%d,%d,%d) <" %(self.x,self.y,self.width,self.height) + " parent: " + str( self.tritium_parent ) topmost_child = bottommost_child = leftmost_child = rightmost_child = identity def _test(): import doctest doctest.testmod() if __name__ == "__main__": _test() print "DONE" tritium-0.3.8/tritium/frame/title.py0000644000175000017500000003575311026330021016230 0ustar stewstew# title.py -- floating windows with titlebars # # Copyright 2007,2008 Mike O'Connor # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import logging log = logging.getLogger() from Xlib import X,Xatom from plwm import wmevents import frame class TitleScreen(object): def __screen_client_init__(self): # I have these resource based methods turned off for now becuase I # don't like the fact that they pickup the generic '*foreground' and # '*background' making your tabs come up with the same colors as other # windows by default. # self.title_on_fg = self.get_color_res('.tabs.on.foreground', # '.Tabs.On.Foreground', # '#ffff00') # self.title_on_bg = self.get_color_res('.tabls.on.background', # '.Tabs.On.Background', # '#ff0000') # self.title_off_fg = self.get_color_res('.tabs.off.foreground', # '.Tabs.Off.Foreground', # '#ffffff') # self.title_off_bg = self.get_color_res('.tabs.off.background', # '.Tabs.Off.Background', # '#999999') self.title_on_fg = self.get_color( "#000000" ) self.title_on_bg = self.get_color( "#999999" ) self.title_off_fg = self.get_color( "#ffffff" ) self.title_off_bg = self.get_color( "#333333" ) self.title_border_color = self.get_color( "#ffffff" ) self.title_font = self.wm.get_font_res('.tabs.font', '.Tabs.Font', 'fixed' ) fq = self.title_font.query() font_center = (fq.font_ascent + fq.font_descent) / 2 - fq.font_descent self.title_height = fq.font_ascent + fq.font_descent + 6 self.title_base = self.title_height / 2 + font_center # Client mixin class TitleClient(object): """ This class is a client mixing for floating windows which have a titlebar. It will create a new top level window to contain both the client window and a titlebar window """ def __client_init__(self): log.debug( "TitleClient.__client_init__" ) self.titlebar_moving = False self.titlebar_resizing = False def title_manage(self): """ reparent a window into a frame with a titlebar decoration """ log.debug( "title_manage" ) (x, y, width, height, borderwidth) = self.geometry() # the decoration window is the parent of both the client window and the titlebar decoration_window = self.wm.current_frame().screen.root.create_window( max( 30, x-borderwidth), max(30, y - borderwidth - self.wm.current_frame().screen.title_height), width+(2*borderwidth), self.wm.current_frame().screen.title_height + height + (2*borderwidth), 0, X.CopyFromParent, X.InputOutput, X.CopyFromParent, background_pixmap = X.ParentRelative, event_mask = X.ButtonPressMask | X.ButtonReleaseMask | X.PointerMotionMask | X.ExposureMask ) decoration_window.map() # the title_window is the titlebar title_window = decoration_window.create_window( borderwidth, borderwidth, width, self.wm.current_frame().screen.title_height, 1, X.CopyFromParent, X.InputOutput, X.CopyFromParent, background_pixel = self.wm.current_frame().screen.title_on_bg, event_mask = X.ExposureMask ) self._title_create_gcs( title_window ) self.decoration_window = self.wm.current_frame().screen.add_internal_window( decoration_window ) decoration_window.change_attributes( border_pixel = self.wm.current_frame().screen.title_border_color) decoration_window.configure( border_width = 1 ) title_window = decoration_window.create_window( borderwidth, borderwidth, width, self.wm.current_frame().screen.title_height, 0, X.CopyFromParent, X.InputOutput, X.CopyFromParent, background_pixel = self.frame.screen.title_on_bg, event_mask = X.ExposureMask ) self.window.reparent( decoration_window, borderwidth, self.wm.current_frame().screen.title_height+(2*borderwidth) ) title_window.reparent( decoration_window, borderwidth, borderwidth ); self.title_window = self.frame.screen.add_internal_window(title_window) self.active = False self.title_window.map() self.title_text=self.get_title() self.title_window.raisewindow() self.title_window.dispatch.add_handler( X.ButtonPress, self.titlebar_mouse_down ) self.title_window.dispatch.add_handler( X.MotionNotify, self.titlebar_drag ) self.title_window.dispatch.add_handler( X.ButtonRelease, self.titlebar_mouse_up ) self.dispatch.add_handler( X.PropertyNotify, self.title_property_notify ) self.title_window.dispatch.add_handler( X.Expose, self.title_redraw ) self.decoration_window.dispatch.add_handler( wmevents.ClientFocusIn, self.title_get_focus ) self.decoration_window.dispatch.add_handler( wmevents.ClientFocusOut, self.title_lose_focus ) # self.window.grab_button( 1, X.Mod1Mask, True, X.ButtonPressMask, X.GrabModeAsync, X.GrabModeAsync, 0, 0 ) # self.window.grab_button( 3, X.Mod1Mask, True, X.ButtonPressMask, X.GrabModeAsync, X.GrabModeAsync, 0, 0 ) # ugh, these two shouldn't be needed decoration_window.grab_button( 1, X.Mod1Mask, True, X.ButtonPressMask, X.GrabModeAsync, X.GrabModeAsync, 0, 0 ) decoration_window.grab_button( 3, X.Mod1Mask, True, X.ButtonPressMask, X.GrabModeAsync, X.GrabModeAsync, 0, 0 ) self.dispatch.add_handler(X.UnmapNotify, self.title_unmap) self.decoration_window.dispatch.add_handler( X.ButtonRelease, self.titlebar_mouse_up ) self.decoration_window.dispatch.add_handler( X.ButtonPress, self.titlebar_mouse_down ) self.decoration_window.dispatch.add_handler( X.MotionNotify, self.titlebar_drag ) self.title_draw() def title_unmap( self, event ): self.title_unmanage() def title_unmanage(self): """ reparent the managed client back to the root window, get rid of the decoration window """ log.debug( "TitleClient.title_unmanage" ) self.decoration_window.dispatch.remove_handler( self.titlebar_mouse_up ) self.decoration_window.dispatch.remove_handler( self.titlebar_mouse_down ) self.decoration_window.dispatch.remove_handler( self.titlebar_drag ) self.dispatch.remove_handler( self.titlebar_mouse_up ) self.dispatch.remove_handler( self.titlebar_mouse_down ) self.dispatch.remove_handler( self.titlebar_drag ) self.dispatch.remove_handler( self.title_property_notify ) self.dispatch.remove_handler( self.title_remove ) self.dispatch.remove_handler( self.title_remove ) self.title_window.dispatch.remove_handler( self.title_redraw ) self.decoration_window.dispatch.remove_handler( self.title_get_focus ) self.decoration_window.dispatch.remove_handler( self.title_lose_focus ) (x, y, width, height, borderwidth) = self.decoration_window.geometry() self.window.reparent( self.workspace.screen.root, x, y ) self.title_window.unmap() self.title_window.destroy() self.title_window = None self.decoration_window.unmap() self.decoration_window.destroy() self.decoration_window = None def title_property_notify(self, event): log.debug( "TitleClient.modefocusedtitle_property_notify" ) if self.current and event.atom == Xatom.WM_NAME: try: self.set_text( self.get_title()) except: pass def title_get_focus(self, event): self.active = True self.title_draw() def title_lose_focus(self, event): self.active = False self.title_draw() def title_remove(self, event): self.title_window.unmap() def title_hide( self ): (x, y, width, height, borderwidth) = self.decoration_window.geometry() self.hide_x = x self.hide_y = y self.decoration_window.move( -(2*self.workspace.screen.root_width), -(2*self.workspace.screen.root_height) ) def title_show( self ): self.decoration_window.move( self.hide_x,self.hide_y ) def _title_create_gcs( self, window ): self.title_on_fg_gc = window.create_gc(foreground = self.wm.current_frame().screen.title_on_fg, background = self.wm.current_frame().screen.title_on_bg, font = self.wm.current_frame().screen.title_font) self.title_off_fg_gc = window.create_gc(foreground = self.wm.current_frame().screen.title_off_fg, background = self.wm.current_frame().screen.title_off_bg, font = self.wm.current_frame().screen.title_font) self.title_on_bg_gc = window.create_gc( foreground = self.wm.current_frame().screen.title_on_bg ) self.title_off_bg_gc = window.create_gc(foreground = self.wm.current_frame().screen.title_off_bg ) def activate_client( self ): self.decoration_window.raisewindow() self.workspace.raisewindows() # raise the "alwaysontop" windows self.wm.current_frame().wm.set_current_client( self ) def destroy( self ): self.decoration_window.destroy() def titlebar_mouse_down( self, event ): ( x, y, width, height, borderwidth) = self.decoration_window.geometry(); if event.root_y > ( y + self.screen.title_height + (2 * borderwidth) ): if ( event.detail == 1 ) and ( event.state & X.Mod1Mask ): self.start_move( event ) elif ( event.detail == 3 ) and ( event.state & X.Mod1Mask ): self.start_resize( event ) else: if ( event.detail == 3 ) and ( event.state & X.Mod1Mask ): self.start_resize( event ) else: self.start_move( event ) def start_move( self, event ): event.window.grab_pointer( False, X.ButtonPressMask | X.ButtonReleaseMask | X.PointerMotionMask | X.LeaveWindowMask, X.GrabModeAsync, X.GrabModeAsync, 0, 0, X.CurrentTime ) self.titlebar_moving = True self.titlebar_resizing = False self.workspace.raisewindows() # raise the "alwaysontop" windows self.wm.current_frame().wm.set_current_client( self ) (x, y, width, height, borderwidth) = self.decoration_window.geometry() self.tab_drag_start_x = event.root_x - x self.tab_drag_start_y = event.root_y - y # need to make the resizing smarter so it knows which corner we # are nearest and anchor the opposite corner instead of always # anchoring the top-left def start_resize( self, event ): event.window.grab_pointer( False, X.ButtonPressMask | X.ButtonReleaseMask | X.PointerMotionMask | X.LeaveWindowMask, X.GrabModeAsync, X.GrabModeAsync, 0, 0, X.CurrentTime ) self.titlebar_moving = False self.titlebar_resizing = True self.decoration_window.raisewindow() self.workspace.raisewindows() # raise the "alwaysontop" windows self.wm.current_frame().wm.set_current_client( self ) (x, y, width, height, borderwidth) = self.decoration_window.geometry() self.tab_drag_start_x = x self.tab_drag_start_y = y self.title_resize_start_x = width - event.root_x self.title_resize_start_y = height - event.root_y def titlebar_mouse_up( self, event ): if self.titlebar_moving: self.titlebar_moving = False self.wm.display.ungrab_pointer( X.CurrentTime ) elif self.titlebar_resizing: self.titlebar_resizing = False self.wm.display.ungrab_pointer( X.CurrentTime ) # else: # event.window.send_event( event ); def titlebar_drag( self, event ): if self.titlebar_moving: self.decoration_window.move( event.root_x - self.tab_drag_start_x , event.root_y - self.tab_drag_start_y ) elif self.titlebar_resizing: width = max( 0, event.root_x + self.title_resize_start_x ) height = max( (self.screen.title_height + ( 2 ) ), ( event.root_y + self.title_resize_start_y ) ) self.decoration_window.moveresize( self.tab_drag_start_x, self.tab_drag_start_y, width, height ) self.moveresize( 1, self.screen.title_height+2, width-2, height-self.screen.title_height ) self.title_window.moveresize( 1, 1, width-2, self.screen.title_height ) def set_text(self, text): if text == self.title_text: return self.title_undraw() self.title_text = text self.title_draw() def title_draw( self ): (x, y, wwidth, height, borderwidth) = self.title_window.geometry() if not self.title_text: return if self.active: fg_gc = self.title_on_fg_gc bg_gc = self.title_on_bg_gc else: fg_gc = self.title_off_fg_gc bg_gc = self.title_off_bg_gc self.title_window.fill_rectangle( bg_gc, 0, 0, wwidth, self.wm.current_frame().screen.title_height ) # Get width f = fg_gc.query_text_extents(self.title_text) width = f.overall_width + 4 width = min( width, wwidth - 4 ) x = ( wwidth - width ) / 2 self.title_window.draw_text( fg_gc, x, self.wm.current_frame().screen.title_base, self.title_text ) def title_redraw(self, event): self.title_draw() def title_undraw(self): (x, y, width, height, borderwidth) = self.title_window.geometry() self.title_window.clear_area( width = width, height = height ) def _test(): import doctest doctest.testmod() if __name__ == "__main__": _test() print "DONE" tritium-0.3.8/tritium/frame/tab.py0000644000175000017500000002772411026337777015705 0ustar stewstew# tab.py -- floating windows # # Copyright 2007,2008 Mike O'Connor # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import logging log = logging.getLogger() from Xlib import X, Xatom from plwm import wmevents, wmanager import tabbed import sys class Tabs(object): def __init__( self, frame ): log.debug( "Tabs.__init__" ) self.tabs = [] self.frame = frame def _current_index( self ): for index in range( len( self.tabs ) ): if self.tabs[index].active: return index return len( self.tabs ) -1 # stew: you know better then to have both of these functions # looking so similar, please refactor, thanks -stew def next( self ): if len( self.tabs ): i = self._current_index() current_tab = self.tabs[ i ] i = (i + 1) % len( self.tabs ) self.tabs[ i ].activate_client() def prev( self ): if len( self.tabs ): i = self._current_index() current_tab = self.tabs[ i ] i = (i - 1) % len( self.tabs ) self.tabs[ i ].activate_client() def append( self, tab ): self.tabs.insert( self._current_index()+1, tab ) self.resize_tabs() def remove( self, tab ): """ pre: self._contains( tab ) post: not self._contains( tab ) """ self.tabs.remove( tab ) self.resize_tabs() def _contains( self, tab ): try: self.tabs.index( tab ) return True except: return False def remove_all( self ): for tab in self.tabs: tab.tab_remove() self.tabs = [] def tab_redraw( self ): for tab in self.tabs: tab.tab_draw() def show( self ): for tab in self.tabs: tab.show() self.resize_tabs() def hide( self ): for tab in self.tabs: tab.hide() def _tab_width( self ): if len( self.tabs ): return (self.frame.width-( (len( self.tabs ) -1) * 2 ))/ len( self.tabs ) else: return 0 def resize_tabs( self ): width = self._tab_width() x = self.frame.x for tab in self.tabs: tab.resize_tab( x, width ) x += width+2 # Client mixin from plwm.frame import FrameProxy class TabClient(object): def __client_init__(self): log.debug( "TabClient.__client_init__" ) self.tab_managed = False def __client_del__(self): log.debug( "TabClient.__client_del__" ) try: if self.tab_managed: self.tab_remove() except: print sys.exc_info()[1] def tab_manage( self ): log.debug( "tab_manage: %s" % self ) self.tab_managed = True self.dispatch.add_handler(X.PropertyNotify, self.tab_property_notify) self.dispatch.add_handler(wmevents.ClientFocusIn, self.tab_get_focus) self.dispatch.add_handler(wmevents.ClientFocusOut, self.tab_lose_focus) self.dispatch.add_handler(wmevents.RemoveClient, self.tab_remove) self.dispatch.add_handler(X.UnmapNotify, self.tab_remove) self.dispatch.add_handler(X.DestroyNotify, self.tab_remove) def tab_unmanage( self ): log.debug( "tab_unmanage" ) self.tab_managed = False self.dispatch.remove_handler( self.tab_property_notify) self.dispatch.remove_handler( self.tab_get_focus) self.dispatch.remove_handler( self.tab_lose_focus) self.dispatch.remove_handler( self.tab_remove) def tab_property_notify(self, event): if self.current and event.atom == Xatom.WM_NAME: try: self.tab.set_text( self.get_title()) except: pass def tab_get_focus(self, event): if isinstance( self.wm.current_frame(), tabbed.TabbedFrame ): self.tab.tab_activate() def tab_lose_focus(self, event): if isinstance( self.wm.current_frame(), tabbed.TabbedFrame ): self.tab.tab_deactivate() def tab_remove(self, event=None): self.tab_unmanage() if self.tab: self.frame.tabs.remove( self.tab ) self.tab.tab_remove() self.tab._deleted = True class Tab(object): def __init__( self, frame, client ): log.debug( "Tab.__init__" ) self.frame = frame self.text = "" self.width = 0 self.active = False self.window = None self.client = client self.tab_dragging = False self.hide_x = 0 self.hide_y = 0 self._deleted = False # this is just for debugging def __str__( self ): return( "Tab \"%s\"" % self.text ) def create_tab_window( self, x ): """ pre: not self._deleted """ if self.width: window = self.frame.screen.root.create_window( x, self.frame.y, self.width, self.frame.screen.title_height, 0, X.CopyFromParent, X.InputOutput, X.CopyFromParent, background_pixel = self.frame.screen.title_on_bg, event_mask = X.ExposureMask ) self.window = self.frame.screen.add_internal_window(window) window.change_attributes( border_pixel = self.frame.screen.title_border_color) self.window.configure(border_width = 1) self.window.dispatch.add_handler( X.Expose, self.tab_redraw ) self.window.dispatch.add_handler( X.ButtonPress, self.tab_mouse_down ) window.map() self._tab_create_gcs( window ) def hide( self ): """ pre: not self._deleted """ self.window.unmap(); def show( self ): """ pre: not self._deleted """ self.window.map(); def _tab_create_gcs( self, window ): """ pre: not self._deleted """ self.on_fg_gc = window.create_gc(foreground = self.frame.screen.title_on_fg, background = self.frame.screen.title_on_bg, font = self.frame.screen.title_font) self.off_fg_gc = window.create_gc(foreground = self.frame.screen.title_off_fg, background = self.frame.screen.title_off_bg, font = self.frame.screen.title_font) self.on_bg_gc = window.create_gc( foreground = self.frame.screen.title_on_bg ) self.off_bg_gc = window.create_gc(foreground = self.frame.screen.title_off_bg ) def activate_client( self ): """ pre: not self._deleted """ (x, y, width, height, borderwidth) = self.window.geometry() self.client.raisewindow() self.client.workspace.raisewindows() # raise the "alwaysontop" windows self.window.raisewindow() self.frame.wm.set_current_client( self.client ) def tab_mouse_down( self, event ): """ pre: not self._deleted """ if( event.detail == 1 ): self.tab_dragging = True self.window.dispatch.add_handler( X.ButtonRelease, self.tab_mouse_up ) self.window.dispatch.add_handler( X.MotionNotify, self.tab_drag ) event.window.grab_pointer( False, X.ButtonPressMask | X.ButtonReleaseMask | X.PointerMotionMask, X.GrabModeAsync, X.GrabModeAsync, 0, 0, X.CurrentTime ) self.activate_client() (x, y, width, height, borderwidth) = self.window.geometry() self.tab_drag_start_x = event.root_x - x self.tab_drag_start_y = event.root_y - y def tab_mouse_up( self, event ): """ pre: not self._deleted """ self.window.dispatch.remove_handler( self.tab_mouse_up ) self.window.dispatch.remove_handler( self.tab_drag ) self.frame.wm.display.ungrab_pointer( X.CurrentTime ) if self.tab_dragging: self.tab_dragging = False f = self.frame.wm.workspaces.current().find_frame( event.root_x, event.root_y ) if f and (f != self.frame): if isinstance( f, tabbed.TabbedFrame ): self.client.move_to_frame( f ) #self.client.tab_remove() #self.client.frame.remove( self.client ) #self.client.add_to_frame( ) self.window.destroy() else: f = self.frame if f: f.tabs.resize_tabs() else: self.frame.tabs.resize_tabs() def tab_drag( self, event ): if self.tab_dragging: self.window.move( event.root_x - self.tab_drag_start_x , event.root_y - self.tab_drag_start_y ) def tab_remove( self ): """ pre: not self._deleted """ if self.window: self.window.destroy() def tab_activate( self ): """ pre: not self._deleted """ if not self.active: self.active = True self.tab_draw() def tab_deactivate( self ): """ pre: not self._deleted """ if self.active: self.active = False self.tab_draw() def resize_tab( self, x, width ): """ pre: not self._deleted """ self.width = width if not self.window: self.create_tab_window( x ) else: self.window.moveresize( x, self.frame.y, width, self.frame.screen.title_height) def set_text(self, text): """ pre: not self._deleted """ if text == self.text: return self.text = text self.tab_undraw() self.tab_draw() def tab_draw( self ): """ pre: not self._deleted """ if not self.text: return if self.active: fg_gc = self.on_fg_gc bg_gc = self.on_bg_gc else: fg_gc = self.off_fg_gc bg_gc = self.off_bg_gc self.window.fill_rectangle( bg_gc, 0, 0, self.width, self.frame.screen.title_height ) # Get width f = fg_gc.query_text_extents(self.text) width = f.overall_width + 4 width = min( width, self.width - 4 ) x = ( self.width - width ) / 2 self.window.draw_text( fg_gc, x, self.frame.screen.title_base, self.text ) def tab_redraw(self, event): """ pre: not self._deleted """ self.tab_draw() def tab_undraw(self): """ pre: not self._deleted """ self.window.clear_area( width = self.width ) def _test(): import doctest doctest.testmod() if __name__ == "__main__": _test() print "DONE" tritium-0.3.8/tritium/frame/floating.py0000644000175000017500000000422411026334746016721 0ustar stewstew# floating.py -- a frame that is a container for free floating # decorated windows # # Copyright 2007,2008 Mike O'Connor # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import logging log = logging.getLogger() from frame import Frame class FloatingFrame( Frame ): """ a Frame that contains floating windows """ def place_window(self, window = None): "Figure out where the window should be put." pass def append( self, window ): window.title_manage() Frame.append( self, window ) def remove( self, window ): # this is doing nothing becuase during reparenting, we get an # unmap event when we are being unmapped from the root window. # probably here we should be doing SOMETHING when we get # ummapped from the decorator window though. (but that # belongs in title.py, not here, i'm writing it here, becuase # this is where I am right now, and it should be written down pass def show( self ): if not self.shown: for window in self.windows: window.title_show() self.shown = True def hide( self ): if self.shown: for window in self.windows: window.title_hide() self.shown = False def __str__( self ): return "FloatingFrame(%d,%d,%d,%d):" %(self.x,self.y,self.width,self.height) def _test(): import doctest doctest.testmod() if __name__ == "__main__": _test() print "DONE" tritium-0.3.8/tritium/frame/xinerama.py0000644000175000017500000001237211024475614016723 0ustar stewstew# xinerama.py -- a frame split into multiple frames, one the size and # location of each monitor in a multi-monitor desktop # # Copyright 2007,2008 Mike O'Connor # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import logging log = logging.getLogger() class XineramaFrame( Frame ): """ a special case of a split frame which is split so that each sub frame is one display in a xinerama enabled display. """ """ A frame that contains two frames split either vertically or horizontally. """ def __init__( self, screen, infos ): log.debug( "XineramaFrame.__init__" ) min_x = min_y = sys.maxint max_x = max_y = -sys.maxint for info in infos: min_x = min( min_x, info.x_org ) min_y = min( min_y, info.y_org ) max_x = max( max_x, info.x_org + info.width ) max_y = max( max_y, info.y_org + info.height ) Frame.__init__( self, screen, min_x, min_y, max_x-min_x, max_y-min_y ) self.frames = [] for info in infos: frame = TabbedFrame( self.screen, info.x_org, info.y_org, info.width, info.height ) frame.tritium_parent = self self.frames.append( frame ) def find_frame_right( self, frame ): seen = False for child_frome in self.frames: if seen: return child_frame if frame == child_frame: seen = True return self.tritium_parent.find_frame_right( self ) def find_frame_left( self, frame ): last = None for child_frome in self.frames: if frame == child_frame: if last: return last else: return self.tritium_parent.find_frame_left( self ) else: last = child_frame return self.tritium_parent.find_frame_left( self ) def find_frame_above( self, frame ): last = None for child_frome in self.frames: if frame == child_frame: if last: return last else: return self.tritium_parent.find_frame_above( self ) else: last = child_frame return self.tritium_parent.find_frame_above( self ) def find_frame_below( self, frame ): seen = False for child_frome in self.frames: if seen: return child_frame if frame == child_frame: seen = True return self.tritium_parent.find_frame_below( self ) # these four below could probably be made smarter at some point, # like topmost could be topmost containing a certain x def topmost_child( self ): return self.frames[0] def bottommost_child( self ): return self.frames[-1] leftmost_child = topmost_child rightmost_child = bottommost_child def hide( self ): Frame.hide( self ) for frame in self.frames: frame.hide() def show( self ): Frame.hide( self ) for frame in self.frames: frame.show() def find_frame( self, x, y ): log.debug( "SplitFrame.find_frame" ) for frame in self.frames: if ( frame.x <= x ) and \ ( frame.y <= y ) and \ ((frame.x+frame.width) >= x ) and \ ((frame.y+frame.height) >= y): return frame return self.frames[0] def next_sibling_frame( self, frame ): log.debug( "SplitFrame.next_sibling_frame" ) if frame == self.frame1: return self.frame2.first_child_frame() else: return self.next_frame() def first_child_frame( self ): log.debug( "SplitFrame.first_child_frame" ) return self.frame1.first_child_frame() def remove_me( self, me ): """you can't remove one of my child frames, so this does nothing""" return def replace_me( self, me, replacewith ): log.debug( "SplitFrame.replace_me" ) for i in range( len( self.frames ) ): if self.frames[i] == me: self.frame[i] = replacewith break replacewith.tritium_parent = self def __str__( self ): log.debug( "XineramaFrame.__str__" ) return "XineramaFrame(%d,%d,%d,%d): <" %(self.x,self.y,self.width,self.height) + ">" def _test(): import doctest doctest.testmod() if __name__ == "__main__": _test() print "DONE" tritium-0.3.8/tritium/frame/tabbed.py0000644000175000017500000001201011026337602016321 0ustar stewstew# tabbed.py -- a container for maximized clients with tabs across the top # # Copyright 2007,2008 Mike O'Connor # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import logging log = logging.getLogger() from frame import Frame from split import SplitFrame from tab import Tabs, Tab from Xlib import X from plwm.frame import FrameProxy class TabbedFrame( Frame ): def __init__( self, screen, x, y, width, height ): log.debug( "TabbedFrame.__init__" ) Frame.__init__( self, screen, x, y, width, height ) self.tabs = Tabs( self ) def append( self, window ): Frame.append( self, window ) tab = Tab( self, window ) window.tab = tab self.tabs.append( tab ) if not self.visible(): tab.hide() tab.set_text( window.get_title() ) # shouldn't some of the stuff above be moved into tab_manage? window.tab_manage() def moveresize( self, x, y, width, height ): self.x = x self.y = y self.height = height self.width = width map( self.place_window, self.windows ) self.tabs.resize_tabs(); def show( self ): Frame.show( self ) self.tabs.show() if not self.shown: for window in self.windows: window.show() self.shown = True def hide( self ): Frame.hide( self ) self.tabs.hide() if self.shown: for window in self.windows: window.hide() self.shown = False def __str__( self ): return "TabbedFrame: " + Frame.__str__( self ) def place_window( self, window = None ): """ Figure out where the window should be put. """ if not window: window = self.windows.current() if( window.transient ): # width, height = window.follow_size_hints(self.width - 2, self.height - 2) width, height = window.width, window.height if not window.gravity: window.gravity = X.SouthGravity width = min( width, self.width-self.screen.title_height - 2 ) height = min( height, self.height-2 ) if window.gravity in ( X.NorthEastGravity, X.EastGravity, X.SouthEastGravity): x = self.x elif window.gravity in ( X.NorthGravity, X.CenterGravity, X.SouthGravity ): x = self.x + ( self.width - width - 2 ) / 2 else: x = self.x + self.width - width - 1 if window.gravity in ( X.NorthEastGravity, X.NorthGravity, X.NorthWestGravity): y = self.y + self.screen.title_height + 2 elif window.gravity in ( X.EastGravity, X.CenterGravity, X.WestGravity ): y = self.y + self.screen.title_height + ( self.height - height - 2 ) / 2 else: y = self.y + self.screen.title_height + self.height - height - 1 window.moveresize( x, y, width, height ) else: window.moveresize( self.x, self.y + self.screen.title_height, self.width-2, self.height-self.screen.title_height-2) window.hidden = False # ugh, i don't like having to set it here, but this seems to be before __client_init__ is called if not self.visible(): window.hide() def split_vertically( self ): SplitFrame( self.screen, self.x, self.y, self.width, self.height, True, self ) def split_horizontally( self ): SplitFrame( self.screen, self.x, self.y, self.width, self.height, False, self ) def remove_split( self ): if self.tritium_parent: self.tritium_parent.remove_me( self ) self.tabs.remove_all() def next( self ): "Move to the next window in this pane." #clients = self.screen.query_clients(panefilter(self), 1) self.tabs.next() def prev( self ): "Move to the next window in this pane." #clients = self.screen.query_clients(panefilter(self), 1) self.tabs.prev() def _test(): import doctest doctest.testmod() if __name__ == "__main__": _test() print "DONE" tritium-0.3.8/tritium/frame/split.py0000644000175000017500000002440511026330517016244 0ustar stewstew# split.py -- a frame split either vertically or horizontalliy # that contains two other frames # # Copyright 2007,2008 Mike O'Connor # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from Xlib import X, Xutil, Xatom from frame import Frame import logging log = logging.getLogger() class SplitFrame( Frame ): """ A frame that contains two frames split either vertically or horizontally. """ def __init__( self, screen, x, y, width, height, vertical, frame1 ): log.debug( "SplitFrame.__init__" ) Frame.__init__( self, screen, x, y, width, height ) self.split_dragging = False self.vertical = vertical if vertical: self.split = height else: self.split = width self.split = self.split >> 1 self.frame1 = frame1 if vertical: self.frame1.moveresize( x, y, self.width, self.height - self.split ) # this is a horrible hack to get around circular importing self.frame2 = self.frame1.topmost_child().__class__( self.screen, self.x, self.y + self.split+4, self.width, self.height-self.split-4) else: self.frame1.moveresize( x, y, self.width - self.split, self.height ) self.frame2 = self.frame1.topmost_child().__class__( self.screen, self.x + self.split+4, self.y, self.width-self.split-4, self.height) frame1.tritium_parent.replace_me( frame1, self ) self.frame1.tritium_parent = self.frame2.tritium_parent = self self.frame1.activate() self.create_split_window() def _activate(self): Frame._activate( self ) self.window.raisewindow() activate=_activate def moveresize( self, x, y, width, height ): self.x = x self.y = y self.width = width self.height = height if self.vertical: # self.split = height >> 1 self.window.moveresize( self.x, self.y + self.split, self.width, 4 ) self.frame1.moveresize( self.x, self.y, self.width, self.split ) self.frame2.moveresize( self.x, self.split+4, self.width, self.height - self.split - 4 ) else: # self.split = width >> 1 self.window.moveresize( self.x + self.split, self.y, 4, self.height ) self.frame1.moveresize( self.x, self.y, self.split, self.height ) self.frame2.moveresize( self.x + self.split+4, self.y, self.width - self.split - 4, self.height ) self.redraw( self ) def find_frame_right( self, frame ): if not self.vertical and self.frame1 == frame: return self.frame2.leftmost_child() else: return self.tritium_parent.find_frame_right( self ) def find_frame_left( self, frame ): if not self.vertical and self.frame2 == frame: return self.frame1.rightmost_child() else: return self.tritium_parent.find_frame_left( self ) def find_frame_above( self, frame ): if self.vertical and self.frame2 == frame: return self.frame1.bottommost_child() else: return self.tritium_parent.find_frame_above( self ) def find_frame_below( self, frame ): if self.vertical and self.frame1 == frame: return self.frame2.topmost_child() else: return self.tritium_parent.find_frame_below( self ) # these four below could probably be made smarter at some point, # like topmost could be topmost containing a certain x def topmost_child( self ): return self.frame1.topmost_child() def bottommost_child( self ): return self.frame2.bottommost_child() leftmost_child = topmost_child rightmost_child = bottommost_child def hide( self ): Frame.hide( self ) self.frame1.hide() self.frame2.hide() self.window.unmap() def show( self ): Frame.show( self ) self.frame1.show() self.frame2.show() self.window.map() def find_frame( self, x, y ): if ( self.x <= x ) and \ ( self.y <= y ) and \ ((self.x+self.width) >= x ) and \ ((self.y+self.height) >= y): r = self.frame1.find_frame( x, y ) if not r: r = self.frame2.find_frame( x, y ) if not r: r = self return r def next_sibling_frame( self, frame ): if frame == self.frame1: return self.frame2.first_child_frame() else: return self.next_frame() def first_child_frame( self ): return self.frame1.first_child_frame() def create_split_window( self ): if self.vertical: window = self.screen.root.create_window( self.x, self.y + self.split, self.width, 8, 0, X.CopyFromParent, X.InputOutput, X.CopyFromParent, event_mask = X.ExposureMask ) else: window = self.screen.root.create_window( self.x + self.split, self.y, 8, self.height, 0, X.CopyFromParent, X.InputOutput, X.CopyFromParent, event_mask = X.ExposureMask ) self.window = self.screen.add_internal_window(window) self.window.dispatch.add_handler( X.Expose, self.redraw ) self.window.dispatch.add_handler( X.ButtonRelease, self.splitbar_mouse_up ) self.window.dispatch.add_handler( X.ButtonPress, self.splitbar_mouse_down ) self.window.dispatch.add_handler( X.MotionNotify, self.splitbar_drag ) window.map() self.window.raisewindow() self._create_gcs( window ) def _create_gcs( self, window ): self.split_gc1 = window.create_gc( foreground = self.screen.get_color( "#ffffff" )) self.split_gc2 = window.create_gc( foreground = self.screen.get_color( "#cccccc" )) self.split_gc3 = window.create_gc( foreground = self.screen.get_color( "#999999" )) self.split_gc4 = window.create_gc( foreground = self.screen.get_color( "#666666" )) def redraw( self, event = None ): if self.vertical: self.window.fill_rectangle( self.split_gc1, 0, 0, self.width, 2 ) self.window.fill_rectangle( self.split_gc2, 0, 2, self.width, 2 ) self.window.fill_rectangle( self.split_gc3, 0, 4, self.width, 2 ) self.window.fill_rectangle( self.split_gc4, 0, 6, self.width, 2 ) else: self.window.fill_rectangle( self.split_gc1, 0, 0, 2, self.height ) self.window.fill_rectangle( self.split_gc2, 2, 0, 2, self.height ) self.window.fill_rectangle( self.split_gc3, 4, 0, 2, self.height ) self.window.fill_rectangle( self.split_gc4, 6, 0, 2, self.height ) def splitbar_mouse_down( self, event ): self.split_dragging = True if self.vertical: self.splitbar_drag_start = event.root_y - self.split else: self.splitbar_drag_start = event.root_x - self.split def splitbar_mouse_up( self, event ): self.split_dragging = False def splitbar_drag( self, event ): if self.split_dragging: self.resize_point( event.root_x - self.splitbar_drag_start, event.root_y - self.splitbar_drag_start ) def resize_point( self, x, y ): if self.vertical: if self.split + self.y != y: self.split = y - self.y self.window.move( self.x, self.y + self.split ) self.frame1.moveresize( self.x, self.y, self.width, self.split + self.y ) self.frame2.moveresize( self.x, self.y + self.split+8, self.width, self.height - self.split - 8 ) else: if self.split != x: self.split = x self.window.move( self.x + self.split, self.y ) self.frame1.moveresize( self.x, self.y, self.split, self.height ) self.frame2.moveresize( self.x + self.split+8, self.y, self.width - self.split - 8, self.height ) def resize_fraction( self, fraction ): """ move the split bar to (frame_size * fraction) pixels pre: (fraction >= 0) and (fraction <= 1.0) """ if self.vertical: self.resize_point( 0, int(fraction * self.height) ) else: self.resize_point( int(fraction * self.width), 0 ) def resize_incr( self, incr ): """ move the split bar to (frame_size * fraction) pixels pre: (fraction >= 0) and (fraction <= 1.0) """ if self.vertical: self.resize_point( 0, self.split + incr ) else: self.resize_point( self.split + incr, 0 ) def remove_me( self, me ): if self.frame1 == me: replacewith = self.frame2 else: replacewith = self.frame1 for client in me.windows: client.add_to_frame( replacewith ) self.window.destroy() self.tritium_parent.replace_me( self, replacewith ) replacewith.moveresize( self.x, self.y, self.width, self.height ) def replace_me( self, me, replacewith ): if self.frame1 == me: self.frame1 = replacewith elif self.frame2 == me: self.frame2 = replacewith replacewith.tritium_parent = self def _test(): import doctest doctest.testmod() if __name__ == "__main__": _test() print "DONE" tritium-0.3.8/tritium/frame/__init__.py0000644000175000017500000000624111026354213016645 0ustar stewstew# frame.py -- a container for clients # # Copyright 2007,2008 Mike O'Connor # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import logging log = logging.getLogger() from Xlib import X from plwm import wmevents from plwm.frame import FrameProxy import traceback class FrameWindowManager( object ): def find_me_a_home( self, client ): frame = client.screen.layout.which_frame( client ) if frame: return frame return self.current_frame() class FrameClient( object ): """ mixin for client windows that are contained in frames inv: self._check_frame_state() """ #TODO: find me a home: default_gravity = X.SouthGravity def __init__(self, screen, window, maprequest): try: transient = window.get_wm_transient_for() if transient: self.transient = True except: traceback.print_exc() pass self.gravity = self.default_gravity def __client_init__( self ): log.debug( "FrameClient.__client_init__" ) frame = self.wm.find_me_a_home( self ) if self.dockapp: log.debug( "that frame is a dockapp!" ) self.frame = None self.dock_manage() else: self.frame_dragging = False self.add_to_frame( frame ) self.dispatch.add_handler(wmevents.ClientFocusIn, self.frame_get_focus) # moved this into its own function so I can allow for frame not # yet being assigned def _check_frame_state( self ): try: return self.dockapp or (self.__getattribute__( 'frame' ) != None) except: return True def frame_unmap( self, event ): self.frame.remove( self ) def frame_get_focus( self, event ): self.wm.workspaces.current().current_frame = self.frame self.frame.set_current_window( self ) def add_to_frame( self, frame ): self.frame = frame frame.append( self ) def move_to_frame( self, new_frame ): if new_frame and self.frame != new_frame: old_workspace = self.frame.workspace() new_workspace = new_frame.workspace() if new_workspace != old_workspace: old_workspace.deactivate() new_workspace.activate() self.tab_remove() self.frame.remove( self ) self.add_to_frame( new_frame ) def _test(): import doctest doctest.testmod() if __name__ == "__main__": _test() print "DONE" tritium-0.3.8/tritium/submap.py0000644000175000017500000000340211026330620015273 0ustar stewstew# submap.py -- a keyboard "submap" for creating prefix keybindings # # Copyright 2007,2008 Mike O'Connor # # Portions of code plagarized from plwm's panes.py which is # Copyright (C,2008) 2001 Mike Meyer # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import logging import time from plwm import wmanager, event, keys from Xlib import X log = logging.getLogger() class SubMap(keys.KeyGrabKeyboard): def _keyevent(self, event): # Store key press time (approximate to the current time # as the X event.time isn't synced with that) self.last_key_time = time.time() log.debug('keys: %s %d %d, keyhandler %s' % (event.__class__.__name__, event.detail, event.state, self)) if event.type != X.KeyPress: return # First check for an exact modifier match match = keys.hash_keycode(event.detail, event.state) if self.bindings.has_key(match): self.bindings[match](event) # else, check for an AnyModifier key else: match = keys.hash_keycode(event.detail, X.AnyModifier) if self.bindings.has_key(match): self.bindings[match](event) self._cleanup() tritium-0.3.8/tritium/identify.py0000644000175000017500000000725011025667252015641 0ustar stewstew# identify.py -- show the user info about the current window # # Copyright 2007,2008 Mike O'Connor # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import logging log = logging.getLogger() from Xlib import X, Xatom from Xlib.keysymdef import latin1 from plwm import wmanager, event, keys from submap import SubMap import threading class IdentifyWindow(object): """ A window to display properties about a client """ fontname= "9x15" foreground = "black" background = "white" borderwidth = 3 bordercolor = "black" def __init__( self, client, time ): screen = client.screen appname,appclass = client.window.get_wm_class() self.strings = [ "Title: %s" % client.get_title(), "Application Name: %s" % appname, "Class: %s" % appclass ] fg = screen.get_color( self.foreground ) bg = screen.get_color( self.background ) bc = screen.get_color( self.bordercolor ) font = screen.wm.get_font( self.fontname, 'fixed' ) self.size = font.query() self.height = (self.size.font_ascent + self.size.font_descent + 1) * len( self.strings ) self.width = 0 for string in self.strings: self.width = max( self.width, font.query_text_extents( string ).overall_width ) window = screen.root.create_window(0, 0, self.width, self.height, self.borderwidth, X.CopyFromParent, X.InputOutput, X.CopyFromParent, background_pixel = bg, border_pixel = bc, event_mask = (X.VisibilityChangeMask | X.ExposureMask)) self.gc = window.create_gc(font = font, function = X.GXinvert, foreground = fg, background = bg) self.font = font self.window = screen.add_internal_window(window) self.window.dispatch.add_handler(X.VisibilityNotify, self.raisewindow) self.window.dispatch.add_handler(X.Expose, self.redraw) (x,y,width,height,borderwidth) = client.geometry() x, y, width, height = self.window.keep_on_screen( x, y, self.width, self.height ) self.window.configure( x = x, y = y, width = self.width, height = self.height ) self.window.map() threading.Timer( 5, self.done ).start() def raisewindow( self, event ): self.window.raisewindow() def redraw( self, event = None ): self.window.clear_area( width = self.width, height = self.height ) baseline = self.size.font_ascent + 1 for string in self.strings: self.window.image_text( self.gc, 0, baseline, string ) baseline += self.size.font_ascent + 1 + self.size.font_descent def done( self ): log.debug( "Identify.done" ) self.window.destroy() tritium-0.3.8/tritium/dock.py0000644000175000017500000000455111026360304014734 0ustar stewstew# dock.py -- floating windows with titlebars # # Copyright 2007,2008 Mike O'Connor # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import logging log = logging.getLogger() from Xlib import X,Xatom,Xutil BOTTOM = 'bottom' TOP = 'top' LEFT = 'left' RIGHT = 'right' class DockScreen(object): def __screen_client_init__( self ): self.dock_windows = [] self.dock_size = 0 def set_dock_area( self, where=BOTTOM, size=64, gravity=X.SouthGravity ): self.dock_where = where self.dock_x, self.dock_y, self.dock_width, self.dock_height = self.alloc_border( where, size ) def add_dock_window( self, client ): self.dock_windows.append( client ) # Client mixin class DockClient(object): """ This class is a client mixin for window that provides an IconWindow """ def __client_init__(self): log.debug( "DockClient.__client_init__" ) # if( self.screen.dock_where ): # # is this client a dockapp? # wmh = self.window.get_wm_hints() # if wmh and wmh.flags & Xutil.IconWindowHint: # self.dock_manage() def dock_manage( self ): self.on_top = True self.dockapp = True self.screen.add_dock_window( self ) self.dispatch.add_handler( X.ConfigureRequest, self.dock_configure ) log.debug( "moving dock window to (%d,%d)" % ( self.screen.dock_x, self.screen.dock_y ) ) self.move( self.screen.dock_x, self.screen.dock_y ) def dock_configure( self, event ): log.debug( "moving dock window to (%d,%d) becuase of ConfigureRequest" % ( self.screen.dock_x, self.screen.dock_y ) ) self.move( self.screen.dock_x, self.screen.dock_y ) tritium-0.3.8/tritium/workspace.py0000644000175000017500000001410611026330744016014 0ustar stewstew# workspace.py -- manage multiple 'desktops' or 'workspaces' # # Copyright 2007,2008 Mike O'Connor # # Portions of code plagarized from plwm's panes.py which is # Copyright (C) 2001 Mike Meyer # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from cycle import Cycle from frame.floating import FloatingFrame from frame.tabbed import TabbedFrame import logging log = logging.getLogger() FLOATING, TABBED = range( 2 ) class WorkspaceWindowManager(object): """ window manager mixin for a wm with workspaces """ def __wm_screen_init__( self ): log.debug( "WorkspaceWindowManager.__wm_screen_init__" ) self.workspaces = Cycle() self.workspaceDict = {} def __wm_init__( self ): log.debug( "WorkspaceWindowManager.__wm_init__" ) workspace = self.workspaces.current() if workspace: workspace.activate() def current_frame( self ): ws = self.workspaces.current() if ws: return ws.current_frame def set_current_frame( self, frame ): if frame: self.workspaces.current().current_frame = frame else: log.error( "wtf, set_current_frame got a null frame" ) def set_current_workspace( self, index ): if( index != self.workspaces.index and index >= 0 and index < len( self.workspaces ) ): self.workspaces.current().deactivate() self.workspaces.index = index self.workspaces.current().activate() def get_workspace_by_name( self, name ): (ws,index) = self.workspaceDict[ name ] return ws def new_workspace( self, screen, type=TABBED, name="" ): try: (ws,index) = self.workspaceDict[ name ] except KeyError: ws = Workspace( screen, type, name ) index = len(self.workspaces) self.workspaceDict[ name ] = (ws,index) self.workspaces.append( ws ) self.set_current_workspace( index ) return ws class OnTopFilter(object): def __call__( self, client ): try: return client.__getattribute__( 'on_top' ) except: return False class WorkspaceClient(object): def __client_init__( self ): log.debug( "WorkspaceClient.__client_init__: %s" % self ) if not self.dockapp: self.workspace = self.wm.workspaces.current() def hide( self ): if not self.hidden: (x, y, width, height, borderwidth) = self.geometry() self.hide_x = x self.hide_y = y new_x = self.screen.root_width+1 new_y = self.screen.root_height+1 self.move( new_x, new_y ) self.hidden = True def show( self ): if self.hidden: self.move( self.hide_x,self.hide_y ) self.hidden = False class WorkspaceScreen(object): def __screen_init__( self ): pass class Workspace(object): def __init__( self, screen, type=TABBED, name="" ): log.debug( "Workspace.__init__" ) self.name = name self.screen = screen self.active = False if type == FLOATING: self.current_frame = self.frame = FloatingFrame( self.screen, 0, 0, screen.root_width, screen.root_height ) else: self.current_frame = self.frame = TabbedFrame( self.screen, 0, 0, screen.root_width, screen.root_height ) self.current_frame.tritium_parent = self def raisewindows( self ): """ way to notify the workspace that a window was raised, and therefore all "on top" windows need to be raised as well. perhaps if python-xlib supported XRestackWindows, this would be better """ for client in self.screen.query_clients( OnTopFilter() ): client.raisewindow() pass def next_frame( self ): self.current_frame.deactivate() self.current_frame = self.current_frame.next_frame() self.current_frame.activate() def next_sibling_frame( self, frame ): """ this function ends up getting called when the last frame in a workspace calls "next_sibling_frame" so here we will return the first frame so that the frames will cycle """ return self.frame.first_child_frame() def find_frame( self, x, y ): return self.frame.find_frame( x, y ) def visible( self ): return self.active def activate( self ): self.active = True self.frame.show() # TODO this should really be the last current window's frame self.frame.topmost_child().activate() def deactivate( self ): self.active = False self.frame.deactivate() self.frame.hide() def replace_me( self, me, replacewith ): assert( self.frame == me ) self.frame = replacewith self.frame.tritium_parent = self self.current_frame = self.frame.first_child_frame() def __str__( self ): return "Workspace: " + str( self.name ) def find_frame_right( self, frame ): self.screen.wm.set_current_workspace( self.screen.wm.workspaces.index + 1 ) return self.screen.wm.current_frame(); def find_frame_left( self, frame ): self.screen.wm.set_current_workspace( self.screen.wm.workspaces.index - 1 ) return self.screen.wm.current_frame(); find_frame_above = find_frame_left find_frame_below = find_frame_right tritium-0.3.8/tritium/xinerama.py0000644000175000017500000000202211025667306015622 0ustar stewstewfrom Xlib.protocol import request, rq XineramaScreenInfo = rq.Struct( rq.Int16('x_org'), rq.Int16('y_org'), rq.Card16('width'), rq.Card16('height') ) class XineramaQueryScreenInfo(rq.ReplyRequest): _request = rq.Struct( rq.Card8( 'opcode' ), rq.Opcode( 5 ), rq.RequestLength() ) _reply = rq.Struct( rq.Int8( 'type' ), rq.Pad( 1 ), rq.Card16( 'sequence_number' ), rq.Card32( 'length' ), rq.LengthOf( 'screen_info', 4 ), rq.Pad( 20 ), rq.List( 'screen_info', XineramaScreenInfo ) ) class Xinerama(object): def __init__( self, display ): self.extension = display.query_extension( "XINERAMA" ) def get_screen_info( self ): if self.extension: infos = XineramaQueryScreenInfo( opcode = self.extension.major_opcode ) if infos: return infos.screen_info tritium-0.3.8/tritium/query.py0000644000175000017500000000733411025667311015172 0ustar stewstew# query.py -- promt the user for input # # Copyright 2007 Mike O'Connor # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import logging from plwm import input, message log = logging.getLogger() class MyEditHandler(input.InputKeyHandler): Any_Escape = C_g = input.InputKeyHandler._abort Any_Return = input.InputKeyHandler._done Any_Enter = input.InputKeyHandler._done Any_BackSpace = C_h = input.InputKeyHandler._delback C_d = input.InputKeyHandler._delforw C_b = input.InputKeyHandler._back C_f = input.InputKeyHandler._forw C_k = input.InputKeyHandler._deltoend C_a = input.InputKeyHandler._begin C_e = input.InputKeyHandler._end C_y = input.InputKeyHandler._paste C_p = input.InputKeyHandler._history_up C_n = input.InputKeyHandler._history_down Up = input.InputKeyHandler._history_up Down = input.InputKeyHandler._history_down Left = input.InputKeyHandler._back Right = input.InputKeyHandler._forw class query(object): "base class for queries" def __init__( self, prompt ): self.history = [] self.prompt = prompt def query( self, frame ): self.system = frame.screen.system window = input.inputWindow( self.prompt, frame.screen, length = 64) window.history = self.history x = frame.x + ( ( frame.width - window.width ) >> 1 ) y = (frame.height - frame.y)>>1 window.read( self, MyEditHandler, x, y ) def __call__( self, string ): self.history.append( string ) class runCommand( query ): "Read a string from the user, and run it as a command." def __init__( self ): query.__init__( self, "Run: " ) def __call__(self, string): query.__call__( self, string ) self.system(string + " &") class runCommandInXterm( query ): "Read a string from the user, and run it as a command." def __init__( self ): query.__init__( self, "Run in terminal emulator: " ) def __call__( self, string): query.__call__( self, string ) self.system( "x-terminal-emulator -e \"" + string + "\" &") class runSSH( query ): "Read a hostname from from the user, and ssh to it in a terminal." def __init__( self ): query.__init__( self, "ssh: " ) def __call__( self, string): query.__call__( self, string ) self.system("x-terminal-emulator -e ssh " + string + " &") class runPython( query ): """Read a string from the user and exec it with 'wm' as the context. """ def __init__( self ): query.__init__( self, ">>> " ) def query( self, frame ): query.query( self, frame ) self.wm = frame.wm def __call__( self, string): query.__call__( self, string ) exec string in {'wm': self.wm} class runMan( query ): """ Read manpage from the user, and display the manpage in a terminal emulator. """ def __init__( self ): query.__init__( self, "man: " ) def __call__( self, string): query.__call__( self, string ) self.system("x-terminal-emulator -e man " + string + " &") tritium-0.3.8/tritium/cycle.py0000644000175000017500000000751011025667315015124 0ustar stewstew# clientlist.py -- a container for clients # # Copyright 2007 Mike O'Connor # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import logging log = logging.getLogger() """ A list with a rotating iterator """ class Cycle(object): """ >>> f=Cycle() >>> f.append(1) >>> f.append(2) >>> f.append(3) >>> for a in f: print a 1 2 3 >>> f[1] 2 """ def __init__( self ): self.items = [] self.index = 0 def __str__( self ): return "Cycle: %s" % self.items def append( self, item ): self.items.append( item ) def insert_after_current( self, item ): self.items.insert( self.index+1, item ) self.index += 1 def contains( self, item ): try: self.items.index( item ) return True except: return False def current( self ): self._adjust() if len( self.items ): return self.items[ self.index ] def set_current( self, obj ): for self.index in range( len( self ) ): if( self.current() == obj ): return def remove( self, client ): self.items.remove( client ) def __getitem__( self, i ): return self.items.__getitem__( i ) def __iter__( self ): return self.items.__iter__() def __len__( self ): return self.items.__len__() def next( self ): """ # test the cycling >>> f=Cycle() >>> f.append( 1 ) >>> f.append( 2 ) >>> f.append( 3 ) >>> f.items [1, 2, 3] >>> f.next() 2 >>> f.next() 3 >>> f.next() 1 """ self.index += 1 return self.current() def prev( self ): """ # test the cycling >>> f=Cycle() >>> f.append( 1 ) >>> f.append( 2 ) >>> f.append( 3 ) >>> f.items [1, 2, 3] >>> f.prev() 3 >>> f.prev() 2 >>> f.prev() 1 >>> f.prev() 3 """ self.index -= 1 return self.current() def _adjust( self ): """ Make sure our index is sane: >>> f = Cycle() >>> f.index 0 >>> f._adjust() >>> f.index 0 >>> f.index = 10 >>> f._adjust() >>> f.index 0 >>> f.append( 1 ) >>> f.index 0 >>> f._adjust() >>> f.index 0 >>> f.index = 10 >>> f._adjust() >>> f.index 0 >>> f.index = 1 >>> f._adjust() >>> f.index 0 >>> f.append( 1 ) >>> f.append( 2 ) >>> f.index = 2 >>> f._adjust() >>> f.index 2 >>> f.index = 3 >>> f._adjust() >>> f.index 0 >>> f.index = -1 >>> f._adjust() >>> f.index 2 """ if self.index >= len( self.items ): self.index = 0 if self.index < 0: self.index = len( self.items ) -1 def _test(): import doctest doctest.testmod() if __name__ == "__main__": _test() print "DONE" tritium-0.3.8/tritium/__init__.py0000644000175000017500000001045211026361077015560 0ustar stewstew""" tritium -- a window manager inspired by tuomo Copyright 2007 Mike O'Connor 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """ __distname__ = 'tritium' __version__ = '0.3.8' __description__ = 'a tabbed/tiling window manager' __long_description__ = \ """ tritium is a tiling/tabbed window manager for the X Window System inspired by the ion window manager. It was written completely from scratch in Python and shares no actual code with ion. tritium is implemented in Python using the plwm ("pointless window manager") library, which is available here: http://plwm.sourceforge.net """ __author__ = "Mike O'Connor" __email__ = 'stew@vireo.org' __url__ = 'http://tritium.vireo.org/' __license__ = 'GPL' import logging from Xlib import X from plwm import wmanager, wmevents, modewindow from workspace import Workspace from frame.frame import Frame from cycle import Cycle import query import traceback log = logging.getLogger() class tritiumScreen(object): "tritium mixin for Screens." def __screen_client_init__( self ): log.debug( "tritiumScreen. __screen_client_init__" ) "Create the initial pane object for this screen." wmanager.debug( 'panesScreen', 'Initializing screen %d', self.number ) self.dispatch.add_handler( X.ConfigureRequest, self.configure_frame ) # ws = self.wm.new_floating_workspace( self ) # ws = self.wm.new_workspace( self ) def configure_frame( self, event ): w = self.get_window( event.window ) if w: if event.value_mask & (X.CWX | X.CWY | X.CWWidth | X.CWHeight): if w.frame: w.frame.place_window( w ) if event.value_mask & X.CWStackMode and event.stack_mode == X.Above \ and self.allow_self_changes( w ): if w.frame: w.frame.place_window( w ) def maximize_frame( self, frame ): "Make the pane use the all the available screen." # todo, make root for a toolbar/statusbar? frame.x = 0 frame.y = 0 frame.width = self.root_width frame.height = self.root_height class tritiumWindowManager(object): """ tritium window manager mixin """ def __wm_init__( self ): "Enable activation, then activate the first pane." log.debug( "tritiumWindowManager.__wm_init__" ) self.runCommand = query.runCommand() self.runCommandInXterm = query.runCommandInXterm() self.runSSH = query.runSSH() self.runPython = query.runPython() self.runMan = query.runMan() #TODO: figure out what this is all about: Frame.activate = Frame._activate class actions_menu(object): def __init__( self, client ): width, height = client.screen.menu_make( [ 'close', client.delete ] ) pass def __call__( self, choice ): pass class appmenu(object): "Creates a menu of applications to run in a pane." def __init__( self, wm, apps ): "Create and run the applications menu from the keys." frame = wm.current_frame() labels = apps.keys() labels.sort() width, height = frame.screen.menu_make(labels) self.wm = wm self.system = frame.screen.system self.apps = apps frame.screen.menu_run( ( frame.width - width ) / 2 + frame.x, ( frame.height - height ) / 2 + frame.y, self ) def __call__(self, choice): "Call the system function on the value of the given choice." exec self.apps[choice] in {'wm':self.wm}