~emesene-team/emesene/master

« back to all changes in this revision

Viewing changes to emesene/gui/gtkui/IconView.py

  • Committer: Riccardo (C10uD)
  • Date: 2012-07-01 16:54:34 UTC
  • Revision ID: git-v1:17ed298a3acb830f76aa2703351993cff749ed35
import2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
'''module to define a class to present a list of thumbnails'''
 
2
# -*- coding: utf-8 -*-
 
3
 
 
4
#    This file is part of emesene.
 
5
#
 
6
#    emesene 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.
 
10
#
 
11
#    emesene 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.
 
15
#
 
16
#    You should have received a copy of the GNU General Public License
 
17
#    along with emesene; if not, write to the Free Software
 
18
#    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
19
 
 
20
import os
 
21
import gtk
 
22
import gobject
 
23
 
 
24
import thread
 
25
import time
 
26
import gc
 
27
 
 
28
from gui.gtkui import check_gtk3
 
29
from gui.base import MarkupParser
 
30
 
 
31
# Class that holds the iconview from the avatar chooser dialog
 
32
class IconView(gtk.HBox):
 
33
    ''' class representing a listview in icon mode
 
34
        (using gtk.IconView + gtk.ListStore)        '''
 
35
    TYPE_SYSTEM_PICS, TYPE_CONTACTS_PICS, TYPE_SELF_PICS = range(3)
 
36
    def __init__(self, label, path_list, on_remove_cb, on_accept_cb,
 
37
                 iconv_type, on_drag_data_accepted):
 
38
        gtk.HBox.__init__(self)
 
39
        self.set_spacing(4)
 
40
 
 
41
        self.on_remove_cb = on_remove_cb
 
42
        self.on_accept_cb = on_accept_cb
 
43
        self.on_drag_data_accepted = on_drag_data_accepted
 
44
        self.iconv_type = iconv_type
 
45
 
 
46
        self.model = gtk.ListStore(gtk.gdk.Pixbuf, str)
 
47
        self.iconview = gtk.IconView(self.model)
 
48
 
 
49
        if not check_gtk3():
 
50
            target = ('text/uri-list', 0, 0)
 
51
        else:
 
52
            #FIXME: this is not working in gtk3
 
53
            target = gtk.TargetEntry.new('text/uri-list', 0, 0)
 
54
 
 
55
        self.iconview.enable_model_drag_dest([target],
 
56
                                    gtk.gdk.ACTION_DEFAULT | gtk.gdk.ACTION_COPY)
 
57
        self.iconview.connect("drag-data-received", self._drag_data_received)
 
58
        self.iconview.set_pixbuf_column(0)
 
59
        self.iconview.connect("item-activated", self._on_icon_activated)
 
60
        self.iconview.connect("button_press_event", self.pop_up)
 
61
 
 
62
        self.label = gtk.Label(label)
 
63
 
 
64
        self.scroll = gtk.ScrolledWindow()
 
65
        self.scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
 
66
        self.scroll.set_shadow_type(gtk.SHADOW_IN)
 
67
        self.scroll.add(self.iconview)
 
68
        self.pack_start(self.scroll, True, True)
 
69
 
 
70
        self.stop = False
 
71
        # Start a new thread to fill the iconview with images from path_list
 
72
        thread.start_new_thread(self.fill, (path_list,))
 
73
 
 
74
    def stop_and_clear(self):
 
75
        ''' stop the threads and clean the model '''
 
76
        self.stop = True
 
77
        self.model.clear()
 
78
 
 
79
    def fill(self, path_list):
 
80
        '''fill the IconView with avatars from the list of pictures'''
 
81
        for search_path in path_list:
 
82
            if os.path.exists(search_path):
 
83
                for path in os.listdir(search_path):
 
84
                    name = os.path.splitext(path)[0]
 
85
                    if self.stop:
 
86
                        return False
 
87
                    full_path = os.path.join(search_path, path)
 
88
                    if not name.endswith('_thumb') and \
 
89
                        not path.endswith(('tmp', 'xml', 'db', 'info',
 
90
                                           'last', 'avatars')) and \
 
91
                        not os.path.isdir(full_path):
 
92
                        gtk.gdk.threads_enter()
 
93
                        self.add_picture(full_path)
 
94
                        # make update the iconview
 
95
                        self.iconview.queue_draw()
 
96
                        gtk.gdk.threads_leave()
 
97
 
 
98
                        # give some time to the main thread (for GUI updates)
 
99
                        time.sleep(0.001)
 
100
        # Force Garbage Collector to tidy objects
 
101
        # see http://faq.pygtk.org/index.py?req=show&file=faq08.004.htp
 
102
        gc.collect()
 
103
 
 
104
    def pop_up(self, iconview, event):
 
105
        ''' manage the context menu (?) '''
 
106
        if event.button == 3 and self.iconv_type != IconView.TYPE_SYSTEM_PICS:
 
107
            path = self.iconview.get_path_at_pos(int(event.x), int(event.y))
 
108
            if path != None:
 
109
                self.iconview.select_path(path)
 
110
 
 
111
                if self.on_remove_cb != None:
 
112
                    remove_menu = gtk.Menu()
 
113
                    remove_item = gtk.ImageMenuItem(_('Delete'))
 
114
                    remove_item.set_image(gtk.image_new_from_stock(\
 
115
                        gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU))
 
116
                    remove_item.connect('activate', self.on_remove_cb)
 
117
                    remove_menu.append(remove_item)
 
118
                    remove_menu.popup(None, None, None, event.button,
 
119
                        event.time)
 
120
                    remove_menu.show_all()
 
121
 
 
122
    def _drag_data_received(self, treeview, context, posx, posy, \
 
123
                            selection, info, timestamp):
 
124
        '''method called on an image dragged to the view'''
 
125
        urls = selection.data.split('\n')
 
126
        for url in urls:
 
127
            path = url.replace('file://', '')
 
128
            path = path.replace('\r', '')
 
129
 
 
130
            # the '\x00' value makes an error
 
131
            path = path.replace(chr(0), '')
 
132
 
 
133
            path = MarkupParser.urllib.url2pathname(path)
 
134
 
 
135
            # this seems to be an error on ntpath but we take care :S
 
136
            try:
 
137
                if os.path.exists(path):
 
138
                    self.on_drag_data_accepted(path,
 
139
                        gtk.gdk.PixbufAnimation(path).is_static_image())
 
140
            except TypeError, error:
 
141
                if self.on_drag_data_accepted is None:
 
142
                    print _("Could not add picture:\n %s") % \
 
143
                        _("Drag and drop to this IconView is not allowed.")
 
144
                else:
 
145
                    print _("Could not add picture:\n %s") % (str(error),)
 
146
 
 
147
    def add_picture(self, path):
 
148
        '''Adds an avatar into the IconView'''
 
149
        try:
 
150
            if os.path.exists(path) and os.access(path, os.R_OK)\
 
151
                    and not self.is_in_view(path):
 
152
                try:
 
153
                    animation = gtk.gdk.PixbufAnimation(path)
 
154
                    if animation.is_static_image():
 
155
                        pixbuf = gtk.gdk.pixbuf_new_from_file(path)
 
156
                    else:
 
157
                        pixbuf = animation.get_static_image().scale_simple( \
 
158
                                    64, 64, gtk.gdk.INTERP_BILINEAR)
 
159
                except gobject.GError:
 
160
                    print _('image at %s could not be loaded') % (path, )
 
161
                    print gobject.GError
 
162
                    return
 
163
 
 
164
                # On nt images are 128x128 (48x48 on xp)
 
165
                # On kde, images are 64x64
 
166
                if (self.iconv_type == IconView.TYPE_SYSTEM_PICS or \
 
167
                 self.iconv_type == IconView.TYPE_CONTACTS_PICS) and \
 
168
                 (pixbuf.get_width() != 96 or pixbuf.get_height() != 96):
 
169
                    pixbuf = pixbuf.scale_simple(96, 96,
 
170
                                        gtk.gdk.INTERP_BILINEAR)
 
171
 
 
172
                if self.model != None and not self.stop:
 
173
                    self.model.append([pixbuf, path])
 
174
                    # Esplicitely delete gtkpixbuf
 
175
                    del pixbuf
 
176
            else:
 
177
                print path, 'not readable'
 
178
        except gobject.GError:
 
179
            print "image at %s could not be loaded" % path
 
180
 
 
181
    def is_in_view(self, filename):
 
182
        '''return True if filename already on the iconview'''
 
183
        if os.name == 'nt':
 
184
            # nt doesn't include os.path.samefile
 
185
            return False
 
186
 
 
187
        for (pixbuf, path) in self.model:
 
188
            if os.path.samefile(filename, path):
 
189
                return True
 
190
            if self.stop:
 
191
                return False
 
192
 
 
193
        return False
 
194
 
 
195
    def _on_icon_activated(self, *args):
 
196
        '''method called when a picture is double clicked'''
 
197
        if self.on_accept_cb != None:
 
198
            self.on_accept_cb(None)
 
199
 
 
200
    def get_selected_items(self):
 
201
        ''' gets the selected pictures '''
 
202
        return self.iconview.get_selected_items()