#!/usr/bin/python
# $Id: plpwm.py,v 1.16 2002/02/18 04:27:48 mwm Exp $
#
# plpwm.py -- Exemple PLWM window manager configuration with panes.
#
#    Copyright (C) 2001  Mike Meyer <mwm@mired.org>
#
#    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

"""plpwm.py - Pointer Less Paned Window Manager

Example PLWM window manager configuration with panes."""

import sys, os

###SETUP PATH
sys.path[1:1] = [os.path.join(sys.path[0], '..')]
import plwm.xlibpath
###END SETUP PATH

from Xlib import X, XK

from plwm import wmanager, keys, inspect, \
     border, color, font, menu, panes, cfilter, input, message

border.BorderClient.border_default_width = 5

class MyMenuHandler(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 MyEditHandler(input.InputKeyHandler):
    Any_Escape = C_g = input.InputKeyHandler._abort
    Any_Return = 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

class MyClient(wmanager.Client, border.BorderClient, panes.panesClient):
    "Put the clients in panes, with a border."

class MyScreen(wmanager.Screen,
               color.Color,
               message.screenMessage,
               panes.panesScreen,
               menu.screenMenu):
    "And panes on the screen, and I'm going to want some menus."

    menu_handler = MyMenuHandler
    allow_raise_requests = 0

class WMConfig:
    def __wm_init__(my):
        "install the panes key map"
        PaneKeys(my)

class PLPWM(wmanager.WindowManager,
            font.Font,
            panes.panesManager,
            inspect.InspectServer,
            WMConfig):
    "Set up my window manager."

    client_class = MyClient
    screen_class = MyScreen

class PaneKeys(keys.KeyHandler):
    "The pane control keys."

    # Commands for navigating and manipulating panes
    def M1_Escape(my, event): my.wm.quit()

    def C_0(my, event):
        "Go to the pane numbered by the keycode."

        my.wm.panes_goto(my.wm.display.keycode_to_keysym(event.detail, 0) - XK.XK_0)

    C_1 = C_2 = C_3 = C_4 = C_5 = C_6 = C_7 = C_8 = C_9 = C_0

    def M1_Tab(my, event): my.wm.panes_next()
    def S_M1_Tab(my, event): my.wm.panes_prev()

    def M1_1(my, event):
        my.wm.panes_list[my.wm.panes_current].maximize()

    def M1_2(my, event):
        split_pane(my.wm.display.keycode_to_keysym(event.detail, 0) - XK.XK_0,
                   my.wm.panes_list[my.wm.panes_current].horizontal_split)

    M1_3 = M1_4 = M1_5 = M1_6 = M1_7 = M1_8 = M1_9 = M1_2

    def S_M1_2(my, event):
        split_pane(my.wm.display.keycode_to_keysym(event.detail, 0) - XK.XK_0,
                   my.wm.panes_list[my.wm.panes_current].vertical_split)

    S_M1_3 = S_M1_4 = S_M1_5 = S_M1_6 = S_M1_7 = S_M1_8 = S_M1_9 = S_M1_2
 
    # Keystrokes for launching applications
    def M1_exclam(my, event):
        runcommand(my.wm.panes_list[my.wm.panes_current])

    def M1_m(my, event):
        appmenu(my.wm.panes_list[my.wm.panes_current],
                {'words': 'applix -wp',
                 'sql': 'applix -db',
                 'sheet': 'applix -ss',
                 'fxtv': 'fxtv',
                 'w3m': 'w3m',
                 'skipstone': 'skipstone'})

    def M1_b(my, event): getapp(my.wm.panes_list[my.wm.panes_current], "w3m")

    def M1_c(my, event): my.wm.system("xterm -ls &")

    def M1_e(my, event):
        my.wm.panes_goto(0)	# always run emacs in pane 0.
        getapp(my.wm.panes_list[my.wm.panes_current], "emacs", "xemacs")

    def M1_i(my, event):
        getapp(my.wm.panes_list[my.wm.panes_current],
               "/home/mwm/axhome/axMail/Inbox.sp", "applix -inbox")

    def M1_j(my, event):
        getapp(my.wm.panes_list[my.wm.panes_current], "J-Pilot", "jpilot")

    def M1_s(my, event):
        getapp(my.wm.panes_list[my.wm.panes_current], "SkipStone", "skipstone")

    # Keystrokes for window manager operations
    def M1_quoteright(my, event):
        pullwindow(my.wm.panes_list[my.wm.panes_current])

    def M1_quotedbl(my, event):
        gotowindow(my.wm.panes_list[my.wm.panes_current])

    def M1_Return(my, event):
        my.wm.panes_list[my.wm.panes_current].next_window()

    def S_M1_Return(my, event):
        my.wm.panes_list[my.wm.panes_current].prev_window()

    def M1_k(my, event):
        my.wm.panes_list[my.wm.panes_current].window.delete(1)

    def S_M1_k(my, event):
        my.wm.panes_list[my.wm.panes_current].window.destroy()

    def M1_r(my, event):
        my.wm.panes_list[my.wm.panes_current].force_window()

    def M1_u(my, event):
        windowmenu(my.wm.panes_list[my.wm.panes_current],
                   lambda w: not (w.panes_pane and w.panes_pane.window == w))

    def M1_x(my, event):
        ExtendedPaneKeys(my.wm, event.time)

    # Aliases for some of the keystrokes
    M1_space = M1_Return
    S_M1_space = S_M1_Return

class ExtendedPaneKeys(keys.KeyGrabKeyboard):
    "Extended command, with a timeout."

    def M1_b(my, event):
        windowmenu(my.wm.panes_list[my.wm.panes_current])
        my._cleanup()

    def M1_f(my, event):
        width, height = my.wm.current_screen.message_make("Current pane %d" \
                                                          % my.wm.panes_current)
        pane = my.wm.panes_list[my.wm.panes_current]
        my.wm.current_screen.message_display((pane.width - width) / 2 + pane.x,
                                             (pane.height - height) / 2 + pane.y)
        my._cleanup()

    def M1_i(my, event):
        my.wm.inspect_enable()
        my.wm.system('xterm -title Inspector -e inspect_plwm &')
        my._cleanup()

    def M1_m(my, event):
        appmenu(my.wm.panes_list[my.wm.panes_current],
                {'mixer': 'xgmixer',
                 'console': 'xconsole -daemon',
                 'gkrellm': 'gkrellm'})
        my._cleanup()

    def M1_p(my, event):
        panesmenu(my.wm.current_screen)
        my._cleanup()

    def M1_r(my, event):
        restore(my.wm)
        my._cleanup()

    def M1_s(my, event):
        my.wm.panes_save()
        my._cleanup()

    def M1_u(my, event):
        windowmenu(my.wm.panes_list[my.wm.panes_current], cfilter.iconified)
        my._cleanup()

    def M1_w(my, event):
        pane = my.wm.panes_list[my.wm.panes_current]
        windowmenu(pane, panes.panefilter(pane))
        my._cleanup()

    def M1_x(my, event):
        my.wm.panes_list[my.wm.panes_current].iconify_window()
        my._cleanup()

    Any_g = Any_Escape = keys.KeyGrabKeyboard._timeout
    

class appmenu:
    "Creates a menu of applications to run in a pane."

    def __init__(my, pane, apps):
        "Create and run the applications menu from the keys."

        labels = apps.keys()
        labels.sort()
        width, height = pane.screen.menu_make(labels)
        my.system = pane.screen.system
        my.apps = apps
        pane.screen.menu_run((pane.width - width) / 2 + pane.x,
                         (pane.height - height) / 2 + pane.y,
                         my)

    def __call__(my, choice):
        "Call the system function on the value of the given choice."

        my.system(my.apps[choice] + " &")
                                                               
class windowmenu:
    "Create a menu of windows to add to a pane."

    def __init__(my, pane, filter = cfilter.true, list = None):
        labels = []
        clients = {}
        i = 'a'
        for c in list or pane.screen.query_clients(filter, 1):
            l = "%c: %s" % (i, c.get_title())
            labels.append(l)
            clients[l] = c
            i = chr(ord(i) + 1)
        if labels:
            width, height = pane.screen.menu_make(labels, align = 'left')
            my.add = pane.add_window
            my.clients = clients
            pane.screen.menu_run((pane.width - width) / 2 + pane.x,
                                 (pane.height - height) / 2 + pane.y,
                                 my)
        else:
            width, height = pane.screen.message_make("No windows")
            pane.screen.message_display((pane.width - width) / 2 + pane.x,
                                        (pane.height - height) / 2 + pane.y)

    def __call__(my, choice):
        "Add the selected window to the pane."

        my.add(my.clients[choice])

class panesmenu:
    "Create a menu of all the panes."

    def __init__(my, screen):
        wm = screen.wm
        labels = []
        panes = {}
        for i in range(len(wm.panes_list)):
            w = wm.panes_list[i].window
            if w: l = "%d: %s" % (i, w.get_title())
            else: l = "%d: <EMPTY>" % i
            labels.append(l)
            panes[l] = i
        width, height = screen.menu_make(labels, align = 'left')
        my.goto = wm.panes_goto
        my.panes = panes
        screen.menu_run((screen.root_width - width) / 2,
                        (screen.root_height - height) / 2, my)

    def __call__(my, choice):
        "Activate the selected pane."

        my.goto(my.panes[choice])
            
class runcommand:
    "Read a string from the user, and run it."

    def __init__(my, pane):
        my.system = pane.screen.system
        window = input.inputWindow("! ", pane.screen, length = 50)
        window.read(my, MyEditHandler, pane.x, pane.y)

    def __call__(my, string):
        my.system(string + " &")

class pullwindow:
    "Read a window's name, and pull it to the current pane."

    def __init__(my, pane):
        my.pane = pane
        window = input.inputWindow("pull ", pane.screen)
        window.read(my, MyEditHandler, pane.x, pane.y)

    def __call__(my, name):
        clients = my.pane.screen.query_clients(cfilter.re_title(name + ".*"), 1)
        if len(clients) == 1: my.pane.add_window(clients[0])
        elif clients: windowmenu(my.pane, list = clients)
        else:
            width, height = my.pane.screen.message_make("No windows")
            my.pane.screen.message_display(my.pane.x, my.pane.y)


class gotowindow:
    "Emulate the rp 'goto' command."

    def __init__(my, pane):
        my.pane = pane
        window = input.inputWindow("goto ", pane.screen)
        window.read(my, MyEditHandler)

    def __call__(my, name):
        clients = my.pane.screen.query_clients(cfilter.re_title(name + ".*"), 1)
        if clients:
            window = clients[0]
            if window.panes_pane.window and window.panes_pane.window == window:
                my.pane.wm.panes_activate(window.panes_pane)
            else:
                my.pane.add_window(window)
        else:
            width, height = my.pane.screen.message_make("No windows")
            my.pane.screen.message_display(0, 0)

def getapp(pane, name, command = None):
    "Find a window starting with name, and run command if it doesn't exist."

    clients = pane.screen.query_clients(cfilter.re_title(name + ".*"), 1)
    if len(clients) > 1: windowmenu(pane, list = clients)
    elif clients: pane.add_window(clients[0])
    else: pane.screen.system((command or name) + " &")
    

def restore(wm):
    "Build my standard work environment."

    pane = wm.panes_list[wm.panes_current]
    pane.maximize()

    # Disconnect all windows from panes to avoid displaying everything
    # that's about to happen. wm.panes_restore() will put things back.
    for c in wm.query_clients():
        c.panes_pane = None
    pane.window = None

    pane.vertical_split()
    wm.panes_number(0)
    wm.panes_goto(1)
    pane.horizontal_split(.0625)
    pane.vertical_split(.75)
    wm.panes_number(1)
    pane = wm.panes_list[1]
    pane.horizontal_split(94. / 225.)
    wm.panes_number(3)
    pane.horizontal_split()
    wm.panes_number(2)
    pane = wm.panes_list[4]
    pane.horizontal_split(16. / 45.)
    pane.horizontal_split(24. / 29.)
    wm.panes_goto(4)
    wm.panes_restore()

def split_pane(count, splitter):
    "Split current pane into 2-9 equal-sized panes."

    while count > 1:
        splitter(1. / count)
        count -= 1


if __name__ == '__main__':
    wmanager.main(PLPWM)


syntax highlighted by Code2HTML, v. 0.9.1