99
99
from picard .ui .collectionmenu import CollectionMenu
100
100
from picard .ui .colors import interface_colors
101
101
from picard .ui .enums import MainAction
102
+ from picard .ui .itemviews .columns import (
103
+ DEFAULT_COLUMNS ,
104
+ ITEM_ICON_COLUMN ,
105
+ ColumnAlign ,
106
+ ColumnSortType ,
107
+ )
102
108
from picard .ui .ratingwidget import RatingWidget
103
109
from picard .ui .scriptsmenu import ScriptsMenu
104
110
from picard .ui .util import menu_builder
@@ -124,46 +130,6 @@ def get_match_color(similarity, basecolor):
124
130
125
131
class MainPanel (QtWidgets .QSplitter ):
126
132
127
- columns = [
128
- (N_ ("Title" ), 'title' ),
129
- (N_ ("Length" ), '~length' ),
130
- (N_ ("Artist" ), 'artist' ),
131
- (N_ ("Album Artist" ), 'albumartist' ),
132
- (N_ ("Composer" ), 'composer' ),
133
- (N_ ("Album" ), 'album' ),
134
- (N_ ("Disc Subtitle" ), 'discsubtitle' ),
135
- (N_ ("Track No." ), 'tracknumber' ),
136
- (N_ ("Disc No." ), 'discnumber' ),
137
- (N_ ("Catalog No." ), 'catalognumber' ),
138
- (N_ ("Barcode" ), 'barcode' ),
139
- (N_ ("Media" ), 'media' ),
140
- (N_ ("Size" ), '~filesize' ),
141
- (N_ ("Genre" ), 'genre' ),
142
- (N_ ("Fingerprint status" ), '~fingerprint' ),
143
- (N_ ("Date" ), 'date' ),
144
- (N_ ("Original Release Date" ), 'originaldate' ),
145
- (N_ ("Release Date" ), 'releasedate' ),
146
- (N_ ("Cover" ), 'covercount' ),
147
- ]
148
-
149
- _column_indexes = {column [1 ]: i for i , column in enumerate (columns )}
150
-
151
- TITLE_COLUMN = _column_indexes ['title' ]
152
- TRACKNUMBER_COLUMN = _column_indexes ['tracknumber' ]
153
- DISCNUMBER_COLUMN = _column_indexes ['discnumber' ]
154
- LENGTH_COLUMN = _column_indexes ['~length' ]
155
- FILESIZE_COLUMN = _column_indexes ['~filesize' ]
156
- FINGERPRINT_COLUMN = _column_indexes ['~fingerprint' ]
157
-
158
- NAT_SORT_COLUMNS = [
159
- _column_indexes ['title' ],
160
- _column_indexes ['album' ],
161
- _column_indexes ['discsubtitle' ],
162
- _column_indexes ['tracknumber' ],
163
- _column_indexes ['discnumber' ],
164
- _column_indexes ['catalognumber' ],
165
- ]
166
-
167
133
def __init__ (self , window , parent = None ):
168
134
super ().__init__ (parent )
169
135
self .tagger = QtCore .QCoreApplication .instance ()
@@ -300,64 +266,38 @@ def select_object(self, obj):
300
266
break
301
267
302
268
303
- def paint_column_icon (painter , rect , icon ):
304
- if not icon :
305
- return
306
- size = COLUMN_ICON_SIZE
307
- padding_h = COLUMN_ICON_BORDER
308
- padding_v = (rect .height () - size ) // 2
309
- target_rect = QtCore .QRect (rect .x () + padding_h , rect .y () + padding_v , size , size )
310
- painter .drawPixmap (target_rect , icon .pixmap (size , size ))
311
-
312
-
313
269
class ConfigurableColumnsHeader (TristateSortHeaderView ):
314
270
315
271
def __init__ (self , parent = None ):
316
272
super ().__init__ (QtCore .Qt .Orientation .Horizontal , parent )
317
- self ._visible_columns = set ([0 ])
318
-
319
- # The following are settings applied to default headers
320
- # of QTreeView and QTreeWidget.
321
- self .setSectionsMovable (True )
322
- self .setStretchLastSection (True )
323
- self .setDefaultAlignment (QtCore .Qt .AlignmentFlag .AlignLeft | QtCore .Qt .AlignmentFlag .AlignVCenter )
324
- self .setSectionsClickable (False )
273
+ self ._visible_columns = set ([ITEM_ICON_COLUMN ])
274
+
325
275
self .sortIndicatorChanged .connect (self .on_sort_indicator_changed )
326
276
327
277
# enable sorting, but don't actually use it by default
328
278
# XXX it would be nice to be able to go to the 'no sort' mode, but the
329
279
# internal model that QTreeWidget uses doesn't support it
330
280
self .setSortIndicator (- 1 , QtCore .Qt .SortOrder .AscendingOrder )
331
- self .setDefaultSectionSize (DEFAULT_SECTION_SIZE )
332
281
333
282
def show_column (self , column , show ):
334
- if column == 0 : # The first column is fixed
335
- return
283
+ if column == ITEM_ICON_COLUMN :
284
+ # The first column always visible
285
+ # Still execute following to ensure it is shown
286
+ show = True
336
287
self .parent ().setColumnHidden (column , not show )
337
288
if show :
338
- if self .sectionSize (column ) == 0 :
339
- self .resizeSection (column , self .defaultSectionSize ())
340
289
self ._visible_columns .add (column )
341
- if column == MainPanel .FINGERPRINT_COLUMN :
342
- self .setSectionResizeMode (column , QtWidgets .QHeaderView .ResizeMode .Fixed )
343
- self .resizeSection (column , COLUMN_ICON_SIZE )
344
- else :
345
- self .setSectionResizeMode (column , QtWidgets .QHeaderView .ResizeMode .Interactive )
346
- elif column in self ._visible_columns :
347
- self ._visible_columns .remove (column )
348
-
349
- def update_visible_columns (self , columns ):
350
- for i , column in enumerate (MainPanel .columns ):
351
- self .show_column (i , i in columns )
290
+ else :
291
+ self ._visible_columns .discard (column )
352
292
353
293
def contextMenuEvent (self , event ):
354
294
menu = QtWidgets .QMenu (self )
355
295
parent = self .parent ()
356
296
357
- for i , column in enumerate (MainPanel . columns ):
358
- if i == 0 :
297
+ for i , column in enumerate (DEFAULT_COLUMNS ):
298
+ if i == ITEM_ICON_COLUMN :
359
299
continue
360
- action = QtGui .QAction (_ (column [ 0 ] ), parent )
300
+ action = QtGui .QAction (_ (column . title ), parent )
361
301
action .setCheckable (True )
362
302
action .setChecked (i in self ._visible_columns )
363
303
action .setEnabled (not self .is_locked )
@@ -383,16 +323,17 @@ def restore_defaults(self):
383
323
self .parent ().restore_default_columns ()
384
324
385
325
def paintSection (self , painter , rect , index ):
386
- if index == MainPanel .FINGERPRINT_COLUMN :
326
+ column = DEFAULT_COLUMNS [index ]
327
+ if column .is_icon :
387
328
painter .save ()
388
329
super ().paintSection (painter , rect , index )
389
330
painter .restore ()
390
- paint_column_icon (painter , rect , FileItem . icon_fingerprint_gray )
331
+ column . paint_icon (painter , rect )
391
332
else :
392
333
super ().paintSection (painter , rect , index )
393
334
394
335
def on_sort_indicator_changed (self , index , order ):
395
- if index == MainPanel . FINGERPRINT_COLUMN :
336
+ if DEFAULT_COLUMNS [ index ]. is_icon :
396
337
self .setSortIndicator (- 1 , QtCore .Qt .SortOrder .AscendingOrder )
397
338
398
339
def lock (self , is_locked ):
@@ -696,13 +637,23 @@ def save_state(self):
696
637
config .persist [self .header_locked ] = header .is_locked
697
638
698
639
def restore_default_columns (self ):
699
- labels = [_ (h ) if n != '~fingerprint' else '' for h , n in MainPanel . columns ]
640
+ labels = [_ (c . title ) if not c . is_icon else '' for c in DEFAULT_COLUMNS ]
700
641
self .setHeaderLabels (labels )
701
642
702
643
header = self .header ()
703
- header .update_visible_columns ([0 , 1 , 2 ])
704
- for i , size in enumerate ([250 , 50 , 100 ]):
705
- header .resizeSection (i , size )
644
+ header .setStretchLastSection (True )
645
+ header .setDefaultAlignment (QtCore .Qt .AlignmentFlag .AlignLeft | QtCore .Qt .AlignmentFlag .AlignVCenter )
646
+ header .setDefaultSectionSize (DEFAULT_SECTION_SIZE )
647
+
648
+ for i , c in enumerate (DEFAULT_COLUMNS ):
649
+ header .show_column (i , c .is_default )
650
+ if c .is_icon :
651
+ header .resizeSection (i , c .header_icon_size_with_border .width ())
652
+ header .setSectionResizeMode (i , QtWidgets .QHeaderView .ResizeMode .Fixed )
653
+ else :
654
+ header .resizeSection (i , c .size if c .size is not None else DEFAULT_SECTION_SIZE )
655
+ header .setSectionResizeMode (i , QtWidgets .QHeaderView .ResizeMode .Interactive )
656
+
706
657
self .sortByColumn (- 1 , QtCore .Qt .SortOrder .AscendingOrder )
707
658
708
659
def _init_header (self ):
@@ -897,7 +848,7 @@ def remove_file_cluster(self, cluster):
897
848
self .set_clusters_text ()
898
849
899
850
def set_clusters_text (self ):
900
- self .clusters .setText (MainPanel . TITLE_COLUMN , "%s (%d)" % (_ ("Clusters" ), len (self .tagger .clusters )))
851
+ self .clusters .setText (ITEM_ICON_COLUMN , "%s (%d)" % (_ ("Clusters" ), len (self .tagger .clusters )))
901
852
902
853
@property
903
854
def default_drop_target (self ):
@@ -923,12 +874,12 @@ def add_album(self, album):
923
874
self .insertTopLevelItem (0 , item )
924
875
else :
925
876
item = AlbumItem (album , True , self )
926
- item .setIcon (MainPanel . TITLE_COLUMN , AlbumItem .icon_cd )
927
- for i , column in enumerate (MainPanel . columns ):
877
+ item .setIcon (ITEM_ICON_COLUMN , AlbumItem .icon_cd )
878
+ for i , column in enumerate (DEFAULT_COLUMNS ):
928
879
font = item .font (i )
929
880
font .setBold (True )
930
881
item .setFont (i , font )
931
- item .setText (i , album .column (column [ 1 ] ))
882
+ item .setText (i , album .column (column . key ))
932
883
self .add_cluster (album .unmatched_files , item )
933
884
934
885
def remove_album (self , album ):
@@ -945,14 +896,6 @@ def __init__(self, obj, sortable, *args):
945
896
obj .item = self
946
897
self .sortable = sortable
947
898
self ._sortkeys = {}
948
- for column in (
949
- MainPanel .LENGTH_COLUMN ,
950
- MainPanel .FILESIZE_COLUMN ,
951
- MainPanel .TRACKNUMBER_COLUMN ,
952
- MainPanel .DISCNUMBER_COLUMN ,
953
- ):
954
- self .setTextAlignment (column , QtCore .Qt .AlignmentFlag .AlignRight | QtCore .Qt .AlignmentFlag .AlignVCenter )
955
- self .setSizeHint (MainPanel .FINGERPRINT_COLUMN , ICON_SIZE )
956
899
957
900
def setText (self , column , text ):
958
901
self ._sortkeys [column ] = None
@@ -970,30 +913,39 @@ def sortkey(self, column):
970
913
if sortkey is not None :
971
914
return sortkey
972
915
973
- if column == MainPanel .LENGTH_COLUMN :
974
- sortkey = self .obj .metadata .length or 0
975
- elif column == MainPanel .FILESIZE_COLUMN :
976
- try :
977
- sortkey = int (self .obj .metadata ['~filesize' ] or self .obj .orig_metadata ['~filesize' ])
978
- except ValueError :
979
- sortkey = 0
980
- elif column in MainPanel .NAT_SORT_COLUMNS :
916
+ this_column = DEFAULT_COLUMNS [column ]
917
+
918
+ if this_column .sort_type == ColumnSortType .SORTKEY :
919
+ sortkey = this_column .sortkey (self .obj )
920
+ elif this_column .sort_type == ColumnSortType .NAT :
981
921
sortkey = natsort .natkey (self .text (column ))
982
922
else :
983
923
sortkey = strxfrm (self .text (column ))
984
924
self ._sortkeys [column ] = sortkey
985
925
return sortkey
986
926
927
+ def update_colums_text (self , color = None , bgcolor = None ):
928
+ for i , column in enumerate (DEFAULT_COLUMNS ):
929
+ if color is not None :
930
+ self .setForeground (i , color )
931
+ if bgcolor is not None :
932
+ self .setBackground (i , bgcolor )
933
+ if column .is_icon :
934
+ self .setSizeHint (i , column .header_icon_size_with_border )
935
+ else :
936
+ if column .align == ColumnAlign .RIGHT :
937
+ self .setTextAlignment (i , QtCore .Qt .AlignmentFlag .AlignRight | QtCore .Qt .AlignmentFlag .AlignVCenter )
938
+ self .setText (i , self .obj .column (column .key ))
939
+
987
940
988
941
class ClusterItem (TreeItem ):
989
942
990
943
def __init__ (self , * args ):
991
944
super ().__init__ (* args )
992
- self .setIcon (MainPanel . TITLE_COLUMN , ClusterItem .icon_dir )
945
+ self .setIcon (ITEM_ICON_COLUMN , ClusterItem .icon_dir )
993
946
994
947
def update (self , update_selection = True ):
995
- for i , column in enumerate (MainPanel .columns ):
996
- self .setText (i , self .obj .column (column [1 ]))
948
+ self .update_colums_text ()
997
949
album = self .obj .related_album
998
950
if self .obj .special and album and album .loaded :
999
951
album .item .update (update_tracks = False )
@@ -1064,24 +1016,23 @@ def update(self, update_tracks=True, update_selection=True):
1064
1016
for item in items : # Update after insertChildren so that setExpanded works
1065
1017
item .update (update_album = False )
1066
1018
if album .errors :
1067
- self .setIcon (MainPanel . TITLE_COLUMN , AlbumItem .icon_error )
1068
- self .setToolTip (MainPanel . TITLE_COLUMN , _ ("Processing error(s): See the Errors tab in the Album Info dialog" ))
1019
+ self .setIcon (ITEM_ICON_COLUMN , AlbumItem .icon_error )
1020
+ self .setToolTip (ITEM_ICON_COLUMN , _ ("Processing error(s): See the Errors tab in the Album Info dialog" ))
1069
1021
elif album .is_complete ():
1070
1022
if album .is_modified ():
1071
- self .setIcon (MainPanel . TITLE_COLUMN , AlbumItem .icon_cd_saved_modified )
1072
- self .setToolTip (MainPanel . TITLE_COLUMN , _ ("Album modified and complete" ))
1023
+ self .setIcon (ITEM_ICON_COLUMN , AlbumItem .icon_cd_saved_modified )
1024
+ self .setToolTip (ITEM_ICON_COLUMN , _ ("Album modified and complete" ))
1073
1025
else :
1074
- self .setIcon (MainPanel . TITLE_COLUMN , AlbumItem .icon_cd_saved )
1075
- self .setToolTip (MainPanel . TITLE_COLUMN , _ ("Album unchanged and complete" ))
1026
+ self .setIcon (ITEM_ICON_COLUMN , AlbumItem .icon_cd_saved )
1027
+ self .setToolTip (ITEM_ICON_COLUMN , _ ("Album unchanged and complete" ))
1076
1028
else :
1077
1029
if album .is_modified ():
1078
- self .setIcon (MainPanel . TITLE_COLUMN , AlbumItem .icon_cd_modified )
1079
- self .setToolTip (MainPanel . TITLE_COLUMN , _ ("Album modified" ))
1030
+ self .setIcon (ITEM_ICON_COLUMN , AlbumItem .icon_cd_modified )
1031
+ self .setToolTip (ITEM_ICON_COLUMN , _ ("Album modified" ))
1080
1032
else :
1081
- self .setIcon (MainPanel .TITLE_COLUMN , AlbumItem .icon_cd )
1082
- self .setToolTip (MainPanel .TITLE_COLUMN , _ ("Album unchanged" ))
1083
- for i , column in enumerate (MainPanel .columns ):
1084
- self .setText (i , album .column (column [1 ]))
1033
+ self .setIcon (ITEM_ICON_COLUMN , AlbumItem .icon_cd )
1034
+ self .setToolTip (ITEM_ICON_COLUMN , _ ("Album unchanged" ))
1035
+ self .update_colums_text ()
1085
1036
if selection_changed and update_selection :
1086
1037
TreeItem .window .update_selection (new_selection = False )
1087
1038
# Workaround for PICARD-1446: Expand/collapse indicator for the release
@@ -1110,6 +1061,7 @@ class TrackItem(TreeItem):
1110
1061
def update (self , update_album = True , update_files = True , update_selection = True ):
1111
1062
track = self .obj
1112
1063
num_linked_files = track .num_linked_files
1064
+ fingerprint_column = DEFAULT_COLUMNS .pos ('~fingerprint' )
1113
1065
if num_linked_files == 1 :
1114
1066
file = track .files [0 ]
1115
1067
file .item = self
@@ -1119,16 +1071,16 @@ def update(self, update_album=True, update_files=True, update_selection=True):
1119
1071
self .takeChildren ()
1120
1072
self .setExpanded (False )
1121
1073
fingerprint_icon , fingerprint_tooltip = FileItem .decide_fingerprint_icon_info (file )
1122
- self .setToolTip (MainPanel . FINGERPRINT_COLUMN , fingerprint_tooltip )
1123
- self .setIcon (MainPanel . FINGERPRINT_COLUMN , fingerprint_icon )
1074
+ self .setToolTip (fingerprint_column , fingerprint_tooltip )
1075
+ self .setIcon (fingerprint_column , fingerprint_icon )
1124
1076
else :
1125
1077
if num_linked_files == 0 :
1126
1078
icon_tooltip = _ ("There are no files matched to this track" )
1127
1079
else :
1128
1080
icon_tooltip = ngettext ('%i matched file' , '%i matched files' ,
1129
1081
num_linked_files ) % num_linked_files
1130
- self .setToolTip (MainPanel . FINGERPRINT_COLUMN , "" )
1131
- self .setIcon (MainPanel . FINGERPRINT_COLUMN , QtGui .QIcon ())
1082
+ self .setToolTip (fingerprint_column , "" )
1083
+ self .setIcon (fingerprint_column , QtGui .QIcon ())
1132
1084
if track .ignored_for_completeness ():
1133
1085
color = TreeItem .text_color_secondary
1134
1086
else :
@@ -1162,15 +1114,12 @@ def update(self, update_album=True, update_files=True, update_selection=True):
1162
1114
self .addChildren (items )
1163
1115
self .setExpanded (True )
1164
1116
if track .errors :
1165
- self .setIcon (MainPanel . TITLE_COLUMN , TrackItem .icon_error )
1166
- self .setToolTip (MainPanel . TITLE_COLUMN , _ ("Processing error(s): See the Errors tab in the Track Info dialog" ))
1117
+ self .setIcon (ITEM_ICON_COLUMN , TrackItem .icon_error )
1118
+ self .setToolTip (ITEM_ICON_COLUMN , _ ("Processing error(s): See the Errors tab in the Track Info dialog" ))
1167
1119
else :
1168
- self .setIcon (MainPanel .TITLE_COLUMN , icon )
1169
- self .setToolTip (MainPanel .TITLE_COLUMN , icon_tooltip )
1170
- for i , column in enumerate (MainPanel .columns ):
1171
- self .setText (i , track .column (column [1 ]))
1172
- self .setForeground (i , color )
1173
- self .setBackground (i , bgcolor )
1120
+ self .setIcon (ITEM_ICON_COLUMN , icon )
1121
+ self .setToolTip (ITEM_ICON_COLUMN , icon_tooltip )
1122
+ self .update_colums_text (color = color , bgcolor = bgcolor )
1174
1123
if update_selection and self .isSelected ():
1175
1124
TreeItem .window .update_selection (new_selection = False )
1176
1125
if update_album :
@@ -1182,17 +1131,17 @@ class FileItem(TreeItem):
1182
1131
def update (self , update_track = True , update_selection = True ):
1183
1132
file = self .obj
1184
1133
icon , icon_tooltip = FileItem .decide_file_icon_info (file )
1185
- self .setIcon (MainPanel .TITLE_COLUMN , icon )
1186
- self .setToolTip (MainPanel .TITLE_COLUMN , icon_tooltip )
1134
+ self .setIcon (ITEM_ICON_COLUMN , icon )
1135
+ self .setToolTip (ITEM_ICON_COLUMN , icon_tooltip )
1136
+
1137
+ fingerprint_column = DEFAULT_COLUMNS .pos ('~fingerprint' )
1187
1138
fingerprint_icon , fingerprint_tooltip = FileItem .decide_fingerprint_icon_info (file )
1188
- self .setToolTip (MainPanel .FINGERPRINT_COLUMN , fingerprint_tooltip )
1189
- self .setIcon (MainPanel .FINGERPRINT_COLUMN , fingerprint_icon )
1139
+ self .setToolTip (fingerprint_column , fingerprint_tooltip )
1140
+ self .setIcon (fingerprint_column , fingerprint_icon )
1141
+
1190
1142
color = FileItem .file_colors [file .state ]
1191
1143
bgcolor = get_match_color (file .similarity , TreeItem .base_color )
1192
- for i , column in enumerate (MainPanel .columns ):
1193
- self .setText (i , file .column (column [1 ]))
1194
- self .setForeground (i , color )
1195
- self .setBackground (i , bgcolor )
1144
+ self .update_colums_text (color = color , bgcolor = bgcolor )
1196
1145
if update_selection and self .isSelected ():
1197
1146
TreeItem .window .update_selection (new_selection = False )
1198
1147
parent = self .parent ()
0 commit comments