1
# Copyright (C) 2010 Matthew McGowan
6
# This program is free software: you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation, either version 3 of the License, or
9
# (at your option) any later version.
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
# GNU General Public License for more details.
16
# You should have received a copy of the GNU General Public License
17
# along with this program. If not, see <http://www.gnu.org/licenses/>.
25
from gettext import gettext as _
28
DEFAULT_PART_SIZE = (29, -1)
29
DEFAULT_ARROW_SIZE = (12, 12)
32
class BackForwardButton(gtk.HBox):
34
__gsignals__ = {'left-clicked':(gobject.SIGNAL_RUN_LAST,
38
'right-clicked':(gobject.SIGNAL_RUN_LAST,
42
def __init__(self, part_size=None):
43
gtk.HBox.__init__(self)
44
self.theme = mkit.get_mkit_theme()
45
self.separator = SeparatorPart()
48
part_size = part_size or DEFAULT_PART_SIZE
50
if self.get_direction() != gtk.TEXT_DIR_RTL:
52
self.left = ButtonPartLeft('left-clicked', part_size)
53
self.right = ButtonPartRight('right-clicked', part_size)
54
self.set_button_atk_info_ltr()
57
self.left = ButtonPartRight('left-clicked', part_size, gtk.ARROW_LEFT)
58
self.right = ButtonPartLeft('right-clicked', part_size, gtk.ARROW_RIGHT)
59
self.set_button_atk_info_rtl()
61
atk_obj = self.get_accessible()
62
atk_obj.set_name(_('History Navigation'))
63
atk_obj.set_description(_('Navigate forwards and backwards.'))
64
atk_obj.set_role(atk.ROLE_PANEL)
66
self.pack_start(self.left)
67
self.pack_start(self.separator, False)
68
self.pack_end(self.right)
70
self.separator.connect_after("style-set", self._on_style_set)
71
self.connect_after('size-allocate', self._on_size_allocate)
74
def set_button_atk_info_ltr(self):
76
atk_obj = self.left.get_accessible()
77
atk_obj.set_name(_('Back Button'))
78
atk_obj.set_description(_('Navigates back.'))
79
atk_obj.set_role(atk.ROLE_PUSH_BUTTON)
82
atk_obj = self.right.get_accessible()
83
atk_obj.set_name(_('Forward Button'))
84
atk_obj.set_description(_('Navigates forward.'))
85
atk_obj.set_role(atk.ROLE_PUSH_BUTTON)
88
def set_button_atk_info_rtl(self):
90
atk_obj = self.right.get_accessible()
91
atk_obj.set_name(_('Back Button'))
92
atk_obj.set_description(_('Navigates back.'))
93
atk_obj.set_role(atk.ROLE_PUSH_BUTTON)
96
atk_obj = self.left.get_accessible()
97
atk_obj.set_name(_('Forward Button'))
98
atk_obj.set_description(_('Navigates forward.'))
99
atk_obj.set_role(atk.ROLE_PUSH_BUTTON)
102
def set_use_hand_cursor(self, use_hand):
103
self.use_hand = use_hand
106
def _on_style_set(self, widget, oldstyle):
107
# when alloc.width == 1, this is typical of an unallocated widget,
108
# lets not break a sweat for nothing...
109
if self.allocation.width == 1:
112
old_xthickness = self.theme['xthickness']
113
self.theme = mkit.get_mkit_theme()
114
if old_xthickness > self.theme['xthickness']:
116
self.queue_draw_area(a.x, a.y,
117
a.width+self.theme['xthickness'], a.height)
122
def _on_size_allocate(self, widget, allocation):
126
def draw(self, cr, expose_area, left_alpha=1.0, right_alpha=1.0):
127
self.separator.alpha = max(left_alpha, right_alpha)
128
self.separator.queue_draw()
129
self.left.draw(cr, expose_area, left_alpha)
130
self.right.draw(cr, expose_area, right_alpha)
134
class SeparatorPart(gtk.DrawingArea):
137
gtk.DrawingArea.__init__(self)
139
self.theme = mkit.get_mkit_theme()
140
self.set_size_request(self.theme['xthickness'], -1)
142
atk_obj = self.get_accessible()
143
atk_obj.set_role(atk.ROLE_SEPARATOR)
145
self.connect("expose-event", self._on_expose)
146
self.connect("style-set", self._on_style_set)
149
def _on_expose(self, widget, event):
150
parent = self.get_parent()
151
if not parent: return
152
cr = widget.window.cairo_create()
153
cr.rectangle(event.area)
154
if self.alpha == 1.0:
155
cr.set_source_rgb(*self.theme.dark_line[self.state].floats())
157
r, g, b = self.theme.dark_line[self.state].floats()
158
cr.set_source_rgba(r, g, b, self.alpha)
164
def _on_style_set(self, widget, old_style):
165
self.theme = mkit.get_mkit_theme()
166
self.set_size_request(self.theme['xthickness'], -1)
170
class ButtonPart(gtk.EventBox):
172
def __init__(self, arrow_type, signal_name, part_size):
173
gtk.EventBox.__init__(self)
175
self.is_active = False
177
self.set_redraw_on_allocate(False)
178
self.set_visible_window(False)
180
self.set_size_request(*part_size)
181
self.shape = mkit.SHAPE_RECTANGLE
182
self.button_down = False
183
self.add(gtk.Arrow(arrow_type, gtk.SHADOW_OUT))
185
self.set_flags(gtk.CAN_FOCUS)
186
self.set_events(gtk.gdk.ENTER_NOTIFY_MASK|
187
gtk.gdk.LEAVE_NOTIFY_MASK|
188
gtk.gdk.BUTTON_PRESS_MASK|
189
gtk.gdk.BUTTON_RELEASE_MASK)
191
self.connect("enter-notify-event", self._on_enter)
192
self.connect("leave-notify-event", self._on_leave)
193
self.connect("button-press-event", self._on_press)
194
self.connect("button-release-event", self._on_release, signal_name)
195
self.connect("key-press-event", self._on_key_press)
196
self.connect("key-release-event", self._on_key_release, signal_name)
199
def set_sensitive(self, is_sensitive):
201
self.set_state(gtk.STATE_NORMAL)
202
super(ButtonPart, self).set_sensitive(is_sensitive)
205
def set_active(self, is_active):
207
self.shadow_type = gtk.SHADOW_IN
208
self.set_state(gtk.STATE_ACTIVE)
210
self.shadow_type = gtk.SHADOW_OUT
211
self.set_state(gtk.STATE_NORMAL)
214
def _on_enter(self, widget, event):
215
if self.state == gtk.STATE_INSENSITIVE: return
216
if not self.button_down:
217
self.set_state(gtk.STATE_PRELIGHT)
219
self.set_active(True)
220
if self.parent.use_hand:
221
self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND2))
224
def _on_key_press(self, widget, event):
225
# react to spacebar, enter, numpad-enter
226
if event.keyval in (32, 65293, 65421):
227
self.set_state(gtk.STATE_ACTIVE)
230
def _on_key_release(self, widget, event, signal_name):
231
# react to spacebar, enter, numpad-enter
232
if event.keyval in (32, 65293, 65421):
233
self.set_state(gtk.STATE_SELECTED)
234
self.get_parent().emit(signal_name, event)
237
def _on_leave(self, widget, event):
238
if self.state == gtk.STATE_INSENSITIVE: return
239
self.set_active(False)
240
if self.parent.use_hand:
241
self.window.set_cursor(None)
244
def _on_press(self, widget, event):
245
if self.state == gtk.STATE_INSENSITIVE: return
246
self.button_down = True
247
self.set_active(True)
250
def _on_release(self, widget, event, signal_name):
251
if self.state == gtk.STATE_INSENSITIVE: return
252
self.button_down = False
253
self.shadow_type = gtk.SHADOW_OUT
254
p = gtk.gdk.device_get_core_pointer()
255
x, y = p.get_state(widget.window)[0]
256
rr = gtk.gdk.region_rectangle(widget.allocation)
257
if rr.point_in(int(x), int(y)):
258
self.set_state(gtk.STATE_PRELIGHT)
259
self.get_parent().emit(signal_name, event)
261
self.set_state(gtk.STATE_NORMAL)
264
def do_draw(self, cr, a, expose_area, xo, wo, alpha):
265
if gtk.gdk.region_rectangle(expose_area).rect_in(a) == gtk.gdk.OVERLAP_RECTANGLE_OUT:
267
if not self.parent.get_property('visible'): return
273
self.parent.theme.paint_bg(cr,
276
a.width+wo, a.height,
280
self.style.paint_focus(self.window,
282
(a.x+4, a.y+4, a.width-8, a.height-8),
286
a.width-8, a.height-8)
291
class ButtonPartLeft(ButtonPart):
293
def __init__(self, sig_name, part_size, arrow_type=gtk.ARROW_LEFT):
294
ButtonPart.__init__(self,
298
self.connect("expose-event", self._on_expose)
301
def _on_expose(self, widget, event):
302
cr = self.window.cairo_create()
303
self.draw(cr, event.area, alpha=1.0)
306
def draw(self, cr, expose_area, alpha):
307
r = int(self.parent.theme['curvature'])
317
class ButtonPartRight(ButtonPart):
319
def __init__(self, sig_name, part_size, arrow_type=gtk.ARROW_RIGHT):
320
ButtonPart.__init__(self,
324
self.connect("expose-event", self._on_expose)
327
def _on_expose(self, widget, event):
328
cr = self.window.cairo_create()
329
self.draw(cr, event.area, alpha=1.0)
332
def draw(self, cr, expose_area, alpha):
333
r = int(self.parent.theme['curvature'])
342
if __name__ == "__main__":
344
win.set_default_size(300,100)
345
backforward = BackForwardButton()