1 # -*- coding: UTF-8 -*-
2 # vim: expandtab sw=4 ts=4 sts=4:
7 __author__
= 'Michal Čihař'
8 __email__
= 'michal@cihar.com'
10 Copyright © 2003 - 2010 Michal Čihař
12 This program is free software; you can redistribute it and/or modify it
13 under the terms of the GNU General Public License version 2 as published by
14 the Free Software Foundation.
16 This program is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
21 You should have received a copy of the GNU General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
32 from Wammu
.Locales
import StrConv
34 import wx
.lib
.mixins
.listctrl
137 class FilterException(Exception):
139 Exception which occurs when there is something wrong in filtering
144 class Browser(wx
.ListCtrl
, wx
.lib
.mixins
.listctrl
.ListCtrlAutoWidthMixin
):
146 Generic class for browsing values.
148 def __init__(self
, parent
, win
, cfg
):
149 wx
.ListCtrl
.__init
__(self
,
152 style
= wx
.LC_REPORT |
167 self
.popup_index
= -1
169 color
= wx
.SystemSettings
.GetColour(wx
.SYS_COLOUR_3DLIGHT
)
171 self
.attr1
= wx
.ListItemAttr()
173 self
.attr2
= wx
.ListItemAttr()
174 self
.attr2
.SetBackgroundColour(color
)
176 self
.attr3
= wx
.ListItemAttr()
177 fnt
= self
.attr3
.GetFont()
178 fnt
.SetStyle(wx
.FONTSTYLE_ITALIC
)
179 self
.attr3
.SetFont(fnt
)
181 self
.attr4
= wx
.ListItemAttr()
182 self
.attr4
.SetBackgroundColour(color
)
183 self
.attr4
.SetFont(fnt
)
185 image_list
= wx
.ImageList(16, 16)
186 down_bitmap
= wx
.Bitmap(Wammu
.Paths
.MiscPath('downarrow'))
187 up_bitmap
= wx
.Bitmap(Wammu
.Paths
.MiscPath('uparrow'))
188 self
.downarrow
= image_list
.Add(down_bitmap
)
189 self
.uparrow
= image_list
.Add(up_bitmap
)
190 self
.AssignImageList(image_list
, wx
.IMAGE_LIST_SMALL
)
192 wx
.lib
.mixins
.listctrl
.ListCtrlAutoWidthMixin
.__init
__(self
)
194 # Create IDs for popup menu
195 self
.popup_id_send
= wx
.NewId()
196 self
.popup_id_edit
= wx
.NewId()
197 self
.popup_id_message
= wx
.NewId()
198 self
.popup_id_call
= wx
.NewId()
199 self
.popup_id_delete
= wx
.NewId()
200 self
.popup_id_delete_selection
= wx
.NewId()
201 self
.popup_id_duplicate
= wx
.NewId()
202 self
.popup_id_reply
= wx
.NewId()
203 self
.popup_id_backup_one
= wx
.NewId()
204 self
.popup_id_backup_selection
= wx
.NewId()
205 self
.popup_id_backup_all
= wx
.NewId()
210 def BindEvents(self
):
212 Bind various event handlers to events we need.
214 self
.Bind(wx
.EVT_LIST_ITEM_SELECTED
,
217 self
.Bind(wx
.EVT_LIST_ITEM_ACTIVATED
,
218 self
.OnItemActivated
,
220 self
.Bind(wx
.EVT_LIST_KEY_DOWN
,
223 self
.Bind(wx
.EVT_LIST_COL_CLICK
,
226 self
.Bind(wx
.EVT_LIST_ITEM_RIGHT_CLICK
,
229 self
.Bind(wx
.EVT_MENU
,
231 id = self
.popup_id_send
)
232 self
.Bind(wx
.EVT_MENU
,
234 id = self
.popup_id_edit
)
235 self
.Bind(wx
.EVT_MENU
,
237 id = self
.popup_id_message
)
238 self
.Bind(wx
.EVT_MENU
,
240 id = self
.popup_id_call
)
241 self
.Bind(wx
.EVT_MENU
,
243 id = self
.popup_id_delete
)
244 self
.Bind(wx
.EVT_MENU
,
245 self
.OnPopupDeleteSel
,
246 id = self
.popup_id_delete_selection
)
247 self
.Bind(wx
.EVT_MENU
,
248 self
.OnPopupDuplicate
,
249 id = self
.popup_id_duplicate
)
250 self
.Bind(wx
.EVT_MENU
,
252 id = self
.popup_id_reply
)
253 self
.Bind(wx
.EVT_MENU
,
254 self
.OnPopupBackupOne
,
255 id = self
.popup_id_backup_one
)
256 self
.Bind(wx
.EVT_MENU
,
257 self
.OnPopupBackupSel
,
258 id = self
.popup_id_backup_selection
)
259 self
.Bind(wx
.EVT_MENU
,
260 self
.OnPopupBackupAll
,
261 id = self
.popup_id_backup_all
)
263 def ShowHeaders(self
):
265 Updates which headers and keys should be show and displays them.
267 self
.columns
= COLUMN_INFO
[self
.type][0]
268 self
.keys
= COLUMN_INFO
[self
.type][1]
270 cnt
= len(self
.columns
)
273 self
.InsertColumn(i
, self
.columns
[i
])
275 # resize columns to fit content
277 # FIXME: this should be acquired better!
282 size
= self
.GetTextExtent(StrConv(self
.columns
[i
]))[0]
283 # 16 bellow is for sort arrrow
284 if (size
+ 16 > maxval
[i
]):
285 maxval
[i
] = size
+ 16
287 for current
in self
.values
:
289 size
= self
.GetTextExtent(StrConv(current
[self
.keys
[i
]]))
290 if (size
[0] > maxval
[i
]):
292 for i
in range(cnt
- 1):
293 self
.SetColumnWidth(i
, maxval
[i
] + spc
)
294 self
.resizeLastColumn(maxval
[cnt
- 1] + spc
)
296 def Filter(self
, text
, filter_type
):
298 Filters content of browser by various expressions (type of expression
299 is defined by filter_type).
302 self
.values
= self
.allvalues
308 match
= re
.compile('.*%s.*' % re
.escape(text
), re
.I
)
309 elif filter_type
== 1:
311 match
= re
.compile(text
, re
.I
)
313 raise FilterException('Failed to compile regexp')
314 elif filter_type
== 2:
315 text
= text
.replace('*', '__SEARCH_ALL__')
316 text
= text
.replace('?', '__SEARCH_ONE__')
317 text
= re
.escape(text
)
318 text
= text
.replace('\\_\\_SEARCH\\_ALL\\_\\_', '.*')
319 text
= text
.replace('\\_\\_SEARCH\\_ONE\\_\\_', '.')
320 match
= re
.compile('.*%s.*' % text
, re
.I
)
322 raise Exception('Unsupported filter type %s!' % filter_type
)
323 self
.values
= [item
for item
in self
.allvalues
324 if Wammu
.Utils
.MatchesText(item
, match
, num
)]
325 self
.SetItemCount(len(self
.values
))
329 def Sorter(self
, item1
, item2
):
331 Compare function for internal list of values.
333 if self
.sortkey
== 'Location' and type(item1
[self
.sortkey
]) == type(''):
334 return self
.sortorder
* cmp(
335 int(item1
[self
.sortkey
].split(',')[0]),
336 int(item2
[self
.sortkey
].split(', ')[0]))
337 elif item1
[self
.sortkey
] == None:
338 return -self
.sortorder
339 elif item2
[self
.sortkey
] == None:
340 return self
.sortorder
341 return self
.sortorder
* cmp(item1
[self
.sortkey
], item2
[self
.sortkey
])
343 def ShowLocation(self
, loc
, second
= None):
345 Shows row which is stored on defined location. Search can be extended
346 by specifiyng second tupe of search attribute and value.
348 result
= Wammu
.Utils
.SearchLocation(self
.values
, loc
, second
)
352 def ShowRow(self
, index
):
356 if (self
.GetItemCount() > index
358 and self
.GetCountPerPage() > 0):
361 while self
.GetFirstSelected() != -1:
362 self
.SetItemState(self
.GetFirstSelected(), 0, wx
.LIST_STATE_SELECTED
)
364 self
.SetItemState(index
,
365 wx
.LIST_STATE_FOCUSED | wx
.LIST_STATE_SELECTED
,
366 wx
.LIST_STATE_FOCUSED | wx
.LIST_STATE_SELECTED
)
367 self
.EnsureVisible(index
)
369 evt
= Wammu
.Events
.ShowEvent(data
= None)
370 wx
.PostEvent(self
.win
, evt
)
372 def Change(self
, newtype
, values
):
374 Change type of browser component.
377 self
.cfg
.Write('/BrowserSortKey/%s' % self
.type,
379 self
.cfg
.WriteInt('/BrowserSortOrder/%s' % self
.type,
383 self
.allvalues
= values
387 self
.SetItemCount(len(values
))
391 readsort
= self
.cfg
.Read('/BrowserSortKey/%s' % self
.type)
392 readorder
= self
.cfg
.ReadInt('/BrowserSortOrder/%s' % self
.type)
393 for i
in range(len(self
.keys
)):
394 if self
.keys
[i
] == readsort
:
396 self
.sortkey
= readsort
402 def Resort(self
, col
):
404 Changes sort order of listing.
408 item
= self
.values
[self
.itemno
]
411 # find keys and order
412 nextsort
= self
.keys
[col
]
413 if nextsort
== self
.sortkey
:
414 self
.sortorder
= -1 * self
.sortorder
417 self
.sortkey
= nextsort
420 self
.values
.sort(self
.Sorter
)
423 for i
in range(self
.GetColumnCount()):
424 self
.ClearColumnImage(i
)
425 if self
.sortorder
== 1:
426 image
= self
.downarrow
429 self
.SetColumnImage(col
, image
)
433 self
.ShowRow(self
.values
.index(item
))
435 def RefreshView(self
):
437 Refresh displayed items.
439 if self
.GetItemCount() != 0:
440 top
= self
.GetTopItem()
443 count
= self
.GetCountPerPage()
444 totalcount
= self
.GetItemCount()
447 last
= min(totalcount
- 1, top
+ count
)
448 self
.RefreshItems(top
, last
)
451 def OnKey(self
, evt
):
453 Key handler which catches delete key for deletion of current item and
454 R/r key for message reply.
456 if evt
.GetKeyCode() == wx
.WXK_DELETE
:
457 self
.DoSelectedDelete()
458 elif evt
.GetKeyCode() in [114, 82]:
461 def DoSelectedDelete(self
):
463 Delete selected message.
466 index
= self
.GetFirstSelected()
468 lst
.append(self
.values
[index
])
469 index
= self
.GetNextSelected(index
)
472 def DoDelete(self
, lst
):
474 Send delete event to parent.
476 evt
= Wammu
.Events
.DeleteEvent(lst
= lst
)
477 wx
.PostEvent(self
.win
, evt
)
479 def DoBackup(self
, lst
):
481 Send backup event to parent.
483 evt
= Wammu
.Events
.BackupEvent(lst
= lst
)
484 wx
.PostEvent(self
.win
, evt
)
488 Send reply event to parent.
490 evt
= Wammu
.Events
.ReplyEvent(data
= self
.values
[self
.GetFocusedItem()])
491 wx
.PostEvent(self
.win
, evt
)
493 def OnRightClick(self
, evt
):
495 Handle right click - show context menu with correct options for
496 current type of listing.
498 if self
.type == 'info':
500 self
.popup_index
= evt
.m_itemIndex
506 if self
.popup_index
!= -1 and self
.type == 'message':
507 if self
.values
[evt
.m_itemIndex
]['State'] == 'Sent':
508 menu
.Append(self
.popup_id_send
, _('Resend'))
509 if self
.values
[evt
.m_itemIndex
]['State'] == 'UnSent':
510 menu
.Append(self
.popup_id_send
, _('Send'))
511 if (self
.values
[evt
.m_itemIndex
]['State'] == 'Read'
512 or self
.values
[evt
.m_itemIndex
]['State'] == 'UnRead'):
513 menu
.Append(self
.popup_id_reply
, _('Reply'))
514 if self
.values
[evt
.m_itemIndex
]['Number'] != '':
515 menu
.Append(self
.popup_id_call
, _('Call'))
516 menu
.AppendSeparator()
518 if self
.popup_index
!= -1 and self
.type in ['contact', 'call']:
519 menu
.Append(self
.popup_id_message
, _('Send message'))
520 menu
.Append(self
.popup_id_call
, _('Call'))
521 menu
.AppendSeparator()
523 if self
.popup_index
!= -1 and not self
.type in ['call', 'message']:
524 menu
.Append(self
.popup_id_edit
, _('Edit'))
525 if self
.popup_index
!= -1 and not self
.type in ['call']:
526 menu
.Append(self
.popup_id_duplicate
, _('Duplicate'))
527 menu
.AppendSeparator()
529 if self
.popup_index
!= -1:
530 menu
.Append(self
.popup_id_delete
, _('Delete current'))
531 menu
.Append(self
.popup_id_delete_selection
, _('Delete selected'))
533 menu
.AppendSeparator()
534 if self
.popup_index
!= -1:
535 menu
.Append(self
.popup_id_backup_one
, _('Backup current'))
536 menu
.Append(self
.popup_id_backup_selection
, _('Backup selected'))
537 menu
.Append(self
.popup_id_backup_all
, _('Backup all'))
539 # Popup the menu. If an item is selected then its handler
540 # will be called before PopupMenu returns.
541 self
.PopupMenu(menu
, evt
.GetPoint())
543 def OnPopupDuplicate(self
, event
):
544 evt
= Wammu
.Events
.DuplicateEvent(data
= self
.values
[self
.popup_index
])
545 wx
.PostEvent(self
.win
, evt
)
547 def OnPopupReply(self
, event
):
548 evt
= Wammu
.Events
.ReplyEvent(data
= self
.values
[self
.popup_index
])
549 wx
.PostEvent(self
.win
, evt
)
551 def OnPopupSend(self
, event
):
552 evt
= Wammu
.Events
.SendEvent(data
= self
.values
[self
.popup_index
])
553 wx
.PostEvent(self
.win
, evt
)
555 def OnPopupCall(self
, event
):
556 evt
= Wammu
.Events
.CallEvent(data
= self
.values
[self
.popup_index
])
557 wx
.PostEvent(self
.win
, evt
)
559 def OnPopupMessage(self
, event
):
560 evt
= Wammu
.Events
.MessageEvent(data
= self
.values
[self
.popup_index
])
561 wx
.PostEvent(self
.win
, evt
)
563 def OnPopupEdit(self
, event
):
564 evt
= Wammu
.Events
.EditEvent(data
= self
.values
[self
.popup_index
])
565 wx
.PostEvent(self
.win
, evt
)
567 def OnPopupDelete(self
, event
):
568 self
.DoDelete([self
.values
[self
.popup_index
]])
570 def OnPopupDeleteSel(self
, event
):
571 self
.DoSelectedDelete()
573 def OnPopupBackupOne(self
, event
):
574 self
.DoBackup([self
.values
[self
.popup_index
]])
576 def OnPopupBackupSel(self
, event
):
578 index
= self
.GetFirstSelected()
580 item_list
.append(self
.values
[index
])
581 index
= self
.GetNextSelected(index
)
582 self
.DoBackup(item_list
)
584 def OnPopupBackupAll(self
, event
):
585 self
.DoBackup(self
.values
)
587 def OnColClick(self
, evt
):
588 self
.Resort(evt
.GetColumn())
590 def OnItemSelected(self
, event
):
591 self
.itemno
= event
.m_itemIndex
592 evt
= Wammu
.Events
.ShowEvent(data
= self
.values
[event
.m_itemIndex
])
593 wx
.PostEvent(self
.win
, evt
)
595 def OnItemActivated(self
, event
):
596 evt
= Wammu
.Events
.EditEvent(data
= self
.values
[event
.m_itemIndex
])
597 wx
.PostEvent(self
.win
, evt
)
599 def getColumnText(self
, index
, col
):
600 item
= self
.GetItem(index
, col
)
601 return item
.GetText()
603 def OnGetItemText(self
, item
, col
):
607 if item
>= len(self
.values
):
609 return StrConv(self
.values
[item
][self
.keys
[col
]])
611 def OnGetItemAttr(self
, item
):
613 Get item attributes - highlight synced items, make odd and even rows
616 if self
.values
[item
]['Synced']: