summaryrefslogtreecommitdiffstats
path: root/openbox-shutdown.py
blob: 2f007a8016ceda3ccca99565081d698f849bcb97 (plain)
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#!/usr/bin/env python
#----------------------------------------------------
# Author: ZaB|SHC|@irc.freenode.net
#
# License: Beerware
#----------------------------------------------------
"""

shutdown script for Openbox with configurable buttons and actions
needs pygtk for gui
"""

# standard stuff
import os
import re
import sys

# gui modules
try:
  import gtk
  import pygtk
except ImportError:
  sys.stderr.write('pygtk is needed for using this script')
  sys.exit(1)

if sys.version_info < (2, 5, 0):
  sys.stderr.write('Python 2.5.0 or higher is needed')
  sys.exit(1)

# a simple parser for the configuration
class ConfigParser(dict):
  """
  ConfigParser(file) -> dict

  reads file and creates dict object containing corresponding
  configuration
  """

  def __init__(self, filename):
    self._section_match_regexp = re.compile(r'\[(\w+)\]')
    self._key_val_match_regexp = re.compile(r'\s*=\s*')
    self._comment_remov_regexp = re.compile(r'#.*')
    try:
      config_handle = open(filename, 'r')
    except IOError:
      sys.stderr.write('Could not read %s. Aborting ...\n' % filename)
      sys.exit(1)
    linecount = 0
    for line in config_handle.readlines():
      linecount += 1
      line = self._comment_remov_regexp.sub('', line.strip())
      if len(line) == 0:
        continue
      if self._section_match_regexp.match(line):
        section = self._section_match_regexp.match(line).groups()[0]
        self[section] = {}
        continue
      try:
        (key, value) = self._key_val_match_regexp.split(line)
      except ValueError:
        sys.stderr.write('Syntax error in line %d of %s.' % (linecount,
                                                             filename))
        sys.exit(1)
      if key == 'command' and not key in self[section]:
        self[section][key] = [value]
        continue
      if key in self[section] and key == 'command':
        self[section][key].append(value)
      else:
        self[section][key] = value

  def check(self):
    """
    checks if configuration is complete
    """
    for section in self:
      for needed_key in ['command', 'image', 'info_text', 'label']:
        if not needed_key in self[section]:
          sys.stderr.write('Missing option %s in %s.' % (needed_key, section))
          sys.exit(1)

# the main class for this script
class OpenBoxShutdown(object):
  """
  main class for this script

  reads configuration and displays buttons as requested

  on click, the specified actions are made
  """

  def __init__(self, basedir):
    self._config = ConfigParser(os.path.join(basedir, 'openbox-shutdown.conf'))
    self._config.check()
    # button box on top for the action buttons
    button_box  = gtk.HButtonBox()
    for key in self._config:
      button_box.pack_start(self._create_button(key,
                                                self._config[key]['label'],
                                                self._config[key]['info_text'],
                                                self._config[key]['image']))
    button_box.show()
    # create cancel button
    cancel_button = gtk.Button(stock = gtk.STOCK_CANCEL)
    cancel_button.connect('clicked', self._click_callback, 'cancel')
    cancel_button.show()
    # label widget for small hints
    self._label = gtk.Label()
    self._label.show()
    # frame widget to contain label
    frame = gtk.Frame()
    frame.set_shadow_type(gtk.SHADOW_NONE)
    frame.add(self._label)
    frame.show()
    # area containing hints and cancel button
    action_area = gtk.HBox()
    action_area.pack_end(cancel_button, expand=False)
    action_area.pack_end(frame)
    action_area.show()
    # box containing button box and action area
    vbox = gtk.VBox()
    vbox.pack_start(button_box)
    vbox.pack_end(action_area)
    vbox.show()
    # the main window with all widgets
    # always on top and on all desktops
    window = gtk.Window()
    window.set_position('center')
    window.set_decorated(False)
    window.stick()
    window.set_keep_above(True)
    window.set_skip_pager_hint(True)
    window.set_skip_taskbar_hint(True)
    window.add(vbox)
    window.show()
    # give control to gtk
    gtk.main()

  def _click_callback(self, event, action):
    """
    _click_callback(event, action)

    executes commands on button click
    """
    gtk.main_quit()
    if action == 'cancel':
      sys.exit(0)
    for command in self._config[action]['command']:
      os.system(command)

  def _create_button(self, key, label_text, info_text, image_file):
    """
    _create_button(key, label, info, image) -> button

    creates a button widget with key (used for handling actions),
    a label for the button, a small text as hint and an image
    """
    button = gtk.Button()
    if label_text != 'None':
      button.set_label(label_text)
    if image_file != 'None':
      button.set_image(self._create_image(image_file))
      button.set_image_position(gtk.POS_TOP)
    button.connect('clicked', self._click_callback, key)
    button.connect('leave', lambda event: self._label.set_text(''))
    button.connect('enter', self._set_label, info_text)
    button.show()
    return button

  def _create_image(self, image_file):
    """
    _create_image(image) -> image

    creates an image widget from the specified file
    """
    image = gtk.Image()
    image.set_from_file(image_file)
    return image

  def _set_label(self, event, info_text):
    """
    _set_label(event, info_text)

    set label on entering the widget
    """
    if info_text != 'None':
      self._label.set_markup('<span size="smaller">%s</span>' % info_text)

# start the script if called standalone
if __name__ == '__main__':
  # get the real path for the configuration file
  conf_path = os.path.realpath(os.path.dirname(os.readlink(__file__))) \
              if os.path.islink(__file__) \
              else os.path.realpath(os.path.dirname(__file__))
  shutdown = OpenBoxShutdown(conf_path)