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 = _('generic multi choice dialog')
94
95 if single_selection:
96 dlg = cGenericListSelectorDlg(parent, -1, title = caption, msg = msg, style = wx.LC_SINGLE_SEL)
97 else:
98 dlg = cGenericListSelectorDlg(parent, -1, title = caption, msg = msg)
99
100 dlg.refresh_callback = refresh_callback
101 dlg.edit_callback = edit_callback
102 dlg.new_callback = new_callback
103 dlg.delete_callback = delete_callback
104 dlg.list_tooltip_callback = list_tooltip_callback
105
106 dlg.can_return_empty = can_return_empty
107 dlg.ignore_OK_button = ignore_OK_button
108 dlg.left_extra_button = left_extra_button
109 dlg.middle_extra_button = middle_extra_button
110 dlg.right_extra_button = right_extra_button
111
112 dlg.set_columns(columns = columns)
113
114 if refresh_callback is None:
115 dlg.set_string_items(items = choices)
116 dlg.set_column_widths()
117
118 if data is not None:
119 dlg.set_data(data = data)
120
121 if selections is not None:
122 dlg.set_selections(selections = selections)
123
124 btn_pressed = dlg.ShowModal()
125 sels = dlg.get_selected_item_data(only_one = single_selection)
126 dlg.Destroy()
127
128 if btn_pressed == wx.ID_OK:
129 if can_return_empty and (sels is None):
130 return []
131 return sels
132
133 return None
134
135
136 from Gnumed.wxGladeWidgets import wxgGenericListSelectorDlg
137
139 """A dialog holding a list and a few buttons to act on the items."""
140
166
167
170
171
174
175
179
180
181
192
193
196
197
200
201
202
203
205 if self._LCTRL_items.get_selected_items(only_one=True) == -1:
206 if not self.can_return_empty:
207 self._BTN_cancel.SetDefault()
208 self._BTN_ok.Enable(False)
209 self._BTN_edit.Enable(False)
210 self._BTN_delete.Enable(False)
211
212 event.Skip()
213
214
218
219
225
226
255
256
275
276
295
296
315
316
317
318
331
332
335
336
338 any_deleted = False
339 for item_data in self._LCTRL_items.get_selected_item_data(only_one = False):
340 if item_data is None:
341 continue
342 if self.__delete_callback(item_data):
343 any_deleted = True
344
345 self._LCTRL_items.SetFocus()
346
347 if any_deleted is False:
348 return
349
350 if self.__refresh_callback is None:
351 return
352
353 wx.BeginBusyCursor()
354 try:
355 self.__refresh_callback(lctrl = self._LCTRL_items)
356 self._LCTRL_items.set_column_widths()
357 finally:
358 wx.EndBusyCursor()
359
360
363
364
366 if not self.__edit_callback(self._LCTRL_items.get_selected_item_data(only_one = True)):
367 self._LCTRL_items.SetFocus()
368 return
369 if self.__refresh_callback is None:
370 self._LCTRL_items.SetFocus()
371 return
372 wx.BeginBusyCursor()
373 try:
374 self.__refresh_callback(lctrl = self._LCTRL_items)
375 self._LCTRL_items.set_column_widths()
376 self._LCTRL_items.SetFocus()
377 finally:
378 wx.EndBusyCursor()
379
380
383
384
386 if not self.__new_callback():
387 self._LCTRL_items.SetFocus()
388 return
389 if self.__refresh_callback is None:
390 self._LCTRL_items.SetFocus()
391 return
392 wx.BeginBusyCursor()
393 try:
394 self.__refresh_callback(lctrl = self._LCTRL_items)
395 self._LCTRL_items.set_column_widths()
396 self._LCTRL_items.SetFocus()
397 finally:
398 wx.EndBusyCursor()
399
400
401
402
416
417 ignore_OK_button = property(lambda x:x, _set_ignore_OK_button)
418
419
442
443 left_extra_button = property(lambda x:x, _set_left_extra_button)
444
445
468
469 middle_extra_button = property(lambda x:x, _set_middle_extra_button)
470
471
494
495 right_extra_button = property(lambda x:x, _set_right_extra_button)
496
497
499 return self.__new_callback
500
502 if callback is not None:
503 if self.__refresh_callback is None:
504 raise ValueError('refresh callback must be set before new callback can be set')
505 if not callable(callback):
506 raise ValueError('<new> callback is not a callable: %s' % callback)
507 self.__new_callback = callback
508
509 if callback is None:
510 self._BTN_new.Enable(False)
511 self._BTN_new.Hide()
512 self._LCTRL_items.new_callback = None
513 else:
514 self._BTN_new.Enable(True)
515 self._BTN_new.Show()
516 self._LCTRL_items.new_callback = self._on_insert_key_pressed_in_listctrl
517
518 new_callback = property(_get_new_callback, _set_new_callback)
519
520
522 return self.__edit_callback
523
525 if callback is not None:
526 if not callable(callback):
527 raise ValueError('<edit> callback is not a callable: %s' % callback)
528 self.__edit_callback = callback
529
530 if callback is None:
531 self._BTN_edit.Enable(False)
532 self._BTN_edit.Hide()
533 self._LCTRL_items.edit_callback = None
534 else:
535 self._BTN_edit.Enable(True)
536 self._BTN_edit.Show()
537 self._LCTRL_items.edit_callback = self._on_edit_invoked_in_listctrl
538
539 edit_callback = property(_get_edit_callback, _set_edit_callback)
540
541
543 return self.__delete_callback
544
546 if callback is not None:
547 if self.__refresh_callback is None:
548 raise ValueError('refresh callback must be set before delete callback can be set')
549 if not callable(callback):
550 raise ValueError('<delete> callback is not a callable: %s' % callback)
551 self.__delete_callback = callback
552 if callback is None:
553 self._BTN_delete.Enable(False)
554 self._BTN_delete.Hide()
555 self._LCTRL_items.delete_callback = None
556 else:
557 self._BTN_delete.Enable(True)
558 self._BTN_delete.Show()
559 self._LCTRL_items.delete_callback = self._on_delete_key_pressed_in_listctrl
560
561 delete_callback = property(_get_delete_callback, _set_delete_callback)
562
563
565 return self.__refresh_callback
566
568 wx.BeginBusyCursor()
569 try:
570 self.__refresh_callback(lctrl = self._LCTRL_items)
571 finally:
572 wx.EndBusyCursor()
573 self._LCTRL_items.set_column_widths()
574
576 if callback is not None:
577 if not callable(callback):
578 raise ValueError('<refresh> callback is not a callable: %s' % callback)
579 self.__refresh_callback = callback
580 if callback is not None:
581 wx.CallAfter(self._set_refresh_callback_helper)
582
583 refresh_callback = property(_get_refresh_callback, _set_refresh_callback)
584
585
587 return self.__select_callback
588
590 if callback is not None:
591 if not callable(callback):
592 raise ValueError('<select> callback is not a callable: %s' % callback)
593 self.__select_callback = callback
594
595 select_callback = property(_get_select_callback, _set_select_callback)
596
597
600
601 list_tooltip_callback = property(lambda x:x, _set_list_tooltip_callback)
602
603
604
606 if message is None:
607 self._LBL_message.Hide()
608 return
609 self._LBL_message.SetLabel(message)
610 self._LBL_message.Show()
611
612 message = property(lambda x:x, _set_message)
613
614
615 from Gnumed.wxGladeWidgets import wxgGenericListManagerPnl
616
618 """A panel holding a generic multi-column list and action buttions."""
619
646
647
648
649
652
653
661
662
663
664
667
668
671
672
675
676
677
678
680 event.Skip()
681 if self.__edit_callback is not None:
682 self._BTN_edit.Enable(True)
683 if self.__delete_callback is not None:
684 self._BTN_remove.Enable(True)
685 if self.__select_callback is not None:
686 item = self._LCTRL_items.get_selected_item_data(only_one = True)
687 self.__select_callback(item)
688
689
692
693
695 if not self.__delete_callback(self._LCTRL_items.get_selected_item_data(only_one = True)):
696 return
697 if self.__refresh_callback is None:
698 self._LCTRL_items.SetFocus()
699 return
700 wx.BeginBusyCursor()
701 try:
702 self.__refresh_callback(lctrl = self._LCTRL_items)
703 self._LCTRL_items.set_column_widths()
704 self._LCTRL_items.SetFocus()
705 finally:
706 wx.EndBusyCursor()
707
708
711
712
714 if not self.__edit_callback(self._LCTRL_items.get_selected_item_data(only_one = True)):
715 self._LCTRL_items.SetFocus()
716 return
717 if self.__refresh_callback is None:
718 self._LCTRL_items.SetFocus()
719 return
720 wx.BeginBusyCursor()
721 try:
722 self.__refresh_callback(lctrl = self._LCTRL_items)
723 self._LCTRL_items.set_column_widths()
724 self._LCTRL_items.SetFocus()
725 finally:
726 wx.EndBusyCursor()
727
728
731
732
734 if not self.__new_callback():
735 self._LCTRL_items.SetFocus()
736 return
737 if self.__refresh_callback is None:
738 self._LCTRL_items.SetFocus()
739 return
740 wx.BeginBusyCursor()
741 try:
742 self.__refresh_callback(lctrl = self._LCTRL_items)
743 self._LCTRL_items.set_column_widths()
744 self._LCTRL_items.SetFocus()
745 finally:
746 wx.EndBusyCursor()
747
748
749
750
752 event.Skip()
753 if self._LCTRL_items.get_selected_items(only_one = True) == -1:
754 self._BTN_edit.Enable(False)
755 self._BTN_remove.Enable(False)
756 if self.__select_callback is not None:
757 self.__select_callback(None)
758
759
761 event.Skip()
762 if self.__edit_callback is None:
763 return
764 self._on_edit_button_pressed(event)
765
766
777
778
792
793
798
799
815
816
832
833
849
850
851
852
854 return self.__new_callback
855
857 if callback is not None:
858 if self.__refresh_callback is None:
859 raise ValueError('refresh callback must be set before new callback can be set')
860 if not callable(callback):
861 raise ValueError('<new> callback is not a callable: %s' % callback)
862 self.__new_callback = callback
863
864 if callback is None:
865 self._BTN_add.Enable(False)
866 self._BTN_add.Hide()
867 self._LCTRL_items.new_callback = None
868 else:
869 self._BTN_add.Enable(True)
870 self._BTN_add.Show()
871 self._LCTRL_items.new_callback = self._on_insert_key_pressed_in_listctrl
872
873 new_callback = property(_get_new_callback, _set_new_callback)
874
875
877 return self.__edit_callback
878
880 if callback is not None:
881 if not callable(callback):
882 raise ValueError('<edit> callback is not a callable: %s' % callback)
883 self.__edit_callback = callback
884
885 if callback is None:
886 self._BTN_edit.Enable(False)
887 self._BTN_edit.Hide()
888 self._LCTRL_items.edit_callback = None
889 else:
890 self._BTN_edit.Enable(True)
891 self._BTN_edit.Show()
892 self._LCTRL_items.edit_callback = self._on_edit_invoked_in_listctrl
893
894 edit_callback = property(_get_edit_callback, _set_edit_callback)
895
896
898 return self.__delete_callback
899
901 if callback is not None:
902 if self.__refresh_callback is None:
903 raise ValueError('refresh callback must be set before delete callback can be set')
904 if not callable(callback):
905 raise ValueError('<delete> callback is not a callable: %s' % callback)
906 self.__delete_callback = callback
907 if callback is None:
908 self._BTN_remove.Enable(False)
909 self._BTN_remove.Hide()
910 self._LCTRL_items.delete_callback = None
911 else:
912 self._BTN_remove.Enable(True)
913 self._BTN_remove.Show()
914 self._LCTRL_items.delete_callback = self._on_delete_key_pressed_in_listctrl
915
916 delete_callback = property(_get_delete_callback, _set_delete_callback)
917
918
920 return self.__refresh_callback
921
923 wx.BeginBusyCursor()
924 try:
925 self.__refresh_callback(lctrl = self._LCTRL_items)
926 finally:
927 wx.EndBusyCursor()
928 self._LCTRL_items.set_column_widths()
929
931 if callback is not None:
932 if not callable(callback):
933 raise ValueError('<refresh> callback is not a callable: %s' % callback)
934 self.__refresh_callback = callback
935 if callback is not None:
936 wx.CallAfter(self._set_refresh_callback_helper)
937
938 refresh_callback = property(_get_refresh_callback, _set_refresh_callback)
939
940
942 return self.__select_callback
943
945 if callback is not None:
946 if not callable(callback):
947 raise ValueError('<select> callback is not a callable: %s' % callback)
948 self.__select_callback = callback
949
950 select_callback = property(_get_select_callback, _set_select_callback)
951
952
955
957 if msg is None:
958 self._LBL_message.Hide()
959 self._LBL_message.SetLabel('')
960 else:
961 self._LBL_message.SetLabel(msg)
962 self._LBL_message.Show()
963 self.Layout()
964
965 message = property(_get_message, _set_message)
966
967
983
984 left_extra_button = property(lambda x:x, _set_left_extra_button)
985
986
1002
1003 middle_extra_button = property(lambda x:x, _set_middle_extra_button)
1004
1005
1021
1022 right_extra_button = property(lambda x:x, _set_right_extra_button)
1023
1024
1025 from Gnumed.wxGladeWidgets import wxgItemPickerDlg
1026
1028
1030
1031 try:
1032 msg = kwargs['msg']
1033 del kwargs['msg']
1034 except KeyError:
1035 msg = None
1036
1037 wxgItemPickerDlg.wxgItemPickerDlg.__init__(self, *args, **kwargs)
1038
1039 if msg is None:
1040 self._LBL_msg.Hide()
1041 else:
1042 self._LBL_msg.SetLabel(msg)
1043
1044 self.ignore_dupes_on_picking = True
1045
1046 self._LCTRL_left.activate_callback = self.__pick_selected
1047 self.__extra_button_callback = None
1048
1049 self._LCTRL_left.SetFocus()
1050
1051
1052
1053
1054 - def set_columns(self, columns=None, columns_right=None):
1055 self._LCTRL_left.set_columns(columns = columns)
1056 if columns_right is None:
1057 self._LCTRL_right.set_columns(columns = columns)
1058 else:
1059 if len(columns_right) < len(columns):
1060 cols = columns
1061 else:
1062 cols = columns_right[:len(columns)]
1063 self._LCTRL_right.set_columns(columns = cols)
1064
1065
1073
1074
1077
1078
1079 - def set_choices(self, choices=None, data=None, reshow=True):
1083
1084
1085 - def set_picks(self, picks=None, data=None, reshow=True):
1090
1091
1094
1097
1098 picks = property(get_picks, lambda x:x)
1099
1115
1116 extra_button = property(lambda x:x, _set_extra_button)
1117
1118
1119
1150
1151
1152
1153
1155 if self._LCTRL_right.get_selected_items(only_one = True) == -1:
1156 return
1157
1158 for item_idx in self._LCTRL_right.get_selected_items(only_one = False):
1159 self._LCTRL_right.remove_item(item_idx)
1160
1161 if self._LCTRL_right.GetItemCount() == 0:
1162 self._BTN_right2left.Enable(False)
1163
1164
1165
1166
1167
1168
1169
1171 self._BTN_left2right.Enable(True)
1172
1176
1178 self._BTN_right2left.Enable(True)
1179
1183
1186
1189
1192
1195
1196 left_item_tooltip_callback = property(lambda x:x, _set_left_item_tooltip_callback)
1197
1200
1201 right_item_tooltip_callback = property(lambda x:x, _set_right_item_tooltip_callback)
1202
1203
1204 -class cReportListCtrl(listmixins.ListCtrlAutoWidthMixin, listmixins.ColumnSorterMixin, wx.ListCtrl):
1205
1206
1207
1208
1209
1210
1211
1212
1213 sort_order_tags = {
1214 True: ' [\u03b1\u0391 \u2192 \u03c9\u03A9]',
1215 False: ' [\u03c9\u03A9 \u2192 \u03b1\u0391]'
1216 }
1217
1219
1220 self.debug = None
1221 self.map_item_idx2data_idx = self.GetItemData
1222
1223 try:
1224 kwargs['style'] = kwargs['style'] | wx.LC_REPORT
1225 except KeyError:
1226 kwargs['style'] = wx.LC_REPORT
1227
1228 self.__is_single_selection = ((kwargs['style'] & wx.LC_SINGLE_SEL) == wx.LC_SINGLE_SEL)
1229
1230 wx.ListCtrl.__init__(self, *args, **kwargs)
1231 listmixins.ListCtrlAutoWidthMixin.__init__(self)
1232
1233
1234 self._invalidate_sorting_metadata()
1235 listmixins.ColumnSorterMixin.__init__(self, 0)
1236 self.__secondary_sort_col = None
1237
1238 self.Bind(wx.EVT_LIST_COL_CLICK, self._on_col_click, self)
1239
1240
1241 self.__widths = None
1242 self.__data = None
1243
1244
1245 self.__select_callback = None
1246 self.__deselect_callback = None
1247 self.__activate_callback = None
1248 self.__new_callback = None
1249 self.__edit_callback = None
1250 self.__delete_callback = None
1251
1252
1253 self.__extend_popup_menu_callback = None
1254 self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self._on_list_item_rightclicked)
1255
1256
1257 self.__item_tooltip_callback = None
1258 self.__tt_last_item = None
1259
1260
1261
1262
1263
1264
1265
1266
1267 self.__tt_static_part_base = ''
1268 self.__tt_static_part = self.__tt_static_part_base
1269 self.Bind(wx.EVT_MOTION, self._on_mouse_motion)
1270
1271
1272 self.__search_term = None
1273 self.__next_line_to_search = 0
1274 self.__searchable_cols = None
1275
1276
1277
1278 self.Bind(wx.EVT_CHAR, self._on_char)
1279 self.Bind(wx.EVT_LIST_KEY_DOWN, self._on_list_key_down)
1280
1281
1282
1283
1285 if self.debug is None:
1286 return False
1287 if not self.debug.endswith('_sizing'):
1288 return False
1289 _log.debug('[%s.%s]: *args = (%s), **kwargs = (%s)', self.debug, caller_name, str(args), str(kwargs))
1290 return True
1291
1292
1294 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1295 return super(cReportListCtrl, self).CacheBestSize(*args, **kwargs)
1296
1297
1298 - def Fit(self, *args, **kwargs):
1299 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1300 return super(cReportListCtrl, self).Fit(*args, **kwargs)
1301
1302
1304 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1305 return super(cReportListCtrl, self).FitInside(*args, **kwargs)
1306
1307
1311
1312
1316
1317
1321
1322
1324 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1325 return super(cReportListCtrl, self).SetClientSize(*args, **kwargs)
1326
1327
1331
1332
1336
1337
1339 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1340 return super(cReportListCtrl, self).SetMaxSize(*args, **kwargs)
1341
1342
1346
1347
1349 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1350 return super(cReportListCtrl, self).SetMinSize(*args, **kwargs)
1351
1352
1353 - def SetSize(self, *args, **kwargs):
1354 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1355 return super(cReportListCtrl, self).SetSize(*args, **kwargs)
1356
1357
1359 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1360 return super(cReportListCtrl, self).SetSizeHints(*args, **kwargs)
1361
1362
1366
1367
1369 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1370 return super(cReportListCtrl, self).SetSizeWH(*args, **kwargs)
1371
1372
1376
1377
1381
1382
1386
1387
1391
1392
1394 res = super(cReportListCtrl, self).GetAdjustedBestSize(*args, **kwargs)
1395 kwargs['sizing_function_result'] = res
1396 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1397 return res
1398
1399
1401 res = super(cReportListCtrl, self).GetEffectiveMinSize(*args, **kwargs)
1402 kwargs['sizing_function_result'] = res
1403 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1404 return res
1405
1406
1408 res = super(cReportListCtrl, self).GetBestSize(*args, **kwargs)
1409 kwargs['sizing_function_result'] = res
1410 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1411 return res
1412
1413
1415 res = super(cReportListCtrl, self).GetBestSizeTuple(*args, **kwargs)
1416 kwargs['sizing_function_result'] = res
1417 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1418 return res
1419
1420
1422 res = super(cReportListCtrl, self).GetBestVirtualSize(*args, **kwargs)
1423 kwargs['sizing_function_result'] = res
1424 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1425 return res
1426
1427
1429 res = super(cReportListCtrl, self).GetClientSize(*args, **kwargs)
1430 kwargs['sizing_function_result'] = res
1431 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1432 return res
1433
1434
1436 res = super(cReportListCtrl, self).GetClientSize(*args, **kwargs)
1437 kwargs['sizing_function_result'] = res
1438 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1439 return res
1440
1441
1443 res = super(cReportListCtrl, self).GetMaxClientSize(*args, **kwargs)
1444 kwargs['sizing_function_result'] = res
1445 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1446 return res
1447
1448
1450 res = super(cReportListCtrl, self).GetMaxHeight(*args, **kwargs)
1451 kwargs['sizing_function_result'] = res
1452 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1453 return res
1454
1455
1457 res = super(cReportListCtrl, self).GetMaxSize(*args, **kwargs)
1458 kwargs['sizing_function_result'] = res
1459 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1460 return res
1461
1462
1464 res = super(cReportListCtrl, self).GetMaxWidth(*args, **kwargs)
1465 kwargs['sizing_function_result'] = res
1466 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1467 return res
1468
1469
1471 res = super(cReportListCtrl, self).GetMinClientSize(*args, **kwargs)
1472 kwargs['sizing_function_result'] = res
1473 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1474 return res
1475
1476
1478 res = super(cReportListCtrl, self).GetMinHeight(*args, **kwargs)
1479 kwargs['sizing_function_result'] = res
1480 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1481 return res
1482
1483
1485 res = super(cReportListCtrl, self).GetMinSize(*args, **kwargs)
1486 kwargs['sizing_function_result'] = res
1487 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1488 return res
1489
1490
1492 res = super(cReportListCtrl, self).GetMinWidth(*args, **kwargs)
1493 kwargs['sizing_function_result'] = res
1494 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1495 return res
1496
1497
1498 - def GetSize(self, *args, **kwargs):
1499 res = super(cReportListCtrl, self).GetSize(*args, **kwargs)
1500 kwargs['sizing_function_result'] = res
1501 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1502 return res
1503
1504
1506 res = super(cReportListCtrl, self).GetVirtualSize(*args, **kwargs)
1507 kwargs['sizing_function_result'] = res
1508 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1509 return res
1510
1511
1513 res = super(cReportListCtrl, self).GetVirtualSizeTuple(*args, **kwargs)
1514 kwargs['sizing_function_result'] = res
1515 self.__log_sizing(sys._getframe().f_code.co_name, *args, **kwargs)
1516 return res
1517
1518
1519
1520
1522 """(Re)define the columns.
1523
1524 Note that this will (have to) delete the items.
1525 """
1526 self.ClearAll()
1527 self.__tt_last_item = None
1528 if columns is None:
1529 return
1530 for idx in range(len(columns)):
1531 self.InsertColumn(idx, columns[idx])
1532
1533 listmixins.ColumnSorterMixin.__init__(self, 0)
1534 self._invalidate_sorting_metadata()
1535
1536
1538 """Set the column width policy.
1539
1540 widths = None:
1541 use previous policy if any or default policy
1542 widths != None:
1543 use this policy and remember it for later calls
1544
1545 options:
1546 wx.LIST_AUTOSIZE_USEHEADER
1547 wx.LIST_AUTOSIZE
1548
1549 This means there is no way to *revert* to the default policy :-(
1550 """
1551
1552 if widths is not None:
1553 self.__widths = widths
1554 for idx in range(len(self.__widths)):
1555 self.SetColumnWidth(idx, self.__widths[idx])
1556 return
1557
1558
1559 if self.__widths is not None:
1560 for idx in range(len(self.__widths)):
1561 self.SetColumnWidth(idx, self.__widths[idx])
1562 return
1563
1564
1565 if self.GetItemCount() == 0:
1566 width_type = wx.LIST_AUTOSIZE_USEHEADER
1567 else:
1568 width_type = wx.LIST_AUTOSIZE
1569 for idx in range(self.GetColumnCount()):
1570 self.SetColumnWidth(idx, width_type)
1571
1572
1574 if column != 'LAST':
1575 if column > self.ColumnCount:
1576 return
1577
1578 self.setResizeColumn(column)
1579
1580
1582 tries = 0
1583 while tries < max_tries:
1584 if self.debug is not None:
1585 if self.debug.endswith('_deleting'):
1586 _log.debug('[round %s] <%s>.GetItemCount() before DeleteAllItems(): %s (thread [%s])', tries, self.debug, self.GetItemCount(), threading.get_ident())
1587 if not self.DeleteAllItems():
1588 _log.error('<%s>.DeleteAllItems() failed', self.debug)
1589 item_count = self.GetItemCount()
1590 if item_count == 0:
1591 return True
1592 wx.SafeYield(None, True)
1593 _log.error('<%s>.GetItemCount() not 0 (rather: %s) after DeleteAllItems()', self.debug, item_count)
1594 time.sleep(0.3)
1595 wx.SafeYield(None, True)
1596 tries += 1
1597
1598 _log.error('<%s>: unable to delete list items after looping %s times', self.debug, max_tries)
1599 return False
1600
1601
1603 """All item members must be str()able or None."""
1604
1605 wx.BeginBusyCursor()
1606 self._invalidate_sorting_metadata()
1607
1608 if self.ItemCount == 0:
1609 topmost_visible = 0
1610 else:
1611 topmost_visible = self.GetFirstSelected()
1612 if topmost_visible == -1:
1613 topmost_visible = self.GetFocusedItem()
1614 if topmost_visible == -1:
1615 topmost_visible = self.TopItem
1616
1617 if not self.remove_items_safely(max_tries = 3):
1618 _log.error("cannot remove items (!?), continuing and hoping for the best")
1619
1620 if items is None:
1621 self.data = None
1622 wx.EndBusyCursor()
1623 return
1624
1625
1626 for item in items:
1627 try:
1628 item[0]
1629 if not isinstance(item, str):
1630 is_numerically_iterable = True
1631
1632 else:
1633 is_numerically_iterable = False
1634 except TypeError:
1635 is_numerically_iterable = False
1636
1637 if is_numerically_iterable:
1638
1639
1640 col_val = str(item[0])
1641 row_num = self.InsertItem(index = sys.maxsize, label = col_val)
1642 for col_num in range(1, min(self.GetColumnCount(), len(item))):
1643 col_val = str(item[col_num])
1644 self.SetItem(index = row_num, column = col_num, label = col_val)
1645 else:
1646
1647 col_val = str(item)
1648 row_num = self.InsertItem(index = sys.maxsize, label = col_val)
1649
1650 if reshow:
1651 if self.ItemCount > 0:
1652 if topmost_visible < self.ItemCount:
1653 self.EnsureVisible(topmost_visible)
1654 self.Focus(topmost_visible)
1655 else:
1656 self.EnsureVisible(self.ItemCount - 1)
1657 self.Focus(self.ItemCount - 1)
1658
1659
1660 self.data = items
1661
1662 wx.EndBusyCursor()
1663
1664
1666 if self.ItemCount == 0:
1667 return []
1668
1669 rows = []
1670 for row_idx in range(self.ItemCount):
1671 row = []
1672 for col_idx in range(self.ColumnCount):
1673 row.append(self.GetItem(row_idx, col_idx).GetText())
1674 rows.append(row)
1675 return rows
1676
1677
1678
1679
1680 string_items = property(get_string_items, set_string_items)
1681
1682
1684 if len(new_items) == 0:
1685 return
1686
1687 if new_data is None:
1688 new_data = new_items
1689
1690 existing_data = self.get_item_data()
1691 if existing_data is None:
1692 existing_data = []
1693
1694 if allow_dupes:
1695 self._LCTRL_right.set_string_items (
1696 items = self.string_items.extend(new_items),
1697 reshow = True
1698 )
1699 self.data = existing_data.extend(new_data)
1700 self.set_column_widths()
1701 return
1702
1703 existing_items = self.get_string_items()
1704 for new_item, new_data in zip(new_items, new_data):
1705 if new_item in existing_items:
1706 continue
1707 existing_items.append(new_item)
1708 existing_data.append(new_data)
1709 self.set_string_items (
1710 items = existing_items,
1711 reshow = True
1712 )
1713 self.data = existing_data
1714 self.set_column_widths()
1715
1716
1718 """<data> assumed to be a list corresponding to the item indices"""
1719 if data is not None:
1720 item_count = self.GetItemCount()
1721 if len(data) != item_count:
1722 _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())
1723 for item_idx in range(len(data)):
1724 self.SetItemData(item_idx, item_idx)
1725 self.__data = data
1726 self.__tt_last_item = None
1727
1728
1729 return
1730
1737
1738 data = property(_get_data, set_data)
1739
1740
1742
1743 if self.GetItemCount() > 0:
1744 self.Select(0, on = 0)
1745 if selections is None:
1746 return
1747 for idx in selections:
1748 self.Select(idx = idx, on = 1)
1749
1751 if self.ItemCount == 0:
1752 return []
1753 if self.__is_single_selection:
1754 return [self.GetFirstSelected()]
1755 selections = []
1756 idx = self.GetFirstSelected()
1757 while idx != -1:
1758 selections.append(idx)
1759 idx = self.GetNextSelected(idx)
1760 return selections
1761
1762 selections = property(__get_selections, set_selections)
1763
1764
1765
1766
1768 labels = []
1769 for col_idx in range(self.ColumnCount):
1770 col = self.GetColumn(col = col_idx)
1771 labels.append(col.Text)
1772 return labels
1773
1774 column_labels = property(get_column_labels, lambda x:x)
1775
1776
1778 if self.ItemCount == 0:
1779 _log.warning('no items')
1780 return None
1781 if item_idx is not None:
1782 return self.GetItem(item_idx)
1783 _log.error('get_item(None) called')
1784 return None
1785
1786
1788 if self.ItemCount == 0:
1789 return []
1790 return [ self.GetItem(item_idx) for item_idx in range(self.ItemCount) ]
1791
1792 items = property(get_items, lambda x:x)
1793
1794
1796
1797 if self.ItemCount == 0:
1798 if self.__is_single_selection or only_one:
1799 return None
1800 return []
1801
1802 if self.__is_single_selection or only_one:
1803 return self.GetFirstSelected()
1804
1805 items = []
1806 idx = self.GetFirstSelected()
1807 while idx != -1:
1808 items.append(idx)
1809 idx = self.GetNextSelected(idx)
1810
1811 return items
1812
1813 selected_items = property(get_selected_items, lambda x:x)
1814
1815
1817
1818 if self.ItemCount == 0:
1819 if self.__is_single_selection or only_one:
1820 return None
1821 return []
1822
1823 if self.__is_single_selection or only_one:
1824 return self.GetItemText(self.GetFirstSelected())
1825
1826 items = []
1827 idx = self.GetFirstSelected()
1828 while idx != -1:
1829 items.append(self.GetItemText(idx))
1830 idx = self.GetNextSelected(idx)
1831
1832 return items
1833
1834 selected_string_items = property(get_selected_string_items, lambda x:x)
1835
1836
1838
1839 if self.__data is None:
1840 return None
1841
1842 if item_idx is not None:
1843 return self.__data[self.map_item_idx2data_idx(item_idx)]
1844
1845
1846
1847
1848 return [ self.__data[self.map_item_idx2data_idx(item_idx)] for item_idx in range(self.GetItemCount()) ]
1849
1850 item_data = property(get_item_data, lambda x:x)
1851
1852
1854
1855 if self.__is_single_selection or only_one:
1856 if self.__data is None:
1857 return None
1858 idx = self.GetFirstSelected()
1859 if idx == -1:
1860 return None
1861 return self.__data[self.map_item_idx2data_idx(idx)]
1862
1863 data = []
1864 if self.__data is None:
1865 return data
1866 idx = self.GetFirstSelected()
1867 while idx != -1:
1868 data.append(self.__data[self.map_item_idx2data_idx(idx)])
1869 idx = self.GetNextSelected(idx)
1870
1871 return data
1872
1873 selected_item_data = property(get_selected_item_data, lambda x:x)
1874
1875
1877 self.Select(idx = self.GetFirstSelected(), on = 0)
1878
1879
1881
1882
1883
1884
1885
1886
1887 self.DeleteItem(item_idx)
1888 self.__tt_last_item = None
1889 self._invalidate_sorting_metadata()
1890
1891
1892
1893
1895
1896 if item_idx == -1:
1897 return
1898
1899 if self.ItemCount == 0:
1900 return
1901
1902 items = self.selected_items
1903 if self.__is_single_selection:
1904 if items is None:
1905 no_of_selected_items = 0
1906 else:
1907 no_of_selected_items = 1
1908 else:
1909 no_of_selected_items = len(items)
1910 if no_of_selected_items == 0:
1911 title = _('List Item Actions')
1912 elif no_of_selected_items == 1:
1913 title = _('List Item Actions (selected: 1 entry)')
1914 else:
1915 title = _('List Item Actions (selected: %s entries)') % no_of_selected_items
1916
1917
1918 self._context_menu = wx.Menu(title = title)
1919
1920 needs_separator = False
1921 if self.__new_callback is not None:
1922 menu_item = self._context_menu.Append(-1, _('Add (<INS>)'))
1923 self.Bind(wx.EVT_MENU, self._on_add_row, menu_item)
1924 needs_separator = True
1925 if self.__edit_callback is not None:
1926 menu_item = self._context_menu.Append(-1, _('&Edit'))
1927 self.Bind(wx.EVT_MENU, self._on_edit_row, menu_item)
1928 needs_separator = True
1929 if self.__delete_callback is not None:
1930 menu_item = self._context_menu.Append(-1, _('Delete (<DEL>)'))
1931 self.Bind(wx.EVT_MENU, self._on_delete_row, menu_item)
1932 needs_separator = True
1933 if needs_separator:
1934 self._context_menu.AppendSeparator()
1935
1936 menu_item = self._context_menu.Append(-1, _('Find (<CTRL-F>)'))
1937 self.Bind(wx.EVT_MENU, self._on_show_search_dialog, menu_item)
1938 if self.__search_term is not None:
1939 if self.__search_term.strip() != '':
1940 menu_item = self._context_menu.Append(-1, _('Find next [%s] (<CTRL-N>)') % self.__search_term)
1941 self.Bind(wx.EVT_MENU, self._on_search_match, menu_item)
1942 self._context_menu.AppendSeparator()
1943
1944 col_headers = []
1945 self._rclicked_row_idx = item_idx
1946 self._rclicked_row_data = self.get_item_data(item_idx = self._rclicked_row_idx)
1947 self._rclicked_row_cells = []
1948 self._rclicked_row_cells_w_hdr = []
1949 for col_idx in range(self.ColumnCount):
1950 cell_content = self.GetItem(self._rclicked_row_idx, col_idx).Text.strip()
1951 col_header = self.GetColumn(col_idx).Text.strip()
1952 col_headers.append(col_header)
1953 self._rclicked_row_cells.append(cell_content)
1954 self._rclicked_row_cells_w_hdr.append('%s: %s' % (col_header, cell_content))
1955
1956
1957 save_menu = wx.Menu()
1958 menu_item = save_menu.Append(-1, _('&All rows'))
1959 self.Bind(wx.EVT_MENU, self._all_rows2file, menu_item)
1960 menu_item = save_menu.Append(-1, _('All rows as &CSV'))
1961 self.Bind(wx.EVT_MENU, self._all_rows2csv, menu_item)
1962 menu_item = save_menu.Append(-1, _('&Tooltips of all rows'))
1963 self.Bind(wx.EVT_MENU, self._all_row_tooltips2file, menu_item)
1964 menu_item = save_menu.Append(-1, _('&Data of all rows'))
1965 self.Bind(wx.EVT_MENU, self._all_row_data2file, menu_item)
1966
1967 if no_of_selected_items > 1:
1968 save_menu.AppendSeparator()
1969 menu_item = save_menu.Append(-1, _('&Selected rows'))
1970 self.Bind(wx.EVT_MENU, self._selected_rows2file, menu_item)
1971 menu_item = save_menu.Append(-1, _('&Selected rows as CSV'))
1972 self.Bind(wx.EVT_MENU, self._selected_rows2csv, menu_item)
1973 menu_item = save_menu.Append(-1, _('&Tooltips of selected rows'))
1974 self.Bind(wx.EVT_MENU, self._selected_row_tooltips2file, menu_item)
1975 menu_item = save_menu.Append(-1, _('&Data of selected rows'))
1976 self.Bind(wx.EVT_MENU, self._selected_row_data2file, menu_item)
1977
1978
1979 clip_menu = wx.Menu()
1980
1981
1982 if no_of_selected_items > 1:
1983
1984 menu_item = clip_menu.Append(-1, _('Tooltips of selected rows'))
1985 self.Bind(wx.EVT_MENU, self._tooltips2clipboard, menu_item)
1986
1987 menu_item = clip_menu.Append(-1, _('Data (formatted as text) of selected rows'))
1988 self.Bind(wx.EVT_MENU, self._datas2clipboard, menu_item)
1989
1990 menu_item = clip_menu.Append(-1, _('Content (as one line each) of selected rows'))
1991 self.Bind(wx.EVT_MENU, self._rows2clipboard, menu_item)
1992 clip_menu.AppendSeparator()
1993
1994
1995
1996 menu_item = clip_menu.Append(-1, _('Tooltip of current row'))
1997 self.Bind(wx.EVT_MENU, self._tooltip2clipboard, menu_item)
1998
1999 if hasattr(self._rclicked_row_data, 'format'):
2000 menu_item = clip_menu.Append(-1, _('Data (formatted as text) of current row'))
2001 self.Bind(wx.EVT_MENU, self._data2clipboard, menu_item)
2002
2003 menu_item = clip_menu.Append(-1, _('Content (as one line) of current row'))
2004 self.Bind(wx.EVT_MENU, self._row2clipboard, menu_item)
2005
2006 menu_item = clip_menu.Append(-1, _('Content (one line per column) of current row'))
2007 self.Bind(wx.EVT_MENU, self._row_list2clipboard, menu_item)
2008
2009 clip_menu.AppendSeparator()
2010 for col_idx in range(self.ColumnCount):
2011 col_content = self._rclicked_row_cells[col_idx].strip()
2012
2013 if col_content == '':
2014 continue
2015 col_header = col_headers[col_idx]
2016 if col_header == '':
2017
2018
2019
2020
2021
2022
2023 menu_item = clip_menu.Append(-1, _('Column &%s (current row): "%s" [#%s]') % (col_idx+1, shorten_text(col_content, 35), col_idx))
2024 self.Bind(wx.EVT_MENU, self._col2clipboard, menu_item)
2025 else:
2026 col_menu = wx.Menu()
2027
2028 menu_item = col_menu.Append(-1, '"%s: %s" [#%s]' % (shorten_text(col_header, 8), shorten_text(col_content, 35), col_idx))
2029 self.Bind(wx.EVT_MENU, self._col_w_hdr2clipboard, menu_item)
2030
2031 menu_item = col_menu.Append(-1, '"%s" [#%s]' % (shorten_text(col_content, 35), col_idx))
2032 self.Bind(wx.EVT_MENU, self._col2clipboard, menu_item)
2033 clip_menu.Append(-1, _('Column &%s (current row): %s') % (col_idx+1, col_header), col_menu)
2034
2035
2036 clip_add_menu = wx.Menu()
2037
2038
2039 if no_of_selected_items > 1:
2040
2041 menu_item = clip_add_menu.Append(-1, _('Tooltips of selected rows'))
2042 self.Bind(wx.EVT_MENU, self._add_tooltips2clipboard, menu_item)
2043
2044 menu_item = clip_add_menu.Append(-1, _('Data (formatted as text) of selected rows'))
2045 self.Bind(wx.EVT_MENU, self._add_datas2clipboard, menu_item)
2046
2047 menu_item = clip_add_menu.Append(-1, _('Content (as one line each) of selected rows'))
2048 self.Bind(wx.EVT_MENU, self._add_rows2clipboard, menu_item)
2049 clip_add_menu.AppendSeparator()
2050
2051
2052
2053 menu_item = clip_add_menu.Append(-1, _('Tooltip of current row'))
2054 self.Bind(wx.EVT_MENU, self._add_tooltip2clipboard, menu_item)
2055
2056 if hasattr(self._rclicked_row_data, 'format'):
2057 menu_item = clip_add_menu.Append(-1, _('Data (formatted as text) of current row'))
2058 self.Bind(wx.EVT_MENU, self._add_data2clipboard, menu_item)
2059
2060 menu_item = clip_add_menu.Append(-1, _('Content (as one line) of current row'))
2061 self.Bind(wx.EVT_MENU, self._add_row2clipboard, menu_item)
2062
2063 menu_item = clip_add_menu.Append(-1, _('Content (one line per column) of current row'))
2064 self.Bind(wx.EVT_MENU, self._add_row_list2clipboard, menu_item)
2065
2066 clip_add_menu.AppendSeparator()
2067 for col_idx in range(self.ColumnCount):
2068 col_content = self._rclicked_row_cells[col_idx].strip()
2069
2070 if col_content == '':
2071 continue
2072 col_header = col_headers[col_idx]
2073 if col_header == '':
2074
2075 menu_item = clip_add_menu.Append(-1, _('Column &%s (current row): "%s" [#%s]') % (col_idx+1, shorten_text(col_content, 35), col_idx))
2076 self.Bind(wx.EVT_MENU, self._add_col2clipboard, menu_item)
2077 else:
2078 col_add_menu = wx.Menu()
2079
2080 menu_item = col_add_menu.Append(-1, '"%s: %s" [#%s]' % (shorten_text(col_header, 8), shorten_text(col_content, 35), col_idx))
2081 self.Bind(wx.EVT_MENU, self._add_col_w_hdr2clipboard, menu_item)
2082
2083 menu_item = col_add_menu.Append(-1, '"%s" [#%s]' % (shorten_text(col_content, 35), col_idx))
2084 self.Bind(wx.EVT_MENU, self._add_col2clipboard, menu_item)
2085 clip_add_menu.Append(-1, _('Column &%s (current row): %s') % (col_idx+1, col_header), col_add_menu)
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103 self._context_menu.Append(-1, _('&Save to file...'), save_menu)
2104 self._context_menu.Append(-1, _('&Copy to clipboard...'), clip_menu)
2105 self._context_menu.Append(-1, _('Append (&+) to clipboard...'), clip_add_menu)
2106
2107 if self.__extend_popup_menu_callback is not None:
2108 self._context_menu.AppendSeparator()
2109 self.__extend_popup_menu_callback(menu = self._context_menu)
2110
2111
2112 self.PopupMenu(self._context_menu, wx.DefaultPosition)
2113 self._context_menu.Destroy()
2114 return
2115
2116
2118 if self.__delete_callback is None:
2119 return
2120
2121 no_items = len(self.get_selected_items(only_one = False))
2122 if no_items == 0:
2123 return
2124
2125 if no_items > 1:
2126 question = _(
2127 '%s list items are selected.\n'
2128 '\n'
2129 'Really delete all %s items ?'
2130 ) % (no_items, no_items)
2131 title = _('Deleting list items')
2132 style = wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION | wx.STAY_ON_TOP
2133 dlg = wx.MessageDialog(None, question, title, style)
2134 btn_pressed = dlg.ShowModal()
2135 dlg.Destroy()
2136 if btn_pressed == wx.ID_NO:
2137 self.SetFocus()
2138 return
2139 if btn_pressed == wx.ID_CANCEL:
2140 self.SetFocus()
2141 return
2142
2143 self.__delete_callback()
2144 return
2145
2146
2148 if self.__new_callback is None:
2149 return
2150 self.__new_callback()
2151
2152
2154 if self.__edit_callback is None:
2155 return
2156 self.__edit_callback()
2157
2158
2160
2161 if self.__search_term is None:
2162
2163 default = ''
2164 else:
2165
2166 default = self.__search_term
2167 search_term = wx.GetTextFromUser (
2168 _('Enter the search term:'),
2169 _('List search'),
2170 default_value = default
2171 )
2172 if search_term.strip() == '':
2173
2174 self.__search_term = None
2175 self.__tt_static_part = self.__tt_static_part_base
2176 return
2177
2178
2179 self.__search_term = search_term
2180 self.__tt_static_part = _(
2181 'Current search term: [[%s]]\n'
2182 '\n'
2183 '%s'
2184 ) % (
2185 search_term,
2186 self.__tt_static_part_base
2187 )
2188 self.__search_match()
2189
2190
2191
2192
2194 event.Skip()
2195 if self.__activate_callback is not None:
2196 self.__activate_callback(event)
2197 return
2198
2199 self.__handle_edit()
2200
2201
2203 if self.__select_callback is not None:
2204 self.__select_callback(event)
2205 else:
2206 event.Skip()
2207
2208
2210 if self.__deselect_callback is not None:
2211 self.__deselect_callback(event)
2212 else:
2213 event.Skip()
2214
2215
2217 event.Skip()
2218 self.__show_context_menu(event.Index)
2219
2220
2222 evt.Skip()
2223
2224 if evt.KeyCode == wx.WXK_DELETE:
2225 self.__handle_delete()
2226 return
2227
2228 if evt.KeyCode == wx.WXK_INSERT:
2229 self.__handle_insert()
2230 return
2231
2232 if evt.KeyCode == wx.WXK_MENU:
2233 self.__show_context_menu(evt.Index)
2234 return
2235
2236
2238
2239 if chr(evt.GetRawKeyCode()) == 'f':
2240 if evt.GetModifiers() == wx.MOD_CMD:
2241
2242 self.__show_search_dialog()
2243 return
2244
2245 if chr(evt.GetRawKeyCode()) == 'n':
2246 if evt.GetModifiers() == wx.MOD_CMD:
2247
2248 self.__search_match()
2249 return
2250
2251 evt.Skip()
2252 return
2253
2254
2256 """Update tooltip on mouse motion.
2257
2258 for s in dir(wx):
2259 if s.startswith('LIST_HITTEST'):
2260 print s, getattr(wx, s)
2261
2262 LIST_HITTEST_ABOVE 1
2263 LIST_HITTEST_BELOW 2
2264 LIST_HITTEST_NOWHERE 4
2265 LIST_HITTEST_ONITEM 672
2266 LIST_HITTEST_ONITEMICON 32
2267 LIST_HITTEST_ONITEMLABEL 128
2268 LIST_HITTEST_ONITEMRIGHT 256
2269 LIST_HITTEST_ONITEMSTATEICON 512
2270 LIST_HITTEST_TOLEFT 1024
2271 LIST_HITTEST_TORIGHT 2048
2272 """
2273 item_idx, where_flag = self.HitTest(wx.Point(event.X, event.Y))
2274
2275
2276 if where_flag not in [
2277 wx.LIST_HITTEST_ONITEMLABEL,
2278 wx.LIST_HITTEST_ONITEMICON,
2279 wx.LIST_HITTEST_ONITEMSTATEICON,
2280 wx.LIST_HITTEST_ONITEMRIGHT,
2281 wx.LIST_HITTEST_ONITEM
2282 ]:
2283 self.__tt_last_item = None
2284 self.SetToolTip(self.__tt_static_part)
2285 return
2286
2287
2288 if self.__tt_last_item == item_idx:
2289 return
2290
2291
2292 self.__tt_last_item = item_idx
2293
2294
2295
2296 if item_idx == wx.NOT_FOUND:
2297 self.SetToolTip(self.__tt_static_part)
2298 return
2299
2300
2301 if self.__data is None:
2302 self.SetToolTip(self.__tt_static_part)
2303 return
2304
2305
2306
2307
2308
2309 if (
2310 (item_idx > (len(self.__data) - 1))
2311 or
2312 (item_idx < -1)
2313 ):
2314 self.SetToolTip(self.__tt_static_part)
2315 print("*************************************************************")
2316 print("GNUmed has detected an inconsistency with list item tooltips.")
2317 print("")
2318 print("This is not a big problem and you can keep working.")
2319 print("")
2320 print("However, please send us the following so we can fix GNUmed:")
2321 print("")
2322 print("item idx: %s" % item_idx)
2323 print('where flag: %s' % where_flag)
2324 print('data list length: %s' % len(self.__data))
2325 print("*************************************************************")
2326 return
2327
2328 dyna_tt = None
2329 if self.__item_tooltip_callback is not None:
2330 dyna_tt = self.__item_tooltip_callback(self.__data[self.map_item_idx2data_idx(item_idx)])
2331
2332 if dyna_tt is None:
2333 self.SetToolTip(self.__tt_static_part)
2334 return
2335
2336 self.SetToolTip(dyna_tt)
2337
2338
2339
2340
2342 evt.Skip()
2343 self.__handle_insert()
2344
2345
2347 evt.Skip()
2348 self.__handle_edit()
2349
2350
2352 evt.Skip()
2353 self.__handle_delete()
2354
2355
2357 evt.Skip()
2358 self.__show_search_dialog()
2359
2360
2362 evt.Skip()
2363 self.__search_match()
2364
2365
2367
2368 txt_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-all_rows-%s.txt' % pydt.datetime.now().strftime('%m%d-%H%M%S'))
2369 txt_file = io.open(txt_name, mode = 'wt', encoding = 'utf8')
2370
2371 col_labels = self.column_labels
2372 line = '%s' % ' || '.join(col_labels)
2373 txt_file.write('%s\n' % line)
2374 txt_file.write(('=' * len(line)) + '\n')
2375
2376 for item_idx in range(self.ItemCount):
2377 fields = []
2378 for col_idx in range(self.ColumnCount):
2379 fields.append(self.GetItem(item_idx, col_idx).Text)
2380 txt_file.write('%s\n' % ' || '.join(fields))
2381
2382 txt_file.close()
2383 gmDispatcher.send(signal = 'statustext', msg = _('All rows saved to [%s].') % txt_name)
2384
2385
2387
2388 csv_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-all_rows-%s.csv' % pydt.datetime.now().strftime('%m%d-%H%M%S'))
2389 csv_file = io.open(csv_name, mode = 'wb')
2390
2391 csv_writer = csv.writer(csv_file)
2392 csv_writer.writerow([ l.encode('utf-8') for l in self.column_labels ])
2393 for item_idx in range(self.ItemCount):
2394 fields = []
2395 for col_idx in range(self.ColumnCount):
2396 fields.append(self.GetItem(item_idx, col_idx).Text)
2397 csv_writer.writerow([ f.encode('utf-8') for f in fields ])
2398
2399 csv_file.close()
2400 gmDispatcher.send(signal = 'statustext', msg = _('All rows saved to [%s].') % csv_name)
2401
2402
2419
2420
2422
2423 if self.__data is None:
2424 return
2425
2426 txt_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-list_data-%s.txt' % pydt.datetime.now().strftime('%m%d-%H%M%S'))
2427 txt_file = io.open(txt_name, mode = 'wt', encoding = 'utf8')
2428
2429 for data in self.data:
2430 if hasattr(data, 'format'):
2431 txt = data.format()
2432 if type(txt) is list:
2433 txt = '\n'.join(txt)
2434 else:
2435 txt = '%s' % data
2436 txt_file.write('%s\n\n' % txt)
2437
2438 txt_file.close()
2439 gmDispatcher.send(signal = 'statustext', msg = _('All data saved to [%s].') % txt_name)
2440
2441
2443
2444 txt_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-some_rows-%s.txt' % pydt.datetime.now().strftime('%m%d-%H%M%S'))
2445 txt_file = io.open(txt_name, mode = 'wt', encoding = 'utf8')
2446
2447 col_labels = self.column_labels
2448 line = '%s' % ' || '.join(col_labels)
2449 txt_file.write('%s\n' % line)
2450 txt_file.write(('=' * len(line)) + '\n')
2451
2452 items = self.selected_items
2453 if self.__is_single_selection:
2454 items = [items]
2455
2456 for item_idx in items:
2457 fields = []
2458 for col_idx in range(self.ColumnCount):
2459 fields.append(self.GetItem(item_idx, col_idx).Text)
2460 txt_file.write('%s\n' % ' || '.join(fields))
2461
2462 txt_file.close()
2463 gmDispatcher.send(signal = 'statustext', msg = _('Selected rows saved to [%s].') % txt_name)
2464
2465
2467
2468 csv_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-some_rows-%s.csv' % pydt.datetime.now().strftime('%m%d-%H%M%S'))
2469 csv_file = io.open(csv_name, mode = 'wb')
2470
2471 csv_writer = csv.writer(csv_file)
2472 csv_writer.writerow([ l.encode('utf-8') for l in self.column_labels ])
2473
2474 items = self.selected_items
2475 if self.__is_single_selection:
2476 items = [items]
2477
2478 for item_idx in items:
2479 fields = []
2480 for col_idx in range(self.ColumnCount):
2481 fields.append(self.GetItem(item_idx, col_idx).Text)
2482 csv_writer.writerow([ f.encode('utf-8') for f in fields ])
2483
2484 csv_file.close()
2485 gmDispatcher.send(signal = 'statustext', msg = _('Selected rows saved to [%s].') % csv_name)
2486
2487
2504
2505
2507
2508 if self.__data is None:
2509 return
2510
2511 txt_name = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', 'gm-list_data-%s.txt' % pydt.datetime.now().strftime('%m%d-%H%M%S'))
2512 txt_file = io.open(txt_name, mode = 'wt', encoding = 'utf8')
2513
2514 for data in self.selected_item_data:
2515 if hasattr(data, 'format'):
2516 txt = data.format()
2517 if type(txt) is list:
2518 txt = '\n'.join(txt)
2519 else:
2520 txt = '%s' % data
2521 txt_file.write('%s\n\n' % txt)
2522
2523 txt_file.close()
2524 gmDispatcher.send(signal = 'statustext', msg = _('Selected data saved to [%s].') % txt_name)
2525
2526
2546
2547
2573
2574
2604
2605
2638
2639
2641 if wx.TheClipboard.IsOpened():
2642 _log.debug('clipboard already open')
2643 return
2644 if not wx.TheClipboard.Open():
2645 _log.debug('cannot open clipboard')
2646 return
2647 data_obj = wx.TextDataObject()
2648 data_obj.SetText(' // '.join(self._rclicked_row_cells))
2649 wx.TheClipboard.SetData(data_obj)
2650 wx.TheClipboard.Close()
2651
2652
2654 if wx.TheClipboard.IsOpened():
2655 _log.debug('clipboard already open')
2656 return
2657 if not wx.TheClipboard.Open():
2658 _log.debug('cannot open clipboard')
2659 return
2660
2661 rows = []
2662 for item_idx in self.selected_items:
2663 rows.append(' // '.join([ self.GetItem(item_idx, col_idx).Text.strip() for col_idx in range(self.ColumnCount) ]))
2664
2665 data_obj = wx.TextDataObject()
2666 data_obj.SetText('\n\n'.join(rows))
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 data_obj = wx.TextDataObject()
2679
2680 txt = ''
2681
2682 got_it = wx.TheClipboard.GetData(data_obj)
2683 if got_it:
2684 txt = data_obj.Text + '\n'
2685
2686
2687 txt += ' // '.join(self._rclicked_row_cells)
2688
2689
2690 data_obj.SetText(txt)
2691 wx.TheClipboard.SetData(data_obj)
2692 wx.TheClipboard.Close()
2693
2694
2696 if wx.TheClipboard.IsOpened():
2697 _log.debug('clipboard already open')
2698 return
2699 if not wx.TheClipboard.Open():
2700 _log.debug('cannot open clipboard')
2701 return
2702
2703 rows = []
2704 for item_idx in self.selected_items:
2705 rows.append(' // '.join([ self.GetItem(item_idx, col_idx).Text.strip() for col_idx in range(self.ColumnCount) ]))
2706
2707 data_obj = wx.TextDataObject()
2708
2709 txt = ''
2710
2711 got_it = wx.TheClipboard.GetData(data_obj)
2712 if got_it:
2713 txt = data_obj.Text + '\n'
2714 txt += '\n\n'.join(rows)
2715
2716 data_obj.SetText(txt)
2717 wx.TheClipboard.SetData(data_obj)
2718 wx.TheClipboard.Close()
2719
2720
2722 if wx.TheClipboard.IsOpened():
2723 _log.debug('clipboard already open')
2724 return
2725 if not wx.TheClipboard.Open():
2726 _log.debug('cannot open clipboard')
2727 return
2728 data_obj = wx.TextDataObject()
2729 data_obj.SetText('\n'.join(self._rclicked_row_cells_w_hdr))
2730 wx.TheClipboard.SetData(data_obj)
2731 wx.TheClipboard.Close()
2732
2733
2735 if wx.TheClipboard.IsOpened():
2736 _log.debug('clipboard already open')
2737 return
2738 if not wx.TheClipboard.Open():
2739 _log.debug('cannot open clipboard')
2740 return
2741 data_obj = wx.TextDataObject()
2742
2743 txt = ''
2744
2745 got_it = wx.TheClipboard.GetData(data_obj)
2746 if got_it:
2747 txt = data_obj.Text + '\n'
2748
2749
2750 txt += '\n'.join(self._rclicked_row_cells_w_hdr)
2751
2752
2753 data_obj.SetText(txt)
2754 wx.TheClipboard.SetData(data_obj)
2755 wx.TheClipboard.Close()
2756
2757
2759 if wx.TheClipboard.IsOpened():
2760 _log.debug('clipboard already open')
2761 return
2762 if not wx.TheClipboard.Open():
2763 _log.debug('cannot open clipboard')
2764 return
2765 data_obj = wx.TextDataObject()
2766 txt = self._rclicked_row_data.format()
2767 if type(txt) == type([]):
2768 txt = '\n'.join(txt)
2769 data_obj.SetText(txt)
2770 wx.TheClipboard.SetData(data_obj)
2771 wx.TheClipboard.Close()
2772
2773
2775 if wx.TheClipboard.IsOpened():
2776 _log.debug('clipboard already open')
2777 return
2778 if not wx.TheClipboard.Open():
2779 _log.debug('cannot open clipboard')
2780 return
2781
2782 data_as_txt = []
2783 for data in self.selected_item_data:
2784 if hasattr(data, 'format'):
2785 txt = data.format()
2786 if type(txt) is list:
2787 txt = '\n'.join(txt)
2788 else:
2789 txt = '%s' % data
2790 data_as_txt.append(txt)
2791
2792 data_obj = wx.TextDataObject()
2793 data_obj.SetText('\n\n'.join(data_as_txt))
2794 wx.TheClipboard.SetData(data_obj)
2795 wx.TheClipboard.Close()
2796
2797
2799 if wx.TheClipboard.IsOpened():
2800 _log.debug('clipboard already open')
2801 return
2802 if not wx.TheClipboard.Open():
2803 _log.debug('cannot open clipboard')
2804 return
2805 data_obj = wx.TextDataObject()
2806
2807 txt = ''
2808
2809 got_it = wx.TheClipboard.GetData(data_obj)
2810 if got_it:
2811 txt = data_obj.Text + '\n'
2812
2813
2814 tmp = self._rclicked_row_data.format()
2815 if type(tmp) == type([]):
2816 txt += '\n'.join(tmp)
2817 else:
2818 txt += tmp
2819
2820
2821 data_obj.SetText(txt)
2822 wx.TheClipboard.SetData(data_obj)
2823 wx.TheClipboard.Close()
2824
2825
2827 if wx.TheClipboard.IsOpened():
2828 _log.debug('clipboard already open')
2829 return
2830 if not wx.TheClipboard.Open():
2831 _log.debug('cannot open clipboard')
2832 return
2833
2834 data_as_txt = []
2835 for data in self.selected_item_data:
2836 if hasattr(data, 'format'):
2837 txt = data.format()
2838 if type(txt) is list:
2839 txt = '\n'.join(txt)
2840 else:
2841 txt = '%s' % data
2842 data_as_txt.append(txt)
2843
2844 data_obj = wx.TextDataObject()
2845 txt = ''
2846
2847 got_it = wx.TheClipboard.GetData(data_obj)
2848 if got_it:
2849 txt = data_obj.Text + '\n'
2850 txt += '\n'.join(data_as_txt)
2851
2852
2853 data_obj.SetText(txt)
2854 wx.TheClipboard.SetData(data_obj)
2855 wx.TheClipboard.Close()
2856
2857
2859 if wx.TheClipboard.IsOpened():
2860 _log.debug('clipboard already open')
2861 return
2862 if not wx.TheClipboard.Open():
2863 _log.debug('cannot open clipboard')
2864 return
2865 data_obj = wx.TextDataObject()
2866
2867
2868 col_idx = int(self._context_menu.FindItemById(evt.Id).ItemLabel.rsplit('#', 1)[1].rstrip(']'))
2869 txt = self._rclicked_row_cells[col_idx]
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 txt = ''
2886
2887 got_it = wx.TheClipboard.GetData(data_obj)
2888 if got_it:
2889 txt = data_obj.Text + '\n'
2890
2891
2892
2893 col_idx = int(self._context_menu.FindItemById(evt.Id).ItemLabel.rsplit('#', 1)[1].rstrip(']'))
2894 txt += self._rclicked_row_cells[col_idx]
2895
2896
2897 data_obj.SetText(txt)
2898 wx.TheClipboard.SetData(data_obj)
2899 wx.TheClipboard.Close()
2900
2901
2903 if wx.TheClipboard.IsOpened():
2904 _log.debug('clipboard already open')
2905 return
2906 if not wx.TheClipboard.Open():
2907 _log.debug('cannot open clipboard')
2908 return
2909 data_obj = wx.TextDataObject()
2910
2911
2912 col_idx = int(self._context_menu.FindItemById(evt.Id).ItemLabel.rsplit('#', 1)[1].rstrip(']'))
2913 txt = self._rclicked_row_cells_w_hdr[col_idx]
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 txt = ''
2930
2931 got_it = wx.TheClipboard.GetData(data_obj)
2932 if got_it:
2933 txt = data_obj.Text + '\n'
2934
2935
2936
2937 col_idx = int(self._context_menu.FindItemById(evt.Id).ItemLabel.rsplit('#', 1)[1].rstrip(']'))
2938 txt += self._rclicked_row_cells_w_hdr[col_idx]
2939
2940
2941 data_obj.SetText(txt)
2942 wx.TheClipboard.SetData(data_obj)
2943 wx.TheClipboard.Close()
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2958
2959 if self.__search_term is None:
2960
2961 return False
2962 if self.__search_term.strip() == '':
2963
2964 return False
2965 if self.__searchable_cols is None:
2966
2967 self.searchable_columns = None
2968 if len(self.__searchable_cols) == 0:
2969
2970 return False
2971
2972
2973 for row_idx in range(self.__next_line_to_search, self.ItemCount):
2974 for col_idx in range(self.ColumnCount):
2975 if col_idx not in self.__searchable_cols:
2976 continue
2977 col_val = self.GetItem(row_idx, col_idx).GetText()
2978 if regex.search(self.__search_term, col_val, regex.U | regex.I) is not None:
2979 self.Select(row_idx)
2980 self.EnsureVisible(row_idx)
2981 if row_idx == self.ItemCount - 1:
2982
2983 self.__next_line_to_search = 0
2984 else:
2985 self.__next_line_to_search = row_idx + 1
2986 return True
2987
2988 self.__next_line_to_search = 0
2989 return False
2990
2991
2993
2994
2995 if cols is None:
2996
2997 self.__searchable_cols = range(self.ColumnCount)
2998 return
2999
3000
3001 new_cols = {}
3002 for col in cols:
3003 if col < self.ColumnCount:
3004 new_cols[col] = True
3005
3006 self.__searchable_cols = new_cols.keys()
3007
3008 searchable_columns = property(lambda x:x, _set_searchable_cols)
3009
3010
3011
3012
3014 return self.__activate_callback
3015
3017 if callback is None:
3018 self.Unbind(wx.EVT_LIST_ITEM_ACTIVATED)
3019 self.__activate_callback = None
3020 return
3021 if not callable(callback):
3022 raise ValueError('<activate> callback is not a callable: %s' % callback)
3023 self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self._on_list_item_activated)
3024 self.__activate_callback = callback
3025
3026 activate_callback = property(_get_activate_callback, _set_activate_callback)
3027
3028
3030 return self.__select_callback
3031
3033 if callback is None:
3034 self.Unbind(wx.EVT_LIST_ITEM_SELECTED)
3035 self.__select_callback = None
3036 return
3037 if not callable(callback):
3038 raise ValueError('<selected> callback is not a callable: %s' % callback)
3039 self.Bind(wx.EVT_LIST_ITEM_SELECTED, self._on_list_item_selected)
3040 self.__select_callback = callback
3041
3042 select_callback = property(_get_select_callback, _set_select_callback)
3043
3044
3046 return self.__deselect_callback
3047
3049 if callback is None:
3050 self.Unbind(wx.EVT_LIST_ITEM_DESELECTED)
3051 self.__deselect_callback = None
3052 return
3053 if not callable(callback):
3054 raise ValueError('<deselected> callback is not a callable: %s' % callback)
3055 self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self._on_list_item_deselected)
3056 self.__deselect_callback = callback
3057
3058 deselect_callback = property(_get_deselect_callback, _set_deselect_callback)
3059
3060
3062 return self.__delete_callback
3063
3065 if callback is None:
3066
3067 self.__delete_callback = None
3068 return
3069 if not callable(callback):
3070 raise ValueError('<delete> callback is not a callable: %s' % callback)
3071
3072 self.__delete_callback = callback
3073
3074 delete_callback = property(_get_delete_callback, _set_delete_callback)
3075
3076
3078 return self.__new_callback
3079
3081 if callback is None:
3082 self.__new_callback = None
3083 return
3084 if not callable(callback):
3085 raise ValueError('<new> callback is not a callable: %s' % callback)
3086 self.__new_callback = callback
3087
3088 new_callback = property(_get_new_callback, _set_new_callback)
3089
3090
3092 return self.__edit_callback
3093
3095 if callback is None:
3096 self.__edit_callback = None
3097 return
3098 if not callable(callback):
3099 raise ValueError('<edit> callback is not a callable: %s' % callback)
3100 self.__edit_callback = callback
3101
3102 edit_callback = property(_get_edit_callback, _set_edit_callback)
3103
3104
3110
3111
3112
3113
3114
3115 item_tooltip_callback = property(lambda x:x, _set_item_tooltip_callback)
3116
3117
3119 if callback is not None:
3120 if not callable(callback):
3121 raise ValueError('<extend_popup_menu> callback is not a callable: %s' % callback)
3122 self.__extend_popup_menu_callback = callback
3123
3124 extend_popup_menu_callback = property(lambda x:x, _set_extend_popup_menu_callback)
3125
3126
3127
3128
3130 if self.itemDataMap is None:
3131 self._update_sorting_metadata()
3132 return self
3133
3134
3136 col_idx, is_ascending = self.GetSortState()
3137 if col_idx == -1:
3138 _log.debug('outside any column (idx: -1) clicked, ignoring')
3139 return
3140 self._remove_sorting_indicators_from_column_headers()
3141 col_state = self.GetColumn(col_idx)
3142 col_state.Text += self.sort_order_tags[is_ascending]
3143 self.SetColumn(col_idx, col_state)
3144
3145
3147 return (primary_item1_idx, primary_item2_idx)
3148
3149 if self.__secondary_sort_col is None:
3150 return (primary_item1_idx, primary_item2_idx)
3151 if self.__secondary_sort_col == primary_sort_col:
3152 return (primary_item1_idx, primary_item2_idx)
3153
3154 secondary_val1 = self.itemDataMap[primary_item1_idx][self.__secondary_sort_col]
3155 secondary_val2 = self.itemDataMap[primary_item2_idx][self.__secondary_sort_col]
3156
3157 if type(secondary_val1) == type('') and type(secondary_val2) == type(''):
3158 secondary_cmp_result = locale.strcoll(secondary_val1, secondary_val2)
3159 elif type(secondary_val1) == type('') or type(secondary_val2) == type(''):
3160 secondary_cmp_result = locale.strcoll(str(secondary_val1), str(secondary_val2))
3161 else:
3162 secondary_cmp_result = cmp(secondary_val1, secondary_val2)
3163
3164 if secondary_cmp_result == 0:
3165 return (primary_item1_idx, primary_item2_idx)
3166
3167
3168 currently_ascending = self._colSortFlag[primary_sort_col]
3169 if currently_ascending:
3170 secondary_val1, secondary_val2 = min(secondary_val1, secondary_val2), max(secondary_val1, secondary_val2)
3171 else:
3172 secondary_val1, secondary_val2 = max(secondary_val1, secondary_val2), min(secondary_val1, secondary_val2)
3173 return (secondary_val1, secondary_val2)
3174
3175
3177
3178
3179
3180 sort_col, is_ascending = self.GetSortState()
3181 data1 = self.itemDataMap[item1][sort_col]
3182 data2 = self.itemDataMap[item2][sort_col]
3183 if type(data1) == type('') and type(data2) == type(''):
3184 cmp_result = locale.strcoll(data1, data2)
3185 elif type(data1) == type('') or type(data2) == type(''):
3186 cmp_result = locale.strcoll(str(data1), str(data2))
3187 else:
3188 cmp_result = cmp(data1, data2)
3189
3190
3191 if not is_ascending:
3192 cmp_result = -1 * cmp_result
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206 return cmp_result
3207
3208
3209
3210
3211
3212
3213
3214
3215
3217 dict2sort = {}
3218 item_count = self.GetItemCount()
3219 if item_count == 0:
3220 return dict2sort
3221 col_count = self.GetColumnCount()
3222 for item_idx in range(item_count):
3223 dict2sort[item_idx] = ()
3224 if col_count == 0:
3225 continue
3226 for col_idx in range(col_count):
3227 dict2sort[item_idx] += (self.GetItem(item_idx, col_idx).GetText(), )
3228
3229
3230
3231 return dict2sort
3232
3233
3235 for col_idx in range(self.ColumnCount):
3236 col_state = self.GetColumn(col_idx)
3237 initial_header = col_state.Text
3238 if col_state.Text.endswith(self.sort_order_tags[True]):
3239 col_state.Text = col_state.Text[:-len(self.sort_order_tags[True])]
3240 if col_state.Text.endswith(self.sort_order_tags[False]):
3241 col_state.Text = col_state.Text[:-len(self.sort_order_tags[False])]
3242 if col_state.Text == initial_header:
3243 continue
3244 self.SetColumn(col_idx, col_state)
3245
3246
3251
3252
3256
3257
3263
3264
3265
3266
3267
3268
3269
3270
3271
3273 return self.__secondary_sort_col
3274
3276 if col is None:
3277 self.__secondary_sort_col = None
3278 return
3279 if col > self.GetColumnCount():
3280 raise ValueError('cannot secondary-sort on col [%s], there are only [%s] columns', col, self.GetColumnCount())
3281 self.__secondary_sort_col = col
3282
3283 secondary_sort_column = property(__get_secondary_sort_col, __set_secondary_sort_col)
3284
3285
3286 -def shorten_text(text=None, max_length=None):
3287 if len(text) <= max_length:
3288 return text
3289 return text[:max_length-1] + '\u2026'
3290
3291
3292
3293
3294
3295 if __name__ == '__main__':
3296
3297 if len(sys.argv) < 2:
3298 sys.exit()
3299
3300 if sys.argv[1] != 'test':
3301 sys.exit()
3302
3303 sys.path.insert(0, '../../')
3304
3305 from Gnumed.pycommon import gmI18N
3306 gmI18N.activate_locale()
3307 gmI18N.install_domain()
3308
3309
3311 app = wx.PyWidgetTester(size = (400, 500))
3312 dlg = wx.MultiChoiceDialog (
3313 parent = None,
3314 message = 'test message',
3315 caption = 'test caption',
3316 choices = ['a', 'b', 'c', 'd', 'e']
3317 )
3318 dlg.ShowModal()
3319 sels = dlg.GetSelections()
3320 print("selected:")
3321 for sel in sels:
3322 print(sel)
3323
3325
3326 def edit(argument):
3327 print("editor called with:")
3328 print(argument)
3329
3330 def refresh(lctrl):
3331 choices = ['a', 'b', 'c']
3332 lctrl.set_string_items(choices)
3333
3334 app = wx.PyWidgetTester(size = (200, 50))
3335 chosen = get_choices_from_list (
3336
3337 caption = 'select health issues',
3338
3339
3340 columns = ['issue'],
3341 refresh_callback = refresh
3342
3343 )
3344 print("chosen:")
3345 print(chosen)
3346
3348 app = wx.PyWidgetTester(size = (200, 50))
3349 dlg = cItemPickerDlg(None, -1, msg = 'Pick a few items:')
3350 dlg.set_columns(['Plugins'], ['Load in workplace', 'dummy'])
3351
3352 dlg.set_string_items(['patient', 'emr', 'docs'])
3353 result = dlg.ShowModal()
3354 print(result)
3355 print(dlg.get_picks())
3356
3357
3358
3359 test_item_picker_dlg()
3360
3361
3362
3363