root/trunk/listview.py

Revision 138 (by manatlan, 11/30/06 14:04:41)

- no mode 'shebang line' at the top of all py files, except jbrout.py
;-)

# -*- coding: utf-8 -*-

##
##    Copyright (C) 2005 manatlan manatlan[at]gmail(dot)com
##
## 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; version 2 only.
##
## 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.
##

# example table.py

import pygtk
pygtk.require('2.0')
import gtk

import gobject

from common import cd2d, cd2rd, format_file_size_for_display

class ListView(gtk.EventBox):
    # static
    __nbx=1
    __nby=1

    sort_mode = 0
    reversed_sort = False

    choix = [_("Tags"),_("Comment"),_("Album"),_("Date"),_("Name")]
    __choix = 0
    __focus=0
    #~ __gsignals__ = {'refreshItem' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_INT,) ) }

    #~ # userevent "refreshItem"
    #~ def do_refreshItem(self,a): pass

    #~ def refreshI(self,e,num):
        #~ for item in self.__items:
            #~ if num == self.__yoff + self.__items.index(item):
                #~ item.refresh()

    def refreshItem(self,num):
        #~ print ">>>>>>>>> recept a refresh for",num
        for item in self.__items:
            if num == self.__yoff + self.__items.index(item):
                item.refresh()
    #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    class Item(gtk.EventBox):
        text_size=20
        def __init__(self,pere): # num de la case
            gtk.EventBox.__init__(self)
            self.pere = pere

            #tbl = gtk.Table(2, 1, False)
            #tbl.set_size_request(160,190)
            vbox = gtk.VBox()

            vbox.set_size_request(self.pere.size, self.pere.size+ListView.Item.text_size)
            self.__img = gtk.Image()
            self.__lbl = gtk.Label("-")
            #~ self.__lbl.set_single_line_mode(True)
            self.__lbl.set_line_wrap(True)
            #tbl.attach(self.__img,0,1,0,1,gtk.EXPAND,gtk.EXPAND)
            #tbl.attach(self.__lbl,0,1,1,2,gtk.SHRINK,gtk.SHRINK)
            vbox.pack_start(self.__img, True, True)
            vbox.pack_end(self.__lbl, False, False)
            self.__lbl.set_size_request(self.pere.size,-1)
            self.__lbl.set_justify(gtk.JUSTIFY_CENTER)

            #~ self.__img.set_state(gtk.CAN_FOCUS)
            #~ self.__lbl.set_state(gtk.CAN_FOCUS)
            #tbl.show()
            vbox.show()

            self.set_flags(gtk.CAN_FOCUS)

            #self.add(tbl)
            self.add(vbox)
            self.connect("button_press_event", self.onClick)
            self.connect("button_release_event", self.onClickRelease)


            target = [
                    ('STRING', 0, 111),
                    #~ ('text/plain', 0, 222),
#                    ('application/x-rootwin-drop', 0, 333)
                ]
            #~ self.set_state(gtk.CAN_FOCUS)
            #~ tbl.set_state(gtk.CAN_FOCUS)
            #~ self.__img.set_state(gtk.CAN_FOCUS)
            #~ self.__lbl.set_state(gtk.CAN_FOCUS)

            if self.pere.dragNdropPossible:
                self.drag_source_set(gtk.gdk.BUTTON1_MASK | gtk.gdk.BUTTON2_MASK,
                      target, gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE)


                self.drag_dest_set(gtk.DEST_DEFAULT_ALL, target,
                    gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE)
                self.connect("drag_data_received",
                                  self.drag_data_received_data)

        def drag_data_received_data(self,widget,*args):
            item = self.pere._whichItemAmI(self)
            if item>=0:
                self.pere._onDrop( item , *args)


        def onClick(self,w,event):
            item = self.pere._whichItemAmI(self)
            if item>=0:
                self.pere.onItemClick( item , event)

        def onClickRelease(self,w,event):
            item = self.pere._whichItemAmI(self)
            if item>=0:
                self.pere.onItemClickRelease( item , event)

        def refresh(self):
            item = self.pere._whichItemAmI(self)

            self.__lbl.set_state(gtk.STATE_NORMAL) # to correct a *bug* of gtk/states

            if item>=0:
                #~ if self.pere.isSelected( item ):
                    #~ img = self.pere.getImage(item, selected = True)
                #~ else:
                    #~ img = self.pere.getImage(item)
                img = self.pere.getImage(item)
                nom = self.pere._getTitle( item )

                self.__img.set_from_pixbuf(img)
                self.__lbl.set_text( nom )
                self.__img.show()
                self.__lbl.show()

                if self.pere.isFocused( item ):
                    pass
                    #self.__lbl.set_text( "["+nom+"]" ) # to present the cursor


                if self.pere.isSelected( item ):
                    #self.__img.set_sensitive(False)
                    self.set_state(gtk.STATE_SELECTED)
                else:
                    #~ self.__img.set_sensitive(True)
                    self.set_state(gtk.STATE_NORMAL)

            else:
                self.__img.hide()
                self.__lbl.hide()
                self.set_state(gtk.STATE_NORMAL)


    #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

    def __init__(self,parent,size,dragNdropPossible=False):
        #~ gobject.GObject.__init__(self)
        gtk.EventBox.__init__(self)
        self.parentWin = parent
        self.size=size
        self.dragNdropPossible = dragNdropPossible
        #~ self.connect("refreshItem",self.refreshI)
        self.list=[]
        self.__focus=0
        self.__selected=[0]
        self.__yoff = 0
        self.tbl = gtk.Table(self.__nby, self.__nbx, False)
        self.tbl.set_col_spacings(1)
        self.tbl.set_flags(gtk.CAN_FOCUS)

        #~ self.btn_up = gtk.Button("^")
        #~ self.btn_up.connect("clicked", self.moveFocus, "page_up")
        #~ self.btn_up.show()

        #~ self.btn_down = gtk.Button("v")
        #~ self.btn_down.connect("clicked", self.moveFocus, "page_down")
        #~ self.btn_down.show()

        #~ self.btn_up.unset_flags(gtk.CAN_FOCUS)
        #~ self.btn_down.unset_flags(gtk.CAN_FOCUS)
        self.vscroll = gtk.VScrollbar()
        self.vscroll.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
        self.vscroll.connect("value_changed",self.onScroll)
        self.vscroll.connect("scroll_event", self.onScrollMouse)

        self.build()

        #~ if initChoix!=None:
            #~ self.combo = gtk.combo_box_new_text()
            #~ for i in ListView.choix:
                #~ self.combo.append_text(i)
            #~ self.combo.connect('changed', self.on_combo_change)
            #~ self.combo.set_active(initChoix)
            #~ self.combo.unset_flags(gtk.CAN_FOCUS)

            #~ self.combo.show()
        #~ else:
            #~ self.combo = None

        self.tbl.show()

        #~ ascenceur = gtk.Table(1,2,False)
        #~ ascenceur.attach(self.btn_up,0,1,0,1,xoptions=gtk.FILL)
        #~ ascenceur.attach(self.btn_down,0,1,1,2,xoptions=gtk.FILL)
        #~ ascenceur.show()

        paned = gtk.Table(2,1)
        paned.attach(self.tbl,0,1,0,1,yoptions=gtk.FILL)#,xoptions=gtk.EXPAND)
        paned.attach(self.vscroll,1,2,0,1,xoptions=gtk.FILL)
        paned.show()

        #~ total = gtk.Table(1,2,False)
        #~ total.attach(paned,0,1,0,1)
        #~ if self.combo:
            #~ total.attach(self.combo,0,1,1,2,yoptions=gtk.FILL)
        #~ total.show()

        self.add(paned)

        #~ self.resize=False
        #~ ascenceur.resize=False
        #~ paned.resize=False

        self.connect("key_press_event", self.onKeyDown)
        self.connect("scroll_event", self.onScrollMouse)
        self.hid = self.connect_after("size_request", self.onSizeRequest)
        self.hid = self.connect_after("size_allocate", self.onSizeAllocate)

        #~ self.set_state(gtk.CAN_FOCUS)
        #~ tbl.set_state(gtk.CAN_FOCUS)
        self.width,self.height = 100,100
        self.refresh()

    def changeSize(self,newSize):
        self.size=newSize
        self.init(self.list)

    def onSizeAllocate(self,e,alloc):
        self.width,self.height = alloc.width,alloc.height

    def onSizeRequest(self,a,req):
        req.width=560   # min width
        req.height=400  # min width
        return False

    def sort_list(self, rearrange_selection = True):        # new
        if not self.list:
            return
        if rearrange_selection:
            nodes = [self.list[x] for x in self.__selected]
        if self.sort_mode == 0: # name
            self.list.sort(lambda x,y: cmp(x.name, y.name))
        elif self.sort_mode == 1: # size
            self.list.sort(lambda x,y: cmp(x.filesize, y.filesize))
        elif self.sort_mode == 2: # time
            self.list.sort(lambda x,y: cmp(x.date, y.date))

        if self.reversed_sort:
            self.list.reverse()

        if rearrange_selection:
            self.__selected = [self.list.index(x) for x in nodes]

    def init(self, list):
        self.resizer(self.width,self.height)
        self.list = list
        self.__focus=0
        self.sort_list(rearrange_selection = False)
        self.__selected=[0]
        self.__yoff = 0

        if len(list)<=self.__nbx*self.__nby:
            self.vscroll.hide()
        else:
            self.vscroll.show()
            self.vscroll.set_range(0,(len(list)-(self.__nbx*(self.__nby -1))-1)/self.__nbx)
            self.vscroll.set_increments(1,self.__nby)
        self.refresh()

    def onScroll(self,a):
        self.__yoff=int(self.vscroll.get_value() * self.__nbx)
        self.refresh()

    def build(self):
        self.__items = []

        for y in range(0,self.__nby):
            for x in range(0,self.__nbx):
                item = ListView.Item( self )
                self.tbl.attach(item, x, x+1, y, y+1 ) #yoptions=gtk.FILL)
                item.show()

                self.__items.append(item)

    def resizer(self,w,h):
        self.__nbx = int(w/self.size)
        self.__nby = int(h/(self.size+ListView.Item.text_size))
        self.rebuild()

    def rebuild(self):
        self.tbl.resize(self.__nby,self.__nbx)
        for i in self.__items:
            i.destroy()
            del(i)
        del(self.__items)
        self.build()
        self.refresh()

    #~ def on_combo_change(self,combo):
        #~ self.refresh()

    def onKeyDown(self, widget, event):
        keyname = gtk.gdk.keyval_name(event.keyval).lower()

        if keyname in ('page_up', 'page_down', 'down', 'up', 'right', 'left', 'home', 'end'):
            self.moveFocus(None, keyname)
            return True
        elif keyname == 'a' and event.get_state() & gtk.gdk.CONTROL_MASK: # ctrl-a
            self.__selected = range(len(self.list))
            self.refresh()
            return True

        return False

    def onScrollMouse(self, a,b):
        #~ print int(b.direction)
        if int(b.direction)==1:
            if self.__yoff+len(self.__items)< len(self.list):
                self.__yoff+=self.__nbx
        else:
            self.__yoff-=self.__nbx
            if self.__yoff<0:
                self.__yoff=0
        self.refresh()

    def onItemClickRelease(self,i,event):
        """ button_release_event"""
        self.mouseClick(i,event)

    def onItemClick(self,i,event):
        """ button_press_event"""
        if event.button == 1:
            if event.type == gtk.gdk.BUTTON_PRESS:
                if event.get_state() & gtk.gdk.CONTROL_MASK:
                    if i in self.__selected:
                        self.__selected.remove(i)
                    else:
                        self.__selected.append(i)
                elif event.get_state() & gtk.gdk.SHIFT_MASK:
                    l = self.__focus
                    for j in range(min(l,i),1+max(l,i)):
                        if j not in self.__selected:
                            self.__selected.append(j)
                else:
                    self.__selected=[i]
            elif event.type == gtk.gdk._2BUTTON_PRESS:
                self.__selected=[i]

            self.__focus = i
            self.refresh()
        elif event.button ==2:
            self.__focus=i
            if i in self.__selected:
                self.__selected.remove(i)
            else:
                self.__selected.append(i)
            self.refresh()

        elif event.button ==3:
            if i not in self.__selected:
                self.__selected = [i]
                self.__focus = i
                self.refresh()

        self.setFocus()
        self.mouseClick(i,event)

    def refresh(self):
        self.vscroll.set_value(self.__yoff/self.__nbx)

        for item in self.__items:
            item.refresh()

        self.onRefresh(self.__focus,len(self.__selected))

    def moveFocus(self, widget, data=None):
        if data == 'page_up':
            self.__focus -=self.__nbx*self.__nby
        elif data == 'page_down':
            self.__focus +=self.__nbx*self.__nby
        elif data == "home":
            self.__focus = 0
        elif data == "end":
            self.__focus = len(self.list) -1
        elif data == "right":
            self.__focus +=1
        elif data == "left":
            self.__focus -=1
        elif data == "down":
            self.__focus +=self.__nbx
        elif data == "up":
            self.__focus -=self.__nbx

        if self.__focus >= len(self.list):
            self.__focus = len(self.list)-1
        if self.__focus < 0:
            self.__focus = 0

        self.__selected = [self.__focus]

        idx = self.__focus
        nbc = self.__nbx * self.__nby

        #~ if idx>=(self.__yoff+nbc ) or idx<self.__yoff:
            #~ self.__yoff=(idx/nbc) * nbc

        if idx>=(self.__yoff+nbc ):
            self.__yoff+=self.__nbx * ( 1 + ((idx - (self.__yoff+nbc) ) / self.__nbx))

        if idx<self.__yoff:
            self.__yoff-=self.__nbx * ( 1 + ((self.__yoff - idx - 1) / self.__nbx))

        self.refresh()
        #self.setFocus()

    def remove(self,nitem):
        ret=self.list.remove(nitem)
        self.refresh()
        return ret

    def _whichItemAmI(self, idx):
        item = self.__yoff + self.__items.index(idx)
        if item in range(0,len(self.list)):
            return item

    def isFocused(self, item):
        return item == self.__focus
    def isSelected(self, item):
        return item in self.__selected
    def setFocus(self):
        self.tbl.grab_focus()

    def getSelected(self):
        return [ p for k,p in enumerate(self.list) if k in self.__selected ]

    def select(self,idx):
        self.__focus=idx
        self.moveFocus(self,"")

    def setAff(self,i):
        assert i in range(1,len(self.choix)+1)
        self.__choix = i - 1
        self.refresh()

    def setSort(self, i):   #new
        self.sort_mode = i
        self.sort_list()
        self.refresh()

    def setReversedSort(self, reversed):    #new
        self.reversed_sort = reversed
        self.sort_list()
        self.refresh()

    def _getTitle(self,item):
        return self.getTitle(item,self.__choix)

    def _onDrop(self,item,*args):
        if item not in self.__selected:
            self.focus=item
            self.__selected=[item]
            self.refresh()
        return self.onDrop(item,*args)

    # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # to override !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def getTitle(self, item, choix):
        return ""

    def getImage(self, item):
        return None

    def onRefresh(self, focusedItem, nbSelected):
        return None

    # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # to extend !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def mouseClick(self,item,button,x,y):
        return False
    def onDrop(self,item,*args):
        return False

#~ gobject.type_register(ListView)

class Selecteur(ListView):
    def getTitle(self, item):
        return self.list[item]

    def getImage(self, item):
        return None

    def mouseClick(self,i,event):
        if int(event.button)==3:
            entries =[
                (gtk.STOCK_ADD, self.on_add_clicked,1),
                (gtk.STOCK_REMOVE, self.on_delete_clicked,1),
                ("Edit", self.on_edit_clicked,1),
            ]

            menu = gtk.Menu()
            for stock_id,callback,sensitivity in entries:
                item = gtk.ImageMenuItem(stock_id)
                if callback:
                    item.connect("activate",callback)
                item.set_sensitive(sensitivity)
                item.show()
                menu.append(item)
            menu.popup(None,None,None,event.button,0)

    def on_add_clicked(self,b):pass
    def on_delete_clicked(self,b):pass
    def on_edit_clicked(self,b):pass

class Table:

    # This callback quits the program
    def delete_event(self, widget, event, data=None):
        gtk.main_quit()
        return False

    def __init__(self):
        # Create a new window
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)

        # Set the window title
        self.window.set_title("Table")

        # Set a handler for delete_event that immediately
        # exits GTK.
        self.window.connect("delete_event", self.delete_event)

        # Sets the border width of the window.
        self.window.set_border_width(5)

        table = Selecteur( [ "item %d"%i for i in range(57)] )
        self.window.add(table)
        table.show()


        self.window.show()

def main():
    gtk.main()
    return 0

if __name__ == "__main__":
    Table()
    main()

Note: See TracBrowser for help on using the browser.