1 __doc__ = """GNUmed list controls and widgets.
2
3 TODO:
4
5 From: Rob McMullen <rob.mcmullen@gmail.com>
6 To: wxPython-users@lists.wxwidgets.org
7 Subject: Re: [wxPython-users] ANN: ColumnSizer mixin for ListCtrl
8
9 Thanks for all the suggestions, on and off line. There's an update
10 with a new name (ColumnAutoSizeMixin) and better sizing algorithm at:
11
12 http://trac.flipturn.org/browser/trunk/peppy/lib/column_autosize.py
13
14 sorting: http://code.activestate.com/recipes/426407/
15 """
16
17 __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
18 __license__ = "GPL v2 or later"
19
20
21 import sys
22 import types
23 import logging
24 import threading
25 import time
26 import locale
27 import os
28 import io
29 import csv
30 import re as regex
31 import datetime as pydt
32
33
34 import wx
35 import wx.lib.mixins.listctrl as listmixins
36
37
38 from Gnumed.pycommon import gmTools
39 from Gnumed.pycommon import gmDispatcher
40
41
42 _log = logging.getLogger('gm.list_ui')
43
44
45
46 -def get_choices_from_list (
47 parent=None,
48 msg=None,
49 caption=None,
50 columns=None,
51 choices=None,
52 data=None,
53 selections=None,
54 edit_callback=None,
55 new_callback=None,
56 delete_callback=None,
57 refresh_callback=None,
58 single_selection=False,
59 can_return_empty=False,
60 ignore_OK_button=False,
61 left_extra_button=None,
62 middle_extra_button=None,
63 right_extra_button=None,
64 list_tooltip_callback=None):
65 """Let user select item(s) from a list.
66
67 - new_callback: ()
68 - edit_callback: (item data)
69 - delete_callback: (item data)
70 - refresh_callback: (listctrl)
71 - list_tooltip_callback: (item data)
72
73 - left/middle/right_extra_button: (label, tooltip, <callback> [, wants_list_ctrl])
74 <wants_list_ctrl> is optional
75 <callback> is called with item_data (or listctrl) as the only argument
76 if <callback> returns TRUE, the listctrl will be refreshed, if a refresh_callback is available
77
78 returns:
79 on [CANCEL]: None
80 on [OK]:
81 if any items selected:
82 if single_selection:
83 the data of the selected item
84 else:
85 list of data of selected items
86 else:
87 if can_return_empty is True AND [OK] button was pressed:
88 empty list
89 else:
90 None
91 """
92 if caption is None:
93 caption = u'GMd: ' + _('generic multi choice dialog')
94 else:
95 if not caption.startswith('GMd: '):
96 caption = 'GMd: %s' % caption
97
98 if single_selection:
99 dlg = cGenericListSelectorDlg(parent, -1, title = caption, msg = msg, style = wx.LC_SINGLE_SEL)
100 else:
101 dlg = cGenericListSelectorDlg(parent, -1, title = caption, msg = msg)
102
103 dlg.refresh_callback = refresh_callback
104 dlg.edit_callback = edit_callback
105 dlg.new_callback = new_callback
106 dlg.delete_callback = delete_callback
107 dlg.list_tooltip_callback = list_tooltip_callback
108
109 dlg.can_return_empty = can_return_empty
110 dlg.ignore_OK_button = ignore_OK_button
111 dlg.left_extra_button = left_extra_button
112 dlg.middle_extra_button = middle_extra_button
113 dlg.right_extra_button = right_extra_button
114
115 dlg.set_columns(columns = columns)
116
117 if refresh_callback is None:
118 dlg.set_string_items(items = choices)
119 dlg.set_column_widths()
120
121 if data is not None:
122 dlg.set_data(data = data)
123
124 if selections is not None:
125 dlg.set_selections(selections = selections)
126
127 btn_pressed = dlg.ShowModal()
128 sels = dlg.get_selected_item_data(only_one = single_selection)
129 dlg.Destroy()
130
131 if btn_pressed == wx.ID_OK:
132 if can_return_empty and (sels is None):
133 return []
134 return sels
135
136 return None
137
138
139 from Gnumed.wxGladeWidgets import wxgGenericListSelectorDlg
140
142 """A dialog holding a list and a few buttons to act on the items."""
143
177
178
181
182
185
186
190
191
192
203
204
207
208
211
212
213
214
216 if self._LCTRL_items.get_selected_items(only_one=True) == -1:
217 if not self.can_return_empty:
218 self._BTN_cancel.SetDefault()
219 self._BTN_ok.Enable(False)
220 self._BTN_edit.Enable(False)
221 self._BTN_delete.Enable(False)
222
223 event.Skip()
224
225
229
230
236
237
266
267
286
287
306
307
326
327
328
329
342
343
346
347
349 any_deleted = False
350 for item_data in self._LCTRL_items.get_selected_item_data(only_one = False):
351 if item_data is None:
352 continue
353 if self.__delete_callback(item_data):
354 any_deleted = True
355
356 self._LCTRL_items.SetFocus()
357
358 if any_deleted is False:
359 return
360
361 if self.__refresh_callback is None:
362 return
363
364 wx.BeginBusyCursor()
365 try:
366 self.__refresh_callback(lctrl = self._LCTRL_items)
367 self._LCTRL_items.set_column_widths()
368 finally:
369 wx.EndBusyCursor()
370
371
374
375
377 if not self.__edit_callback(self._LCTRL_items.get_selected_item_data(only_one = True)):
378 self._LCTRL_items.SetFocus()
379 return
380 if self.__refresh_callback is None:
381 self._LCTRL_items.SetFocus()
382 return
383 wx.BeginBusyCursor()
384 try:
385 self.__refresh_callback(lctrl = self._LCTRL_items)
386 self._LCTRL_items.set_column_widths()
387 self._LCTRL_items.SetFocus()
388 finally:
389 wx.EndBusyCursor()
390
391
394
395
397 if not self.__new_callback():
398 self._LCTRL_items.SetFocus()
399 return
400 if self.__refresh_callback is None:
401 self._LCTRL_items.SetFocus()
402 return
403 wx.BeginBusyCursor()
404 try:
405 self.__refresh_callback(lctrl = self._LCTRL_items)
406 self._LCTRL_items.set_column_widths()
407 self._LCTRL_items.SetFocus()
408 finally:
409 wx.EndBusyCursor()
410
411
412
413
427
428 ignore_OK_button = property(lambda x:x, _set_ignore_OK_button)
429
430
453
454 left_extra_button = property(lambda x:x, _set_left_extra_button)
455
456
479
480 middle_extra_button = property(lambda x:x, _set_middle_extra_button)
481
482
505
506 right_extra_button = property(lambda x:x, _set_right_extra_button)
507
508
510 return self.__new_callback
511
513 if callback is not None:
514 if self.__refresh_callback is None:
515 raise ValueError('refresh callback must be set before new callback can be set')
516 if not callable(callback):
517 raise ValueError('<new> callback is not a callable: %s' % callback)
518 self.__new_callback = callback
519
520 if callback is None:
521 self._BTN_new.Enable(False)
522 self._BTN_new.Hide()
523 self._LCTRL_items.new_callback = None
524 else:
525 self._BTN_new.Enable(True)
526 self._BTN_new.Show()
527 self._LCTRL_items.new_callback = self._on_insert_key_pressed_in_listctrl
528
529 new_callback = property(_get_new_callback, _set_new_callback)
530
531
533 return self.__edit_callback
534
536 if callback is not None:
537 if not callable(callback):
538 raise ValueError('<edit> callback is not a callable: %s' % callback)
539 self.__edit_callback = callback
540
541 if callback is None:
542 self._BTN_edit.Enable(False)
543 self._BTN_edit.Hide()
544 self._LCTRL_items.edit_callback = None
545 else:
546 self._BTN_edit.Enable(True)
547 self._BTN_edit.Show()
548 self._LCTRL_items.edit_callback = self._on_edit_invoked_in_listctrl
549
550 edit_callback = property(_get_edit_callback, _set_edit_callback)
551
552
554 return self.__delete_callback
555
557 if callback is not None:
558 if self.__refresh_callback is None:
559 raise ValueError('refresh callback must be set before delete callback can be set')
560 if not callable(callback):
561 raise ValueError('<delete> callback is not a callable: %s' % callback)
562 self.__delete_callback = callback
563 if callback is None:
564 self._BTN_delete.Enable(False)
565 self._BTN_delete.Hide()
566 self._LCTRL_items.delete_callback = None
567 else:
568 self._BTN_delete.Enable(True)
569 self._BTN_delete.Show()
570 self._LCTRL_items.delete_callback = self._on_delete_key_pressed_in_listctrl
571
572 delete_callback = property(_get_delete_callback, _set_delete_callback)
573
574
576 return self.__refresh_callback
577
579 wx.BeginBusyCursor()
580 try:
581 self.__refresh_callback(lctrl = self._LCTRL_items)
582 finally:
583 wx.EndBusyCursor()
584 self._LCTRL_items.set_column_widths()
585
587 if callback is not None:
588 if not callable(callback):
589 raise ValueError('<refresh> callback is not a callable: %s' % callback)
590 self.__refresh_callback = callback
591 if callback is not None:
592 wx.CallAfter(self._set_refresh_callback_helper)
593
594 refresh_callback = property(_get_refresh_callback, _set_refresh_callback)
595
596
598 return self.__select_callback
599
601 if callback is not None:
602 if not callable(callback):
603 raise ValueError('<select> callback is not a callable: %s' % callback)
604 self.__select_callback = callback
605
606 select_callback = property(_get_select_callback, _set_select_callback)
607
608
611
612 list_tooltip_callback = property(lambda x:x, _set_list_tooltip_callback)
613
614
615
617 if message is None:
618 self._LBL_message.Hide()
619 return
620 self._LBL_message.SetLabel(message)
621 self._LBL_message.Show()
622
623 message = property(lambda x:x, _set_message)
624
625
626 from Gnumed.wxGladeWidgets import wxgGenericListManagerPnl
627
629 """A panel holding a generic multi-column list and action buttions."""
630
657
658
659
660
663
664
672
673
674
675
678
679
682
683
686
687
688
689
691 event.Skip()
692 if self.__edit_callback is not None:
693 self._BTN_edit.Enable(True)
694 if self.__delete_callback is not None:
695 self._BTN_remove.Enable(True)
696 if self.__select_callback is not None:
697 item = self._LCTRL_items.get_selected_item_data(only_one = True)
698 self.__select_callback(item)
699
700
703
704
706 if not self.__delete_callback(self._LCTRL_items.get_selected_item_data(only_one = True)):
707 return
708 if self.__refresh_callback is None:
709 self._LCTRL_items.SetFocus()
710 return
711 wx.BeginBusyCursor()
712 try:
713 self.__refresh_callback(lctrl = self._LCTRL_items)
714 self._LCTRL_items.set_column_widths()
715 self._LCTRL_items.SetFocus()
716 finally:
717 wx.EndBusyCursor()
718
719
722
723
725 if not self.__edit_callback(self._LCTRL_items.get_selected_item_data(only_one = True)):
726 self._LCTRL_items.SetFocus()
727 return
728 if self.__refresh_callback is None:
729 self._LCTRL_items.SetFocus()
730 return
731 wx.BeginBusyCursor()
732 try:
733 self.__refresh_callback(lctrl = self._LCTRL_items)
734 self._LCTRL_items.set_column_widths()
735 self._LCTRL_items.SetFocus()
736 finally:
737 wx.EndBusyCursor()
738
739
742
743
745 if not self.__new_callback():
746 self._LCTRL_items.SetFocus()
747 return
748 if self.__refresh_callback is None:
749 self._LCTRL_items.SetFocus()
750 return
751 wx.BeginBusyCursor()
752 try:
753 self.__refresh_callback(lctrl = self._LCTRL_items)
754 self._LCTRL_items.set_column_widths()
755 self._LCTRL_items.SetFocus()
756 finally:
757 wx.EndBusyCursor()
758
759
760
761
763 event.Skip()
764 if self._LCTRL_items.get_selected_items(only_one = True) == -1:
765 self._BTN_edit.Enable(False)
766 self._BTN_remove.Enable(False)
767 if self.__select_callback is not None:
768 self.__select_callback(None)
769
770
772 event.Skip()
773 if self.__edit_callback is None:
774 return
775 self._on_edit_button_pressed(event)
776
777
788
789
803
804
809
810
826
827
843
844
860
861
862
863
865 return self.__new_callback
866
868 if callback is not None:
869 if self.__refresh_callback is None:
870 raise ValueError('refresh callback must be set before new callback can be set')
871 if not callable(callback):
872 raise ValueError('<new> callback is not a callable: %s' % callback)
873 self.__new_callback = callback
874
875 if callback is None:
876 self._BTN_add.Enable(False)
877 self._BTN_add.Hide()
878 self._LCTRL_items.new_callback = None
879 else:
880 self._BTN_add.Enable(True)
881 self._BTN_add.Show()
882 self._LCTRL_items.new_callback = self._on_insert_key_pressed_in_listctrl
883
884 new_callback = property(_get_new_callback, _set_new_callback)
885
886
888 return self.__edit_callback
889
891 if callback is not None:
892 if not callable(callback):
893 raise ValueError('<edit> callback is not a callable: %s' % callback)
894 self.__edit_callback = callback
895
896 if callback is None:
897 self._BTN_edit.Enable(False)
898 self._BTN_edit.Hide()
899 self._LCTRL_items.edit_callback = None
900 else:
901 self._BTN_edit.Enable(True)
902 self._BTN_edit.Show()
903 self._LCTRL_items.edit_callback = self._on_edit_invoked_in_listctrl
904
905 edit_callback = property(_get_edit_callback, _set_edit_callback)
906
907
909 return self.__delete_callback
910
912 if callback is not None:
913 if self.__refresh_callback is None:
914 raise ValueError('refresh callback must be set before delete callback can be set')
915 if not callable(callback):
916 raise ValueError('<delete> callback is not a callable: %s' % callback)
917 self.__delete_callback = callback
918 if callback is None:
919 self._BTN_remove.Enable(False)
920 self._BTN_remove.Hide()
921 self._LCTRL_items.delete_callback = None
922 else:
923 self._BTN_remove.Enable(True)
924 self._BTN_remove.Show()
925 self._LCTRL_items.delete_callback = self._on_delete_key_pressed_in_listctrl
926
927 delete_callback = property(_get_delete_callback, _set_delete_callback)
928
929
931 return self.__refresh_callback
932
934 wx.BeginBusyCursor()
935 try:
936 self.__refresh_callback(lctrl = self._LCTRL_items)
937 finally:
938 wx.EndBusyCursor()
939 self._LCTRL_items.set_column_widths()
940
942 if callback is not None:
943 if not callable(callback):
944 raise ValueError('<refresh> callback is not a callable: %s' % callback)
945 self.__refresh_callback = callback
946 if callback is not None:
947 wx.CallAfter(self._set_refresh_callback_helper)
948
949 refresh_callback = property(_get_refresh_callback, _set_refresh_callback)
950
951
953 return self.__select_callback
954
956 if callback is not None:
957 if not callable(callback):
958 raise ValueError('<select> callback is not a callable: %s' % callback)
959 self.__select_callback = callback
960
961 select_callback = property(_get_select_callback, _set_select_callback)
962
963
966
968 if msg is None:
969 self._LBL_message.Hide()
970 self._LBL_message.SetLabel('')
971 else:
972 self._LBL_message.SetLabel(msg)
973 self._LBL_message.Show()
974 self.Layout()
975
976 message = property(_get_message, _set_message)
977
978
994
995 left_extra_button = property(lambda x:x, _set_left_extra_button)
996
997
1013
1014 middle_extra_button = property(lambda x:x, _set_middle_extra_button)
1015
1016
1032
1033 right_extra_button = property(lambda x:x, _set_right_extra_button)
1034
1035
1036 from Gnumed.wxGladeWidgets import wxgItemPickerDlg
1037
1039
1041
1042 try:
1043 msg = kwargs['msg']
1044 del kwargs['msg']
1045 except KeyError:
1046 msg = None
1047
1048 try:
1049 title = kwargs['title']
1050 if not title.startswith('GMd: '):
1051 kwargs['title'] = 'GMd: %s' % title
1052 except KeyError:
1053 kwargs['title'] = 'GMd: %s' % self.__class__.__name__
1054
1055 wxgItemPickerDlg.wxgItemPickerDlg.__init__(self, *args, **kwargs)
1056
1057 if msg is None:
1058 self._LBL_msg.Hide()
1059 else:
1060 self._LBL_msg.SetLabel(msg)
1061
1062 self.ignore_dupes_on_picking = True
1063
1064 self._LCTRL_left.activate_callback = self.__pick_selected
1065 self.__extra_button_callback = None
1066
1067 self._LCTRL_left.SetFocus()
1068
1069
1070
1071
1072 - def set_columns(self, columns=None, columns_right=None):
1073 self._LCTRL_left.set_columns(columns = columns)
1074 if columns_right is None:
1075 self._LCTRL_right.set_columns(columns = columns)
1076 return
1077
1078 if len(columns_right) < len(columns):
1079 cols = columns
1080 else:
1081 cols = columns_right[:len(columns)]
1082 self._LCTRL_right.set_columns(columns = cols)
1083
1084
1092
1093
1096
1097
1098 - def set_choices(self, choices=None, data=None, reshow=True):
1102
1103
1104 - def set_picks(self, picks=None, data=None, reshow=True):
1109
1110
1113
1114
1117
1118 picks = property(get_picks, lambda x:x)
1119
1120
1136
1137 extra_button = property(lambda x:x, _set_extra_button)
1138
1139
1140
1141
1172
1173
1174
1175
1177 if self._LCTRL_right.get_selected_items(only_one = True) == -1:
1178 return
1179
1180 for item_idx in self._LCTRL_right.get_selected_items(only_one = False):
1181 self._LCTRL_right.remove_item(item_idx)
1182
1183 if self._LCTRL_right.GetItemCount() == 0:
1184 self._BTN_right2left.Enable(False)
1185
1186
1187
1188
1189
1190
1191
1193 self._BTN_left2right.Enable(True)
1194
1198
1200 self._BTN_right2left.Enable(True)
1201
1205
1208
1211
1214
1217
1218 left_item_tooltip_callback = property(lambda x:x, _set_left_item_tooltip_callback)
1219
1222
1223 right_item_tooltip_callback = property(lambda x:x, _set_right_item_tooltip_callback)
1224
1225
1226 -class cReportListCtrl(listmixins.ListCtrlAutoWidthMixin, listmixins.ColumnSorterMixin, wx.ListCtrl):
1227
1228
1229
1230
1231
1232
1233
1234
1235 sort_order_tags = {
1236 True: ' [\u03b1\u0391 \u2192 \u03c9\u03A9]',
1237 False: ' [\u03c9\u03A9 \u2192 \u03b1\u0391]'
1238 }
1239
1241
1242 self.debug = None
1243 self.map_item_idx2data_idx = self.GetItemData
1244
1245 try:
1246 kwargs['style'] = kwargs['style'] | wx.LC_REPORT
1247 except KeyError:
1248 kwargs['style'] = wx.LC_REPORT
1249
1250 self.__is_single_selection = ((kwargs['style'] & wx.LC_SINGLE_SEL) == wx.LC_SINGLE_SEL)
1251
1252 wx.ListCtrl.__init__(self, *args, **kwargs)
1253 listmixins.ListCtrlAutoWidthMixin.__init__(self)
1254
1255
1256 self._invalidate_sorting_metadata()
1257 listmixins.ColumnSorterMixin.__init__(self, 0)
1258 self.__secondary_sort_col = None
1259
1260 self.Bind(wx.EVT_LIST_COL_CLICK, self._on_col_click, self)
1261
1262
1263 self.__widths = None
1264 self.__data = None
1265
1266
1267 self.__select_callback = None
1268 self.__deselect_callback = None
1269 self.__activate_callback = None
1270 self.__new_callback = None
1271 self.__edit_callback = None
1272 self.__delete_callback = None
1273
1274
1275 self.__extend_popup_menu_callback = None
1276 self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self._on_list_item_rightclicked)
1277
1278
1279 self.__item_tooltip_callback = None
1280 self.__tt_last_item = None
1281
1282
1283
1284
1285
1286
1287
1288
1289 self.__tt_static_part_base = ''
1290 self.__tt_static_part = self.__tt_static_part_base
1291 self.Bind(wx.EVT_MOTION, self._on_mouse_motion)
1292
1293
1294 self.__search_term = None
1295 self.__next_line_to_search = 0
1296 self.__searchable_cols = None
1297
1298
1299
1300 self.Bind(wx.EVT_CHAR, self._on_char)
1301 self.Bind(wx.EVT_LIST_KEY_DOWN, self._on_list_key_down)
1302
1303
1304
1305
1307 if self.debug is None:
1308 return False
1309 if not self.debug.endswith('_sizing'):
1310 return False
1311 _log.debug('[%s.%s]: *args = (%s), **kwargs = (%s)', self.debug, caller_name, str(args), str(kwargs))
1312 return True
1313
1314
1316 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1317 return super(cReportListCtrl, self).CacheBestSize(*args, **kwargs)
1318
1319
1320 - def Fit(self, *args, **kwargs):
1321 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1322 return super(cReportListCtrl, self).Fit(*args, **kwargs)
1323
1324
1326 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1327 return super(cReportListCtrl, self).FitInside(*args, **kwargs)
1328
1329
1333
1334
1338
1339
1343
1344
1346 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1347 return super(cReportListCtrl, self).SetClientSize(*args, **kwargs)
1348
1349
1353
1354
1358
1359
1361 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1362 return super(cReportListCtrl, self).SetMaxSize(*args, **kwargs)
1363
1364
1368
1369
1371 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1372 return super(cReportListCtrl, self).SetMinSize(*args, **kwargs)
1373
1374
1375 - def SetSize(self, *args, **kwargs):
1376 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1377 return super(cReportListCtrl, self).SetSize(*args, **kwargs)
1378
1379
1381 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1382 return super(cReportListCtrl, self).SetSizeHints(*args, **kwargs)
1383
1384
1388
1389
1391 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1392 return super(cReportListCtrl, self).SetSizeWH(*args, **kwargs)
1393
1394
1398
1399
1403
1404
1408
1409
1413
1414
1416 res = super(cReportListCtrl, self).GetAdjustedBestSize(*args, **kwargs)
1417 kwargs['sizing_function_result'] = res
1418 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1419 return res
1420
1421
1423 res = super(cReportListCtrl, self).GetEffectiveMinSize(*args, **kwargs)
1424 kwargs['sizing_function_result'] = res
1425 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1426 return res
1427
1428
1430 res = super(cReportListCtrl, self).GetBestSize(*args, **kwargs)
1431 kwargs['sizing_function_result'] = res
1432 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1433 return res
1434
1435
1437 res = super(cReportListCtrl, self).GetBestSizeTuple(*args, **kwargs)
1438 kwargs['sizing_function_result'] = res
1439 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1440 return res
1441
1442
1444 res = super(cReportListCtrl, self).GetBestVirtualSize(*args, **kwargs)
1445 kwargs['sizing_function_result'] = res
1446 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1447 return res
1448
1449
1451 res = super(cReportListCtrl, self).GetClientSize(*args, **kwargs)
1452 kwargs['sizing_function_result'] = res
1453 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1454 return res
1455
1456
1458 res = super(cReportListCtrl, self).GetClientSize(*args, **kwargs)
1459 kwargs['sizing_function_result'] = res
1460 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1461 return res
1462
1463
1465 res = super(cReportListCtrl, self).GetMaxClientSize(*args, **kwargs)
1466 kwargs['sizing_function_result'] = res
1467 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1468 return res
1469
1470
1472 res = super(cReportListCtrl, self).GetMaxHeight(*args, **kwargs)
1473 kwargs['sizing_function_result'] = res
1474 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1475 return res
1476
1477
1479 res = super(cReportListCtrl, self).GetMaxSize(*args, **kwargs)
1480 kwargs['sizing_function_result'] = res
1481 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1482 return res
1483
1484
1486 res = super(cReportListCtrl, self).GetMaxWidth(*args, **kwargs)
1487 kwargs['sizing_function_result'] = res
1488 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1489 return res
1490
1491
1493 res = super(cReportListCtrl, self).GetMinClientSize(*args, **kwargs)
1494 kwargs['sizing_function_result'] = res
1495 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1496 return res
1497
1498
1500 res = super(cReportListCtrl, self).GetMinHeight(*args, **kwargs)
1501 kwargs['sizing_function_result'] = res
1502 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1503 return res
1504
1505
1507 res = super(cReportListCtrl, self).GetMinSize(*args, **kwargs)
1508 kwargs['sizing_function_result'] = res
1509 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1510 return res
1511
1512
1514 res = super(cReportListCtrl, self).GetMinWidth(*args, **kwargs)
1515 kwargs['sizing_function_result'] = res
1516 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1517 return res
1518
1519
1520 - def GetSize(self, *args, **kwargs):
1521 res = super(cReportListCtrl, self).GetSize(*args, **kwargs)
1522 kwargs['sizing_function_result'] = res
1523 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1524 return res
1525
1526
1528 res = super(cReportListCtrl, self).GetVirtualSize(*args, **kwargs)
1529 kwargs['sizing_function_result'] = res
1530 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1531 return res
1532
1533
1535 res = super(cReportListCtrl, self).GetVirtualSizeTuple(*args, **kwargs)
1536 kwargs['sizing_function_result'] = res
1537 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1538 return res
1539
1540
1541
1542
1544 """(Re)define the columns.
1545
1546 Note that this will (have to) delete the items.
1547 """
1548 self.ClearAll()
1549 self.__tt_last_item = None
1550 if columns is None:
1551 return
1552 for idx in range(len(columns)):
1553 self.InsertColumn(idx, columns[idx])
1554
1555 listmixins.ColumnSorterMixin.__init__(self, 0)
1556 self._invalidate_sorting_metadata()
1557
1558
1560 """Set the column width policy.
1561
1562 widths = None:
1563 use previous policy if any or default policy
1564 widths != None:
1565 use this policy and remember it for later calls
1566
1567 options:
1568 wx.LIST_AUTOSIZE_USEHEADER
1569 wx.LIST_AUTOSIZE
1570
1571 This means there is no way to *revert* to the default policy :-(
1572 """
1573
1574 if widths is not None:
1575 self.__widths = widths
1576 for idx in range(len(self.__widths)):
1577 self.SetColumnWidth(idx, self.__widths[idx])
1578 return
1579
1580
1581 if self.__widths is not None:
1582 for idx in range(len(self.__widths)):
1583 self.SetColumnWidth(idx, self.__widths[idx])
1584 return
1585
1586
1587 if self.GetItemCount() == 0:
1588 width_type = wx.LIST_AUTOSIZE_USEHEADER
1589 else:
1590 width_type = wx.LIST_AUTOSIZE
1591 for idx in range(self.GetColumnCount()):
1592 self.SetColumnWidth(idx, width_type)
1593
1594
1596 if column != 'LAST':
1597 if column > self.ColumnCount:
1598 return
1599
1600 self.setResizeColumn(column)
1601
1602
1604 tries = 0
1605 while tries < max_tries:
1606 if self.debug is not None:
1607 if self.debug.endswith('_deleting'):
1608 _log.debug('[round %s] <%s>.GetItemCount() before DeleteAllItems(): %s (thread [%s])', tries, self.debug, self.GetItemCount(), threading.get_ident())
1609 if not self.DeleteAllItems():
1610 _log.error('<%s>.DeleteAllItems() failed', self.debug)
1611 item_count = self.GetItemCount()
1612 if item_count == 0:
1613 return True
1614 wx.SafeYield(None, True)
1615 _log.error('<%s>.GetItemCount() not 0 (rather: %s) after DeleteAllItems()', self.debug, item_count)
1616 time.sleep(0.3)
1617 wx.SafeYield(None, True)
1618 tries += 1
1619
1620 _log.error('<%s>: unable to delete list items after looping %s times', self.debug, max_tries)
1621 return False
1622
1623
1625 """All item members must be str()able or None."""
1626
1627 wx.BeginBusyCursor()
1628 self._invalidate_sorting_metadata()
1629
1630 if self.ItemCount == 0:
1631 topmost_visible = 0
1632 else:
1633 topmost_visible = self.GetFirstSelected()
1634 if topmost_visible == -1:
1635 topmost_visible = self.GetFocusedItem()
1636 if topmost_visible == -1:
1637 topmost_visible = self.TopItem
1638
1639 if not self.remove_items_safely(max_tries = 3):
1640 _log.error("cannot remove items (!?), continuing and hoping for the best")
1641
1642 if items is None:
1643 self.data = None
1644 wx.EndBusyCursor()
1645 return
1646
1647
1648 for item in items:
1649
1650
1651 if isinstance(item, str):
1652 self.InsertItem(index = sys.maxsize, label = item.replace('\r\n', ' [CRLF] ').replace('\n', ' [LF] '))
1653 continue
1654
1655 try:
1656
1657 col_val = str(item[0])
1658 row_num = self.InsertItem(index = sys.maxsize, label = col_val)
1659 for col_num in range(1, min(self.GetColumnCount(), len(item))):
1660 col_val = str(item[col_num]).replace('\r\n', ' [CRLF] ').replace('\n', ' [LF] ')
1661 self.SetItem(index = row_num, column = col_num, label = col_val)
1662 except (TypeError, KeyError, IndexError):
1663
1664
1665 col_val = str(item).replace('\r\n', ' [CRLF] ').replace('\n', ' [LF] ')
1666 self.InsertItem(index = sys.maxsize, label = col_val)
1667
1668 if reshow:
1669 if self.ItemCount > 0:
1670 if topmost_visible < self.ItemCount:
1671 self.EnsureVisible(topmost_visible)
1672 self.Focus(topmost_visible)
1673 else:
1674 self.EnsureVisible(self.ItemCount - 1)
1675 self.Focus(self.ItemCount - 1)
1676
1677
1678 self.data = items
1679
1680 wx.EndBusyCursor()
1681
1682
1684 if self.ItemCount == 0:
1685 return []
1686
1687 rows = []
1688 for row_idx in range(self.ItemCount):
1689 row = []
1690 for col_idx in range(self.ColumnCount):
1691 row.append(self.GetItem(row_idx, col_idx).GetText())
1692 rows.append(row)
1693 return rows
1694
1695
1696
1697
1698 string_items = property(get_string_items, set_string_items)
1699
1700
1702 if len(new_items) == 0:
1703 return
1704
1705 if new_data is None:
1706 new_data = new_items
1707
1708 existing_data = self.get_item_data()
1709 if existing_data is None:
1710 existing_data = []
1711
1712 if allow_dupes:
1713 self._LCTRL_right.set_string_items (
1714 items = self.string_items.extend(new_items),
1715 reshow = True
1716 )
1717 self.data = existing_data.extend(new_data)
1718 self.set_column_widths()
1719 return
1720
1721 existing_items = self.get_string_items()
1722 for new_item, new_data in zip(new_items, new_data):
1723 if new_item in existing_items:
1724 continue
1725 existing_items.append(new_item)
1726 existing_data.append(new_data)
1727 self.set_string_items (
1728 items = existing_items,
1729 reshow = True
1730 )
1731 self.data = existing_data
1732 self.set_column_widths()
1733
1734
1736 """<data> assumed to be a list corresponding to the item indices"""
1737 if data is not None:
1738 item_count = self.GetItemCount()
1739 if len(data) != item_count:
1740 _log.debug('<data> length (%s) must be equal to number of list items (%s) (%s, thread [%s])', len(data), item_count, self.debug, threading.get_ident())
1741 for item_idx in range(len(data)):
1742 self.SetItemData(item_idx, item_idx)
1743 self.__data = data
1744 self.__tt_last_item = None
1745
1746
1747 return
1748
1755
1756 data = property(_get_data, set_data)
1757
1758
1760
1761 if self.GetItemCount() > 0:
1762 self.Select(0, on = 0)
1763 if selections is None:
1764 return
1765 for idx in selections:
1766 self.Select(idx = idx, on = 1)
1767
1769 if self.ItemCount == 0:
1770 return []
1771 if self.__is_single_selection:
1772 return [self.GetFirstSelected()]
1773 selections = []
1774 idx = self.GetFirstSelected()
1775 while idx != -1:
1776 selections.append(idx)
1777 idx = self.GetNextSelected(idx)
1778 return selections
1779
1780 selections = property(__get_selections, set_selections)
1781
1782
1783
1784
1786 labels = []
1787 for col_idx in range(self.ColumnCount):
1788 col = self.GetColumn(col = col_idx)
1789 labels.append(col.Text)
1790 return labels
1791
1792 column_labels = property(get_column_labels, lambda x:x)
1793
1794
1796 if self.ItemCount == 0:
1797 _log.warning('no items')
1798 return None
1799 if item_idx is not None:
1800 return self.GetItem(item_idx)
1801 _log.error('get_item(None) called')
1802 return None
1803
1804
1806 if self.ItemCount == 0:
1807 return []
1808 return [ self.GetItem(item_idx) for item_idx in range(self.ItemCount) ]
1809
1810 items = property(get_items, lambda x:x)
1811
1812
1814
1815 if self.ItemCount == 0:
1816 if self.__is_single_selection or only_one:
1817 return None
1818 return []
1819
1820 if self.__is_single_selection or only_one:
1821 return self.GetFirstSelected()
1822
1823 items = []
1824 idx = self.GetFirstSelected()
1825 while idx != -1:
1826 items.append(idx)
1827 idx = self.GetNextSelected(idx)
1828
1829 return items
1830
1831 selected_items = property(get_selected_items, lambda x:x)
1832
1833
1835
1836 if self.ItemCount == 0:
1837 if self.__is_single_selection or only_one:
1838 return None
1839 return []
1840
1841 if self.__is_single_selection or only_one:
1842 return self.GetItemText(self.GetFirstSelected())
1843
1844 items = []
1845 idx = self.GetFirstSelected()
1846 while idx != -1:
1847 items.append(self.GetItemText(idx))
1848 idx = self.GetNextSelected(idx)
1849
1850 return items
1851
1852 selected_string_items = property(get_selected_string_items, lambda x:x)
1853
1854
1856
1857 if self.__data is None:
1858 return None
1859
1860 if item_idx is not None:
1861 return self.__data[self.map_item_idx2data_idx(item_idx)]
1862
1863
1864
1865
1866 return [ self.__data[self.map_item_idx2data_idx(item_idx)] for item_idx in range(self.GetItemCount()) ]
1867
1868 item_data = property(get_item_data, lambda x:x)
1869
1870
1872
1873 if self.__is_single_selection or only_one:
1874 if self.__data is None:
1875 return None
1876 idx = self.GetFirstSelected()
1877 if idx == -1:
1878 return None
1879 return self.__data[self.map_item_idx2data_idx(idx)]
1880
1881 data = []
1882 if self.__data is None:
1883 return data
1884 idx = self.GetFirstSelected()
1885 while idx != -1:
1886 data.append(self.__data[self.map_item_idx2data_idx(idx)])
1887 idx = self.GetNextSelected(idx)
1888
1889 return data
1890
1891 selected_item_data = property(get_selected_item_data, lambda x:x)
1892
1893
1895 self.Select(idx = self.GetFirstSelected(), on = 0)
1896
1897
1899
1900
1901
1902
1903
1904
1905 self.DeleteItem(item_idx)
1906 self.__tt_last_item = None
1907 self._invalidate_sorting_metadata()
1908
1909
1910
1911
1913
1914 if item_idx == -1:
1915 return
1916
1917 if self.ItemCount == 0:
1918 return
1919
1920 items = self.selected_items
1921 if self.__is_single_selection:
1922 if items is None:
1923 no_of_selected_items = 0
1924 else:
1925 no_of_selected_items = 1
1926 else:
1927 no_of_selected_items = len(items)
1928 if no_of_selected_items == 0:
1929 title = _('List Item Actions')
1930 elif no_of_selected_items == 1:
1931 title = _('List Item Actions (selected: 1 entry)')
1932 else:
1933 title = _('List Item Actions (selected: %s entries)') % no_of_selected_items
1934
1935
1936 self._context_menu = wx.Menu(title = title)
1937
1938 needs_separator = False
1939 if self.__new_callback is not None:
1940 menu_item = self._context_menu.Append(-1, _('Add (<INS>)'))
1941 self.Bind(wx.EVT_MENU, self._on_add_row, menu_item)
1942 needs_separator = True
1943 if self.__edit_callback is not None:
1944 menu_item = self._context_menu.Append(-1, _('&Edit'))
1945 self.Bind(wx.EVT_MENU, self._on_edit_row, menu_item)
1946 needs_separator = True
1947 if self.__delete_callback is not None:
1948 menu_item = self._context_menu.Append(-1, _('Delete (<DEL>)'))
1949 self.Bind(wx.EVT_MENU, self._on_delete_row, menu_item)
1950 needs_separator = True
1951 if needs_separator:
1952 self._context_menu.AppendSeparator()
1953
1954 menu_item = self._context_menu.Append(-1, _('Find (<CTRL-F>)'))
1955 self.Bind(wx.EVT_MENU, self._on_show_search_dialog, menu_item)
1956 if self.__search_term is not None:
1957 if self.__search_term.strip() != '':
1958 menu_item = self._context_menu.Append(-1, _('Find next [%s] (<CTRL-N>)') % self.__search_term)
1959 self.Bind(wx.EVT_MENU, self._on_search_match, menu_item)
1960 self._context_menu.AppendSeparator()
1961
1962 col_headers = []
1963 self._rclicked_row_idx = item_idx
1964 self._rclicked_row_data = self.get_item_data(item_idx = self._rclicked_row_idx)
1965 self._rclicked_row_cells = []
1966 self._rclicked_row_cells_w_hdr = []
1967 for col_idx in range(self.ColumnCount):
1968 cell_content = self.GetItem(self._rclicked_row_idx, col_idx).Text.strip()
1969 col_header = self.GetColumn(col_idx).Text.strip()
1970 col_headers.append(col_header)
1971 self._rclicked_row_cells.append(cell_content)
1972 self._rclicked_row_cells_w_hdr.append('%s: %s' % (col_header, cell_content))
1973
1974
1975 save_menu = wx.Menu()
1976 menu_item = save_menu.Append(-1, _('&All rows'))
1977 self.Bind(wx.EVT_MENU, self._all_rows2file, menu_item)
1978 menu_item = save_menu.Append(-1, _('All rows as &CSV'))
1979 self.Bind(wx.EVT_MENU, self._all_rows2csv, menu_item)
1980 menu_item = save_menu.Append(-1, _('&Tooltips of all rows'))
1981 self.Bind(wx.EVT_MENU, self._all_row_tooltips2file, menu_item)
1982 menu_item = save_menu.Append(-1, _('&Data of all rows'))
1983 self.Bind(wx.EVT_MENU, self._all_row_data2file, menu_item)
1984
1985 if no_of_selected_items > 1:
1986 save_menu.AppendSeparator()
1987 menu_item = save_menu.Append(-1, _('&Selected rows'))
1988 self.Bind(wx.EVT_MENU, self._selected_rows2file, menu_item)
1989 menu_item = save_menu.Append(-1, _('&Selected rows as CSV'))
1990 self.Bind(wx.EVT_MENU, self._selected_rows2csv, menu_item)
1991 menu_item = save_menu.Append(-1, _('&Tooltips of selected rows'))
1992 self.Bind(wx.EVT_MENU, self._selected_row_tooltips2file, menu_item)
1993 menu_item = save_menu.Append(-1, _('&Data of selected rows'))
1994 self.Bind(wx.EVT_MENU, self._selected_row_data2file, menu_item)
1995
1996
1997 clip_menu = wx.Menu()
1998
1999
2000 if no_of_selected_items > 1:
2001
2002 menu_item = clip_menu.Append(-1, _('Tooltips of selected rows'))
2003 self.Bind(wx.EVT_MENU, self._tooltips2clipboard, menu_item)
2004
2005 menu_item = clip_menu.Append(-1, _('Data (formatted as text) of selected rows'))
2006 self.Bind(wx.EVT_MENU, self._datas2clipboard, menu_item)
2007
2008 menu_item = clip_menu.Append(-1, _('Content (as one line each) of selected rows'))
2009 self.Bind(wx.EVT_MENU, self._rows2clipboard, menu_item)
2010 clip_menu.AppendSeparator()
2011
2012
2013
2014 menu_item = clip_menu.Append(-1, _('Tooltip of current row'))
2015 self.Bind(wx.EVT_MENU, self._tooltip2clipboard, menu_item)
2016
2017 if hasattr(self._rclicked_row_data, 'format'):
2018 menu_item = clip_menu.Append(-1, _('Data (formatted as text) of current row'))
2019 self.Bind(wx.EVT_MENU, self._data2clipboard, menu_item)
2020
2021 menu_item = clip_menu.Append(-1, _('Content (as one line) of current row'))
2022 self.Bind(wx.EVT_MENU, self._row2clipboard, menu_item)
2023
2024 menu_item = clip_menu.Append(-1, _('Content (one line per column) of current row'))
2025 self.Bind(wx.EVT_MENU, self._row_list2clipboard, menu_item)
2026
2027 clip_menu.AppendSeparator()
2028 for col_idx in range(self.ColumnCount):
2029 col_content = self._rclicked_row_cells[col_idx].strip()
2030
2031 if col_content == '':
2032 continue
2033 col_header = col_headers[col_idx]
2034 if col_header == '':
2035
2036
2037
2038
2039
2040
2041 menu_item = clip_menu.Append(-1, _('Column &%s (current row): "%s" [#%s]') % (col_idx+1, shorten_text(col_content, 35), col_idx))
2042 self.Bind(wx.EVT_MENU, self._col2clipboard, menu_item)
2043 else:
2044 col_menu = wx.Menu()
2045
2046 menu_item = col_menu.Append(-1, '"%s: %s" [#%s]' % (shorten_text(col_header, 8), shorten_text(col_content, 35), col_idx))
2047 self.Bind(wx.EVT_MENU, self._col_w_hdr2clipboard, menu_item)
2048
2049 menu_item = col_menu.Append(-1, '"%s" [#%s]' % (shorten_text(col_content, 35), col_idx))
2050 self.Bind(wx.EVT_MENU, self._col2clipboard, menu_item)
2051 clip_menu.Append(-1, _('Column &%s (current row): %s') % (col_idx+1, col_header), col_menu)
2052
2053
2054 clip_add_menu = wx.Menu()
2055
2056
2057 if no_of_selected_items > 1:
2058
2059 menu_item = clip_add_menu.Append(-1, _('Tooltips of selected rows'))
2060 self.Bind(wx.EVT_MENU, self._add_tooltips2clipboard, menu_item)
2061
2062 menu_item = clip_add_menu.Append(-1, _('Data (formatted as text) of selected rows'))
2063 self.Bind(wx.EVT_MENU, self._add_datas2clipboard, menu_item)
2064
2065 menu_item = clip_add_menu.Append(-1, _('Content (as one line each) of selected rows'))
2066 self.Bind(wx.EVT_MENU, self._add_rows2clipboard, menu_item)
2067 clip_add_menu.AppendSeparator()
2068
2069
2070
2071 menu_item = clip_add_menu.Append(-1, _('Tooltip of current row'))
2072 self.Bind(wx.EVT_MENU, self._add_tooltip2clipboard, menu_item)
2073
2074 if hasattr(self._rclicked_row_data, 'format'):
2075 menu_item = clip_add_menu.Append(-1, _('Data (formatted as text) of current row'))
2076 self.Bind(wx.EVT_MENU, self._add_data2clipboard, menu_item)
2077
2078 menu_item = clip_add_menu.Append(-1, _('Content (as one line) of current row'))
2079 self.Bind(wx.EVT_MENU, self._add_row2clipboard, menu_item)
2080
2081 menu_item = clip_add_menu.Append(-1, _('Content (one line per column) of current row'))
2082 self.Bind(wx.EVT_MENU, self._add_row_list2clipboard, menu_item)
2083
2084 clip_add_menu.AppendSeparator()
2085 for col_idx in range(self.ColumnCount):
2086 col_content = self._rclicked_row_cells[col_idx].strip()
2087
2088 if col_content == '':
2089 continue
2090 col_header = col_headers[col_idx]
2091 if col_header == '':
2092
2093 menu_item = clip_add_menu.Append(-1, _('Column &%s (current row): "%s" [#%s]') % (col_idx+1, shorten_text(col_content, 35), col_idx))
2094 self.Bind(wx.EVT_MENU, self._add_col2clipboard, menu_item)
2095 else:
2096 col_add_menu = wx.Menu()
2097
2098 menu_item = col_add_menu.Append(-1, '"%s: %s" [#%s]' % (shorten_text(col_header, 8), shorten_text(col_content, 35), col_idx))
2099 self.Bind(wx.EVT_MENU, self._add_col_w_hdr2clipboard, menu_item)
2100
2101 menu_item = col_add_menu.Append(-1, '"%s" [#%s]' % (shorten_text(col_content, 35), col_idx))
2102 self.Bind(wx.EVT_MENU, self._add_col2clipboard, menu_item)
2103 clip_add_menu.Append(-1, _('Column &%s (current row): %s') % (col_idx+1, col_header), col_add_menu)
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121 self._context_menu.Append(-1, _('&Save to file...'), save_menu)
2122 self._context_menu.Append(-1, _('&Copy to clipboard...'), clip_menu)
2123 self._context_menu.Append(-1, _('Append (&+) to clipboard...'), clip_add_menu)
2124
2125 if self.__extend_popup_menu_callback is not None:
2126 self._context_menu.AppendSeparator()
2127 self.__extend_popup_menu_callback(menu = self._context_menu)
2128
2129
2130 self.PopupMenu(self._context_menu, wx.DefaultPosition)
2131 self._context_menu.Destroy()
2132 return
2133
2134
2136 if self.__delete_callback is None:
2137 return
2138
2139 no_items = len(self.get_selected_items(only_one = False))
2140 if no_items == 0:
2141 return
2142
2143 if no_items > 1:
2144 question = _(
2145 '%s list items are selected.\n'
2146 '\n'
2147 'Really delete all %s items ?'
2148 ) % (no_items, no_items)
2149 title = _('Deleting list items')
2150 style = wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION | wx.STAY_ON_TOP
2151 dlg = wx.MessageDialog(None, question, title, style)
2152 btn_pressed = dlg.ShowModal()
2153 dlg.Destroy()
2154 if btn_pressed == wx.ID_NO:
2155 self.SetFocus()
2156 return
2157 if btn_pressed == wx.ID_CANCEL:
2158 self.SetFocus()
2159 return
2160
2161 self.__delete_callback()
2162 return
2163
2164
2166 if self.__new_callback is None:
2167 return
2168 self.__new_callback()
2169
2170
2172 if self.__edit_callback is None:
2173 return
2174 self.__edit_callback()
2175
2176
2178
2179 if self.__search_term is None:
2180
2181 default = ''
2182 else:
2183
2184 default = self.__search_term
2185 search_term = wx.GetTextFromUser (
2186 _('Enter the search term:'),
2187 _('List search'),
2188 default_value = default
2189 )
2190 if search_term.strip() == '':
2191
2192 self.__search_term = None
2193 self.__tt_static_part = self.__tt_static_part_base
2194 return
2195
2196
2197 self.__search_term = search_term
2198 self.__tt_static_part = _(
2199 'Current search term: [[%s]]\n'
2200 '\n'
2201 '%s'
2202 ) % (
2203 search_term,
2204 self.__tt_static_part_base
2205 )
2206 self.__search_match()
2207
2208
2209
2210
2212 event.Skip()
2213 if self.__activate_callback is not None:
2214 self.__activate_callback(event)
2215 return
2216
2217 self.__handle_edit()
2218
2219
2221 if self.__select_callback is not None:
2222 self.__select_callback(event)
2223 else:
2224 event.Skip()
2225
2226
2228 if self.__deselect_callback is not None:
2229 self.__deselect_callback(event)
2230 else:
2231 event.Skip()
2232
2233
2235 event.Skip()
2236 self.__show_context_menu(event.Index)
2237
2238
2240 evt.Skip()
2241
2242 if evt.KeyCode == wx.WXK_DELETE:
2243 self.__handle_delete()
2244 return
2245
2246 if evt.KeyCode == wx.WXK_INSERT:
2247 self.__handle_insert()
2248 return
2249
2250 if evt.KeyCode == wx.WXK_MENU:
2251 self.__show_context_menu(evt.Index)
2252 return
2253
2254
2256
2257 if chr(evt.GetRawKeyCode()) == 'f':
2258 if evt.GetModifiers() == wx.MOD_CMD:
2259
2260 self.__show_search_dialog()
2261 return
2262
2263 if chr(evt.GetRawKeyCode()) == 'n':
2264 if evt.GetModifiers() == wx.MOD_CMD:
2265
2266 self.__search_match()
2267 return
2268
2269 evt.Skip()
2270 return
2271
2272
2274 """Update tooltip on mouse motion.
2275
2276 for s in dir(wx):
2277 if s.startswith('LIST_HITTEST'):
2278 print s, getattr(wx, s)
2279
2280 LIST_HITTEST_ABOVE 1
2281 LIST_HITTEST_BELOW 2
2282 LIST_HITTEST_NOWHERE 4
2283 LIST_HITTEST_ONITEM 672
2284 LIST_HITTEST_ONITEMICON 32
2285 LIST_HITTEST_ONITEMLABEL 128
2286 LIST_HITTEST_ONITEMRIGHT 256
2287 LIST_HITTEST_ONITEMSTATEICON 512
2288 LIST_HITTEST_TOLEFT 1024
2289 LIST_HITTEST_TORIGHT 2048
2290 """
2291 item_idx, where_flag = self.HitTest(wx.Point(event.X, event.Y))
2292
2293
2294 if where_flag not in [
2295 wx.LIST_HITTEST_ONITEMLABEL,
2296 wx.LIST_HITTEST_ONITEMICON,
2297 wx.LIST_HITTEST_ONITEMSTATEICON,
2298 wx.LIST_HITTEST_ONITEMRIGHT,
2299 wx.LIST_HITTEST_ONITEM
2300 ]:
2301 self.__tt_last_item = None
2302 self.SetToolTip(self.__tt_static_part)
2303 return
2304
2305
2306 if self.__tt_last_item == item_idx:
2307 return
2308
2309
2310 self.__tt_last_item = item_idx
2311
2312
2313
2314 if item_idx == wx.NOT_FOUND:
2315 self.SetToolTip(self.__tt_static_part)
2316 return
2317
2318
2319 if self.__data is None:
2320 self.SetToolTip(self.__tt_static_part)
2321 return
2322
2323
2324
2325
2326
2327 if (
2328 (item_idx > (len(self.__data) - 1))
2329 or
2330 (item_idx < -1)
2331 ):
2332 self.SetToolTip(self.__tt_static_part)
2333 print("*************************************************************")
2334 print("GNUmed has detected an inconsistency with list item tooltips.")
2335 print("")
2336 print("This is not a big problem and you can keep working.")
2337 print("")
2338 print("However, please send us the following so we can fix GNUmed:")
2339 print("")
2340 print("item idx: %s" % item_idx)
2341 print('where flag: %s' % where_flag)
2342 print('data list length: %s' % len(self.__data))
2343 print("*************************************************************")
2344 return
2345
2346 dyna_tt = None
2347 if self.__item_tooltip_callback is not None:
2348 dyna_tt = self.__item_tooltip_callback(self.__data[self.map_item_idx2data_idx(item_idx)])
2349
2350 if dyna_tt is None:
2351 self.SetToolTip(self.__tt_static_part)
2352 return
2353
2354 self.SetToolTip(dyna_tt)
2355
2356
2357
2358
2360 evt.Skip()
2361 self.__handle_insert()
2362
2363
2365 evt.Skip()
2366 self.__handle_edit()
2367
2368
2370 evt.Skip()
2371 self.__handle_delete()
2372
2373
2375 evt.Skip()
2376 self.__show_search_dialog()
2377
2378
2380 evt.Skip()
2381 self.__search_match()
2382
2383
2385
2386 txt_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-all_rows-%s.txt' % pydt.datetime.now().strftime('%m%d-%H%M%S'))
2387 txt_file = io.open(txt_name, mode = 'wt', encoding = 'utf8')
2388
2389 col_labels = self.column_labels
2390 line = '%s' % ' || '.join(col_labels)
2391 txt_file.write('%s\n' % line)
2392 txt_file.write(('=' * len(line)) + '\n')
2393
2394 for item_idx in range(self.ItemCount):
2395 fields = []
2396 for col_idx in range(self.ColumnCount):
2397 fields.append(self.GetItem(item_idx, col_idx).Text)
2398 txt_file.write('%s\n' % ' || '.join(fields))
2399
2400 txt_file.close()
2401 gmDispatcher.send(signal = 'statustext', msg = _('All rows saved to [%s].') % txt_name)
2402
2403
2405
2406 csv_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-all_rows-%s.csv' % pydt.datetime.now().strftime('%m%d-%H%M%S'))
2407 csv_file = io.open(csv_name, mode = 'wb')
2408
2409 csv_writer = csv.writer(csv_file)
2410 csv_writer.writerow([ l.encode('utf-8') for l in self.column_labels ])
2411 for item_idx in range(self.ItemCount):
2412 fields = []
2413 for col_idx in range(self.ColumnCount):
2414 fields.append(self.GetItem(item_idx, col_idx).Text)
2415 csv_writer.writerow([ f.encode('utf-8') for f in fields ])
2416
2417 csv_file.close()
2418 gmDispatcher.send(signal = 'statustext', msg = _('All rows saved to [%s].') % csv_name)
2419
2420
2437
2438
2440
2441 if self.__data is None:
2442 return
2443
2444 txt_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-list_data-%s.txt' % pydt.datetime.now().strftime('%m%d-%H%M%S'))
2445 txt_file = io.open(txt_name, mode = 'wt', encoding = 'utf8')
2446
2447 for data in self.data:
2448 if hasattr(data, 'format'):
2449 txt = data.format()
2450 if type(txt) is list:
2451 txt = '\n'.join(txt)
2452 else:
2453 txt = '%s' % data
2454 txt_file.write('%s\n\n' % txt)
2455
2456 txt_file.close()
2457 gmDispatcher.send(signal = 'statustext', msg = _('All data saved to [%s].') % txt_name)
2458
2459
2461
2462 txt_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-some_rows-%s.txt' % pydt.datetime.now().strftime('%m%d-%H%M%S'))
2463 txt_file = io.open(txt_name, mode = 'wt', encoding = 'utf8')
2464
2465 col_labels = self.column_labels
2466 line = '%s' % ' || '.join(col_labels)
2467 txt_file.write('%s\n' % line)
2468 txt_file.write(('=' * len(line)) + '\n')
2469
2470 items = self.selected_items
2471 if self.__is_single_selection:
2472 items = [items]
2473
2474 for item_idx in items:
2475 fields = []
2476 for col_idx in range(self.ColumnCount):
2477 fields.append(self.GetItem(item_idx, col_idx).Text)
2478 txt_file.write('%s\n' % ' || '.join(fields))
2479
2480 txt_file.close()
2481 gmDispatcher.send(signal = 'statustext', msg = _('Selected rows saved to [%s].') % txt_name)
2482
2483
2485
2486 csv_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-some_rows-%s.csv' % pydt.datetime.now().strftime('%m%d-%H%M%S'))
2487 csv_file = io.open(csv_name, mode = 'wb')
2488
2489 csv_writer = csv.writer(csv_file)
2490 csv_writer.writerow([ l.encode('utf-8') for l in self.column_labels ])
2491
2492 items = self.selected_items
2493 if self.__is_single_selection:
2494 items = [items]
2495
2496 for item_idx in items:
2497 fields = []
2498 for col_idx in range(self.ColumnCount):
2499 fields.append(self.GetItem(item_idx, col_idx).Text)
2500 csv_writer.writerow([ f.encode('utf-8') for f in fields ])
2501
2502 csv_file.close()
2503 gmDispatcher.send(signal = 'statustext', msg = _('Selected rows saved to [%s].') % csv_name)
2504
2505
2522
2523
2525
2526 if self.__data is None:
2527 return
2528
2529 txt_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-list_data-%s.txt' % pydt.datetime.now().strftime('%m%d-%H%M%S'))
2530 txt_file = io.open(txt_name, mode = 'wt', encoding = 'utf8')
2531
2532 for data in self.selected_item_data:
2533 if hasattr(data, 'format'):
2534 txt = data.format()
2535 if type(txt) is list:
2536 txt = '\n'.join(txt)
2537 else:
2538 txt = '%s' % data
2539 txt_file.write('%s\n\n' % txt)
2540
2541 txt_file.close()
2542 gmDispatcher.send(signal = 'statustext', msg = _('Selected data saved to [%s].') % txt_name)
2543
2544
2564
2565
2591
2592
2622
2623
2656
2657
2659 if wx.TheClipboard.IsOpened():
2660 _log.debug('clipboard already open')
2661 return
2662 if not wx.TheClipboard.Open():
2663 _log.debug('cannot open clipboard')
2664 return
2665 data_obj = wx.TextDataObject()
2666 data_obj.SetText(' // '.join(self._rclicked_row_cells))
2667 wx.TheClipboard.SetData(data_obj)
2668 wx.TheClipboard.Close()
2669
2670
2672 if wx.TheClipboard.IsOpened():
2673 _log.debug('clipboard already open')
2674 return
2675 if not wx.TheClipboard.Open():
2676 _log.debug('cannot open clipboard')
2677 return
2678
2679 rows = []
2680 for item_idx in self.selected_items:
2681 rows.append(' // '.join([ self.GetItem(item_idx, col_idx).Text.strip() for col_idx in range(self.ColumnCount) ]))
2682
2683 data_obj = wx.TextDataObject()
2684 data_obj.SetText('\n\n'.join(rows))
2685 wx.TheClipboard.SetData(data_obj)
2686 wx.TheClipboard.Close()
2687
2688
2690 if wx.TheClipboard.IsOpened():
2691 _log.debug('clipboard already open')
2692 return
2693 if not wx.TheClipboard.Open():
2694 _log.debug('cannot open clipboard')
2695 return
2696 data_obj = wx.TextDataObject()
2697
2698 txt = ''
2699
2700 got_it = wx.TheClipboard.GetData(data_obj)
2701 if got_it:
2702 txt = data_obj.Text + '\n'
2703
2704
2705 txt += ' // '.join(self._rclicked_row_cells)
2706
2707
2708 data_obj.SetText(txt)
2709 wx.TheClipboard.SetData(data_obj)
2710 wx.TheClipboard.Close()
2711
2712
2714 if wx.TheClipboard.IsOpened():
2715 _log.debug('clipboard already open')
2716 return
2717 if not wx.TheClipboard.Open():
2718 _log.debug('cannot open clipboard')
2719 return
2720
2721 rows = []
2722 for item_idx in self.selected_items:
2723 rows.append(' // '.join([ self.GetItem(item_idx, col_idx).Text.strip() for col_idx in range(self.ColumnCount) ]))
2724
2725 data_obj = wx.TextDataObject()
2726
2727 txt = ''
2728
2729 got_it = wx.TheClipboard.GetData(data_obj)
2730 if got_it:
2731 txt = data_obj.Text + '\n'
2732 txt += '\n\n'.join(rows)
2733
2734 data_obj.SetText(txt)
2735 wx.TheClipboard.SetData(data_obj)
2736 wx.TheClipboard.Close()
2737
2738
2740 if wx.TheClipboard.IsOpened():
2741 _log.debug('clipboard already open')
2742 return
2743 if not wx.TheClipboard.Open():
2744 _log.debug('cannot open clipboard')
2745 return
2746 data_obj = wx.TextDataObject()
2747 data_obj.SetText('\n'.join(self._rclicked_row_cells_w_hdr))
2748 wx.TheClipboard.SetData(data_obj)
2749 wx.TheClipboard.Close()
2750
2751
2753 if wx.TheClipboard.IsOpened():
2754 _log.debug('clipboard already open')
2755 return
2756 if not wx.TheClipboard.Open():
2757 _log.debug('cannot open clipboard')
2758 return
2759 data_obj = wx.TextDataObject()
2760
2761 txt = ''
2762
2763 got_it = wx.TheClipboard.GetData(data_obj)
2764 if got_it:
2765 txt = data_obj.Text + '\n'
2766
2767
2768 txt += '\n'.join(self._rclicked_row_cells_w_hdr)
2769
2770
2771 data_obj.SetText(txt)
2772 wx.TheClipboard.SetData(data_obj)
2773 wx.TheClipboard.Close()
2774
2775
2777 if wx.TheClipboard.IsOpened():
2778 _log.debug('clipboard already open')
2779 return
2780 if not wx.TheClipboard.Open():
2781 _log.debug('cannot open clipboard')
2782 return
2783 data_obj = wx.TextDataObject()
2784 txt = self._rclicked_row_data.format()
2785 if type(txt) == type([]):
2786 txt = '\n'.join(txt)
2787 data_obj.SetText(txt)
2788 wx.TheClipboard.SetData(data_obj)
2789 wx.TheClipboard.Close()
2790
2791
2793 if wx.TheClipboard.IsOpened():
2794 _log.debug('clipboard already open')
2795 return
2796 if not wx.TheClipboard.Open():
2797 _log.debug('cannot open clipboard')
2798 return
2799
2800 data_as_txt = []
2801 for data in self.selected_item_data:
2802 if hasattr(data, 'format'):
2803 txt = data.format()
2804 if type(txt) is list:
2805 txt = '\n'.join(txt)
2806 else:
2807 txt = '%s' % data
2808 data_as_txt.append(txt)
2809
2810 data_obj = wx.TextDataObject()
2811 data_obj.SetText('\n\n'.join(data_as_txt))
2812 wx.TheClipboard.SetData(data_obj)
2813 wx.TheClipboard.Close()
2814
2815
2817 if wx.TheClipboard.IsOpened():
2818 _log.debug('clipboard already open')
2819 return
2820 if not wx.TheClipboard.Open():
2821 _log.debug('cannot open clipboard')
2822 return
2823 data_obj = wx.TextDataObject()
2824
2825 txt = ''
2826
2827 got_it = wx.TheClipboard.GetData(data_obj)
2828 if got_it:
2829 txt = data_obj.Text + '\n'
2830
2831
2832 tmp = self._rclicked_row_data.format()
2833 if type(tmp) == type([]):
2834 txt += '\n'.join(tmp)
2835 else:
2836 txt += tmp
2837
2838
2839 data_obj.SetText(txt)
2840 wx.TheClipboard.SetData(data_obj)
2841 wx.TheClipboard.Close()
2842
2843
2845 if wx.TheClipboard.IsOpened():
2846 _log.debug('clipboard already open')
2847 return
2848 if not wx.TheClipboard.Open():
2849 _log.debug('cannot open clipboard')
2850 return
2851
2852 data_as_txt = []
2853 for data in self.selected_item_data:
2854 if hasattr(data, 'format'):
2855 txt = data.format()
2856 if type(txt) is list:
2857 txt = '\n'.join(txt)
2858 else:
2859 txt = '%s' % data
2860 data_as_txt.append(txt)
2861
2862 data_obj = wx.TextDataObject()
2863 txt = ''
2864
2865 got_it = wx.TheClipboard.GetData(data_obj)
2866 if got_it:
2867 txt = data_obj.Text + '\n'
2868 txt += '\n'.join(data_as_txt)
2869
2870
2871 data_obj.SetText(txt)
2872 wx.TheClipboard.SetData(data_obj)
2873 wx.TheClipboard.Close()
2874
2875
2877 if wx.TheClipboard.IsOpened():
2878 _log.debug('clipboard already open')
2879 return
2880 if not wx.TheClipboard.Open():
2881 _log.debug('cannot open clipboard')
2882 return
2883 data_obj = wx.TextDataObject()
2884
2885
2886 col_idx = int(self._context_menu.FindItemById(evt.Id).ItemLabel.rsplit('#', 1)[1].rstrip(']'))
2887 txt = self._rclicked_row_cells[col_idx]
2888
2889 data_obj.SetText(txt)
2890 wx.TheClipboard.SetData(data_obj)
2891 wx.TheClipboard.Close()
2892
2893
2895 if wx.TheClipboard.IsOpened():
2896 _log.debug('clipboard already open')
2897 return
2898 if not wx.TheClipboard.Open():
2899 _log.debug('cannot open clipboard')
2900 return
2901 data_obj = wx.TextDataObject()
2902
2903 txt = ''
2904
2905 got_it = wx.TheClipboard.GetData(data_obj)
2906 if got_it:
2907 txt = data_obj.Text + '\n'
2908
2909
2910
2911 col_idx = int(self._context_menu.FindItemById(evt.Id).ItemLabel.rsplit('#', 1)[1].rstrip(']'))
2912 txt += self._rclicked_row_cells[col_idx]
2913
2914
2915 data_obj.SetText(txt)
2916 wx.TheClipboard.SetData(data_obj)
2917 wx.TheClipboard.Close()
2918
2919
2921 if wx.TheClipboard.IsOpened():
2922 _log.debug('clipboard already open')
2923 return
2924 if not wx.TheClipboard.Open():
2925 _log.debug('cannot open clipboard')
2926 return
2927 data_obj = wx.TextDataObject()
2928
2929
2930 col_idx = int(self._context_menu.FindItemById(evt.Id).ItemLabel.rsplit('#', 1)[1].rstrip(']'))
2931 txt = self._rclicked_row_cells_w_hdr[col_idx]
2932
2933 data_obj.SetText(txt)
2934 wx.TheClipboard.SetData(data_obj)
2935 wx.TheClipboard.Close()
2936
2937
2939 if wx.TheClipboard.IsOpened():
2940 _log.debug('clipboard already open')
2941 return
2942 if not wx.TheClipboard.Open():
2943 _log.debug('cannot open clipboard')
2944 return
2945 data_obj = wx.TextDataObject()
2946
2947 txt = ''
2948
2949 got_it = wx.TheClipboard.GetData(data_obj)
2950 if got_it:
2951 txt = data_obj.Text + '\n'
2952
2953
2954
2955 col_idx = int(self._context_menu.FindItemById(evt.Id).ItemLabel.rsplit('#', 1)[1].rstrip(']'))
2956 txt += self._rclicked_row_cells_w_hdr[col_idx]
2957
2958
2959 data_obj.SetText(txt)
2960 wx.TheClipboard.SetData(data_obj)
2961 wx.TheClipboard.Close()
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2976
2977 if self.__search_term is None:
2978
2979 return False
2980 if self.__search_term.strip() == '':
2981
2982 return False
2983 if self.__searchable_cols is None:
2984
2985 self.searchable_columns = None
2986 if len(self.__searchable_cols) == 0:
2987
2988 return False
2989
2990
2991 for row_idx in range(self.__next_line_to_search, self.ItemCount):
2992 for col_idx in range(self.ColumnCount):
2993 if col_idx not in self.__searchable_cols:
2994 continue
2995 col_val = self.GetItem(row_idx, col_idx).GetText()
2996 if regex.search(self.__search_term, col_val, regex.U | regex.I) is not None:
2997 self.Select(row_idx)
2998 self.EnsureVisible(row_idx)
2999 if row_idx == self.ItemCount - 1:
3000
3001 self.__next_line_to_search = 0
3002 else:
3003 self.__next_line_to_search = row_idx + 1
3004 return True
3005
3006 self.__next_line_to_search = 0
3007 return False
3008
3009
3011
3012
3013 if cols is None:
3014
3015 self.__searchable_cols = range(self.ColumnCount)
3016 return
3017
3018
3019 new_cols = {}
3020 for col in cols:
3021 if col < self.ColumnCount:
3022 new_cols[col] = True
3023
3024 self.__searchable_cols = new_cols.keys()
3025
3026 searchable_columns = property(lambda x:x, _set_searchable_cols)
3027
3028
3029
3030
3032 return self.__activate_callback
3033
3035 if callback is None:
3036 self.Unbind(wx.EVT_LIST_ITEM_ACTIVATED)
3037 self.__activate_callback = None
3038 return
3039 if not callable(callback):
3040 raise ValueError('<activate> callback is not a callable: %s' % callback)
3041 self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self._on_list_item_activated)
3042 self.__activate_callback = callback
3043
3044 activate_callback = property(_get_activate_callback, _set_activate_callback)
3045
3046
3048 return self.__select_callback
3049
3051 if callback is None:
3052 self.Unbind(wx.EVT_LIST_ITEM_SELECTED)
3053 self.__select_callback = None
3054 return
3055 if not callable(callback):
3056 raise ValueError('<selected> callback is not a callable: %s' % callback)
3057 self.Bind(wx.EVT_LIST_ITEM_SELECTED, self._on_list_item_selected)
3058 self.__select_callback = callback
3059
3060 select_callback = property(_get_select_callback, _set_select_callback)
3061
3062
3064 return self.__deselect_callback
3065
3067 if callback is None:
3068 self.Unbind(wx.EVT_LIST_ITEM_DESELECTED)
3069 self.__deselect_callback = None
3070 return
3071 if not callable(callback):
3072 raise ValueError('<deselected> callback is not a callable: %s' % callback)
3073 self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self._on_list_item_deselected)
3074 self.__deselect_callback = callback
3075
3076 deselect_callback = property(_get_deselect_callback, _set_deselect_callback)
3077
3078
3080 return self.__delete_callback
3081
3083 if callback is None:
3084
3085 self.__delete_callback = None
3086 return
3087 if not callable(callback):
3088 raise ValueError('<delete> callback is not a callable: %s' % callback)
3089
3090 self.__delete_callback = callback
3091
3092 delete_callback = property(_get_delete_callback, _set_delete_callback)
3093
3094
3096 return self.__new_callback
3097
3099 if callback is None:
3100 self.__new_callback = None
3101 return
3102 if not callable(callback):
3103 raise ValueError('<new> callback is not a callable: %s' % callback)
3104 self.__new_callback = callback
3105
3106 new_callback = property(_get_new_callback, _set_new_callback)
3107
3108
3110 return self.__edit_callback
3111
3113 if callback is None:
3114 self.__edit_callback = None
3115 return
3116 if not callable(callback):
3117 raise ValueError('<edit> callback is not a callable: %s' % callback)
3118 self.__edit_callback = callback
3119
3120 edit_callback = property(_get_edit_callback, _set_edit_callback)
3121
3122
3128
3129
3130
3131
3132
3133 item_tooltip_callback = property(lambda x:x, _set_item_tooltip_callback)
3134
3135
3137 if callback is not None:
3138 if not callable(callback):
3139 raise ValueError('<extend_popup_menu> callback is not a callable: %s' % callback)
3140 self.__extend_popup_menu_callback = callback
3141
3142 extend_popup_menu_callback = property(lambda x:x, _set_extend_popup_menu_callback)
3143
3144
3145
3146
3148 if self.itemDataMap is None:
3149 self._update_sorting_metadata()
3150 return self
3151
3152
3154 col_idx, is_ascending = self.GetSortState()
3155 if col_idx == -1:
3156 _log.debug('outside any column (idx: -1) clicked, ignoring')
3157 return
3158 self._remove_sorting_indicators_from_column_headers()
3159 col_state = self.GetColumn(col_idx)
3160 col_state.Text += self.sort_order_tags[is_ascending]
3161 self.SetColumn(col_idx, col_state)
3162
3163
3165 return (primary_item1_idx, primary_item2_idx)
3166
3167 if self.__secondary_sort_col is None:
3168 return (primary_item1_idx, primary_item2_idx)
3169 if self.__secondary_sort_col == primary_sort_col:
3170 return (primary_item1_idx, primary_item2_idx)
3171
3172 secondary_val1 = self.itemDataMap[primary_item1_idx][self.__secondary_sort_col]
3173 secondary_val2 = self.itemDataMap[primary_item2_idx][self.__secondary_sort_col]
3174
3175 if type(secondary_val1) == type('') and type(secondary_val2) == type(''):
3176 secondary_cmp_result = locale.strcoll(secondary_val1, secondary_val2)
3177 elif type(secondary_val1) == type('') or type(secondary_val2) == type(''):
3178 secondary_cmp_result = locale.strcoll(str(secondary_val1), str(secondary_val2))
3179 else:
3180 secondary_cmp_result = cmp(secondary_val1, secondary_val2)
3181
3182 if secondary_cmp_result == 0:
3183 return (primary_item1_idx, primary_item2_idx)
3184
3185
3186 currently_ascending = self._colSortFlag[primary_sort_col]
3187 if currently_ascending:
3188 secondary_val1, secondary_val2 = min(secondary_val1, secondary_val2), max(secondary_val1, secondary_val2)
3189 else:
3190 secondary_val1, secondary_val2 = max(secondary_val1, secondary_val2), min(secondary_val1, secondary_val2)
3191 return (secondary_val1, secondary_val2)
3192
3193
3195
3196
3197
3198 sort_col, is_ascending = self.GetSortState()
3199 data1 = self.itemDataMap[item1][sort_col]
3200 data2 = self.itemDataMap[item2][sort_col]
3201 if type(data1) == type('') and type(data2) == type(''):
3202 cmp_result = locale.strcoll(data1, data2)
3203 elif type(data1) == type('') or type(data2) == type(''):
3204 cmp_result = locale.strcoll(str(data1), str(data2))
3205 else:
3206 cmp_result = cmp(data1, data2)
3207
3208
3209 if not is_ascending:
3210 cmp_result = -1 * cmp_result
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224 return cmp_result
3225
3226
3227
3228
3229
3230
3231
3232
3233
3235 dict2sort = {}
3236 item_count = self.GetItemCount()
3237 if item_count == 0:
3238 return dict2sort
3239 col_count = self.GetColumnCount()
3240 for item_idx in range(item_count):
3241 dict2sort[item_idx] = ()
3242 if col_count == 0:
3243 continue
3244 for col_idx in range(col_count):
3245 dict2sort[item_idx] += (self.GetItem(item_idx, col_idx).GetText(), )
3246
3247
3248
3249 return dict2sort
3250
3251
3253 for col_idx in range(self.ColumnCount):
3254 col_state = self.GetColumn(col_idx)
3255 initial_header = col_state.Text
3256 if col_state.Text.endswith(self.sort_order_tags[True]):
3257 col_state.Text = col_state.Text[:-len(self.sort_order_tags[True])]
3258 if col_state.Text.endswith(self.sort_order_tags[False]):
3259 col_state.Text = col_state.Text[:-len(self.sort_order_tags[False])]
3260 if col_state.Text == initial_header:
3261 continue
3262 self.SetColumn(col_idx, col_state)
3263
3264
3269
3270
3274
3275
3281
3282
3283
3284
3285
3286
3287
3288
3289
3291 return self.__secondary_sort_col
3292
3294 if col is None:
3295 self.__secondary_sort_col = None
3296 return
3297 if col > self.GetColumnCount():
3298 raise ValueError('cannot secondary-sort on col [%s], there are only [%s] columns', col, self.GetColumnCount())
3299 self.__secondary_sort_col = col
3300
3301 secondary_sort_column = property(__get_secondary_sort_col, __set_secondary_sort_col)
3302
3303
3304 -def shorten_text(text=None, max_length=None):
3305 if len(text) <= max_length:
3306 return text
3307 return text[:max_length-1] + '\u2026'
3308
3309
3310
3311
3312 if __name__ == '__main__':
3313
3314 if len(sys.argv) < 2:
3315 sys.exit()
3316
3317 if sys.argv[1] != 'test':
3318 sys.exit()
3319
3320 sys.path.insert(0, '../../')
3321
3322 from Gnumed.pycommon import gmI18N
3323 gmI18N.activate_locale()
3324 gmI18N.install_domain()
3325
3326
3328 app = wx.PyWidgetTester(size = (400, 500))
3329 dlg = wx.MultiChoiceDialog (
3330 parent = None,
3331 message = 'test message',
3332 caption = 'test caption',
3333 choices = ['a', 'b', 'c', 'd', 'e']
3334 )
3335 dlg.ShowModal()
3336 sels = dlg.GetSelections()
3337 print("selected:")
3338 for sel in sels:
3339 print(sel)
3340
3342
3343 def edit(argument):
3344 print("editor called with:")
3345 print(argument)
3346
3347 def refresh(lctrl):
3348 choices = ['a', 'b', 'c']
3349 lctrl.set_string_items(choices)
3350
3351 app = wx.PyWidgetTester(size = (200, 50))
3352 chosen = get_choices_from_list (
3353
3354 caption = 'select health issues',
3355
3356
3357 columns = ['issue'],
3358 refresh_callback = refresh
3359
3360 )
3361 print("chosen:")
3362 print(chosen)
3363
3365 app = wx.PyWidgetTester(size = (200, 50))
3366 dlg = cItemPickerDlg(None, -1, msg = 'Pick a few items:')
3367 dlg.set_columns(['Plugins'], ['Load in workplace', 'dummy'])
3368
3369 dlg.set_string_items(['patient', 'emr', 'docs'])
3370 result = dlg.ShowModal()
3371 print(result)
3372 print(dlg.get_picks())
3373
3374
3375
3376 test_item_picker_dlg()
3377
3378
3379
3380