File: AutoListCtrl.py

package info (click to toggle)
londonlaw 0.2.1-20
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 3,832 kB
  • sloc: python: 3,659; makefile: 24
file content (198 lines) | stat: -rw-r--r-- 7,748 bytes parent folder | download | duplicates (2)
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
196
197
198
#  London Law -- a networked manhunting board game
#  Copyright (C) 2003-2004, 2005 Paul Pelzl
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License, Version 2, as 
#  published by the Free Software Foundation.
#
#  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.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA




# AutoListCtrl.py
#
# This module contains a base class list control that does the following:
#     * sort by column when clicking on headers (ColumnSorterMixin)
#     * auto-expands the width of the last column to fill available space
#       (ListCtrlAutoWidthMixin)
#     * supports realtime addition and removal of items
#
# This base class will be used in both the game room browser and the
# team selection window.
#


# appears after first logging in to a server.  Users may join exactly one game
# at a time, at which point a player registration window is spawned.


from twisted.python import log
import wx
from wx.lib.mixins.listctrl import ColumnSorterMixin, ListCtrlAutoWidthMixin
from londonlaw.common.config import *
import os.path



# the AutoWidthMixin simply resizes the last column of of the
# ListCtrl to take up all remaining space.
class AutoWidthListCtrl(wx.ListCtrl, ListCtrlAutoWidthMixin):
   def __init__(self, parent, ID, pos = wx.DefaultPosition,
         size = wx.DefaultSize, style = 0):
      wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
      ListCtrlAutoWidthMixin.__init__(self)


# 'headers' is a list of column headers.
# 'placeholder' should be a list of display data that is shown when
# the list is empty (same length as 'headers').
class AutoListCtrl(AutoWidthListCtrl, ColumnSorterMixin):
   def __init__(self, parent, ID, headers, placeholder = None):
      AutoWidthListCtrl.__init__(self, parent, ID, wx.DefaultPosition, wx.DefaultSize,
            wx.LC_REPORT|wx.LC_SINGLE_SEL)

      self.headers = headers

      # load in the tiny arrow images that ColumnSorterMixin draws
      # in the headers of sorted columns
      # WARNING: this segfaults if imageList is a local variable.
      # Maybe a wxPython bug... imageList falls out of scope and gets deleted prematurely?
      self.imageList = wx.ImageList(16, 16, True)
      file1 = os.path.normpath(os.path.join(MEDIAROOT, "images/smalluparrow.png"))
      file2 = os.path.normpath(os.path.join(MEDIAROOT, "images/smalldownarrow.png"))
      image = wx.Image(file1, wx.BITMAP_TYPE_ANY)
      image.SetMaskColour(255, 255, 255)
      self.smallUpArrow = self.imageList.Add(wx.BitmapFromImage(image))
      image = wx.Image(file2, wx.BITMAP_TYPE_ANY)
      image.SetMaskColour(255, 255, 255)
      self.smallDnArrow = self.imageList.Add(wx.BitmapFromImage(image))
      self.SetImageList(self.imageList, wx.IMAGE_LIST_SMALL)

      self.placeholder = placeholder
      # data from the server should be formatted as
      # ("game name", "game type", "game status", "number of players")
      if self.placeholder:
         self.itemDataMap = {0 : self.placeholder}
      else:
         self.itemDataMap = {}
      self.populateList() 

      # this must be called *after* the list has been created
      ColumnSorterMixin.__init__(self, len(self.headers)) 


   def populateList(self):
      info          = wx.ListItem()
      info.m_mask   = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT
      info.m_image  = -1
      info.m_format = wx.LIST_FORMAT_CENTRE

      for i in range(len(self.headers)):
         info.m_text = self.headers[i]
         self.InsertColumnInfo(i, info)

      items = self.itemDataMap.items()
      for i in range(len(items)):
         key, data = items[i]
         self.InsertStringItem(i, data[0])
         for j in range(1, len(self.headers)):
            self.SetStringItem(i, j, data[j])
         self.SetItemData(i, key)

      # dirty hack... wxWidgets needs a wx.LIST_AUTOSIZE_* that
      # chooses the maximum of BOTH header size and list item size
      for i in range(len(self.headers) - 1):
         self.SetColumnWidth(i, wx.LIST_AUTOSIZE) 
         itemWidth = self.GetColumnWidth(i)
         self.SetColumnWidth(i, wx.LIST_AUTOSIZE_USEHEADER)
         headerWidth = self.GetColumnWidth(i)
         if headerWidth < itemWidth:
            self.SetColumnWidth(i, wx.LIST_AUTOSIZE) 
      # size of last column is set automatically by ListCtrlAutoWidthMixin


#   def logListData(self):
#      print "itemDataMap = " + str(self.itemDataMap)
#      for i in range(len(self.itemDataMap)):
#         print "item[" + `i` + "]      = " + `self.GetItemData(i)` + ", " + \
#            self.GetItemText(i)


   # this will either add a new item or update an existing item if the first
   # piece of data matches
   def addItem(self, data):
      log.msg("called GameListWindow.addGameInfo(), data = " + str(data))
      # check for pre-existing matching data
      foundMatch = False
      for item in range(len(self.itemDataMap)):
         if data[0] == self.itemDataMap[item][0]:
            foundMatch = True
            for i in range(1, len(data)):
               self.SetStringItem(item, i, data[i])

      if not foundMatch:
         # if no matches found, add a new item to the list
         if self.placeholder:
            if self.itemDataMap[0] == self.placeholder:
               log.msg("deleting placeholder")
               self.DeleteItem(0)
               del self.itemDataMap[0]
         index = len(self.itemDataMap)
         self.itemDataMap[index] = data
         self.InsertStringItem(index, data[0])
         for i in range(1, len(self.headers)):
            self.SetStringItem(index, i, data[i])
         self.SetItemData(index, index)


   # Note: This is completely asinine.  Removing an item should be an easy
   #       process that does not impact sorting.
   # This routine will remove the item with matching data[0] value.
   def removeItemByData(self, data):
      for key in self.itemDataMap:
         if self.itemDataMap[key][0] == data[0]:
            # update the datamap 
            for i in range(key, len(self.itemDataMap)-1):
               self.itemDataMap[i] = self.itemDataMap[i+1]
            del self.itemDataMap[len(self.itemDataMap)-1]
            if len(self.itemDataMap) == 0 and self.placeholder:
               self.itemDataMap[0] = self.placeholder
               self.InsertStringItem(0, self.placeholder[0])
               for i in range(len(self.headers)):
                  self.SetStringItem(0, i, self.placeholder[i])
            break

      # delete the requested item
      for item in range(self.GetItemCount()):
         if self.GetItemText(item) == data[0]:
            self.DeleteItem(item)
            break
      # update the ItemData field associated with the remaining items,
      # to keep them in sync with the keys of the itemDataMap
      for item in range(len(self.itemDataMap)):
         for key in range(len(self.itemDataMap)):
            if self.itemDataMap[key][0] == self.GetItemText(item):
               self.SetItemData(item, key)


   # required by ColumnSorterMixin
   def GetListCtrl(self):
      return self


   # used by ColumnSorterMixin to display up and down arrows
   # on sorted column headers
   def GetSortImages(self):
      return (self.smallDnArrow, self.smallUpArrow)



# arch-tag: DO_NOT_CHANGE_805cd141-cd81-40ae-a5c6-c8f7bacdd2f3