3838import org .eclipse .swt .widgets .Composite ;
3939import org .eclipse .swt .widgets .Control ;
4040import org .eclipse .swt .widgets .Display ;
41+ import org .eclipse .swt .widgets .Label ;
4142import org .eclipse .swt .widgets .Listener ;
4243import org .eclipse .swt .widgets .ScrollBar ;
4344import org .eclipse .swt .widgets .Scrollable ;
@@ -127,6 +128,7 @@ private static final class KeyboardShortcuts {
127128 private Composite searchContainer ;
128129 private Composite searchBarContainer ;
129130 private HistoryTextWrapper searchBar ;
131+ private Label countLabel ;
130132 private AccessibleToolBar searchTools ;
131133 private ToolItem searchInSelectionButton ;
132134 private ToolItem wholeWordSearchButton ;
@@ -723,6 +725,7 @@ public void focusLost(FocusEvent e) {
723725
724726 private void updateIncrementalSearch () {
725727 findReplaceLogic .setFindString (searchBar .getText ());
728+ updateCount ();
726729 evaluateStatusAfterFind ();
727730 }
728731
@@ -747,14 +750,142 @@ private void createReplaceBar() {
747750 private void createSearchContainer () {
748751 searchContainer = new FixedColorComposite (contentGroup , SWT .NONE , widgetBackgroundColor );
749752 GridDataFactory .fillDefaults ().grab (true , true ).align (GridData .FILL , GridData .FILL ).applyTo (searchContainer );
750- GridLayoutFactory .fillDefaults ().numColumns (3 ).extendedMargins (7 , 4 , 3 , 5 ).equalWidth (false )
753+ GridLayoutFactory .fillDefaults ().numColumns (4 ).extendedMargins (7 , 4 , 3 , 5 ).equalWidth (false )
751754 .applyTo (searchContainer );
752755
753756 createSearchBar ();
757+ createCountLabel ();
754758 createSearchTools ();
755759 createCloseTools ();
756760 }
757761
762+ private void createCountLabel () {
763+ countLabel = new Label (searchContainer , SWT .NONE );
764+ countLabel .setForeground (normalTextForegroundColor );
765+ countLabel .setBackground (widgetBackgroundColor );
766+ GridDataFactory .fillDefaults ().align (SWT .CENTER , SWT .CENTER ).hint (SWT .DEFAULT , SWT .DEFAULT ).exclude (true )
767+ .applyTo (countLabel );
768+ countLabel .setVisible (false );
769+ }
770+
771+ private void updateCount () {
772+ if (!okayToUse (countLabel )) {
773+ return ;
774+ }
775+ String inputString = searchBar .getText ();
776+ if (inputString .isEmpty ()) {
777+ hideCountLabel ();
778+ return ;
779+ }
780+
781+ try {
782+ int totalCount = inputTextCount (inputString );
783+ if (totalCount == 0 ) {
784+ hideCountLabel ();
785+ } else {
786+ int currentIndex = getCurrentIndex (inputString , totalCount );
787+ countLabel .setText (currentIndex + " of " + totalCount ); //$NON-NLS-1$
788+ showCountLabel ();
789+ }
790+ } catch (Exception e ) {
791+ hideCountLabel ();
792+ }
793+ }
794+
795+ private void showCountLabel () {
796+ if (!countLabel .isVisible ()) {
797+ countLabel .setVisible (true );
798+ ((GridData ) countLabel .getLayoutData ()).exclude = false ;
799+ }
800+ countLabel .pack ();
801+ countLabel .getParent ().layout ();
802+ }
803+
804+ private void hideCountLabel () {
805+ if (countLabel .isVisible ()) {
806+ countLabel .setVisible (false );
807+ ((GridData ) countLabel .getLayoutData ()).exclude = true ;
808+ countLabel .getParent ().layout ();
809+ }
810+ }
811+
812+ private int inputTextCount (String inputString ) {
813+ if (inputString .isEmpty () || findReplaceLogic .getTarget () == null ) {
814+ return 0 ;
815+ }
816+ if (!(targetPart instanceof StatusTextEditor )) {
817+ return 0 ;
818+ }
819+ StatusTextEditor textEditor = (StatusTextEditor ) targetPart ;
820+ ITextViewer viewer = textEditor .getAdapter (ITextViewer .class );
821+ if (viewer == null || viewer .getDocument () == null ) {
822+ return 0 ;
823+ }
824+
825+ FindReplaceDocumentAdapter docAdapter = new FindReplaceDocumentAdapter (viewer .getDocument ());
826+ return findMatches (docAdapter , inputString , -1 );
827+ }
828+
829+ private int getCurrentIndex (String inputString , int totalCount ) {
830+ if (totalCount == 0 || findReplaceLogic .getTarget () == null ) {
831+ return 0 ;
832+ }
833+
834+ IFindReplaceTarget target = findReplaceLogic .getTarget ();
835+ Point selection = target .getSelection ();
836+ int currentOffset = selection .x ;
837+
838+ if (!(targetPart instanceof StatusTextEditor )) {
839+ return 0 ;
840+ }
841+
842+ StatusTextEditor textEditor = (StatusTextEditor ) targetPart ;
843+ ITextViewer viewer = textEditor .getAdapter (ITextViewer .class );
844+ if (viewer == null || viewer .getDocument () == null ) {
845+ return 0 ;
846+ }
847+
848+ FindReplaceDocumentAdapter docAdapter = new FindReplaceDocumentAdapter (viewer .getDocument ());
849+ int index = findMatches (docAdapter , inputString , currentOffset );
850+ return index > 0 ? index : 1 ;
851+ }
852+
853+ /**
854+ * Iterates through all matches of the search string in the document.
855+ *
856+ * @param docAdapter the document adapter to search in
857+ * @param searchString the string to search for
858+ * @param stopOffset if stopOffset>= 0, stops and returns the index when a
859+ * match at or after this offset is found; if stopOffset< 0,
860+ * counts all matches
861+ * @return the count of all matches (if stopOffset < 0) or the index of the
862+ * first match at or after stopOffset.
863+ */
864+ private int findMatches (FindReplaceDocumentAdapter docAdapter , String searchString , int stopOffset ) {
865+ boolean isCaseSensitive = findReplaceLogic .isActive (SearchOptions .CASE_SENSITIVE );
866+ boolean isWholeWord = findReplaceLogic .isActive (SearchOptions .WHOLE_WORD );
867+ boolean isRegEx = findReplaceLogic .isActive (SearchOptions .REGEX );
868+
869+ int index = 0 ;
870+ int startOffset = 0 ;
871+ try {
872+ while (true ) {
873+ org .eclipse .jface .text .IRegion match = docAdapter .find (startOffset , searchString , true ,
874+ isCaseSensitive , isWholeWord , isRegEx );
875+ if (match == null ) {
876+ break ;
877+ }
878+ index ++;
879+ if (stopOffset >= 0 && match .getOffset () >= stopOffset ) {
880+ return index ;
881+ }
882+ startOffset = match .getOffset () + Math .max (1 , match .getLength ());
883+ }
884+ } catch (Exception e ) {
885+ }
886+ return index ;
887+ }
888+
758889 private void createReplaceContainer () {
759890 replaceContainer = new FixedColorComposite (contentGroup , SWT .NONE , widgetBackgroundColor );
760891 GridDataFactory .fillDefaults ().grab (true , true ).align (GridData .FILL , GridData .FILL ).applyTo (replaceContainer );
@@ -973,6 +1104,7 @@ private String getFindString() {
9731104 private void performSingleReplace () {
9741105 if (findReplaceLogic .performSelectAndReplace ()) {
9751106 findReplaceLogic .performSearch ();
1107+ updateCount ();
9761108 evaluateStatusAfterFind ();
9771109 } else {
9781110 evaluateStatusAfterReplace ();
@@ -987,6 +1119,7 @@ private void performSearch(boolean forward) {
9871119 activateInFindReplacerIf (SearchOptions .FORWARD , forward );
9881120 findReplaceLogic .performSearch ();
9891121 activateInFindReplacerIf (SearchOptions .FORWARD , oldForwardSearchSetting );
1122+ updateCount ();
9901123 evaluateStatusAfterFind ();
9911124 searchBar .storeHistory ();
9921125 }
0 commit comments