~ubuntu-branches/ubuntu/karmic/sonata/karmic

« back to all changes in this revision

Viewing changes to sonata/breadcrumbs.py

  • Committer: Bazaar Package Importer
  • Author(s): Michal Čihař
  • Date: 2009-05-11 09:10:00 UTC
  • mfrom: (4.2.3 squeeze)
  • Revision ID: james.westby@ubuntu.com-20090511091000-jzspxudws5ngxb5e
Tags: 1.6.2-1
New upstream version (Closes: #528045).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
 
 
3
import gtk
 
4
 
 
5
class CrumbButton(gtk.ToggleButton):
 
6
        """A ToggleButton tailored for use as a breadcrumb."""
 
7
        def __init__(self, image, label):
 
8
                gtk.ToggleButton.__init__(self)
 
9
 
 
10
                self.set_properties(can_focus=False, relief=gtk.RELIEF_NONE)
 
11
 
 
12
                # adapt gtk.Button internal layout code:
 
13
                hbox = gtk.HBox(spacing=2)
 
14
                align = gtk.Alignment(xalign=0.5, yalign=0.5)
 
15
                hbox.pack_start(image, False, False)
 
16
                hbox.pack_start(label, True, True)
 
17
                align.add(hbox)
 
18
 
 
19
                self.add(align)
 
20
 
 
21
class CrumbBox(gtk.Box):
 
22
        """A box layout similar to gtk.HBox, but specifically for breadcrumbs.
 
23
 
 
24
        * Crumbs in the middle are replaced with an ellipsis if necessary.
 
25
        * The root, parent, and current element are always kept visible.
 
26
        * The root and parent are put in a condensed form if necessary.
 
27
        * The current element is truncated if necessary.
 
28
        """
 
29
        __gtype_name__ = 'CrumbBox'
 
30
        def __init__(self, *args, **kwargs):
 
31
                gtk.Box.__init__(self, *args, **kwargs)
 
32
 
 
33
                # FIXME i can't get an internal child ellipsis to render...
 
34
#               gtk.widget_push_composite_child()
 
35
#               self.ellipsis = gtk.Label("...")
 
36
#               self.ellipsis.props.visible = True
 
37
#               gtk.widget_pop_composite_child()
 
38
#               self.ellipsis.set_parent(self)
 
39
 
 
40
        def do_size_request(self, requisition):
 
41
                """This gets called to determine the size we request"""
 
42
#               ellipsis_req = self.ellipsis.size_request()
 
43
                reqs = [w.size_request() for w in self]
 
44
 
 
45
                # This would request "natural" size:
 
46
#               pad = 0 if not reqs else (len(reqs)-1)*self.props.spacing
 
47
#               requisition.width  = sum(    [r[0] for r in reqs]) + pad
 
48
#               requisition.height = max([0]+[r[1] for r in reqs])
 
49
#               return
 
50
 
 
51
                # Request "minimum" size:
 
52
 
 
53
                height = max([0]+[r[1] for r in reqs])
 
54
 
 
55
                if len(reqs) == 0: # empty
 
56
                        width = 0 
 
57
                elif len(reqs) < 3: # current crumb
 
58
                        width = height
 
59
                elif len(reqs) == 3: # parent and current
 
60
                        width = height + height + self.props.spacing
 
61
                elif len(reqs) == 4: # root, parent and current
 
62
                        width = height + height + height + 2*self.props.spacing
 
63
                elif len(reqs) > 4: # root, ellipsis, parent, current
 
64
                        pad = 3*self.props.spacing
 
65
                        width = height + reqs[1][0] + height + height + pad
 
66
 
 
67
                requisition.width = width
 
68
                requisition.height = height
 
69
 
 
70
        def _req_sum(self, reqs):
 
71
                pad = 0 if not reqs else (len(reqs)-1)*self.props.spacing
 
72
                return pad+sum([req[0] for req in reqs])
 
73
 
 
74
        def _condense(self, req, w):
 
75
# FIXME show and hide cause a fight in an infinite loop
 
76
#               try:
 
77
#                       w.get_child().get_child().get_children()[1].hide()
 
78
#               except (AttributeError, IndexError):
 
79
#                       pass
 
80
                wr, hr = req
 
81
                return (hr, hr) # XXX simplistic: set square size for now
 
82
 
 
83
        def _uncondense(self, w):
 
84
#               try:
 
85
#                       w.get_child().get_child().get_children()[1].show()
 
86
#               except (AttributeError, IndexError):
 
87
                        pass
 
88
 
 
89
        def _truncate(self, req, amount):
 
90
                wr, hr = req
 
91
                return (wr-amount, hr) # XXX this can be less than hr, even <0
 
92
 
 
93
        def do_size_allocate(self, allocation):
 
94
                """This gets called to layout our child widgets"""
 
95
                x0, y0 = allocation.x, allocation.y
 
96
                w0, h0 = allocation.width, allocation.height
 
97
 
 
98
                crumbs = self.get_children()
 
99
 
 
100
                if len(crumbs) < 2:
 
101
                        return
 
102
 
 
103
                # FIXME:
 
104
                self.ellipsis = crumbs.pop(1)
 
105
                hidden = [self.ellipsis]
 
106
 
 
107
                # Undo any earlier condensing
 
108
                if len(crumbs) > 0:
 
109
                        self._uncondense(crumbs[0])
 
110
                if len(crumbs) > 1:
 
111
                        self._uncondense(crumbs[-2])
 
112
 
 
113
                reqs = [w.get_child_requisition() for w in crumbs]
 
114
 
 
115
                # If necessary, condense the root crumb
 
116
                if self._req_sum(reqs) > w0 and len(crumbs) > 2:
 
117
                        reqs[0] = self._condense(reqs[0], crumbs[0])
 
118
                        
 
119
                # If necessary, replace an increasing amount of the
 
120
                # crumbs after the root with the ellipsis
 
121
                while self._req_sum(reqs) > w0:
 
122
                        if self.ellipsis in hidden and len(crumbs) > 3:
 
123
                                hidden = [crumbs.pop(1)]
 
124
                                reqs.pop(1)
 
125
                                crumbs.insert(1, self.ellipsis)
 
126
                                req = self.ellipsis.get_child_requisition()
 
127
                                reqs.insert(1, req)
 
128
                        elif self.ellipsis in crumbs and len(crumbs) > 4:
 
129
                                hidden.append(crumbs.pop(2))
 
130
                                reqs.pop(2)
 
131
                        else:
 
132
                                break # don't remove the parent
 
133
 
 
134
                # If necessary, condense the parent crumb
 
135
                if self._req_sum(reqs) > w0 and len(crumbs) > 1:
 
136
                        reqs[-2] = self._condense(reqs[-2], crumbs[-2])
 
137
 
 
138
                # If necessary, truncate the current crumb
 
139
                if self._req_sum(reqs) > w0:
 
140
                        reqs[-1] = self._truncate(reqs[-1], 
 
141
                                                  self._req_sum(reqs)-w0)
 
142
                        # Now we are at minimum width
 
143
 
 
144
                x = 0
 
145
                for w, req in zip(crumbs, reqs):
 
146
                        wr, _hr = req
 
147
                        w.size_allocate(gtk.gdk.Rectangle(x0+x, y0, wr, h0))
 
148
                        w.show()
 
149
                        x += wr + self.props.spacing
 
150
 
 
151
                for w in hidden:
 
152
                        w.size_allocate(gtk.gdk.Rectangle(-1, -1, 0, 0))
 
153
                        w.hide()
 
154
 
 
155
#               def do_forall(self, internal, callback, data):
 
156
#                       callback(self.ellipsis, data)
 
157
#                       for w in self.get_children():
 
158
#                               callback(w, data)
 
159
 
 
160
# this file can be run as a simple test program:
 
161
if __name__  == '__main__':
 
162
        w = gtk.Window()
 
163
        crumbs = CrumbBox(spacing=2)
 
164
 
 
165
        items = [
 
166
                (gtk.STOCK_HARDDISK, "Filesystem"),
 
167
                (None, None), # XXX for ellipsis
 
168
                (gtk.STOCK_OPEN, "home"),
 
169
                (gtk.STOCK_OPEN, "user"),
 
170
                (gtk.STOCK_OPEN, "music"),
 
171
                (gtk.STOCK_OPEN, "genre"),
 
172
                (gtk.STOCK_OPEN, "artist"),
 
173
                (gtk.STOCK_OPEN, "album"),
 
174
                ]
 
175
        for stock, text in items:
 
176
                if stock:
 
177
                        image = gtk.image_new_from_stock(stock, 
 
178
                                                         gtk.ICON_SIZE_MENU)
 
179
                        crumbs.pack_start(CrumbButton(image, gtk.Label(text)))
 
180
                else:
 
181
                        crumbs.pack_start(gtk.Label("..."))
 
182
 
 
183
        w.add(crumbs)
 
184
        w.connect('hide', gtk.main_quit)
 
185
        w.show_all()
 
186
 
 
187
        gtk.main()