1
1
#-*- coding: utf-8 -*-
2
2
3
3
"""
4
- Copyright (C) 2011, 2012, 2013 Michal Goral.
4
+ Copyright (C) 2011-2015 Michal Goral.
5
5
6
6
This file is part of Subconvert
7
7
22
22
import os
23
23
import logging
24
24
import encodings
25
- from collections import OrderedDict
25
+ import bisect
26
+ from enum import Enum
26
27
27
28
from PyQt4 .QtGui import QWidget , QHBoxLayout , QVBoxLayout , QGridLayout , QIcon , QTreeWidgetItem
28
29
from PyQt4 .QtGui import QTableView , QHeaderView , QStandardItemModel , QStandardItem , QSizePolicy
29
30
from PyQt4 .QtGui import QMessageBox , QAbstractItemView , QAction , QMenu , QCursor , QFileDialog
31
+ from PyQt4 .QtGui import QPushButton
30
32
from PyQt4 .QtCore import pyqtSignal , pyqtSlot , Qt , QTimer
31
33
32
34
from subconvert .parsing .FrameTime import FrameTime
36
38
from subconvert .utils .SubSettings import SubSettings
37
39
from subconvert .utils .PropertyFile import SubtitleProperties , PropertiesFileApplier
38
40
from subconvert .utils .SubFile import File
41
+ from subconvert .utils .SubtitleSearch import SearchIterator , matchText
39
42
from subconvert .gui .FileDialogs import FileDialog
40
43
from subconvert .gui .Detail import ActionFactory , SubtitleList , ComboBoxWithHistory , FPS_VALUES
41
44
from subconvert .gui .Detail import CustomDataRoles , SubListItemDelegate , DisableSignalling
45
+ from subconvert .gui .Detail import SearchEdit
42
46
from subconvert .gui .SubtitleCommands import *
43
47
44
48
log = logging .getLogger ('Subconvert.%s' % __name__ )
@@ -495,6 +499,9 @@ def __initWidgets(self):
495
499
self ._subList .setItemDelegateForColumn (1 , subListDelegate )
496
500
self ._subList .horizontalHeader ().setResizeMode (2 , QHeaderView .Stretch )
497
501
502
+ self ._searchBar = SearchBar (self )
503
+ self ._searchBar .hide ()
504
+
498
505
# Top toolbar
499
506
toolbar = QHBoxLayout ()
500
507
toolbar .setAlignment (Qt .AlignLeft )
@@ -507,6 +514,7 @@ def __initWidgets(self):
507
514
grid .setContentsMargins (0 , 3 , 0 , 0 )
508
515
grid .addLayout (toolbar , 0 , 0 , 1 , 1 ) # stretch to the right
509
516
grid .addWidget (self ._subList , 1 , 0 )
517
+ grid .addWidget (self ._searchBar , 2 , 0 )
510
518
self .setLayout (grid )
511
519
512
520
def __initContextMenu (self ):
@@ -659,6 +667,10 @@ def removeSelectedSubtitles(self):
659
667
else :
660
668
self ._subList .selectRow (self ._model .rowCount () - 1 )
661
669
670
+ def highlight (self ):
671
+ self ._searchBar .show ()
672
+ self ._searchBar .highlight ()
673
+
662
674
def showContextMenu (self ):
663
675
self ._contextMenu .exec (QCursor .pos ())
664
676
@@ -743,14 +755,19 @@ def updateTab(self):
743
755
self .refreshSubtitles ()
744
756
745
757
def selectedSubtitles (self ):
758
+ rows = self .selectedRows ()
759
+ subtitleList = [self .subtitles [row ] for row in rows ]
760
+ return subtitleList
761
+
762
+ def selectedRows (self ):
746
763
indices = self ._subList .selectedIndexes ()
747
- if len ( indices ) > 0 :
748
- tempDict = OrderedDict . fromkeys ( [index .row () for index in indices ])
749
- rows = list ( tempDict )
750
- rows . sort ()
751
- subtitleList = [ self . subtitles [ row ] for row in rows ]
752
- return subtitleList
753
- return []
764
+ # unique list
765
+ rows = list ( set ( [index .row () for index in indices ]) )
766
+ rows . sort ( )
767
+ return rows
768
+
769
+ def selectRow ( self , row ):
770
+ self . _subList . selectRow ( row )
754
771
755
772
@property
756
773
def filePath (self ):
@@ -783,3 +800,91 @@ def outputEncoding(self):
783
800
@property
784
801
def outputFormat (self ):
785
802
return self .data .outputFormat
803
+
804
+ class SearchBar (QWidget ):
805
+ class SearchDirection (Enum ):
806
+ Forward = 1
807
+ Backward = 2
808
+
809
+ def __init__ (self , parent ):
810
+ super (SearchBar , self ).__init__ (parent )
811
+
812
+ self ._sit = None
813
+
814
+ self ._editor = SearchEdit (self )
815
+ self ._prevButton = QPushButton ("<" , self )
816
+ self ._nextButton = QPushButton (">" , self )
817
+ self ._closeButton = QPushButton (QIcon .fromTheme ("window-close" ), "" , self )
818
+
819
+ layout = QHBoxLayout ()
820
+ layout .setAlignment (Qt .AlignLeft )
821
+ layout .setSpacing (0 )
822
+
823
+ layout .addWidget (self ._editor )
824
+ layout .addWidget (self ._prevButton )
825
+ layout .addWidget (self ._nextButton )
826
+ layout .addWidget (self ._closeButton )
827
+ layout .setContentsMargins (1 , 1 , 1 , 1 )
828
+
829
+ self .setLayout (layout )
830
+
831
+ self ._nextButton .clicked .connect (self .next )
832
+ self ._prevButton .clicked .connect (self .prev )
833
+ self ._closeButton .clicked .connect (self .hide )
834
+ self ._editor .textChanged .connect (self ._reset )
835
+ self ._editor .returnPressed .connect (self .next )
836
+ self ._editor .escapePressed .connect (self .hide )
837
+
838
+ def highlight (self ):
839
+ self ._editor .setFocus ()
840
+ self ._editor .selectAll ()
841
+
842
+ def next (self ):
843
+ self ._search (self .SearchDirection .Forward )
844
+
845
+ def prev (self ):
846
+ self ._search (self .SearchDirection .Backward )
847
+
848
+ def _search (self , direction ):
849
+ if self ._editor .text () == "" :
850
+ self ._reset ()
851
+ return
852
+
853
+ if self ._sit is None :
854
+ data = self .parent ().data
855
+ case = any (map (str .isupper , self ._editor .text ()))
856
+ self ._sit = SearchIterator (data .subtitles ,
857
+ lambda sub , text = self ._editor .text (), case = case : matchText (sub , text , case ))
858
+
859
+ self ._updateIteratorPositionFromSelection (direction )
860
+
861
+ fn = self ._sit .next if direction == self .SearchDirection .Forward else self ._sit .prev
862
+ try :
863
+ subNo = fn ()
864
+ self .parent ().selectRow (subNo )
865
+ except StopIteration :
866
+ self ._searchError ()
867
+
868
+ def _updateIteratorPositionFromSelection (self , direction ):
869
+ selections = self .parent ().selectedRows ()
870
+ startRow = selections [- 1 ] if len (selections ) > 0 else - 1
871
+
872
+ if startRow == - 1 or len (self ._sit .range ()) == 0 :
873
+ return
874
+
875
+ # When iterator points at different row, it means that user changed it
876
+ if startRow != self ._sit .get ():
877
+ pos = None
878
+ if direction == self .SearchDirection .Forward :
879
+ pos = bisect .bisect_right (self ._sit .range (), startRow ) - 1
880
+ else :
881
+ pos = bisect .bisect_left (self ._sit .range (), startRow )
882
+ self ._sit .setpos (pos )
883
+
884
+ def _searchError (self ):
885
+ self ._editor .setStyleSheet ("background-color: #CD5555" )
886
+
887
+ def _reset (self ):
888
+ self ._sit = None
889
+ self ._editor .setStyleSheet ("" )
890
+
0 commit comments