aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/menu.py
blob: 87e93cff057883cc679fe2f5726c709fd678167f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# -*- python -*-
'''
xpybar – xmobar replacement written in python
Copyright © 2014, 2015, 2016, 2017, 2018  Mattias Andrée (maandree@kth.se)

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 3 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, see <http://www.gnu.org/licenses/>.
'''


class Menu:
    '''
    Dropdown menu

    Instances of this class is compatible with `Bar`
    
    You should extend the global function `stop` to unmap any menu that
    is still open at the time that function is called
    
    @variable  window            The X window
    @variable  gc                The window's graphics context
    @variable  cmap              The window's colour map
    @variable  width:int         The menu's pixel width
    @variable  height:int        The output's pixel height
    @variable  left:int          The output's left position
    @variable  top:int           The output's top position
    @variable  ypos:int          The position of the menu in relation to either the top or bottom edge of the output
    @variable  panel_height:int  The menu's height, note that the name is `panel_height` for compatibility with `Bar`
    @variable  at_top:bool       Whether the menu is to be positioned relative the top of the output, otherwise to the bottom
    @variable  background        The default background
    @variable  foreground        The default foreground
    @variable  font              The default font
    @variable  font_metrics      The default font's metrics
    @variable  font_height:int   The height of the default font
    @variable  font_width:int    The width of an 'X' with the default font
    @variable  palette           A 16-array of standard colours
    @variable  cmap_cache        Custom colour map cache
    '''
    
    
    methods_set = False
    '''
    :bool  Indicates whether a constructor has set methods common with `Bar`
    '''
    
    
    def __init__(self, output, width, height, xpos, ypos, left, top, font = None,
                 background = None, foreground = None, event_mask = None, override_redirect = True):
        '''
        Constructor
        
        @param  output:int                                  The index of the output within the screen as printed by xrandr, except primary is first
        @param  width:int                                   The width of the menu
        @param  height:int                                  The height of the menu
        @param  xpos:int                                    The position of the menu in relation the either the left or right edge of the output
        @param  ypos:int                                    The position of the menu in relation the either the top or bottom edge of the output
        @param  left:int                                    Whether the menu is positioned relative to the left of the output, otherwise to the right
        @param  top:int                                     Whether the menu is positioned relative to the top of the output, otherwise to the bottom
        @param  font:str?                                   The default font, `None` for the default
        @param  background:(red:int, green:int, blue:int)?  The default background, `None` for the default
        @param  foreground:(red:int, green:int, blue:int)?  The default foreground, `None` for the default
        @param  event:int?                                  Mask of events that should be caught, `None` for the default
        @param  override_redirect:bool                      Whether override_redirect should be set
        '''
        import os
        import Xlib.display, Xlib.Xatom, Xlib.ext.randr, Xlib.X
        
        if not Menu.methods_set:
            Menu.map                         = Bar.map
            Menu.unmap                       = Bar.unmap
            Menu.text_width                  = Bar.text_width
            Menu.draw_text                   = Bar.draw_text
            Menu.draw_coloured_text          = Bar.draw_coloured_text
            Menu.draw_coloured_splitted_text = Bar.draw_coloured_splitted_text
            Menu.create_colour               = Bar.create_colour
            Menu.create_font                 = Bar.create_font
            Menu.change_colour               = Bar.change_colour
            Menu.change_font                 = Bar.change_font
            Menu.clear_rectangle             = Bar.clear_rectangle
            Menu.clear                       = Bar.clear
            Menu.invalidate                  = Bar.invalidate
            Menu.coloured_length             = Bar.coloured_length
            Menu.methods_set = True
        
        if event_mask is None:
            event_mask = ( Xlib.X.StructureNotifyMask
                         | Xlib.X.ButtonPressMask
                         | Xlib.X.ButtonReleaseMask
                         | Xlib.X.ExposureMask
                         | Xlib.X.KeyPress
                         | Xlib.X.KeyRelease
                         | Xlib.X.MotionNotify
                         | Xlib.X.EnterNotify
                         | Xlib.X.LeaveNotify
                         )
        if font       is None:  font       = FONT
        if background is None:  background = BACKGROUND
        if foreground is None:  foreground = FOREGROUND
        
        [self.width, self.height, self.left, self.top] = outputs[output][:4]
        [self.ypos, self.panel_height, self.at_top] = [ypos, height, top]
        self.left += xpos if left else (self.width - xpos - width)
        self.top += ypos if top else (self.height - ypos - height)
        self.width = width
        
        self.window = screen.root.create_window(self.left, self.top, self.width, self.panel_height, 0,
                                                Xlib.X.CopyFromParent,
                                                Xlib.X.InputOutput,
                                                Xlib.X.CopyFromParent,
                                                event_mask = event_mask,
                                                colormap = Xlib.X.CopyFromParent,
                                                override_redirect = override_redirect)
        
        self.window.set_wm_name('xpybar')
        self.window.set_wm_icon_name('xpybar')
        self.window.set_wm_class('menu', 'xpybar')
        self.window.set_wm_client_machine(os.uname().nodename)
        
        _CARD    = display.intern_atom("CARDINAL")
        _DESKTOP = display.intern_atom("_NET_WM_DESKTOP")
        _PID     = display.intern_atom("_NET_WM_PID")
        self.window.change_property(_DESKTOP, _CARD, 32, [0xFFFFFFFF])
        self.window.change_property(_PID,     _CARD, 32, [os.getpid()])
        
        _ATOM   = display.intern_atom("ATOM")
        _TYPE   = display.intern_atom("_NET_WM_WINDOW_TYPE")
        _NORMAL = display.intern_atom("_NET_WM_WINDOW_TYPE_NORMAL")
        self.window.change_property(_TYPE, _ATOM, 32, [_NORMAL])
        
        self.gc = self.window.create_gc()
        self.cmap = self.window.get_attributes().colormap
        self.cmap_cache = {}
        self.background = self.create_colour(*background)
        self.foreground = self.create_colour(*foreground)
        (self.font, self.font_metrics, self.font_height) = self.create_font(font)
        self.font_width = self.text_width('X')
        self.palette = [0x000000, 0xCD656C, 0x32A679, 0xCCAD47, 0x2495BE, 0xA46EB0, 0x00A09F, 0xD8D8D8]
        self.palette += [0x555555, 0xEB5E6A, 0x0EC287, 0xF2CA38, 0x00ACE0, 0xC473D1, 0x00C3C7, 0xEEEEEE]
        self.palette = [((p >> 16) & 255, (p >> 8) & 255, p & 255) for p in self.palette]
        self.palette = [self.create_colour(*p) for p in self.palette]