root/sx-moveresize/sxmoveresize.py

Revision 880d60de64b64afb023616ad34572f19e3068f3b, 8.8 KB (checked in by Friedrich Weber <fred@…>, 14 months ago)

samurai-x2 and plugins: fixed imports (hope i didn't forget anything)

  • Property mode set to 100644
Line 
1# Copyright (c) 2008-2009, samurai-x.org
2# All rights reserved.
3#
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions are met:
6#     * Redistributions of source code must retain the above copyright
7#       notice, this list of conditions and the following disclaimer.
8#     * Redistributions in binary form must reproduce the above copyright
9#       notice, this list of conditions and the following disclaimer in the
10#       documentation and/or other materials provided with the distribution.
11#     * Neither the name of the samurai-x.org nor the
12#       names of its contributors may be used to endorse or promote products
13#       derived from this software without specific prior written permission.
14#
15# THIS SOFTWARE IS PROVIDED BY SAMURAI-X.ORG ``AS IS'' AND ANY
16# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18# DISCLAIMED. IN NO EVENT SHALL SAMURAI-X.ORG  BE LIABLE FOR ANY
19# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
26"""
27    sx-moveresize is a plugin that adds the actions 'moveresize.move' and 'moveresize.resize'
28    which move and resize the current or specified window respectivly.
29
30    Configuration
31    -------------
32       
33    .. attribute:: moveresize.border-move
34
35        Boolean value that when True shows a rectangle preview of the moved/resized
36        window instead of moving/resizing the window directly
37
38    .. attribute:: moveresize.hide-win
39   
40        Boolean value that when True will hide the window that is being moved/resized.
41        Most usefull when moveresize.border-move is True.
42
43    Actions
44    -------
45
46    .. function:: moveresize.move
47        :module:
48
49        Start moving a client.
50
51        :Parameters:
52            `screen`
53                required
54            `client`
55                the :class:`client <samuraix.client.Client>` to move
56                (optional, defaults to the currently focused client)
57            `x`, `y`
58                the coordinates of the event that emitted the action (optional)
59       
60
61    .. function:: moveresize.resize
62        :module:
63       
64        Start resizing a client
65
66        :Parameters:
67            `screen`
68                required
69            `client`
70                the :class:`client <samuraix.client.Client>` to resize
71                (optional, defaults to the currently focused client)
72            `x`, `y`
73                the coordinates of the event that emitted the action (optional)
74       
75
76"""
77
78
79import logging
80log = logging.getLogger(__name__)
81
82from samuraix.plugin import Plugin
83from samuraix.rect import Rect
84from samuraix.util import DictProxy
85
86from ooxcb.protocol import xproto
87
88MOUSE_MASK = xproto.EventMask.ButtonPress | xproto.EventMask.ButtonRelease | xproto.EventMask.PointerMotion
89
90class ClientHandler(object):
91    def __init__(self, client, x, y, cursor=None, border_move=True, hide_win=True):
92        log.debug('created %s', self)
93
94        self.client = client
95        self.offset_x, self.offset_y = x, y
96        self.border_move = border_move
97        self.hide_win = hide_win
98
99        self.gc = xproto.GContext.create(
100                self.client.conn,
101                self.client.window,
102                function=xproto.GX.xor, 
103                foreground=self.client.screen.info.white_pixel,
104                subwindow_mode=xproto.SubwindowMode.IncludeInferiors,
105        )
106
107        client.screen.root.grab_pointer(MOUSE_MASK, cursor=cursor)
108        client.screen.focus(client)
109        if self.hide_win:
110            client.ban()
111        client.conn.flush()
112
113    def on_motion_notify(self, evt):
114        pass
115
116    def on_button_release(self, evt):
117        pass
118
119
120class MoveHandler(ClientHandler):
121    def __init__(self, client, x, y, cursor=None, **kwargs):
122        ClientHandler.__init__(self, client, x, y, client.app.cursors['Move'], **kwargs)
123        self._x = None
124        self._y = None
125
126    def on_motion_notify(self, evt):
127        x, y = evt.root_x - self.offset_x, evt.root_y - self.offset_y
128        if self.border_move:
129            self.clear_preview()
130            self.gc.poly_rectangle(self.client.screen.root,
131                    [Rect(x, y, self.client.geom.width, self.client.geom.height)])
132        else:
133            self.client.actor.configure(x=x, y=y)
134        self.client.conn.flush()
135        self._x = evt.root_x
136        self._y = evt.root_y
137        return True
138
139    def on_button_release(self, evt):
140        self.client.screen.root.remove_handlers(self)
141        self.client.conn.core.ungrab_pointer()
142        if self.border_move:
143            self.clear_preview()
144        if self.hide_win:
145            self.client.unban()
146        if self._x is not None:
147            self.client.actor.configure(x=self._x - self.offset_x, y=self._y - self.offset_y)
148#            self.client.force_update_geom()
149        self.client.conn.flush()
150        return True
151
152    def clear_preview(self):
153        """ clear old preview if necessary """
154        if self._x is not None:
155            x, y = self._x - self.offset_x, self._y - self.offset_y
156            self.gc.poly_rectangle(self.client.screen.root,
157                                    [Rect(x, y, self.client.geom.width, self.client.geom.height)])
158
159
160class ResizeHandler(ClientHandler):
161    def __init__(self, client, x, y, **kwargs):
162        ClientHandler.__init__(self, client, x, y, client.app.cursors['Resize'], **kwargs)
163
164#        geom = self.client.geom
165#        client.frame.warp_pointer(geom.width, geom.height)
166
167        self._w = None
168        self._h = None
169
170    def on_motion_notify(self, evt):
171        geom = self.client.geom # TODO: I'm sure that's wrong. -- is it?
172        w = evt.root_x - geom.x
173        h = evt.root_y - geom.y
174        if self.border_move: 
175            self.clear_preview()
176            self.gc.poly_rectangle(self.client.screen.root,
177                    [Rect(geom.x, geom.y, w, h)])
178        else:
179            self.client.actor.configure(w=w, h=h)
180        self.client.conn.flush()
181        self._w = w
182        self._h = h
183        return True
184
185    def on_button_release(self, evt):
186        if self.border_move:
187            self.clear_preview()
188
189        geom = self.client.geom.copy()
190        geom.width, geom.height = self._w, self._h
191        if geom.width:
192            # we cannot use self.client.resize here, because
193            # that resizes the window. we want to resize the
194            # actor. That's not really optimal. TODO?
195            self.client.actor.configure(
196                    width=geom.width,
197                    height=geom.height
198                    )
199        self.client.screen.root.remove_handlers(self)
200        self.client.conn.core.ungrab_pointer()
201        self.client.unban()
202        # put the mouse back where it was # TODO: necessary?
203#        self.client.frame.warp_pointer(self.offset_x, self.offset_y)
204        self.client.conn.flush()
205
206        return True
207
208    def clear_preview(self):
209        """ clear old preview if necessary """
210        if self._w:
211            self.gc.poly_rectangle(
212                    self.client.screen.root,
213                    [Rect(
214                        self.client.geom.x, self.client.geom.y, 
215                        self._w, self._h
216                    )]
217            )
218
219class SXMoveResize(Plugin):
220    key = 'moveresize'
221
222    def __init__(self, app):
223        self.app = app
224        app.push_handlers(self)
225
226        app.plugins['actions'].register('moveresize.move', self.action_move)
227        app.plugins['actions'].register('moveresize.resize', self.action_resize)
228
229    def on_load_config(self, config):
230        self.config = DictProxy(config, self.key+'.')
231
232    def action_move(self, info):
233        client = info.get('client', info['screen'].focused_client)
234        if client is not None:
235            client.screen.root.push_handlers(
236                    MoveHandler(client, info.get('x', 0), info.get('y', 0), 
237                            border_move=self.config.get('border-move', True),
238                            hide_win=self.config.get('hide-win', True),
239                    )
240            )
241
242    def action_resize(self, info):
243        client = info.get('client', info['screen'].focused_client)
244        if client is not None:
245            client.screen.root.push_handlers(
246                    ResizeHandler(client, info.get('x', 0), info.get('y', 0),
247                            border_move=self.config.get('border-move', True),
248                            hide_win=self.config.get('hide-win', True),
249                    )
250            )
251
Note: See TracBrowser for help on using the browser.