View Issue Details

IDProjectCategoryView StatusLast Update
0036384LazarusIDEpublic2019-12-11 10:52
ReporterBrunoKAssigned ToJuha Manninen 
PrioritynormalSeverityminorReproducibilityN/A
Status assignedResolutionopen 
Product Version2.0.7 (SVN)Product Build 
Target VersionFixed in Version 
Summary0036384: Add buttons in find in files results to clean tabs.
DescriptionSo added (relative to active tab) :
  - Close tabs to the left
  - Close other tabs
  - Close tabs to the right
  - Close all tabs
Additional Informationfiles in attachements
---------------------
searchresultview_new.png : screen copy of added buttons

Patches relative to main lazarus directory (hope it is correct)
searchresultview.pp.patch : patches to .lfm, .pp
lazarusidestrconsts.pas.patch : additional literals
laz_images_list.txt.patch : to update lazarus bitmap resources

searchresultview_png.7z : 7z archive of new buttons to copy to \images\actions

French translation +/- valid patch ?
patch.po.fr.txt


Hope nothing was forgotten ...


Used for many months on W10.

Roughly tried with GTK2, no other widget sets tested.
TagsNo tags attached.
Fixed in Revisionr62364
LazTarget-
WidgetsetWin32/Win64
Attached Files
  • searchresultview_new.png (4,003 bytes)
    searchresultview_new.png (4,003 bytes)
  • searchresultview.pp.patch (15,872 bytes)
    Index: ide/searchresultview.lfm
    ===================================================================
    --- ide/searchresultview.lfm	(revision 62137)
    +++ ide/searchresultview.lfm	(working copy)
    @@ -1,46 +1,61 @@
     object SearchResultsView: TSearchResultsView
    -  Left = 344
    -  Height = 273
    -  Top = 327
    -  Width = 799
    -  BorderIcons = [biSystemMenu]
    +  Left = 250
    +  Height = 300
    +  Top = 250
    +  Width = 896
    +  AutoSize = True
       Caption = 'SearchResultsView'
    -  ClientHeight = 273
    -  ClientWidth = 799
    +  ClientHeight = 300
    +  ClientWidth = 896
    +  Constraints.MinWidth = 400
       KeyPreview = True
       OnClose = FormClose
       OnCreate = Form1Create
       OnKeyDown = FormKeyDown
    -  LCLVersion = '1.9.0.0'
    +  Position = poDefault
    +  LCLVersion = '2.1.0.0'
       object ResultsNoteBook: TPageControl
    -    AnchorSideTop.Control = ToolBar
    -    AnchorSideTop.Side = asrBottom
         Left = 0
    -    Height = 251
    -    Top = 22
    -    Width = 799
    -    Anchors = [akTop, akLeft, akRight, akBottom]
    +    Height = 274
    +    Top = 26
    +    Width = 896
    +    Align = alClient
         MultiLine = True
    -    TabOrder = 1
    +    TabOrder = 0
         OnChange = ResultsNoteBookPageChanged
    -    OnChanging = ResultsNoteBookChanging
         OnCloseTabClicked = ResultsNoteBookClosetabclicked
         OnMouseDown = ResultsNoteBookMouseDown
    +    OnResize = ResultsNoteBookResize
         Options = [nboShowCloseButtons, nboMultiLine]
       end
    +  object ControlBar1: TPanel
    +    Left = 0
    +    Height = 26
    +    Top = 0
    +    Width = 896
    +    Align = alTop
    +    BevelOuter = bvNone
    +    ClientHeight = 26
    +    ClientWidth = 896
    +    TabOrder = 1
    +    TabStop = True
       object ToolBar: TToolBar
    -    Left = 0
    +      Left = 3
         Height = 22
    -    Top = 0
    -    Width = 55
    +      Top = 2
    +      Width = 47
         Align = alNone
    +      Anchors = [akTop, akLeft, akBottom]
         AutoSize = True
    -    EdgeBorders = []
    -    TabOrder = 2
    +      EdgeInner = esNone
    +      EdgeOuter = esNone
    +      TabOrder = 0
         object SearchAgainButton: TToolButton
           Left = 1
           Top = 0
    +        AutoSize = True
           Caption = 'SearchAgainButton'
    +        ImageIndex = 0
           OnClick = SearchAgainButtonClick
         end
         object ClosePageButton: TToolButton
    @@ -47,38 +62,85 @@
           Left = 24
           Top = 0
           Caption = 'ClosePageButton'
    +        ImageIndex = 1
           OnClick = ClosePageButtonClick
         end
    -    object ToolButton3: TToolButton
    -      Left = 47
    -      Height = 22
    -      Top = 0
    -      Caption = 'ToolButton3'
    -      Style = tbsSeparator
         end
    -  end
       object SearchInListEdit: TTreeFilterEdit
    -    AnchorSideLeft.Control = ToolBar
    -    AnchorSideLeft.Side = asrBottom
    -    AnchorSideTop.Control = ToolBar
    -    AnchorSideTop.Side = asrCenter
    -    AnchorSideRight.Control = Owner
    -    AnchorSideRight.Side = asrBottom
    -    Left = 61
    +      Left = 53
         Height = 23
    -    Top = 0
    -    Width = 738
    +      Top = 2
    +      Width = 716
         ButtonWidth = 23
    -    Anchors = [akTop, akLeft, akRight]
    -    BorderSpacing.Left = 6
    +      Anchors = [akTop, akLeft, akRight, akBottom]
    +      BorderSpacing.Left = 2
    +      BorderSpacing.Bottom = 1
    +      BorderSpacing.Around = 2
    +      AutoSize = False
         NumGlyphs = 1
         MaxLength = 0
    -    TabOrder = 0
    +      TabOrder = 1
         OnChange = SearchInListChange
       end
    +    object CloseTabs: TToolBar
    +      Left = 774
    +      Height = 22
    +      Top = 3
    +      Width = 118
    +      Align = alNone
    +      Anchors = [akTop, akRight, akBottom]
    +      BorderSpacing.Around = 2
    +      EdgeBorders = []
    +      EdgeInner = esNone
    +      EdgeOuter = esNone
    +      Indent = 2
    +      TabOrder = 2
    +      Wrapable = False
    +      object tbbCloseLeft: TToolButton
    +        AnchorSideRight.Control = ToolButton1
    +        Left = 2
    +        Top = 0
    +        Action = actCloseLeft
    +      end
    +      object tbbCloseRight: TToolButton
    +        Left = 64
    +        Top = 0
    +        Action = actCloseRight
    +      end
    +      object tbbCloseOthers: TToolButton
    +        Left = 33
    +        Top = 0
    +        Action = actCloseOthers
    +      end
    +      object ToolButton1: TToolButton
    +        Left = 25
    +        Height = 22
    +        Top = 0
    +        Style = tbsSeparator
    +      end
    +      object ToolButton3: TToolButton
    +        Left = 87
    +        Height = 22
    +        Top = 0
    +        Style = tbsSeparator
    +      end
    +      object tbbCloseAll: TToolButton
    +        Left = 95
    +        Top = 0
    +        Action = actCloseAll
    +      end
    +      object ToolButton2: TToolButton
    +        Left = 56
    +        Height = 22
    +        Top = 0
    +        Caption = 'ToolButton2'
    +        Style = tbsSeparator
    +      end
    +    end
    +  end
       object popList: TPopupMenu
    -    Left = 190
    -    Top = 133
    +    left = 190
    +    top = 133
         object mniCopyItem: TMenuItem
           Caption = 'Copy Item'
           OnClick = mniCopyItemClick
    @@ -104,9 +166,10 @@
         end
       end
       object ActionList: TActionList
    -    Left = 93
    -    Top = 133
    +    left = 93
    +    top = 133
         object actClosePage: TAction
    +      ImageIndex = 1
           OnExecute = ClosePageButtonClick
           ShortCut = 16499
         end
    @@ -118,5 +181,22 @@
           OnExecute = actPrevPageExecute
           ShortCut = 24585
         end
    +    object actCloseLeft: TAction
    +      ImageIndex = 5
    +      OnExecute = tbbCloseLeftClick
       end
    +    object actCloseOthers: TAction
    +      ImageIndex = 6
    +      OnExecute = tbbCloseOthersClick
    +    end
    +    object actCloseRight: TAction
    +      ImageIndex = 7
    +      OnExecute = tbbCloseRightClick
    +    end
    +    object actCloseAll: TAction
    +      ImageIndex = 8
    +      OnExecute = tbbCloseAllClick
    +      ShortCut = 24691
    +    end
    +  end
     end
    Index: ide/searchresultview.pp
    ===================================================================
    --- ide/searchresultview.pp	(revision 62137)
    +++ ide/searchresultview.pp	(working copy)
    @@ -40,7 +40,7 @@
       Classes, SysUtils, strutils, Laz_AVL_Tree,
       // LCL
       LCLProc, LCLType, LCLIntf, Forms, Controls, Graphics, ComCtrls, Menus, Clipbrd,
    -  ActnList,
    +  ActnList, ExtCtrls,
       // LazControls
       TreeFilterEdit,
       // LazUtils
    @@ -53,7 +53,7 @@
     
     type
       { TLazSearchMatchPos }
    -  
    +
       TLazSearchMatchPos = class(TObject)
       private
         FFileEndPos: TPoint;
    @@ -131,14 +131,19 @@
         function ItemsAsStrings: TStrings;
       end;
     
    -
       { TSearchResultsView }
     
       TSearchResultsView = class(TForm)
         actClosePage: TAction;
    +    actCloseLeft: TAction;
    +    actCloseOthers: TAction;
    +    actCloseRight: TAction;
    +    actCloseAll: TAction;
         actNextPage: TAction;
         actPrevPage: TAction;
         ActionList: TActionList;
    +    ClosePageButton1: TToolButton;
    +    ControlBar1: TPanel;
         MenuItem1: TMenuItem;
         mniCollapseAll: TMenuItem;
         mniExpandAll: TMenuItem;
    @@ -145,15 +150,28 @@
         mniCopySelected: TMenuItem;
         mniCopyAll: TMenuItem;
         mniCopyItem: TMenuItem;
    +    pnlToolBars: TPanel;
         popList: TPopupMenu;
         ResultsNoteBook: TPageControl;
    +    tbbCloseLeft: TToolButton;
    +    tbbCloseOthers: TToolButton;
    +    tbbCloseRight: TToolButton;
         ToolBar: TToolBar;
         SearchAgainButton: TToolButton;
    -    ToolButton3: TToolButton;
    +    CloseTabs: TToolBar;
    +    ToolButton1: TToolButton;
         ClosePageButton: TToolButton;
         SearchInListEdit: TTreeFilterEdit;
    +    ToolButton2: TToolButton;
    +    ToolButton3: TToolButton;
    +    tbbCloseAll: TToolButton;
         procedure actNextPageExecute(Sender: TObject);
         procedure actPrevPageExecute(Sender: TObject);
    +    procedure ResultsNoteBookResize(Sender: TObject);
    +    procedure tbbCloseAllClick(Sender: TObject);
    +    procedure tbbCloseLeftClick(Sender: TObject);
    +    procedure tbbCloseOthersClick(Sender: TObject);
    +    procedure tbbCloseRightClick(Sender: TObject);
         procedure ClosePageButtonClick(Sender: TObject);
         procedure Form1Create(Sender: TObject);
         procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
    @@ -183,6 +201,9 @@
         procedure TreeViewMouseDown(Sender: TObject; Button: TMouseButton;
           Shift: TShiftState; X, Y: Integer);
       private
    +    type
    +      TOnSide = (osLeft, osOthers, osRight); { Handling of multi tab closure }
    +  private
         FMaxItems: integer;
         FFocusTreeViewInOnChange: Boolean;
         FFocusTreeViewInEndUpdate: Boolean;
    @@ -196,6 +217,9 @@
         function GetItems(Index: integer): TStrings;
         procedure SetMaxItems(const AValue: integer);
         procedure UpdateToolbar;
    +    procedure GetPagesOnActiveLine(var aList : TFPlist);
    +    procedure ClosePageOnSides(aOnSide : TOnSide);
    +    procedure UpdateCloseTabs(aState : boolean);
       protected
         procedure Loaded; override;
         procedure ActivateControl(aWinControl: TWinControl);
    @@ -268,7 +292,7 @@
       Dest.ShownFilename := Src.ShownFilename;
       Result := True;
     end;
    -  
    +
     function GetTreeSelectedItemsAsText(ATreeView: TCustomTreeView): string;
     var
       sl: TStringList;
    @@ -285,6 +309,7 @@
       sl.Free;
     end;
     
    +
     { TSearchResultsView }
     
     procedure TSearchResultsView.Form1Create(Sender: TObject);
    @@ -301,6 +326,12 @@
       SearchAgainButton.Hint:=rsStartANewSearch;
       ClosePageButton.Hint := rsCloseCurrentPage;
       SearchInListEdit.Hint:=rsFilterTheListWithString;
    +  { Close tabs buttons }
    +  actCloseLeft.Hint:=rsCloseLeft;
    +  actCloseRight.Hint:=rsCloseRight;
    +  actCloseOthers.Hint:=rsCloseOthers;
    +  actCloseAll.Hint:=rsCloseAll;
    +
       CloseCommand := IDECommandList.FindIDECommand(ecClose);
       if CloseCommand <> nil then
       begin
    @@ -325,6 +356,13 @@
       ClosePageButton.ImageIndex := IDEImages.LoadImage('menu_close');
       ActionList.Images := IDEImages.Images_16;
       actClosePage.ImageIndex := IDEImages.LoadImage('menu_close');
    +  { Close tabs buttons }
    +  CloseTabs.Images := IDEImages.Images_16;
    +  actCloseLeft.ImageIndex   := IDEImages.LoadImage('tab_close_L');
    +  actCloseOthers.ImageIndex := IDEImages.LoadImage('tab_close_LR');
    +  actCloseRight.ImageIndex  := IDEImages.LoadImage('tab_close_R');
    +  actCloseAll.ImageIndex    := IDEImages.LoadImage('tab_close_All');
    +  UpdateCloseTabs(False);
     end;
     
     procedure TSearchResultsView.FormClose(Sender: TObject; var CloseAction: TCloseAction);
    @@ -345,7 +383,7 @@
       end;
     end;
     
    -procedure TSearchResultsView.FormKeyDown(Sender: TObject; var Key: Word; 
    +procedure TSearchResultsView.FormKeyDown(Sender: TObject; var Key: Word;
       Shift: TShiftState);
     begin
       if (Key = VK_ESCAPE) then
    @@ -352,7 +390,7 @@
       begin
         Key := VK_UNKNOWN;
         Close;
    -  end;  
    +  end;
     end;
     
     procedure TSearchResultsView.mniCopyAllClick(Sender: TObject);
    @@ -422,13 +460,81 @@
     procedure TSearchResultsView.actNextPageExecute(Sender: TObject);
     begin
       ResultsNoteBook.SelectNextPage(True);
    +  UpdateCloseTabs(True);
     end;
     
     procedure TSearchResultsView.actPrevPageExecute(Sender: TObject);
     begin
       ResultsNoteBook.SelectNextPage(False);
    +  UpdateCloseTabs(True);
     end;
     
    +procedure TSearchResultsView.ResultsNoteBookResize(Sender: TObject);
    +begin
    +  UpdateCloseTabs(ResultsNoteBook.PageCount>0);
    +end;
    +
    +{ Handling of tabs closure. Only tabs on pages at the level of active page in
    +  multiline ResultsNoteBook will be closed by Left / Others and Right }
    +procedure TSearchResultsView.ClosePageOnSides(aOnSide: TOnSide);
    +var
    +  lvPageList: TFPList = nil;
    +  lCurTabSheet: TTabSheet;
    +  ix: integer;
    +  lTabSheet: TTabSheet;
    +  lNeedsRefresh : boolean = false;
    +  lCurTV:TLazSearchResultTV;
    +begin
    +  GetPagesOnActiveLine({var} lvPageList);
    +  with ResultsNoteBook do begin
    +    lCurTabSheet := ActivePage;
    +    if aOnSide = osLeft then
    +      ix := lvPageList.IndexOf(lCurTabSheet)-1
    +    else
    +      ix := lvPageList.Count - 1;
    +    while ix >= 0 do begin
    +      lTabSheet := TTabSheet(lvPageList[ix]);
    +      if lTabSheet = lCurTabSheet then begin
    +        if aOnSide = osRight then
    +          break;
    +      end
    +      else begin
    +        ClosePage(lTabSheet.TabIndex);
    +        lNeedsRefresh := True;
    +      end;
    +      Dec(ix);
    +    end;
    +  end;
    +  lvPageList.Free;
    +  if lNeedsRefresh then begin { ~bk force realign alClient }
    +    lCurTV := GetTreeView(ResultsNoteBook.PageIndex);
    +    if assigned(lCurTV) then
    +      lCurTV.Height:=lCurTV.Height+1;
    +  end;
    +end;
    +
    +procedure TSearchResultsView.tbbCloseLeftClick(Sender: TObject);
    +begin
    +  ClosePageOnSides(osLeft);
    +end;
    +
    +procedure TSearchResultsView.tbbCloseOthersClick(Sender: TObject);
    +begin
    +  ClosePageOnSides(osOthers);
    +end;
    +
    +procedure TSearchResultsView.tbbCloseRightClick(Sender: TObject);
    +begin
    +  ClosePageOnSides(osRight);
    +end;
    +
    +procedure TSearchResultsView.tbbCloseAllClick(Sender: TObject);
    +begin
    +  with ResultsNoteBook do
    +    while PageCount>0 do
    +      ClosePage(PageCount-1);
    +end;
    +
     {Keeps track of the Index of the Item the mouse is over, Sets ShowHint to true
     if the Item length is longer than the TreeView client width.}
     procedure TSearchResultsView.LazTVMousemove(Sender: TObject; Shift: TShiftState;
    @@ -684,7 +790,9 @@
         ResultsNoteBook.Pages[PageIndex].Free;
       end;
       if ResultsNoteBook.PageCount = 0 then
    -    Close;
    +    Close
    +  else
    +    UpdateCloseTabs(True);
     end;
     
     {Sets the Items from the treeview on the currently selected page in the TNoteBook}
    @@ -737,8 +845,73 @@
       SearchAgainButton.Enabled := state;
       ClosePageButton.Enabled := state;
       SearchInListEdit.Enabled := state;
    +  if state then
    +    UpdateCloseTabs(state);
     end;
     
    +// <wp> a good tip as usual
    +// https://forum.lazarus.freepascal.org/index.php/
    +//   topic,39500.msg271287.html?PHPSESSID=t8cr445mhbtlnqm2jq0ckj2ul6#msg271287
    +{ Returns a list of all pages on the same line of Tabs as the ActivaPage }
    +procedure TSearchResultsView.GetPagesOnActiveLine(var aList: TFPlist);
    +var
    +  lRect: TRect;
    +  lActiveMidX: integer;
    +  lActiveMidY: integer;
    +  ix: integer;
    +begin
    +  aList := TFPList.Create;
    +  with ResultsNoteBook do begin
    +    if Assigned(ActivePage) then begin
    +      lRect := TabRect(ActivePage.TabIndex);
    +      { Problem with widgetsets (Gtk2) not returning the same coordinates as Windows }
    +{$IFNDEF LCLGtk2}{General case until proven different }
    +      lActiveMidX := (lRect.Right - lRect.Left) div 2;
    +      lActiveMidY := (lRect.Top - lRect.Bottom) div 2;
    +{$ELSE}
    +      lActiveMidX := (lRect.Right - lRect.Left) div 2;
    +      lActiveMidY := (lRect.Bottom - lRect.Top) div 2;
    +{$endif}
    +{    WriteLn('Width=',Width, ' MidX=',lActiveMidX, ' MidY=',lActiveMidY);
    +     WriteLn('Rect(TabIndex)=[',dbgs(lRect), ']'); }
    +      for ix := 0 to PageCount - 1 do begin
    +        lRect := TabRect(ix);
    +        // WriteLn('Rect=[',dbgs(lRect), ']');
    +        if (lRect.Top < lActiveMidY) and (lRect.Bottom > lActiveMidY) and
    +          (lRect.Right > lActiveMidX) and (lRect.Left < Width - lActiveMidX) then
    +          aList.Add(Pages[ix]);
    +      end;
    +    end;
    +  end;
    +end;
    +
    +procedure TSearchResultsView.UpdateCloseTabs(aState: boolean);
    +var
    +  lvPageList: TFPlist = nil;
    +  lActiveIx : integer = -1;
    +begin
    +  with ResultsNoteBook do begin
    +    if aState and (PageCount>0) then begin
    +      // WriteLn('PageCount=',dbgs(PageCount));
    +      GetPagesOnActiveLine({var} lvPageList);
    +      if lvPageList.Count>0 then
    +        repeat
    +          inc(lActiveIx);
    +          if lvPageList[lActiveIx]=Pointer(ActivePage) then
    +            Break;
    +        until lActiveIx>=lvPageList.Count -1
    +      else
    +        FreeAndNil(lvPageList);
    +    end;
    +    aState := aState and Assigned(lvPageList);
    +    actCloseLeft.Enabled  := aState and (lActiveIx>0);
    +    actCloseOthers.Enabled:= aState and (lvPageList.Count>1);
    +    actCloseRight.Enabled := aState and (lActiveIx<lvPageList.Count-1);
    +    actCloseAll.Enabled   := aState;
    +  end;
    +  lvPageList.Free;
    +end;
    +
     procedure TSearchResultsView.ResultsNoteBookClosetabclicked(Sender: TObject);
     begin
       if (Sender is TTabSheet) then
    @@ -769,7 +942,7 @@
         Key:=VK_UNKNOWN;
         if Assigned(FOnSelectionChanged) then
           FOnSelectionChanged(Self);
    -  end;     
    +  end;
     end;
     
     { Add Result will create a tab in the Results view window with an new
    
    searchresultview.pp.patch (15,872 bytes)
  • lazarusidestrconsts.pas.patch (645 bytes)
    Index: lazarusidestrconsts.pas
    ===================================================================
    --- images/lazarusidestrconsts.pas	(revision 62137)
    +++ images/lazarusidestrconsts.pas	(working copy)
    @@ -5465,6 +5465,10 @@
       rsStartANewSearch = 'Start a new search';
       rsCloseCurrentPage = 'Close current page';
       rsFilterTheListWithString = 'Filter the lines in list with a string';
    +  rsCloseLeft = 'Close tab(s) on the left';
    +  rsCloseRight = 'Close tab(s) on the right';
    +  rsCloseOthers = 'Close other tab(s)';
    +  rsCloseAll = 'Close all tabs';
     
       // Application Bundle
       lisErrorLoadingFrom = 'Error loading %s from%s%s%s%s';
    
  • laz_images_list.txt.patch (756 bytes)
    Index: images/laz_images_list.txt
    ===================================================================
    --- images/laz_images_list.txt	(revision 62137)
    +++ images/laz_images_list.txt	(working copy)
    @@ -106,6 +106,18 @@
     actions/restore_defaults.png
     actions/restore_defaults_150.png
     actions/restore_defaults_200.png
    +actions/tab_close_All.png
    +actions/tab_close_All_150.png
    +actions/tab_close_All_200.png
    +actions/tab_close_L.png
    +actions/tab_close_L_150.png
    +actions/tab_close_L_200.png
    +actions/tab_close_LR.png
    +actions/tab_close_LR_150.png
    +actions/tab_close_LR_200.png
    +actions/tab_close_R.png
    +actions/tab_close_R_150.png
    +actions/tab_close_R_200.png
     codecompletion/cc_class.png
     codecompletion/cc_class_150.png
     codecompletion/cc_class_200.png
    
  • searchresultview_png.7z (13,991 bytes)
  • patch.po.fr.txt (976 bytes)
    Index: languages/lazaruside.fr.po
    ===================================================================
    --- languages/lazaruside.fr.po	(revision 62111)
    +++ languages/lazaruside.fr.po	(working copy)
    @@ -20854,10 +20854,26 @@
     msgid "Character set:"
     msgstr "Jeu de caractères :"
     
    +#: lazarusidestrconsts.rscloseall
    +msgid "Close all tabs"
    +msgstr "Fermer tous les onglets"
    +
     #: lazarusidestrconsts.rsclosecurrentpage
     msgid "Close current page"
    -msgstr "Fermer la page en cours"
    +msgstr "Fermer l'onglet actif"
     
    +#: lazarusidestrconsts.rscloseleft
    +msgid "Close tab(s) on the left"
    +msgstr "Fermer le(s) onglet(s) de gauche"
    +
    +#: lazarusidestrconsts.rscloseothers
    +msgid "Close other tab(s)"
    +msgstr "Fermer tous les autre(s) onglet(s)"
    +
    +#: lazarusidestrconsts.rscloseright
    +msgid "Close tab(s) on the right"
    +msgstr "Fermer le(s) onglet(s) de droite"
    +
     #: lazarusidestrconsts.rsconditionaldefines
     msgid "Conditional defines"
     msgstr "Définitions conditionnelles"
    
    patch.po.fr.txt (976 bytes)
  • searchresultview.patch (19,309 bytes)
    Index: ide/lazarusidestrconsts.pas
    ===================================================================
    --- ide/lazarusidestrconsts.pas	(revision 62137)
    +++ ide/lazarusidestrconsts.pas	(working copy)
    @@ -1042,7 +1042,7 @@
       lisTheOutputDirectoryIsMissing = 'The output directory "%s" is missing.';
       lisCreateIt = 'Create it';
       lisInvalidFileName = 'Invalid file name';
    -  lisTheTargetFileNameIsADirectory = 'The target file name is a directory.';
    +  lisTheTargetFileNameIsADirectory = '\"%s\"\nThe target file name is a directory.';
       lisNotAValidFppkgPrefix ='Free Pascal compiler not found at the given prefix.';
       lisIncorrectFppkgConfiguration = 'there is a problem with the Fppkg configuration. (%s)';
       lisFppkgCompilerProblem = 'there is a problem with the Free Pascal compiler executable, ';
    @@ -5465,6 +5465,10 @@
       rsStartANewSearch = 'Start a new search';
       rsCloseCurrentPage = 'Close current page';
       rsFilterTheListWithString = 'Filter the lines in list with a string';
    +  rsCloseLeft = 'Close tab(s) on the left';
    +  rsCloseRight = 'Close tab(s) on the right';
    +  rsCloseOthers = 'Close other tab(s)';
    +  rsCloseAll = 'Close all tabs';
     
       // Application Bundle
       lisErrorLoadingFrom = 'Error loading %s from%s%s%s%s';
    Index: ide/searchresultview.lfm
    ===================================================================
    --- ide/searchresultview.lfm	(revision 62137)
    +++ ide/searchresultview.lfm	(working copy)
    @@ -1,84 +1,146 @@
     object SearchResultsView: TSearchResultsView
    -  Left = 344
    -  Height = 273
    -  Top = 327
    -  Width = 799
    -  BorderIcons = [biSystemMenu]
    +  Left = 250
    +  Height = 300
    +  Top = 250
    +  Width = 896
    +  AutoSize = True
       Caption = 'SearchResultsView'
    -  ClientHeight = 273
    -  ClientWidth = 799
    +  ClientHeight = 300
    +  ClientWidth = 896
    +  Constraints.MinWidth = 400
       KeyPreview = True
       OnClose = FormClose
       OnCreate = Form1Create
       OnKeyDown = FormKeyDown
    -  LCLVersion = '1.9.0.0'
    +  Position = poDefault
    +  LCLVersion = '2.1.0.0'
       object ResultsNoteBook: TPageControl
    -    AnchorSideTop.Control = ToolBar
    -    AnchorSideTop.Side = asrBottom
         Left = 0
    -    Height = 251
    -    Top = 22
    -    Width = 799
    -    Anchors = [akTop, akLeft, akRight, akBottom]
    +    Height = 274
    +    Top = 26
    +    Width = 896
    +    Align = alClient
         MultiLine = True
    -    TabOrder = 1
    +    TabOrder = 0
         OnChange = ResultsNoteBookPageChanged
    -    OnChanging = ResultsNoteBookChanging
         OnCloseTabClicked = ResultsNoteBookClosetabclicked
         OnMouseDown = ResultsNoteBookMouseDown
    +    OnResize = ResultsNoteBookResize
         Options = [nboShowCloseButtons, nboMultiLine]
       end
    -  object ToolBar: TToolBar
    +  object ControlBar1: TPanel
         Left = 0
    -    Height = 22
    +    Height = 26
         Top = 0
    -    Width = 55
    -    Align = alNone
    -    AutoSize = True
    -    EdgeBorders = []
    -    TabOrder = 2
    -    object SearchAgainButton: TToolButton
    -      Left = 1
    -      Top = 0
    -      Caption = 'SearchAgainButton'
    -      OnClick = SearchAgainButtonClick
    +    Width = 896
    +    Align = alTop
    +    BevelOuter = bvNone
    +    ClientHeight = 26
    +    ClientWidth = 896
    +    TabOrder = 1
    +    TabStop = True
    +    object ToolBar: TToolBar
    +      Left = 3
    +      Height = 22
    +      Top = 2
    +      Width = 47
    +      Align = alNone
    +      Anchors = [akTop, akLeft, akBottom]
    +      AutoSize = True
    +      EdgeInner = esNone
    +      EdgeOuter = esNone
    +      TabOrder = 0
    +      object SearchAgainButton: TToolButton
    +        Left = 1
    +        Top = 0
    +        AutoSize = True
    +        Caption = 'SearchAgainButton'
    +        ImageIndex = 0
    +        OnClick = SearchAgainButtonClick
    +      end
    +      object ClosePageButton: TToolButton
    +        Left = 24
    +        Top = 0
    +        Caption = 'ClosePageButton'
    +        ImageIndex = 1
    +        OnClick = ClosePageButtonClick
    +      end
         end
    -    object ClosePageButton: TToolButton
    -      Left = 24
    -      Top = 0
    -      Caption = 'ClosePageButton'
    -      OnClick = ClosePageButtonClick
    +    object SearchInListEdit: TTreeFilterEdit
    +      Left = 53
    +      Height = 23
    +      Top = 2
    +      Width = 716
    +      ButtonWidth = 23
    +      Anchors = [akTop, akLeft, akRight, akBottom]
    +      BorderSpacing.Left = 2
    +      BorderSpacing.Bottom = 1
    +      BorderSpacing.Around = 2
    +      AutoSize = False
    +      NumGlyphs = 1
    +      MaxLength = 0
    +      TabOrder = 1
    +      OnChange = SearchInListChange
         end
    -    object ToolButton3: TToolButton
    -      Left = 47
    +    object CloseTabs: TToolBar
    +      Left = 774
           Height = 22
    -      Top = 0
    -      Caption = 'ToolButton3'
    -      Style = tbsSeparator
    +      Top = 3
    +      Width = 118
    +      Align = alNone
    +      Anchors = [akTop, akRight, akBottom]
    +      BorderSpacing.Around = 2
    +      EdgeBorders = []
    +      EdgeInner = esNone
    +      EdgeOuter = esNone
    +      Indent = 2
    +      TabOrder = 2
    +      Wrapable = False
    +      object tbbCloseLeft: TToolButton
    +        AnchorSideRight.Control = ToolButton1
    +        Left = 2
    +        Top = 0
    +        Action = actCloseLeft
    +      end
    +      object tbbCloseRight: TToolButton
    +        Left = 64
    +        Top = 0
    +        Action = actCloseRight
    +      end
    +      object tbbCloseOthers: TToolButton
    +        Left = 33
    +        Top = 0
    +        Action = actCloseOthers
    +      end
    +      object ToolButton1: TToolButton
    +        Left = 25
    +        Height = 22
    +        Top = 0
    +        Style = tbsSeparator
    +      end
    +      object ToolButton3: TToolButton
    +        Left = 87
    +        Height = 22
    +        Top = 0
    +        Style = tbsSeparator
    +      end
    +      object tbbCloseAll: TToolButton
    +        Left = 95
    +        Top = 0
    +        Action = actCloseAll
    +      end
    +      object ToolButton2: TToolButton
    +        Left = 56
    +        Height = 22
    +        Top = 0
    +        Caption = 'ToolButton2'
    +        Style = tbsSeparator
    +      end
         end
       end
    -  object SearchInListEdit: TTreeFilterEdit
    -    AnchorSideLeft.Control = ToolBar
    -    AnchorSideLeft.Side = asrBottom
    -    AnchorSideTop.Control = ToolBar
    -    AnchorSideTop.Side = asrCenter
    -    AnchorSideRight.Control = Owner
    -    AnchorSideRight.Side = asrBottom
    -    Left = 61
    -    Height = 23
    -    Top = 0
    -    Width = 738
    -    ButtonWidth = 23
    -    Anchors = [akTop, akLeft, akRight]
    -    BorderSpacing.Left = 6
    -    NumGlyphs = 1
    -    MaxLength = 0
    -    TabOrder = 0
    -    OnChange = SearchInListChange
    -  end
       object popList: TPopupMenu
    -    Left = 190
    -    Top = 133
    +    left = 190
    +    top = 133
         object mniCopyItem: TMenuItem
           Caption = 'Copy Item'
           OnClick = mniCopyItemClick
    @@ -104,9 +166,10 @@
         end
       end
       object ActionList: TActionList
    -    Left = 93
    -    Top = 133
    +    left = 93
    +    top = 133
         object actClosePage: TAction
    +      ImageIndex = 1
           OnExecute = ClosePageButtonClick
           ShortCut = 16499
         end
    @@ -118,5 +181,22 @@
           OnExecute = actPrevPageExecute
           ShortCut = 24585
         end
    +    object actCloseLeft: TAction
    +      ImageIndex = 5
    +      OnExecute = tbbCloseLeftClick
    +    end
    +    object actCloseOthers: TAction
    +      ImageIndex = 6
    +      OnExecute = tbbCloseOthersClick
    +    end
    +    object actCloseRight: TAction
    +      ImageIndex = 7
    +      OnExecute = tbbCloseRightClick
    +    end
    +    object actCloseAll: TAction
    +      ImageIndex = 8
    +      OnExecute = tbbCloseAllClick
    +      ShortCut = 24691
    +    end
       end
     end
    Index: ide/searchresultview.pp
    ===================================================================
    --- ide/searchresultview.pp	(revision 62137)
    +++ ide/searchresultview.pp	(working copy)
    @@ -40,7 +40,7 @@
       Classes, SysUtils, strutils, Laz_AVL_Tree,
       // LCL
       LCLProc, LCLType, LCLIntf, Forms, Controls, Graphics, ComCtrls, Menus, Clipbrd,
    -  ActnList,
    +  ActnList, ExtCtrls,
       // LazControls
       TreeFilterEdit,
       // LazUtils
    @@ -53,7 +53,7 @@
     
     type
       { TLazSearchMatchPos }
    -  
    +
       TLazSearchMatchPos = class(TObject)
       private
         FFileEndPos: TPoint;
    @@ -131,14 +131,19 @@
         function ItemsAsStrings: TStrings;
       end;
     
    -
       { TSearchResultsView }
     
       TSearchResultsView = class(TForm)
         actClosePage: TAction;
    +    actCloseLeft: TAction;
    +    actCloseOthers: TAction;
    +    actCloseRight: TAction;
    +    actCloseAll: TAction;
         actNextPage: TAction;
         actPrevPage: TAction;
         ActionList: TActionList;
    +    ClosePageButton1: TToolButton;
    +    ControlBar1: TPanel;
         MenuItem1: TMenuItem;
         mniCollapseAll: TMenuItem;
         mniExpandAll: TMenuItem;
    @@ -145,15 +150,28 @@
         mniCopySelected: TMenuItem;
         mniCopyAll: TMenuItem;
         mniCopyItem: TMenuItem;
    +    pnlToolBars: TPanel;
         popList: TPopupMenu;
         ResultsNoteBook: TPageControl;
    +    tbbCloseLeft: TToolButton;
    +    tbbCloseOthers: TToolButton;
    +    tbbCloseRight: TToolButton;
         ToolBar: TToolBar;
         SearchAgainButton: TToolButton;
    -    ToolButton3: TToolButton;
    +    CloseTabs: TToolBar;
    +    ToolButton1: TToolButton;
         ClosePageButton: TToolButton;
         SearchInListEdit: TTreeFilterEdit;
    +    ToolButton2: TToolButton;
    +    ToolButton3: TToolButton;
    +    tbbCloseAll: TToolButton;
         procedure actNextPageExecute(Sender: TObject);
         procedure actPrevPageExecute(Sender: TObject);
    +    procedure ResultsNoteBookResize(Sender: TObject);
    +    procedure tbbCloseAllClick(Sender: TObject);
    +    procedure tbbCloseLeftClick(Sender: TObject);
    +    procedure tbbCloseOthersClick(Sender: TObject);
    +    procedure tbbCloseRightClick(Sender: TObject);
         procedure ClosePageButtonClick(Sender: TObject);
         procedure Form1Create(Sender: TObject);
         procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
    @@ -183,6 +201,9 @@
         procedure TreeViewMouseDown(Sender: TObject; Button: TMouseButton;
           Shift: TShiftState; X, Y: Integer);
       private
    +    type
    +      TOnSide = (osLeft, osOthers, osRight); { Handling of multi tab closure }
    +  private
         FMaxItems: integer;
         FFocusTreeViewInOnChange: Boolean;
         FFocusTreeViewInEndUpdate: Boolean;
    @@ -196,6 +217,9 @@
         function GetItems(Index: integer): TStrings;
         procedure SetMaxItems(const AValue: integer);
         procedure UpdateToolbar;
    +    function  GetPagesOnActiveLine:TFPlist;
    +    procedure ClosePageOnSides(aOnSide : TOnSide);
    +    procedure UpdateCloseTabs(aState : boolean);
       protected
         procedure Loaded; override;
         procedure ActivateControl(aWinControl: TWinControl);
    @@ -268,7 +292,7 @@
       Dest.ShownFilename := Src.ShownFilename;
       Result := True;
     end;
    -  
    +
     function GetTreeSelectedItemsAsText(ATreeView: TCustomTreeView): string;
     var
       sl: TStringList;
    @@ -285,6 +309,7 @@
       sl.Free;
     end;
     
    +
     { TSearchResultsView }
     
     procedure TSearchResultsView.Form1Create(Sender: TObject);
    @@ -301,6 +326,12 @@
       SearchAgainButton.Hint:=rsStartANewSearch;
       ClosePageButton.Hint := rsCloseCurrentPage;
       SearchInListEdit.Hint:=rsFilterTheListWithString;
    +  { Close tabs buttons }
    +  actCloseLeft.Hint:=rsCloseLeft;
    +  actCloseRight.Hint:=rsCloseRight;
    +  actCloseOthers.Hint:=rsCloseOthers;
    +  actCloseAll.Hint:=rsCloseAll;
    +
       CloseCommand := IDECommandList.FindIDECommand(ecClose);
       if CloseCommand <> nil then
       begin
    @@ -325,6 +356,13 @@
       ClosePageButton.ImageIndex := IDEImages.LoadImage('menu_close');
       ActionList.Images := IDEImages.Images_16;
       actClosePage.ImageIndex := IDEImages.LoadImage('menu_close');
    +  { Close tabs buttons }
    +  CloseTabs.Images := IDEImages.Images_16;
    +  actCloseLeft.ImageIndex   := IDEImages.LoadImage('tab_close_L');
    +  actCloseOthers.ImageIndex := IDEImages.LoadImage('tab_close_LR');
    +  actCloseRight.ImageIndex  := IDEImages.LoadImage('tab_close_R');
    +  actCloseAll.ImageIndex    := IDEImages.LoadImage('tab_close_All');
    +  UpdateCloseTabs(False);
     end;
     
     procedure TSearchResultsView.FormClose(Sender: TObject; var CloseAction: TCloseAction);
    @@ -345,7 +383,7 @@
       end;
     end;
     
    -procedure TSearchResultsView.FormKeyDown(Sender: TObject; var Key: Word; 
    +procedure TSearchResultsView.FormKeyDown(Sender: TObject; var Key: Word;
       Shift: TShiftState);
     begin
       if (Key = VK_ESCAPE) then
    @@ -352,7 +390,7 @@
       begin
         Key := VK_UNKNOWN;
         Close;
    -  end;  
    +  end;
     end;
     
     procedure TSearchResultsView.mniCopyAllClick(Sender: TObject);
    @@ -422,13 +460,81 @@
     procedure TSearchResultsView.actNextPageExecute(Sender: TObject);
     begin
       ResultsNoteBook.SelectNextPage(True);
    +  UpdateCloseTabs(True);
     end;
     
     procedure TSearchResultsView.actPrevPageExecute(Sender: TObject);
     begin
       ResultsNoteBook.SelectNextPage(False);
    +  UpdateCloseTabs(True);
     end;
     
    +procedure TSearchResultsView.ResultsNoteBookResize(Sender: TObject);
    +begin
    +  UpdateCloseTabs(ResultsNoteBook.PageCount>0);
    +end;
    +
    +{ Handling of tabs closure. Only tabs on pages at the level of active page in
    +  multiline ResultsNoteBook will be closed by Left / Others and Right }
    +procedure TSearchResultsView.ClosePageOnSides(aOnSide: TOnSide);
    +var
    +  lvPageList: TFPList = nil;
    +  lCurTabSheet: TTabSheet;
    +  ix: integer;
    +  lTabSheet: TTabSheet;
    +  lNeedsRefresh : boolean = false;
    +  lCurTV:TLazSearchResultTV;
    +begin
    +  lvPageList := GetPagesOnActiveLine;
    +  with ResultsNoteBook do begin
    +    lCurTabSheet := ActivePage;
    +    if aOnSide = osLeft then
    +      ix := lvPageList.IndexOf(lCurTabSheet)-1
    +    else
    +      ix := lvPageList.Count - 1;
    +    while ix >= 0 do begin
    +      lTabSheet := TTabSheet(lvPageList[ix]);
    +      if lTabSheet = lCurTabSheet then begin
    +        if aOnSide = osRight then
    +          break;
    +      end
    +      else begin
    +        ClosePage(lTabSheet.TabIndex);
    +        lNeedsRefresh := True;
    +      end;
    +      Dec(ix);
    +    end;
    +  end;
    +  lvPageList.Free;
    +  if lNeedsRefresh then begin { ~bk force realign alClient }
    +    lCurTV := GetTreeView(ResultsNoteBook.PageIndex);
    +    if assigned(lCurTV) then
    +      lCurTV.Height:=lCurTV.Height+1;
    +  end;
    +end;
    +
    +procedure TSearchResultsView.tbbCloseLeftClick(Sender: TObject);
    +begin
    +  ClosePageOnSides(osLeft);
    +end;
    +
    +procedure TSearchResultsView.tbbCloseOthersClick(Sender: TObject);
    +begin
    +  ClosePageOnSides(osOthers);
    +end;
    +
    +procedure TSearchResultsView.tbbCloseRightClick(Sender: TObject);
    +begin
    +  ClosePageOnSides(osRight);
    +end;
    +
    +procedure TSearchResultsView.tbbCloseAllClick(Sender: TObject);
    +begin
    +  with ResultsNoteBook do
    +    while PageCount>0 do
    +      ClosePage(PageCount-1);
    +end;
    +
     {Keeps track of the Index of the Item the mouse is over, Sets ShowHint to true
     if the Item length is longer than the TreeView client width.}
     procedure TSearchResultsView.LazTVMousemove(Sender: TObject; Shift: TShiftState;
    @@ -684,7 +790,9 @@
         ResultsNoteBook.Pages[PageIndex].Free;
       end;
       if ResultsNoteBook.PageCount = 0 then
    -    Close;
    +    Close
    +  else
    +    UpdateCloseTabs(True);
     end;
     
     {Sets the Items from the treeview on the currently selected page in the TNoteBook}
    @@ -737,8 +845,73 @@
       SearchAgainButton.Enabled := state;
       ClosePageButton.Enabled := state;
       SearchInListEdit.Enabled := state;
    +  if state then
    +    UpdateCloseTabs(state);
     end;
     
    +// <wp> a good tip as usual
    +// https://forum.lazarus.freepascal.org/index.php/
    +//   topic,39500.msg271287.html?PHPSESSID=t8cr445mhbtlnqm2jq0ckj2ul6#msg271287
    +{ Returns a list of all pages on the same line of Tabs as the ActivaPage }
    +function TSearchResultsView.GetPagesOnActiveLine: TFPlist;
    +var
    +  lRect: TRect;
    +  lActiveMidX: integer;
    +  lActiveMidY: integer;
    +  ix: integer;
    +begin
    +  Result := nil;
    +  with ResultsNoteBook do begin
    +    if Assigned(ActivePage) then begin
    +      Result := TFPList.Create;
    +      lRect := TabRect(ActivePage.TabIndex);
    +      { Problem with widgetsets (Gtk2) not returning the same coordinates as Windows }
    +{$IFNDEF LCLGtk2}{General case until proven different }
    +      lActiveMidX := (lRect.Right - lRect.Left) div 2;
    +      lActiveMidY := (lRect.Top - lRect.Bottom) div 2;
    +{$ELSE}
    +      lActiveMidX := (lRect.Right - lRect.Left) div 2;
    +      lActiveMidY := (lRect.Bottom - lRect.Top) div 2;
    +{$endif}
    +{    WriteLn('Width=',Width, ' MidX=',lActiveMidX, ' MidY=',lActiveMidY);
    +     WriteLn('Rect(TabIndex)=[',dbgs(lRect), ']'); }
    +      for ix := 0 to PageCount - 1 do begin
    +        lRect := TabRect(ix);
    +        // WriteLn('Rect=[',dbgs(lRect), ']');
    +        if (lRect.Top < lActiveMidY) and (lRect.Bottom > lActiveMidY) and
    +          (lRect.Right > lActiveMidX) and (lRect.Left < Width - lActiveMidX) then
    +          Result.Add(Pages[ix]);
    +      end;
    +    end;
    +  end;
    +end;
    +
    +procedure TSearchResultsView.UpdateCloseTabs(aState: boolean);
    +var
    +  lPageList: TFPlist = nil;
    +  lActiveIx : integer = -1;
    +begin
    +  with ResultsNoteBook do begin
    +    if aState and (PageCount>0) then begin
    +      lPageList := GetPagesOnActiveLine;
    +      if lPageList.Count>0 then
    +        repeat
    +          inc(lActiveIx);
    +          if lPageList[lActiveIx]=Pointer(ActivePage) then
    +            Break;
    +        until lActiveIx>=lPageList.Count -1
    +      else
    +        FreeAndNil(lPageList);
    +    end;
    +    aState := aState and Assigned(lPageList);
    +    actCloseLeft.Enabled  := aState and (lActiveIx>0);
    +    actCloseOthers.Enabled:= aState and (lPageList.Count>1);
    +    actCloseRight.Enabled := aState and (lActiveIx<lPageList.Count-1);
    +    actCloseAll.Enabled   := aState;
    +  end;
    +  lPageList.Free;
    +end;
    +
     procedure TSearchResultsView.ResultsNoteBookClosetabclicked(Sender: TObject);
     begin
       if (Sender is TTabSheet) then
    @@ -769,7 +942,7 @@
         Key:=VK_UNKNOWN;
         if Assigned(FOnSelectionChanged) then
           FOnSelectionChanged(Self);
    -  end;     
    +  end;
     end;
     
     { Add Result will create a tab in the Results view window with an new
    Index: images/laz_images_list.txt
    ===================================================================
    --- images/laz_images_list.txt	(revision 62137)
    +++ images/laz_images_list.txt	(working copy)
    @@ -106,6 +106,18 @@
     actions/restore_defaults.png
     actions/restore_defaults_150.png
     actions/restore_defaults_200.png
    +actions/tab_close_All.png
    +actions/tab_close_All_150.png
    +actions/tab_close_All_200.png
    +actions/tab_close_L.png
    +actions/tab_close_L_150.png
    +actions/tab_close_L_200.png
    +actions/tab_close_LR.png
    +actions/tab_close_LR_150.png
    +actions/tab_close_LR_200.png
    +actions/tab_close_R.png
    +actions/tab_close_R_150.png
    +actions/tab_close_R_200.png
     codecompletion/cc_class.png
     codecompletion/cc_class_150.png
     codecompletion/cc_class_200.png
    Index: languages/lazaruside.fr.po
    ===================================================================
    --- languages/lazaruside.fr.po	(revision 62137)
    +++ languages/lazaruside.fr.po	(working copy)
    @@ -20854,10 +20854,26 @@
     msgid "Character set:"
     msgstr "Jeu de caractères :"
     
    +#: lazarusidestrconsts.rscloseall
    +msgid "Close all tabs"
    +msgstr "Fermer tous les onglets"
    +
     #: lazarusidestrconsts.rsclosecurrentpage
     msgid "Close current page"
    -msgstr "Fermer la page en cours"
    +msgstr "Fermer l'onglet actif"
     
    +#: lazarusidestrconsts.rscloseleft
    +msgid "Close tab(s) on the left"
    +msgstr "Fermer le(s) onglet(s) de gauche"
    +
    +#: lazarusidestrconsts.rscloseothers
    +msgid "Close other tab(s)"
    +msgstr "Fermer tous les autre(s) onglet(s)"
    +
    +#: lazarusidestrconsts.rscloseright
    +msgid "Close tab(s) on the right"
    +msgstr "Fermer le(s) onglet(s) de droite"
    +
     #: lazarusidestrconsts.rsconditionaldefines
     msgid "Conditional defines"
     msgstr "Définitions conditionnelles"
    
    searchresultview.patch (19,309 bytes)
  • searchresultview_191208.patch (16,870 bytes)
    Index: ide/searchresultview.lfm
    ===================================================================
    --- ide/searchresultview.lfm	(revision 62137)
    +++ ide/searchresultview.lfm	(working copy)
    @@ -1,84 +1,146 @@
     object SearchResultsView: TSearchResultsView
    -  Left = 344
    -  Height = 273
    -  Top = 327
    -  Width = 799
    -  BorderIcons = [biSystemMenu]
    +  Left = 250
    +  Height = 300
    +  Top = 250
    +  Width = 896
    +  AutoSize = True
       Caption = 'SearchResultsView'
    -  ClientHeight = 273
    -  ClientWidth = 799
    +  ClientHeight = 300
    +  ClientWidth = 896
    +  Constraints.MinWidth = 400
       KeyPreview = True
       OnClose = FormClose
       OnCreate = Form1Create
       OnKeyDown = FormKeyDown
    -  LCLVersion = '1.9.0.0'
    +  Position = poDefault
    +  LCLVersion = '2.1.0.0'
       object ResultsNoteBook: TPageControl
    -    AnchorSideTop.Control = ToolBar
    -    AnchorSideTop.Side = asrBottom
         Left = 0
    -    Height = 251
    -    Top = 22
    -    Width = 799
    -    Anchors = [akTop, akLeft, akRight, akBottom]
    +    Height = 274
    +    Top = 26
    +    Width = 896
    +    Align = alClient
         MultiLine = True
    -    TabOrder = 1
    +    TabOrder = 0
         OnChange = ResultsNoteBookPageChanged
    -    OnChanging = ResultsNoteBookChanging
         OnCloseTabClicked = ResultsNoteBookClosetabclicked
         OnMouseDown = ResultsNoteBookMouseDown
    +    OnResize = ResultsNoteBookResize
         Options = [nboShowCloseButtons, nboMultiLine]
       end
    -  object ToolBar: TToolBar
    +  object ControlBar1: TPanel
         Left = 0
    -    Height = 22
    +    Height = 26
         Top = 0
    -    Width = 55
    -    Align = alNone
    -    AutoSize = True
    -    EdgeBorders = []
    -    TabOrder = 2
    -    object SearchAgainButton: TToolButton
    -      Left = 1
    -      Top = 0
    -      Caption = 'SearchAgainButton'
    -      OnClick = SearchAgainButtonClick
    +    Width = 896
    +    Align = alTop
    +    BevelOuter = bvNone
    +    ClientHeight = 26
    +    ClientWidth = 896
    +    TabOrder = 1
    +    TabStop = True
    +    object ToolBar: TToolBar
    +      Left = 3
    +      Height = 22
    +      Top = 2
    +      Width = 47
    +      Align = alNone
    +      Anchors = [akTop, akLeft, akBottom]
    +      AutoSize = True
    +      EdgeInner = esNone
    +      EdgeOuter = esNone
    +      TabOrder = 0
    +      object SearchAgainButton: TToolButton
    +        Left = 1
    +        Top = 0
    +        AutoSize = True
    +        Caption = 'SearchAgainButton'
    +        ImageIndex = 0
    +        OnClick = SearchAgainButtonClick
    +      end
    +      object ClosePageButton: TToolButton
    +        Left = 24
    +        Top = 0
    +        Caption = 'ClosePageButton'
    +        ImageIndex = 1
    +        OnClick = ClosePageButtonClick
    +      end
         end
    -    object ClosePageButton: TToolButton
    -      Left = 24
    -      Top = 0
    -      Caption = 'ClosePageButton'
    -      OnClick = ClosePageButtonClick
    +    object SearchInListEdit: TTreeFilterEdit
    +      Left = 53
    +      Height = 23
    +      Top = 2
    +      Width = 716
    +      ButtonWidth = 23
    +      Anchors = [akTop, akLeft, akRight, akBottom]
    +      BorderSpacing.Left = 2
    +      BorderSpacing.Bottom = 1
    +      BorderSpacing.Around = 2
    +      AutoSize = False
    +      NumGlyphs = 1
    +      MaxLength = 0
    +      TabOrder = 1
    +      OnChange = SearchInListChange
         end
    -    object ToolButton3: TToolButton
    -      Left = 47
    +    object CloseTabs: TToolBar
    +      Left = 774
           Height = 22
    -      Top = 0
    -      Caption = 'ToolButton3'
    -      Style = tbsSeparator
    +      Top = 3
    +      Width = 118
    +      Align = alNone
    +      Anchors = [akTop, akRight, akBottom]
    +      BorderSpacing.Around = 2
    +      EdgeBorders = []
    +      EdgeInner = esNone
    +      EdgeOuter = esNone
    +      Indent = 2
    +      TabOrder = 2
    +      Wrapable = False
    +      object tbbCloseLeft: TToolButton
    +        AnchorSideRight.Control = ToolButton1
    +        Left = 2
    +        Top = 0
    +        Action = actCloseLeft
    +      end
    +      object tbbCloseRight: TToolButton
    +        Left = 64
    +        Top = 0
    +        Action = actCloseRight
    +      end
    +      object tbbCloseOthers: TToolButton
    +        Left = 33
    +        Top = 0
    +        Action = actCloseOthers
    +      end
    +      object ToolButton1: TToolButton
    +        Left = 25
    +        Height = 22
    +        Top = 0
    +        Style = tbsSeparator
    +      end
    +      object ToolButton3: TToolButton
    +        Left = 87
    +        Height = 22
    +        Top = 0
    +        Style = tbsSeparator
    +      end
    +      object tbbCloseAll: TToolButton
    +        Left = 95
    +        Top = 0
    +        Action = actCloseAll
    +      end
    +      object ToolButton2: TToolButton
    +        Left = 56
    +        Height = 22
    +        Top = 0
    +        Caption = 'ToolButton2'
    +        Style = tbsSeparator
    +      end
         end
       end
    -  object SearchInListEdit: TTreeFilterEdit
    -    AnchorSideLeft.Control = ToolBar
    -    AnchorSideLeft.Side = asrBottom
    -    AnchorSideTop.Control = ToolBar
    -    AnchorSideTop.Side = asrCenter
    -    AnchorSideRight.Control = Owner
    -    AnchorSideRight.Side = asrBottom
    -    Left = 61
    -    Height = 23
    -    Top = 0
    -    Width = 738
    -    ButtonWidth = 23
    -    Anchors = [akTop, akLeft, akRight]
    -    BorderSpacing.Left = 6
    -    NumGlyphs = 1
    -    MaxLength = 0
    -    TabOrder = 0
    -    OnChange = SearchInListChange
    -  end
       object popList: TPopupMenu
    -    Left = 190
    -    Top = 133
    +    left = 190
    +    top = 133
         object mniCopyItem: TMenuItem
           Caption = 'Copy Item'
           OnClick = mniCopyItemClick
    @@ -104,9 +166,10 @@
         end
       end
       object ActionList: TActionList
    -    Left = 93
    -    Top = 133
    +    left = 93
    +    top = 133
         object actClosePage: TAction
    +      ImageIndex = 1
           OnExecute = ClosePageButtonClick
           ShortCut = 16499
         end
    @@ -118,5 +181,22 @@
           OnExecute = actPrevPageExecute
           ShortCut = 24585
         end
    +    object actCloseLeft: TAction
    +      ImageIndex = 5
    +      OnExecute = tbbCloseLeftClick
    +    end
    +    object actCloseOthers: TAction
    +      ImageIndex = 6
    +      OnExecute = tbbCloseOthersClick
    +    end
    +    object actCloseRight: TAction
    +      ImageIndex = 7
    +      OnExecute = tbbCloseRightClick
    +    end
    +    object actCloseAll: TAction
    +      ImageIndex = 8
    +      OnExecute = tbbCloseAllClick
    +      ShortCut = 24691
    +    end
       end
     end
    Index: ide/searchresultview.pp
    ===================================================================
    --- ide/searchresultview.pp	(revision 62137)
    +++ ide/searchresultview.pp	(working copy)
    @@ -40,7 +40,7 @@
       Classes, SysUtils, strutils, Laz_AVL_Tree,
       // LCL
       LCLProc, LCLType, LCLIntf, Forms, Controls, Graphics, ComCtrls, Menus, Clipbrd,
    -  ActnList,
    +  ActnList, ExtCtrls,
       // LazControls
       TreeFilterEdit,
       // LazUtils
    @@ -53,7 +53,7 @@
     
     type
       { TLazSearchMatchPos }
    -  
    +
       TLazSearchMatchPos = class(TObject)
       private
         FFileEndPos: TPoint;
    @@ -131,14 +131,19 @@
         function ItemsAsStrings: TStrings;
       end;
     
    -
       { TSearchResultsView }
     
       TSearchResultsView = class(TForm)
         actClosePage: TAction;
    +    actCloseLeft: TAction;
    +    actCloseOthers: TAction;
    +    actCloseRight: TAction;
    +    actCloseAll: TAction;
         actNextPage: TAction;
         actPrevPage: TAction;
         ActionList: TActionList;
    +    ClosePageButton1: TToolButton;
    +    ControlBar1: TPanel;
         MenuItem1: TMenuItem;
         mniCollapseAll: TMenuItem;
         mniExpandAll: TMenuItem;
    @@ -145,15 +150,28 @@
         mniCopySelected: TMenuItem;
         mniCopyAll: TMenuItem;
         mniCopyItem: TMenuItem;
    +    pnlToolBars: TPanel;
         popList: TPopupMenu;
         ResultsNoteBook: TPageControl;
    +    tbbCloseLeft: TToolButton;
    +    tbbCloseOthers: TToolButton;
    +    tbbCloseRight: TToolButton;
         ToolBar: TToolBar;
         SearchAgainButton: TToolButton;
    -    ToolButton3: TToolButton;
    +    CloseTabs: TToolBar;
    +    ToolButton1: TToolButton;
         ClosePageButton: TToolButton;
         SearchInListEdit: TTreeFilterEdit;
    +    ToolButton2: TToolButton;
    +    ToolButton3: TToolButton;
    +    tbbCloseAll: TToolButton;
         procedure actNextPageExecute(Sender: TObject);
         procedure actPrevPageExecute(Sender: TObject);
    +    procedure ResultsNoteBookResize(Sender: TObject);
    +    procedure tbbCloseAllClick(Sender: TObject);
    +    procedure tbbCloseLeftClick(Sender: TObject);
    +    procedure tbbCloseOthersClick(Sender: TObject);
    +    procedure tbbCloseRightClick(Sender: TObject);
         procedure ClosePageButtonClick(Sender: TObject);
         procedure Form1Create(Sender: TObject);
         procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
    @@ -183,6 +201,9 @@
         procedure TreeViewMouseDown(Sender: TObject; Button: TMouseButton;
           Shift: TShiftState; X, Y: Integer);
       private
    +    type
    +      TOnSide = (osLeft, osOthers, osRight); { Handling of multi tab closure }
    +  private
         FMaxItems: integer;
         FFocusTreeViewInOnChange: Boolean;
         FFocusTreeViewInEndUpdate: Boolean;
    @@ -196,6 +217,9 @@
         function GetItems(Index: integer): TStrings;
         procedure SetMaxItems(const AValue: integer);
         procedure UpdateToolbar;
    +    function  GetPagesOnActiveLine(aOnSide : TOnSide = osOthers):TFPlist;
    +    procedure ClosePageOnSides(aOnSide : TOnSide);
    +    procedure UpdateCloseTabs(aState : boolean);
       protected
         procedure Loaded; override;
         procedure ActivateControl(aWinControl: TWinControl);
    @@ -268,7 +292,7 @@
       Dest.ShownFilename := Src.ShownFilename;
       Result := True;
     end;
    -  
    +
     function GetTreeSelectedItemsAsText(ATreeView: TCustomTreeView): string;
     var
       sl: TStringList;
    @@ -301,6 +325,12 @@
       SearchAgainButton.Hint:=rsStartANewSearch;
       ClosePageButton.Hint := rsCloseCurrentPage;
       SearchInListEdit.Hint:=rsFilterTheListWithString;
    +  { Close tabs buttons }
    +  actCloseLeft.Hint:=rsCloseLeft;
    +  actCloseRight.Hint:=rsCloseRight;
    +  actCloseOthers.Hint:=rsCloseOthers;
    +  actCloseAll.Hint:=rsCloseAll;
    +
       CloseCommand := IDECommandList.FindIDECommand(ecClose);
       if CloseCommand <> nil then
       begin
    @@ -325,6 +355,13 @@
       ClosePageButton.ImageIndex := IDEImages.LoadImage('menu_close');
       ActionList.Images := IDEImages.Images_16;
       actClosePage.ImageIndex := IDEImages.LoadImage('menu_close');
    +  { Close tabs buttons }
    +  CloseTabs.Images := IDEImages.Images_16;
    +  actCloseLeft.ImageIndex   := IDEImages.LoadImage('tab_close_L');
    +  actCloseOthers.ImageIndex := IDEImages.LoadImage('tab_close_LR');
    +  actCloseRight.ImageIndex  := IDEImages.LoadImage('tab_close_R');
    +  actCloseAll.ImageIndex    := IDEImages.LoadImage('tab_close_All');
    +  UpdateCloseTabs(False);
     end;
     
     procedure TSearchResultsView.FormClose(Sender: TObject; var CloseAction: TCloseAction);
    @@ -345,7 +382,7 @@
       end;
     end;
     
    -procedure TSearchResultsView.FormKeyDown(Sender: TObject; var Key: Word; 
    +procedure TSearchResultsView.FormKeyDown(Sender: TObject; var Key: Word;
       Shift: TShiftState);
     begin
       if (Key = VK_ESCAPE) then
    @@ -352,7 +389,7 @@
       begin
         Key := VK_UNKNOWN;
         Close;
    -  end;  
    +  end;
     end;
     
     procedure TSearchResultsView.mniCopyAllClick(Sender: TObject);
    @@ -422,13 +459,81 @@
     procedure TSearchResultsView.actNextPageExecute(Sender: TObject);
     begin
       ResultsNoteBook.SelectNextPage(True);
    +  UpdateCloseTabs(True);
     end;
     
     procedure TSearchResultsView.actPrevPageExecute(Sender: TObject);
     begin
       ResultsNoteBook.SelectNextPage(False);
    +  UpdateCloseTabs(True);
     end;
     
    +procedure TSearchResultsView.ResultsNoteBookResize(Sender: TObject);
    +begin
    +  UpdateCloseTabs(ResultsNoteBook.PageCount>0);
    +end;
    +
    +{ Handling of tabs closure. Only tabs on pages at the level of active page in
    +  multiline ResultsNoteBook will be closed by Left / Others and Right }
    +procedure TSearchResultsView.ClosePageOnSides(aOnSide: TOnSide);
    +var
    +  lvPageList: TFPList = nil;
    +  lCurTabSheet: TTabSheet;
    +  ix: integer;
    +  lTabSheet: TTabSheet;
    +  lNeedsRefresh : boolean = false;
    +  lCurTV:TLazSearchResultTV;
    +begin
    +  lvPageList := GetPagesOnActiveLine(aOnSide);
    +  with ResultsNoteBook do begin
    +    lCurTabSheet := ActivePage;
    +    if aOnSide = osLeft then
    +      ix := lvPageList.IndexOf(lCurTabSheet)-1
    +    else
    +      ix := lvPageList.Count - 1;
    +    while ix >= 0 do begin
    +      lTabSheet := TTabSheet(lvPageList[ix]);
    +      if lTabSheet = lCurTabSheet then begin
    +        if aOnSide = osRight then
    +          break;
    +      end
    +      else begin
    +        ClosePage(lTabSheet.TabIndex);
    +        lNeedsRefresh := True;
    +      end;
    +      Dec(ix);
    +    end;
    +  end;
    +  lvPageList.Free;
    +  if lNeedsRefresh then begin { ~bk force realign alClient }
    +    lCurTV := GetTreeView(ResultsNoteBook.PageIndex);
    +    if assigned(lCurTV) then
    +      lCurTV.Height:=lCurTV.Height+1;
    +  end;
    +end;
    +
    +procedure TSearchResultsView.tbbCloseLeftClick(Sender: TObject);
    +begin
    +  ClosePageOnSides(osLeft);
    +end;
    +
    +procedure TSearchResultsView.tbbCloseOthersClick(Sender: TObject);
    +begin
    +  ClosePageOnSides(osOthers);
    +end;
    +
    +procedure TSearchResultsView.tbbCloseRightClick(Sender: TObject);
    +begin
    +  ClosePageOnSides(osRight);
    +end;
    +
    +procedure TSearchResultsView.tbbCloseAllClick(Sender: TObject);
    +begin
    +  with ResultsNoteBook do
    +    while PageCount>0 do
    +      ClosePage(PageCount-1);
    +end;
    +
     {Keeps track of the Index of the Item the mouse is over, Sets ShowHint to true
     if the Item length is longer than the TreeView client width.}
     procedure TSearchResultsView.LazTVMousemove(Sender: TObject; Shift: TShiftState;
    @@ -503,6 +608,7 @@
         if FFocusTreeViewInOnChange then
           ActivateControl(CurrentTV);
       end;
    +  Application.ProcessMessages; // UI must be updated before updating the ToolBar
       UpdateToolbar;
     end;
     
    @@ -684,7 +790,9 @@
         ResultsNoteBook.Pages[PageIndex].Free;
       end;
       if ResultsNoteBook.PageCount = 0 then
    -    Close;
    +    Close
    +  else
    +    UpdateCloseTabs(True);
     end;
     
     {Sets the Items from the treeview on the currently selected page in the TNoteBook}
    @@ -737,8 +845,91 @@
       SearchAgainButton.Enabled := state;
       ClosePageButton.Enabled := state;
       SearchInListEdit.Enabled := state;
    +  if state then
    +    UpdateCloseTabs(state);
     end;
     
    +{ Returns a list of all pages (visible tabs) on the same line of Tabs as
    +  the ActivaPage }
    +function TSearchResultsView.GetPagesOnActiveLine(aOnSide: TOnSide {=osOthers}): TFPlist;
    +var
    +  lActiveMidY: integer;
    +  lActiveRect : TRect;
    +  lRect, lLastRect: TRect;
    +  lActiveIndex : integer;
    +  ix: integer;
    +begin
    +  Result := nil;
    +  with ResultsNoteBook do begin
    +    if Assigned(ActivePage) then begin
    +      Result := TFPList.Create;
    +      lActiveIndex := ResultsNoteBook.ActivePageIndex;
    +      lActiveRect := TabRect(lActiveIndex);
    +      lActiveMidY := lActiveRect.Top + (lActiveRect.Bottom - lActiveRect.Top) div 2;
    +      { Search closable tabs left of current tab }
    +      if aOnSide in [osLeft, osOthers] then begin
    +        lLastRect := lActiveRect;
    +        for ix := lActiveIndex - 1 downto 0 do begin
    +          lRect := TabRect(ix);
    +          if (lRect.Top < lActiveMidY) and (lRect.Bottom > lActiveMidY) then begin
    +            if lRect.Right<=lLastRect.Left then begin
    +              Result.Insert(0, Pages[ix]);
    +              lLastRect := lRect;
    +            end
    +            else
    +              break;
    +          end
    +          else
    +            break;
    +        end;
    +      end;
    +      { Current tab }
    +      Result.Add(Pages[lActiveIndex]);
    +      { Search closable tabs right of current tab }
    +      if aOnSide in [osOthers, osRight] then begin
    +        lLastRect := lActiveRect;
    +        for ix := lActiveIndex + 1 to PageCount - 1  do begin
    +          lRect := TabRect(ix);
    +          if (lRect.Top < lActiveMidY) and (lRect.Bottom > lActiveMidY) then begin
    +            if lRect.Left>=lLastRect.Right then begin
    +              Result.Add(Pages[ix]);
    +              lLastRect := lRect;
    +            end
    +            else
    +              break;
    +          end
    +          else
    +            break;
    +        end;
    +      end;
    +    end;
    +  end;
    +end;
    +
    +procedure TSearchResultsView.UpdateCloseTabs(aState: boolean);
    +var
    +  lPageList: TFPlist = nil;
    +  lActiveIx : integer = -1;
    +begin
    +  with ResultsNoteBook do begin
    +    if aState and (PageCount>0) then begin
    +      lPageList := GetPagesOnActiveLine;
    +      if Assigned(lPageList) and (lPageList.Count>0) then
    +        repeat
    +          inc(lActiveIx);
    +          if lPageList[lActiveIx]=Pointer(ActivePage) then
    +            break;
    +        until lActiveIx>=lPageList.Count -1;
    +    end;
    +    aState := aState and Assigned(lPageList);
    +    actCloseLeft.Enabled  := aState and (lActiveIx>0);
    +    actCloseOthers.Enabled:= aState and (lPageList.Count>1);
    +    actCloseRight.Enabled := aState and (lActiveIx<lPageList.Count-1);
    +    actCloseAll.Enabled   := aState;
    +  end;
    +  lPageList.Free;
    +end;
    +
     procedure TSearchResultsView.ResultsNoteBookClosetabclicked(Sender: TObject);
     begin
       if (Sender is TTabSheet) then
    @@ -769,7 +960,7 @@
         Key:=VK_UNKNOWN;
         if Assigned(FOnSelectionChanged) then
           FOnSelectionChanged(Self);
    -  end;     
    +  end;
     end;
     
     { Add Result will create a tab in the Results view window with an new
    
  • searchresultview_on_62364.patch (8,833 bytes)
    Index: ide/searchresultview.lfm
    ===================================================================
    --- ide/searchresultview.lfm	(revision 62372)
    +++ ide/searchresultview.lfm	(working copy)
    @@ -15,9 +15,9 @@
       LCLVersion = '2.1.0.0'
       object ResultsNoteBook: TPageControl
         Left = 0
    -    Height = 274
    +    Height = 247
         Top = 26
    -    Width = 896
    +    Width = 799
         Align = alClient
         MultiLine = True
         TabOrder = 0
    @@ -25,17 +25,17 @@
         OnCloseTabClicked = ResultsNoteBookClosetabclicked
         OnMouseDown = ResultsNoteBookMouseDown
         OnResize = ResultsNoteBookResize
    -    Options = [nboShowCloseButtons, nboMultiLine]
    +    Options = [nboShowCloseButtons, nboMultiLine, nboDoChangeOnSetIndex]
       end
       object ControlBar1: TPanel
         Left = 0
         Height = 26
         Top = 0
    -    Width = 896
    +    Width = 799
         Align = alTop
         BevelOuter = bvNone
         ClientHeight = 26
    -    ClientWidth = 896
    +    ClientWidth = 799
         TabOrder = 1
         TabStop = True
         object ToolBar: TToolBar
    @@ -69,7 +69,7 @@
           Left = 53
           Height = 23
           Top = 2
    -      Width = 716
    +      Width = 619
           ButtonWidth = 23
           Anchors = [akTop, akLeft, akRight, akBottom]
           BorderSpacing.Left = 2
    @@ -82,7 +82,7 @@
           OnChange = SearchInListChange
         end
         object CloseTabs: TToolBar
    -      Left = 774
    +      Left = 677
           Height = 22
           Top = 3
           Width = 118
    Index: ide/searchresultview.pp
    ===================================================================
    --- ide/searchresultview.pp	(revision 62372)
    +++ ide/searchresultview.pp	(working copy)
    @@ -210,6 +210,7 @@
         FWorkedSearchText: string;
         FOnSelectionChanged: TNotifyEvent;
         FMouseOverIndex: integer;
    +    FClosingTabs: boolean;
         function BeautifyPageName(const APageName: string): string;
         function GetPageIndex(const APageName: string): integer;
         function GetTreeView(APageIndex: integer): TLazSearchResultTV;
    @@ -217,8 +218,10 @@
         function GetItems(Index: integer): TStrings;
         procedure SetMaxItems(const AValue: integer);
         procedure UpdateToolbar;
    -    function  GetPagesOnActiveLine:TFPlist;
    +    function  GetPagesOnActiveLine(aOnSide : TOnSide = osOthers):TFPlist;
         procedure ClosePageOnSides(aOnSide : TOnSide);
    +    procedure ClosePageBegin;
    +    procedure ClosePageEnd;
         procedure UpdateCloseTabs(aState : boolean);
       protected
         procedure Loaded; override;
    @@ -309,7 +312,6 @@
       sl.Free;
     end;
     
    -
     { TSearchResultsView }
     
     procedure TSearchResultsView.Form1Create(Sender: TObject);
    @@ -320,7 +322,7 @@
       ResultsNoteBook.Options:= ResultsNoteBook.Options+[nboShowCloseButtons];
       ResultsNoteBook.Update;
     
    -  Name:=NonModalIDEWindowNames[nmiwSearchResultsView];
    +  Name:=NonModalIDEWindowNames[nmiwSearchResultsViewName];
       Caption:=lisMenuViewSearchResults;
     
       SearchAgainButton.Hint:=rsStartANewSearch;
    @@ -460,13 +462,11 @@
     procedure TSearchResultsView.actNextPageExecute(Sender: TObject);
     begin
       ResultsNoteBook.SelectNextPage(True);
    -  UpdateCloseTabs(True);
     end;
     
     procedure TSearchResultsView.actPrevPageExecute(Sender: TObject);
     begin
       ResultsNoteBook.SelectNextPage(False);
    -  UpdateCloseTabs(True);
     end;
     
     procedure TSearchResultsView.ResultsNoteBookResize(Sender: TObject);
    @@ -483,9 +483,9 @@
       ix: integer;
       lTabSheet: TTabSheet;
       lNeedsRefresh : boolean = false;
    -  lCurTV:TLazSearchResultTV;
     begin
    -  lvPageList := GetPagesOnActiveLine;
    +  ClosePageBegin;
    +  lvPageList := GetPagesOnActiveLine(aOnSide);
       with ResultsNoteBook do begin
         lCurTabSheet := ActivePage;
         if aOnSide = osLeft then
    @@ -506,13 +506,22 @@
         end;
       end;
       lvPageList.Free;
    -  if lNeedsRefresh then begin { ~bk force realign alClient }
    -    lCurTV := GetTreeView(ResultsNoteBook.PageIndex);
    -    if assigned(lCurTV) then
    -      lCurTV.Height:=lCurTV.Height+1;
    -  end;
    +  ClosePageEnd;
    +  if lNeedsRefresh then
    +    lCurTabSheet.Height := lCurTabSheet.Height + 1;
    +  UpdateToolBar;
     end;
     
    +procedure TSearchResultsView.ClosePageBegin;
    +begin
    +  FClosingTabs := True;
    +end;
    +
    +procedure TSearchResultsView.ClosePageEnd;
    +begin
    +  FClosingTabs := False;
    +end;
    +
     procedure TSearchResultsView.tbbCloseLeftClick(Sender: TObject);
     begin
       ClosePageOnSides(osLeft);
    @@ -849,39 +858,60 @@
         UpdateCloseTabs(state);
     end;
     
    -// <wp> a good tip as usual
    -// https://forum.lazarus.freepascal.org/index.php/
    -//   topic,39500.msg271287.html?PHPSESSID=t8cr445mhbtlnqm2jq0ckj2ul6#msg271287
    -{ Returns a list of all pages on the same line of Tabs as the ActivaPage }
    -function TSearchResultsView.GetPagesOnActiveLine: TFPlist;
    +{ Returns a list of all pages (visible tabs) on the same line of Tabs as
    +  the ActivaPage }
    +function TSearchResultsView.GetPagesOnActiveLine(aOnSide: TOnSide {=osOthers}): TFPlist;
     var
    -  lRect: TRect;
    -  lActiveMidX: integer;
       lActiveMidY: integer;
    +  lActiveRect : TRect;
    +  lRect, lLastRect: TRect;
    +  lActiveIndex : integer;
       ix: integer;
     begin
    -  Result := TFPList.Create;
    +  Result := nil;
       with ResultsNoteBook do begin
    -    if ActivePage = Nil then Exit;
    -    lRect := TabRect(ActivePage.TabIndex);
    -    lActiveMidX := (lRect.Right - lRect.Left) div 2;
    -    lActiveMidY := (lRect.Bottom - lRect.Top) div 2;
    -    // Some widgetsets returned a negative value from Bottom-Top calculation
    -    //  although they might be fixed now.
    -    if lActiveMidY < 0 then begin
    -      DebugLn(['TSearchResultsView.GetPagesOnActiveLine: TabRect Bottom-Top calculation'+
    -               ' for ActivePage returned a negative value "', lActiveMidY, '".']);
    -      lActiveMidY := -lActiveMidY;
    +    if Assigned(ActivePage) then begin
    +      Result := TFPList.Create;
    +      lActiveIndex := ResultsNoteBook.ActivePageIndex;
    +      lActiveRect := TabRect(lActiveIndex);
    +      lActiveMidY := lActiveRect.Top + (lActiveRect.Bottom - lActiveRect.Top) div 2;
    +      { Search closable tabs left of current tab }
    +      if aOnSide in [osLeft, osOthers] then begin
    +        lLastRect := lActiveRect;
    +        for ix := lActiveIndex - 1 downto 0 do begin
    +          lRect := TabRect(ix);
    +          if (lRect.Top < lActiveMidY) and (lRect.Bottom > lActiveMidY) then begin
    +            if lRect.Right<=lLastRect.Left then begin
    +              Result.Insert(0, Pages[ix]);
    +              lLastRect := lRect;
    +            end
    +            else
    +              break;
    +          end
    +          else
    +            break;
    +        end;
    +      end;
    +      { Current tab }
    +      Result.Add(Pages[lActiveIndex]);
    +      { Search closable tabs right of current tab }
    +      if aOnSide in [osOthers, osRight] then begin
    +        lLastRect := lActiveRect;
    +        for ix := lActiveIndex + 1 to PageCount - 1  do begin
    +          lRect := TabRect(ix);
    +          if (lRect.Top < lActiveMidY) and (lRect.Bottom > lActiveMidY) then begin
    +            if lRect.Left>=lLastRect.Right then begin
    +              Result.Add(Pages[ix]);
    +              lLastRect := lRect;
    +            end
    +            else
    +              break;
    +          end
    +          else
    +            break;
    +        end;
    +      end;
         end;
    -    //DebugLn(['Width=',Width, ' MidX=',lActiveMidX, ' MidY=',lActiveMidY]);
    -    //DebugLn(['Rect(TabIndex)=[',dbgs(lRect), ']']);
    -    for ix := 0 to PageCount - 1 do begin
    -      lRect := TabRect(ix);
    -      //DebugLn(['Rect=[',dbgs(lRect), ']']);
    -      if (lRect.Top < lActiveMidY) and (lRect.Bottom > lActiveMidY)
    -      and (lRect.Right > lActiveMidX) and (lRect.Left < Width - lActiveMidX) then
    -        Result.Add(Pages[ix]);
    -    end;
       end;
     end;
     
    @@ -890,19 +920,25 @@
       lPageList: TFPlist = nil;
       lActiveIx : integer = -1;
     begin
    -  if aState and (ResultsNoteBook.PageCount>0) then begin
    -    lPageList := GetPagesOnActiveLine;
    -    if lPageList.Count>0 then
    -      repeat
    -        inc(lActiveIx);
    -        if lPageList[lActiveIx]=Pointer(ResultsNoteBook.ActivePage) then
    -          Break;
    -      until lActiveIx>=lPageList.Count -1
    -    else
    -      FreeAndNil(lPageList);
    +  if FClosingTabs then
    +    exit;
    +
    +  Application.ProcessMessages; // UI must be up to date before checking close buttons
    +
    +  with ResultsNoteBook do begin
    +    if aState and (PageCount>0) then begin
    +      lPageList := GetPagesOnActiveLine;
    +      if Assigned(lPageList) and (lPageList.Count>0) then
    +        repeat
    +          inc(lActiveIx);
    +          if lPageList[lActiveIx]=Pointer(ActivePage) then
    +            break;
    +        until lActiveIx>=lPageList.Count -1;
    +    end;
       end;
       aState := aState and Assigned(lPageList);
       actCloseLeft.Enabled  := aState and (lActiveIx>0);
    +  { !!! Left to right boolean evaluation -> first test aState !!! }
       actCloseOthers.Enabled:= aState and (lPageList.Count>1);
       actCloseRight.Enabled := aState and (lActiveIx<lPageList.Count-1);
       actCloseAll.Enabled   := aState;
    
  • searchresultview_on_62364_1.patch (11,878 bytes)
    Index: ide/searchresultview.lfm
    ===================================================================
    --- ide/searchresultview.lfm	(revision 62372)
    +++ ide/searchresultview.lfm	(working copy)
    @@ -1,12 +1,13 @@
     object SearchResultsView: TSearchResultsView
       Left = 344
    -  Height = 273
    +  Height = 275
       Top = 327
    -  Width = 799
    +  Width = 722
       BorderIcons = [biSystemMenu]
       Caption = 'SearchResultsView'
    -  ClientHeight = 273
    -  ClientWidth = 799
    +  ClientHeight = 275
    +  ClientWidth = 722
    +  Constraints.MinHeight = 100
       Constraints.MinWidth = 400
       KeyPreview = True
       OnClose = FormClose
    @@ -15,9 +16,9 @@
       LCLVersion = '2.1.0.0'
       object ResultsNoteBook: TPageControl
         Left = 0
    -    Height = 274
    +    Height = 249
         Top = 26
    -    Width = 896
    +    Width = 722
         Align = alClient
         MultiLine = True
         TabOrder = 0
    @@ -25,17 +26,17 @@
         OnCloseTabClicked = ResultsNoteBookClosetabclicked
         OnMouseDown = ResultsNoteBookMouseDown
         OnResize = ResultsNoteBookResize
    -    Options = [nboShowCloseButtons, nboMultiLine]
    +    Options = [nboShowCloseButtons, nboMultiLine, nboDoChangeOnSetIndex]
       end
       object ControlBar1: TPanel
         Left = 0
         Height = 26
         Top = 0
    -    Width = 896
    +    Width = 722
         Align = alTop
         BevelOuter = bvNone
         ClientHeight = 26
    -    ClientWidth = 896
    +    ClientWidth = 722
         TabOrder = 1
         TabStop = True
         object ToolBar: TToolBar
    @@ -69,7 +70,7 @@
           Left = 53
           Height = 23
           Top = 2
    -      Width = 716
    +      Width = 542
           ButtonWidth = 23
           Anchors = [akTop, akLeft, akRight, akBottom]
           BorderSpacing.Left = 2
    @@ -82,7 +83,7 @@
           OnChange = SearchInListChange
         end
         object CloseTabs: TToolBar
    -      Left = 774
    +      Left = 600
           Height = 22
           Top = 3
           Width = 118
    @@ -138,8 +139,8 @@
         end
       end
       object popList: TPopupMenu
    -    left = 190
    -    top = 133
    +    left = 129
    +    top = 48
         object mniCopyItem: TMenuItem
           Caption = 'Copy Item'
           OnClick = mniCopyItemClick
    @@ -165,8 +166,8 @@
         end
       end
       object ActionList: TActionList
    -    left = 93
    -    top = 133
    +    left = 32
    +    top = 48
         object actClosePage: TAction
           ImageIndex = 1
           OnExecute = ClosePageButtonClick
    Index: ide/searchresultview.pp
    ===================================================================
    --- ide/searchresultview.pp	(revision 62372)
    +++ ide/searchresultview.pp	(working copy)
    @@ -210,6 +210,7 @@
         FWorkedSearchText: string;
         FOnSelectionChanged: TNotifyEvent;
         FMouseOverIndex: integer;
    +    FClosingTabs: boolean;
         function BeautifyPageName(const APageName: string): string;
         function GetPageIndex(const APageName: string): integer;
         function GetTreeView(APageIndex: integer): TLazSearchResultTV;
    @@ -217,9 +218,11 @@
         function GetItems(Index: integer): TStrings;
         procedure SetMaxItems(const AValue: integer);
         procedure UpdateToolbar;
    -    function  GetPagesOnActiveLine:TFPlist;
    +    function  GetPagesOnActiveLine(aOnSide : TOnSide = osOthers):TFPlist;
         procedure ClosePageOnSides(aOnSide : TOnSide);
    -    procedure UpdateCloseTabs(aState : boolean);
    +    procedure ClosePageBegin;
    +    procedure ClosePageEnd;
    +    procedure UpdateCloseButtons(aEnable : boolean);
       protected
         procedure Loaded; override;
         procedure ActivateControl(aWinControl: TWinControl);
    @@ -309,7 +312,6 @@
       sl.Free;
     end;
     
    -
     { TSearchResultsView }
     
     procedure TSearchResultsView.Form1Create(Sender: TObject);
    @@ -362,7 +364,7 @@
       actCloseOthers.ImageIndex := IDEImages.LoadImage('tab_close_LR');
       actCloseRight.ImageIndex  := IDEImages.LoadImage('tab_close_R');
       actCloseAll.ImageIndex    := IDEImages.LoadImage('tab_close_All');
    -  UpdateCloseTabs(False);
    +  UpdateCloseButtons(False);
     end;
     
     procedure TSearchResultsView.FormClose(Sender: TObject; var CloseAction: TCloseAction);
    @@ -460,18 +462,16 @@
     procedure TSearchResultsView.actNextPageExecute(Sender: TObject);
     begin
       ResultsNoteBook.SelectNextPage(True);
    -  UpdateCloseTabs(True);
     end;
     
     procedure TSearchResultsView.actPrevPageExecute(Sender: TObject);
     begin
       ResultsNoteBook.SelectNextPage(False);
    -  UpdateCloseTabs(True);
     end;
     
     procedure TSearchResultsView.ResultsNoteBookResize(Sender: TObject);
     begin
    -  UpdateCloseTabs(ResultsNoteBook.PageCount>0);
    +  UpdateCloseButtons(ResultsNoteBook.PageCount>0);
     end;
     
     { Handling of tabs closure. Only tabs on pages at the level of active page in
    @@ -483,36 +483,47 @@
       ix: integer;
       lTabSheet: TTabSheet;
       lNeedsRefresh : boolean = false;
    -  lCurTV:TLazSearchResultTV;
     begin
    -  lvPageList := GetPagesOnActiveLine;
    -  with ResultsNoteBook do begin
    -    lCurTabSheet := ActivePage;
    -    if aOnSide = osLeft then
    -      ix := lvPageList.IndexOf(lCurTabSheet)-1
    -    else
    -      ix := lvPageList.Count - 1;
    -    while ix >= 0 do begin
    -      lTabSheet := TTabSheet(lvPageList[ix]);
    -      if lTabSheet = lCurTabSheet then begin
    -        if aOnSide = osRight then
    -          break;
    -      end
    -      else begin
    -        ClosePage(lTabSheet.TabIndex);
    -        lNeedsRefresh := True;
    +  lvPageList := GetPagesOnActiveLine(aOnSide);
    +  if Assigned(lvPageList) then begin
    +    ClosePageBegin;
    +    with ResultsNoteBook do begin
    +      lCurTabSheet := ActivePage;
    +      if aOnSide = osLeft then
    +        ix := lvPageList.IndexOf(lCurTabSheet)-1
    +      else
    +        ix := lvPageList.Count - 1;
    +      while ix >= 0 do begin
    +        lTabSheet := TTabSheet(lvPageList[ix]);
    +        if lTabSheet = lCurTabSheet then begin
    +          if aOnSide = osRight then
    +            break;
    +        end
    +        else begin
    +          ClosePage(lTabSheet.TabIndex);
    +          lNeedsRefresh := True;
    +        end;
    +        Dec(ix);
           end;
    -      Dec(ix);
         end;
    +    lvPageList.Free;
    +    ClosePageEnd;
    +    if lNeedsRefresh then { Force resizing of the active TabSheet }
    +      lCurTabSheet.Height := lCurTabSheet.Height + 1;
    +    UpdateToolBar;
       end;
    -  lvPageList.Free;
    -  if lNeedsRefresh then begin { ~bk force realign alClient }
    -    lCurTV := GetTreeView(ResultsNoteBook.PageIndex);
    -    if assigned(lCurTV) then
    -      lCurTV.Height:=lCurTV.Height+1;
    -  end;
     end;
     
    +procedure TSearchResultsView.ClosePageBegin;
    +begin
    +  FClosingTabs := True;
    +end;
    +
    +procedure TSearchResultsView.ClosePageEnd;
    +begin
    +  FClosingTabs := False;
    +end;
    +
     procedure TSearchResultsView.tbbCloseLeftClick(Sender: TObject);
     begin
       ClosePageOnSides(osLeft);
    @@ -792,7 +803,7 @@
       if ResultsNoteBook.PageCount = 0 then
         Close
       else
    -    UpdateCloseTabs(True);
    +    UpdateCloseButtons(True);
     end;
     
     {Sets the Items from the treeview on the currently selected page in the TNoteBook}
    @@ -846,66 +857,98 @@
       ClosePageButton.Enabled := state;
       SearchInListEdit.Enabled := state;
       if state then
    -    UpdateCloseTabs(state);
    +    UpdateCloseButtons(state);
     end;
     
    -// <wp> a good tip as usual
    -// https://forum.lazarus.freepascal.org/index.php/
    -//   topic,39500.msg271287.html?PHPSESSID=t8cr445mhbtlnqm2jq0ckj2ul6#msg271287
    -{ Returns a list of all pages on the same line of Tabs as the ActivaPage }
    -function TSearchResultsView.GetPagesOnActiveLine: TFPlist;
    +{ Returns a list of all pages (visible tabs) on the same line of Tabs as
    +  the ActivaPage }
    +function TSearchResultsView.GetPagesOnActiveLine(aOnSide: TOnSide {=osOthers}): TFPlist;
     var
    -  lRect: TRect;
    -  lActiveMidX: integer;
       lActiveMidY: integer;
    +  lActiveRect : TRect;
    +  lRect, lLastRect: TRect;
    +  lActiveIndex : integer;
       ix: integer;
     begin
    -  Result := TFPList.Create;
    +  Result := nil;
       with ResultsNoteBook do begin
    -    if ActivePage = Nil then Exit;
    -    lRect := TabRect(ActivePage.TabIndex);
    -    lActiveMidX := (lRect.Right - lRect.Left) div 2;
    -    lActiveMidY := (lRect.Bottom - lRect.Top) div 2;
    -    // Some widgetsets returned a negative value from Bottom-Top calculation
    -    //  although they might be fixed now.
    -    if lActiveMidY < 0 then begin
    -      DebugLn(['TSearchResultsView.GetPagesOnActiveLine: TabRect Bottom-Top calculation'+
    -               ' for ActivePage returned a negative value "', lActiveMidY, '".']);
    -      lActiveMidY := -lActiveMidY;
    +    if Assigned(ActivePage) then begin
    +      Result := TFPList.Create;
    +      lActiveIndex := ResultsNoteBook.ActivePageIndex;
    +      lActiveRect := TabRect(lActiveIndex);
    +      lActiveMidY := lActiveRect.Top + (lActiveRect.Bottom - lActiveRect.Top) div 2;
    +      { Search closable tabs left of current tab }
    +      if aOnSide in [osLeft, osOthers] then begin
    +        lLastRect := lActiveRect;
    +        for ix := lActiveIndex - 1 downto 0 do begin
    +          lRect := TabRect(ix);
    +          if (lRect.Top < lActiveMidY) and (lRect.Bottom > lActiveMidY) then begin
    +            if lRect.Right<=lLastRect.Left then begin
    +              Result.Insert(0, Pages[ix]);
    +              lLastRect := lRect;
    +            end
    +            else
    +              break;
    +          end
    +          else
    +            break;
    +        end;
    +      end;
    +      { Current tab }
    +      Result.Add(Pages[lActiveIndex]);
    +      { Search closable tabs right of current tab }
    +      if aOnSide in [osOthers, osRight] then begin
    +        lLastRect := lActiveRect;
    +        for ix := lActiveIndex + 1 to PageCount - 1  do begin
    +          lRect := TabRect(ix);
    +          if (lRect.Top < lActiveMidY) and (lRect.Bottom > lActiveMidY) then begin
    +            if lRect.Left>=lLastRect.Right then begin
    +              Result.Add(Pages[ix]);
    +              lLastRect := lRect;
    +            end
    +            else
    +              break;
    +          end
    +          else
    +            break;
    +        end;
    +      end;
         end;
    -    //DebugLn(['Width=',Width, ' MidX=',lActiveMidX, ' MidY=',lActiveMidY]);
    -    //DebugLn(['Rect(TabIndex)=[',dbgs(lRect), ']']);
    -    for ix := 0 to PageCount - 1 do begin
    -      lRect := TabRect(ix);
    -      //DebugLn(['Rect=[',dbgs(lRect), ']']);
    -      if (lRect.Top < lActiveMidY) and (lRect.Bottom > lActiveMidY)
    -      and (lRect.Right > lActiveMidX) and (lRect.Left < Width - lActiveMidX) then
    -        Result.Add(Pages[ix]);
    -    end;
       end;
     end;
     
    -procedure TSearchResultsView.UpdateCloseTabs(aState: boolean);
    +procedure TSearchResultsView.UpdateCloseButtons(aEnable: boolean);
     var
       lPageList: TFPlist = nil;
       lActiveIx : integer = -1;
     begin
    -  if aState and (ResultsNoteBook.PageCount>0) then begin
    -    lPageList := GetPagesOnActiveLine;
    -    if lPageList.Count>0 then
    -      repeat
    -        inc(lActiveIx);
    -        if lPageList[lActiveIx]=Pointer(ResultsNoteBook.ActivePage) then
    -          Break;
    -      until lActiveIx>=lPageList.Count -1
    -    else
    -      FreeAndNil(lPageList);
    +  if FClosingTabs then
    +    exit;
    +
    +  Application.ProcessMessages; { UI must be up to date before searching candidate tabs }
    +
    +  with ResultsNoteBook do begin
    +    if aEnable and (PageCount>0) then begin
    +      lPageList := GetPagesOnActiveLine;
    +      if Assigned(lPageList) and (lPageList.Count>0) then
    +        repeat
    +          inc(lActiveIx);
    +          if lPageList[lActiveIx]=Pointer(ActivePage) then
    +            break;
    +        until lActiveIx>=lPageList.Count -1;
    +    end;
       end;
    -  aState := aState and Assigned(lPageList);
    -  actCloseLeft.Enabled  := aState and (lActiveIx>0);
    -  actCloseOthers.Enabled:= aState and (lPageList.Count>1);
    -  actCloseRight.Enabled := aState and (lActiveIx<lPageList.Count-1);
    -  actCloseAll.Enabled   := aState;
    +  aEnable := aEnable and Assigned(lPageList);
    +  actCloseLeft.Enabled  := aEnable and (lActiveIx>0);
    +  if aEnable then begin
    +    actCloseOthers.Enabled:= lPageList.Count>1;
    +    actCloseRight.Enabled := lActiveIx<(lPageList.Count-1);
    +  end
    +  else begin
    +    actCloseOthers.Enabled:= False;
    +    actCloseRight.Enabled := False;
    +  end;
    +  actCloseAll.Enabled   := aEnable;
       lPageList.Free;
     end;
     
    

Relationships

related to 0036412 resolvedJuha Manninen TWin32WSCustomTabControl.GetTabRect doesn't return correct rectangle 

Activities

BrunoK

2019-11-30 16:33

reporter  

searchresultview_new.png (4,003 bytes)
searchresultview_new.png (4,003 bytes)
searchresultview.pp.patch (15,872 bytes)
Index: ide/searchresultview.lfm
===================================================================
--- ide/searchresultview.lfm	(revision 62137)
+++ ide/searchresultview.lfm	(working copy)
@@ -1,46 +1,61 @@
 object SearchResultsView: TSearchResultsView
-  Left = 344
-  Height = 273
-  Top = 327
-  Width = 799
-  BorderIcons = [biSystemMenu]
+  Left = 250
+  Height = 300
+  Top = 250
+  Width = 896
+  AutoSize = True
   Caption = 'SearchResultsView'
-  ClientHeight = 273
-  ClientWidth = 799
+  ClientHeight = 300
+  ClientWidth = 896
+  Constraints.MinWidth = 400
   KeyPreview = True
   OnClose = FormClose
   OnCreate = Form1Create
   OnKeyDown = FormKeyDown
-  LCLVersion = '1.9.0.0'
+  Position = poDefault
+  LCLVersion = '2.1.0.0'
   object ResultsNoteBook: TPageControl
-    AnchorSideTop.Control = ToolBar
-    AnchorSideTop.Side = asrBottom
     Left = 0
-    Height = 251
-    Top = 22
-    Width = 799
-    Anchors = [akTop, akLeft, akRight, akBottom]
+    Height = 274
+    Top = 26
+    Width = 896
+    Align = alClient
     MultiLine = True
-    TabOrder = 1
+    TabOrder = 0
     OnChange = ResultsNoteBookPageChanged
-    OnChanging = ResultsNoteBookChanging
     OnCloseTabClicked = ResultsNoteBookClosetabclicked
     OnMouseDown = ResultsNoteBookMouseDown
+    OnResize = ResultsNoteBookResize
     Options = [nboShowCloseButtons, nboMultiLine]
   end
+  object ControlBar1: TPanel
+    Left = 0
+    Height = 26
+    Top = 0
+    Width = 896
+    Align = alTop
+    BevelOuter = bvNone
+    ClientHeight = 26
+    ClientWidth = 896
+    TabOrder = 1
+    TabStop = True
   object ToolBar: TToolBar
-    Left = 0
+      Left = 3
     Height = 22
-    Top = 0
-    Width = 55
+      Top = 2
+      Width = 47
     Align = alNone
+      Anchors = [akTop, akLeft, akBottom]
     AutoSize = True
-    EdgeBorders = []
-    TabOrder = 2
+      EdgeInner = esNone
+      EdgeOuter = esNone
+      TabOrder = 0
     object SearchAgainButton: TToolButton
       Left = 1
       Top = 0
+        AutoSize = True
       Caption = 'SearchAgainButton'
+        ImageIndex = 0
       OnClick = SearchAgainButtonClick
     end
     object ClosePageButton: TToolButton
@@ -47,38 +62,85 @@
       Left = 24
       Top = 0
       Caption = 'ClosePageButton'
+        ImageIndex = 1
       OnClick = ClosePageButtonClick
     end
-    object ToolButton3: TToolButton
-      Left = 47
-      Height = 22
-      Top = 0
-      Caption = 'ToolButton3'
-      Style = tbsSeparator
     end
-  end
   object SearchInListEdit: TTreeFilterEdit
-    AnchorSideLeft.Control = ToolBar
-    AnchorSideLeft.Side = asrBottom
-    AnchorSideTop.Control = ToolBar
-    AnchorSideTop.Side = asrCenter
-    AnchorSideRight.Control = Owner
-    AnchorSideRight.Side = asrBottom
-    Left = 61
+      Left = 53
     Height = 23
-    Top = 0
-    Width = 738
+      Top = 2
+      Width = 716
     ButtonWidth = 23
-    Anchors = [akTop, akLeft, akRight]
-    BorderSpacing.Left = 6
+      Anchors = [akTop, akLeft, akRight, akBottom]
+      BorderSpacing.Left = 2
+      BorderSpacing.Bottom = 1
+      BorderSpacing.Around = 2
+      AutoSize = False
     NumGlyphs = 1
     MaxLength = 0
-    TabOrder = 0
+      TabOrder = 1
     OnChange = SearchInListChange
   end
+    object CloseTabs: TToolBar
+      Left = 774
+      Height = 22
+      Top = 3
+      Width = 118
+      Align = alNone
+      Anchors = [akTop, akRight, akBottom]
+      BorderSpacing.Around = 2
+      EdgeBorders = []
+      EdgeInner = esNone
+      EdgeOuter = esNone
+      Indent = 2
+      TabOrder = 2
+      Wrapable = False
+      object tbbCloseLeft: TToolButton
+        AnchorSideRight.Control = ToolButton1
+        Left = 2
+        Top = 0
+        Action = actCloseLeft
+      end
+      object tbbCloseRight: TToolButton
+        Left = 64
+        Top = 0
+        Action = actCloseRight
+      end
+      object tbbCloseOthers: TToolButton
+        Left = 33
+        Top = 0
+        Action = actCloseOthers
+      end
+      object ToolButton1: TToolButton
+        Left = 25
+        Height = 22
+        Top = 0
+        Style = tbsSeparator
+      end
+      object ToolButton3: TToolButton
+        Left = 87
+        Height = 22
+        Top = 0
+        Style = tbsSeparator
+      end
+      object tbbCloseAll: TToolButton
+        Left = 95
+        Top = 0
+        Action = actCloseAll
+      end
+      object ToolButton2: TToolButton
+        Left = 56
+        Height = 22
+        Top = 0
+        Caption = 'ToolButton2'
+        Style = tbsSeparator
+      end
+    end
+  end
   object popList: TPopupMenu
-    Left = 190
-    Top = 133
+    left = 190
+    top = 133
     object mniCopyItem: TMenuItem
       Caption = 'Copy Item'
       OnClick = mniCopyItemClick
@@ -104,9 +166,10 @@
     end
   end
   object ActionList: TActionList
-    Left = 93
-    Top = 133
+    left = 93
+    top = 133
     object actClosePage: TAction
+      ImageIndex = 1
       OnExecute = ClosePageButtonClick
       ShortCut = 16499
     end
@@ -118,5 +181,22 @@
       OnExecute = actPrevPageExecute
       ShortCut = 24585
     end
+    object actCloseLeft: TAction
+      ImageIndex = 5
+      OnExecute = tbbCloseLeftClick
   end
+    object actCloseOthers: TAction
+      ImageIndex = 6
+      OnExecute = tbbCloseOthersClick
+    end
+    object actCloseRight: TAction
+      ImageIndex = 7
+      OnExecute = tbbCloseRightClick
+    end
+    object actCloseAll: TAction
+      ImageIndex = 8
+      OnExecute = tbbCloseAllClick
+      ShortCut = 24691
+    end
+  end
 end
Index: ide/searchresultview.pp
===================================================================
--- ide/searchresultview.pp	(revision 62137)
+++ ide/searchresultview.pp	(working copy)
@@ -40,7 +40,7 @@
   Classes, SysUtils, strutils, Laz_AVL_Tree,
   // LCL
   LCLProc, LCLType, LCLIntf, Forms, Controls, Graphics, ComCtrls, Menus, Clipbrd,
-  ActnList,
+  ActnList, ExtCtrls,
   // LazControls
   TreeFilterEdit,
   // LazUtils
@@ -53,7 +53,7 @@
 
 type
   { TLazSearchMatchPos }
-  
+
   TLazSearchMatchPos = class(TObject)
   private
     FFileEndPos: TPoint;
@@ -131,14 +131,19 @@
     function ItemsAsStrings: TStrings;
   end;
 
-
   { TSearchResultsView }
 
   TSearchResultsView = class(TForm)
     actClosePage: TAction;
+    actCloseLeft: TAction;
+    actCloseOthers: TAction;
+    actCloseRight: TAction;
+    actCloseAll: TAction;
     actNextPage: TAction;
     actPrevPage: TAction;
     ActionList: TActionList;
+    ClosePageButton1: TToolButton;
+    ControlBar1: TPanel;
     MenuItem1: TMenuItem;
     mniCollapseAll: TMenuItem;
     mniExpandAll: TMenuItem;
@@ -145,15 +150,28 @@
     mniCopySelected: TMenuItem;
     mniCopyAll: TMenuItem;
     mniCopyItem: TMenuItem;
+    pnlToolBars: TPanel;
     popList: TPopupMenu;
     ResultsNoteBook: TPageControl;
+    tbbCloseLeft: TToolButton;
+    tbbCloseOthers: TToolButton;
+    tbbCloseRight: TToolButton;
     ToolBar: TToolBar;
     SearchAgainButton: TToolButton;
-    ToolButton3: TToolButton;
+    CloseTabs: TToolBar;
+    ToolButton1: TToolButton;
     ClosePageButton: TToolButton;
     SearchInListEdit: TTreeFilterEdit;
+    ToolButton2: TToolButton;
+    ToolButton3: TToolButton;
+    tbbCloseAll: TToolButton;
     procedure actNextPageExecute(Sender: TObject);
     procedure actPrevPageExecute(Sender: TObject);
+    procedure ResultsNoteBookResize(Sender: TObject);
+    procedure tbbCloseAllClick(Sender: TObject);
+    procedure tbbCloseLeftClick(Sender: TObject);
+    procedure tbbCloseOthersClick(Sender: TObject);
+    procedure tbbCloseRightClick(Sender: TObject);
     procedure ClosePageButtonClick(Sender: TObject);
     procedure Form1Create(Sender: TObject);
     procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
@@ -183,6 +201,9 @@
     procedure TreeViewMouseDown(Sender: TObject; Button: TMouseButton;
       Shift: TShiftState; X, Y: Integer);
   private
+    type
+      TOnSide = (osLeft, osOthers, osRight); { Handling of multi tab closure }
+  private
     FMaxItems: integer;
     FFocusTreeViewInOnChange: Boolean;
     FFocusTreeViewInEndUpdate: Boolean;
@@ -196,6 +217,9 @@
     function GetItems(Index: integer): TStrings;
     procedure SetMaxItems(const AValue: integer);
     procedure UpdateToolbar;
+    procedure GetPagesOnActiveLine(var aList : TFPlist);
+    procedure ClosePageOnSides(aOnSide : TOnSide);
+    procedure UpdateCloseTabs(aState : boolean);
   protected
     procedure Loaded; override;
     procedure ActivateControl(aWinControl: TWinControl);
@@ -268,7 +292,7 @@
   Dest.ShownFilename := Src.ShownFilename;
   Result := True;
 end;
-  
+
 function GetTreeSelectedItemsAsText(ATreeView: TCustomTreeView): string;
 var
   sl: TStringList;
@@ -285,6 +309,7 @@
   sl.Free;
 end;
 
+
 { TSearchResultsView }
 
 procedure TSearchResultsView.Form1Create(Sender: TObject);
@@ -301,6 +326,12 @@
   SearchAgainButton.Hint:=rsStartANewSearch;
   ClosePageButton.Hint := rsCloseCurrentPage;
   SearchInListEdit.Hint:=rsFilterTheListWithString;
+  { Close tabs buttons }
+  actCloseLeft.Hint:=rsCloseLeft;
+  actCloseRight.Hint:=rsCloseRight;
+  actCloseOthers.Hint:=rsCloseOthers;
+  actCloseAll.Hint:=rsCloseAll;
+
   CloseCommand := IDECommandList.FindIDECommand(ecClose);
   if CloseCommand <> nil then
   begin
@@ -325,6 +356,13 @@
   ClosePageButton.ImageIndex := IDEImages.LoadImage('menu_close');
   ActionList.Images := IDEImages.Images_16;
   actClosePage.ImageIndex := IDEImages.LoadImage('menu_close');
+  { Close tabs buttons }
+  CloseTabs.Images := IDEImages.Images_16;
+  actCloseLeft.ImageIndex   := IDEImages.LoadImage('tab_close_L');
+  actCloseOthers.ImageIndex := IDEImages.LoadImage('tab_close_LR');
+  actCloseRight.ImageIndex  := IDEImages.LoadImage('tab_close_R');
+  actCloseAll.ImageIndex    := IDEImages.LoadImage('tab_close_All');
+  UpdateCloseTabs(False);
 end;
 
 procedure TSearchResultsView.FormClose(Sender: TObject; var CloseAction: TCloseAction);
@@ -345,7 +383,7 @@
   end;
 end;
 
-procedure TSearchResultsView.FormKeyDown(Sender: TObject; var Key: Word; 
+procedure TSearchResultsView.FormKeyDown(Sender: TObject; var Key: Word;
   Shift: TShiftState);
 begin
   if (Key = VK_ESCAPE) then
@@ -352,7 +390,7 @@
   begin
     Key := VK_UNKNOWN;
     Close;
-  end;  
+  end;
 end;
 
 procedure TSearchResultsView.mniCopyAllClick(Sender: TObject);
@@ -422,13 +460,81 @@
 procedure TSearchResultsView.actNextPageExecute(Sender: TObject);
 begin
   ResultsNoteBook.SelectNextPage(True);
+  UpdateCloseTabs(True);
 end;
 
 procedure TSearchResultsView.actPrevPageExecute(Sender: TObject);
 begin
   ResultsNoteBook.SelectNextPage(False);
+  UpdateCloseTabs(True);
 end;
 
+procedure TSearchResultsView.ResultsNoteBookResize(Sender: TObject);
+begin
+  UpdateCloseTabs(ResultsNoteBook.PageCount>0);
+end;
+
+{ Handling of tabs closure. Only tabs on pages at the level of active page in
+  multiline ResultsNoteBook will be closed by Left / Others and Right }
+procedure TSearchResultsView.ClosePageOnSides(aOnSide: TOnSide);
+var
+  lvPageList: TFPList = nil;
+  lCurTabSheet: TTabSheet;
+  ix: integer;
+  lTabSheet: TTabSheet;
+  lNeedsRefresh : boolean = false;
+  lCurTV:TLazSearchResultTV;
+begin
+  GetPagesOnActiveLine({var} lvPageList);
+  with ResultsNoteBook do begin
+    lCurTabSheet := ActivePage;
+    if aOnSide = osLeft then
+      ix := lvPageList.IndexOf(lCurTabSheet)-1
+    else
+      ix := lvPageList.Count - 1;
+    while ix >= 0 do begin
+      lTabSheet := TTabSheet(lvPageList[ix]);
+      if lTabSheet = lCurTabSheet then begin
+        if aOnSide = osRight then
+          break;
+      end
+      else begin
+        ClosePage(lTabSheet.TabIndex);
+        lNeedsRefresh := True;
+      end;
+      Dec(ix);
+    end;
+  end;
+  lvPageList.Free;
+  if lNeedsRefresh then begin { ~bk force realign alClient }
+    lCurTV := GetTreeView(ResultsNoteBook.PageIndex);
+    if assigned(lCurTV) then
+      lCurTV.Height:=lCurTV.Height+1;
+  end;
+end;
+
+procedure TSearchResultsView.tbbCloseLeftClick(Sender: TObject);
+begin
+  ClosePageOnSides(osLeft);
+end;
+
+procedure TSearchResultsView.tbbCloseOthersClick(Sender: TObject);
+begin
+  ClosePageOnSides(osOthers);
+end;
+
+procedure TSearchResultsView.tbbCloseRightClick(Sender: TObject);
+begin
+  ClosePageOnSides(osRight);
+end;
+
+procedure TSearchResultsView.tbbCloseAllClick(Sender: TObject);
+begin
+  with ResultsNoteBook do
+    while PageCount>0 do
+      ClosePage(PageCount-1);
+end;
+
 {Keeps track of the Index of the Item the mouse is over, Sets ShowHint to true
 if the Item length is longer than the TreeView client width.}
 procedure TSearchResultsView.LazTVMousemove(Sender: TObject; Shift: TShiftState;
@@ -684,7 +790,9 @@
     ResultsNoteBook.Pages[PageIndex].Free;
   end;
   if ResultsNoteBook.PageCount = 0 then
-    Close;
+    Close
+  else
+    UpdateCloseTabs(True);
 end;
 
 {Sets the Items from the treeview on the currently selected page in the TNoteBook}
@@ -737,8 +845,73 @@
   SearchAgainButton.Enabled := state;
   ClosePageButton.Enabled := state;
   SearchInListEdit.Enabled := state;
+  if state then
+    UpdateCloseTabs(state);
 end;
 
+// <wp> a good tip as usual
+// https://forum.lazarus.freepascal.org/index.php/
+//   topic,39500.msg271287.html?PHPSESSID=t8cr445mhbtlnqm2jq0ckj2ul6#msg271287
+{ Returns a list of all pages on the same line of Tabs as the ActivaPage }
+procedure TSearchResultsView.GetPagesOnActiveLine(var aList: TFPlist);
+var
+  lRect: TRect;
+  lActiveMidX: integer;
+  lActiveMidY: integer;
+  ix: integer;
+begin
+  aList := TFPList.Create;
+  with ResultsNoteBook do begin
+    if Assigned(ActivePage) then begin
+      lRect := TabRect(ActivePage.TabIndex);
+      { Problem with widgetsets (Gtk2) not returning the same coordinates as Windows }
+{$IFNDEF LCLGtk2}{General case until proven different }
+      lActiveMidX := (lRect.Right - lRect.Left) div 2;
+      lActiveMidY := (lRect.Top - lRect.Bottom) div 2;
+{$ELSE}
+      lActiveMidX := (lRect.Right - lRect.Left) div 2;
+      lActiveMidY := (lRect.Bottom - lRect.Top) div 2;
+{$endif}
+{    WriteLn('Width=',Width, ' MidX=',lActiveMidX, ' MidY=',lActiveMidY);
+     WriteLn('Rect(TabIndex)=[',dbgs(lRect), ']'); }
+      for ix := 0 to PageCount - 1 do begin
+        lRect := TabRect(ix);
+        // WriteLn('Rect=[',dbgs(lRect), ']');
+        if (lRect.Top < lActiveMidY) and (lRect.Bottom > lActiveMidY) and
+          (lRect.Right > lActiveMidX) and (lRect.Left < Width - lActiveMidX) then
+          aList.Add(Pages[ix]);
+      end;
+    end;
+  end;
+end;
+
+procedure TSearchResultsView.UpdateCloseTabs(aState: boolean);
+var
+  lvPageList: TFPlist = nil;
+  lActiveIx : integer = -1;
+begin
+  with ResultsNoteBook do begin
+    if aState and (PageCount>0) then begin
+      // WriteLn('PageCount=',dbgs(PageCount));
+      GetPagesOnActiveLine({var} lvPageList);
+      if lvPageList.Count>0 then
+        repeat
+          inc(lActiveIx);
+          if lvPageList[lActiveIx]=Pointer(ActivePage) then
+            Break;
+        until lActiveIx>=lvPageList.Count -1
+      else
+        FreeAndNil(lvPageList);
+    end;
+    aState := aState and Assigned(lvPageList);
+    actCloseLeft.Enabled  := aState and (lActiveIx>0);
+    actCloseOthers.Enabled:= aState and (lvPageList.Count>1);
+    actCloseRight.Enabled := aState and (lActiveIx<lvPageList.Count-1);
+    actCloseAll.Enabled   := aState;
+  end;
+  lvPageList.Free;
+end;
+
 procedure TSearchResultsView.ResultsNoteBookClosetabclicked(Sender: TObject);
 begin
   if (Sender is TTabSheet) then
@@ -769,7 +942,7 @@
     Key:=VK_UNKNOWN;
     if Assigned(FOnSelectionChanged) then
       FOnSelectionChanged(Self);
-  end;     
+  end;
 end;
 
 { Add Result will create a tab in the Results view window with an new
searchresultview.pp.patch (15,872 bytes)
lazarusidestrconsts.pas.patch (645 bytes)
Index: lazarusidestrconsts.pas
===================================================================
--- images/lazarusidestrconsts.pas	(revision 62137)
+++ images/lazarusidestrconsts.pas	(working copy)
@@ -5465,6 +5465,10 @@
   rsStartANewSearch = 'Start a new search';
   rsCloseCurrentPage = 'Close current page';
   rsFilterTheListWithString = 'Filter the lines in list with a string';
+  rsCloseLeft = 'Close tab(s) on the left';
+  rsCloseRight = 'Close tab(s) on the right';
+  rsCloseOthers = 'Close other tab(s)';
+  rsCloseAll = 'Close all tabs';
 
   // Application Bundle
   lisErrorLoadingFrom = 'Error loading %s from%s%s%s%s';
laz_images_list.txt.patch (756 bytes)
Index: images/laz_images_list.txt
===================================================================
--- images/laz_images_list.txt	(revision 62137)
+++ images/laz_images_list.txt	(working copy)
@@ -106,6 +106,18 @@
 actions/restore_defaults.png
 actions/restore_defaults_150.png
 actions/restore_defaults_200.png
+actions/tab_close_All.png
+actions/tab_close_All_150.png
+actions/tab_close_All_200.png
+actions/tab_close_L.png
+actions/tab_close_L_150.png
+actions/tab_close_L_200.png
+actions/tab_close_LR.png
+actions/tab_close_LR_150.png
+actions/tab_close_LR_200.png
+actions/tab_close_R.png
+actions/tab_close_R_150.png
+actions/tab_close_R_200.png
 codecompletion/cc_class.png
 codecompletion/cc_class_150.png
 codecompletion/cc_class_200.png
searchresultview_png.7z (13,991 bytes)
patch.po.fr.txt (976 bytes)
Index: languages/lazaruside.fr.po
===================================================================
--- languages/lazaruside.fr.po	(revision 62111)
+++ languages/lazaruside.fr.po	(working copy)
@@ -20854,10 +20854,26 @@
 msgid "Character set:"
 msgstr "Jeu de caractères :"
 
+#: lazarusidestrconsts.rscloseall
+msgid "Close all tabs"
+msgstr "Fermer tous les onglets"
+
 #: lazarusidestrconsts.rsclosecurrentpage
 msgid "Close current page"
-msgstr "Fermer la page en cours"
+msgstr "Fermer l'onglet actif"
 
+#: lazarusidestrconsts.rscloseleft
+msgid "Close tab(s) on the left"
+msgstr "Fermer le(s) onglet(s) de gauche"
+
+#: lazarusidestrconsts.rscloseothers
+msgid "Close other tab(s)"
+msgstr "Fermer tous les autre(s) onglet(s)"
+
+#: lazarusidestrconsts.rscloseright
+msgid "Close tab(s) on the right"
+msgstr "Fermer le(s) onglet(s) de droite"
+
 #: lazarusidestrconsts.rsconditionaldefines
 msgid "Conditional defines"
 msgstr "Définitions conditionnelles"
patch.po.fr.txt (976 bytes)

Juha Manninen

2019-12-01 12:10

developer   ~0119563

Last edited: 2019-12-01 12:14

View 2 revisions

I did not test the patches yet but here are some initial thoughts.
You could add all changes into one single patch instead of many separate ones. Creating the patch relative to main lazarus directory makes it possible.

> - Close all tabs
Why would you want to do that? Closing the whole window makes more sense then IMO.

> +procedure TSearchResultsView.GetPagesOnActiveLine(var aList: TFPlist)
This procedure always creates the aList. Logically it could be a function returning TFPlist.

Why is the following test needed? It means there is a serious bug in GTK2 binding which should be fixed.
+{$IFNDEF LCLGtk2}{General case until proven different }
+ lActiveMidX := (lRect.Right - lRect.Left) div 2;
+ lActiveMidY := (lRect.Top - lRect.Bottom) div 2;
+{$ELSE}
+ lActiveMidX := (lRect.Right - lRect.Left) div 2;
+ lActiveMidY := (lRect.Bottom - lRect.Top) div 2;
+{$endif}

BrunoK

2019-12-01 13:48

reporter  

searchresultview.patch (19,309 bytes)
Index: ide/lazarusidestrconsts.pas
===================================================================
--- ide/lazarusidestrconsts.pas	(revision 62137)
+++ ide/lazarusidestrconsts.pas	(working copy)
@@ -1042,7 +1042,7 @@
   lisTheOutputDirectoryIsMissing = 'The output directory "%s" is missing.';
   lisCreateIt = 'Create it';
   lisInvalidFileName = 'Invalid file name';
-  lisTheTargetFileNameIsADirectory = 'The target file name is a directory.';
+  lisTheTargetFileNameIsADirectory = '\"%s\"\nThe target file name is a directory.';
   lisNotAValidFppkgPrefix ='Free Pascal compiler not found at the given prefix.';
   lisIncorrectFppkgConfiguration = 'there is a problem with the Fppkg configuration. (%s)';
   lisFppkgCompilerProblem = 'there is a problem with the Free Pascal compiler executable, ';
@@ -5465,6 +5465,10 @@
   rsStartANewSearch = 'Start a new search';
   rsCloseCurrentPage = 'Close current page';
   rsFilterTheListWithString = 'Filter the lines in list with a string';
+  rsCloseLeft = 'Close tab(s) on the left';
+  rsCloseRight = 'Close tab(s) on the right';
+  rsCloseOthers = 'Close other tab(s)';
+  rsCloseAll = 'Close all tabs';
 
   // Application Bundle
   lisErrorLoadingFrom = 'Error loading %s from%s%s%s%s';
Index: ide/searchresultview.lfm
===================================================================
--- ide/searchresultview.lfm	(revision 62137)
+++ ide/searchresultview.lfm	(working copy)
@@ -1,84 +1,146 @@
 object SearchResultsView: TSearchResultsView
-  Left = 344
-  Height = 273
-  Top = 327
-  Width = 799
-  BorderIcons = [biSystemMenu]
+  Left = 250
+  Height = 300
+  Top = 250
+  Width = 896
+  AutoSize = True
   Caption = 'SearchResultsView'
-  ClientHeight = 273
-  ClientWidth = 799
+  ClientHeight = 300
+  ClientWidth = 896
+  Constraints.MinWidth = 400
   KeyPreview = True
   OnClose = FormClose
   OnCreate = Form1Create
   OnKeyDown = FormKeyDown
-  LCLVersion = '1.9.0.0'
+  Position = poDefault
+  LCLVersion = '2.1.0.0'
   object ResultsNoteBook: TPageControl
-    AnchorSideTop.Control = ToolBar
-    AnchorSideTop.Side = asrBottom
     Left = 0
-    Height = 251
-    Top = 22
-    Width = 799
-    Anchors = [akTop, akLeft, akRight, akBottom]
+    Height = 274
+    Top = 26
+    Width = 896
+    Align = alClient
     MultiLine = True
-    TabOrder = 1
+    TabOrder = 0
     OnChange = ResultsNoteBookPageChanged
-    OnChanging = ResultsNoteBookChanging
     OnCloseTabClicked = ResultsNoteBookClosetabclicked
     OnMouseDown = ResultsNoteBookMouseDown
+    OnResize = ResultsNoteBookResize
     Options = [nboShowCloseButtons, nboMultiLine]
   end
-  object ToolBar: TToolBar
+  object ControlBar1: TPanel
     Left = 0
-    Height = 22
+    Height = 26
     Top = 0
-    Width = 55
-    Align = alNone
-    AutoSize = True
-    EdgeBorders = []
-    TabOrder = 2
-    object SearchAgainButton: TToolButton
-      Left = 1
-      Top = 0
-      Caption = 'SearchAgainButton'
-      OnClick = SearchAgainButtonClick
+    Width = 896
+    Align = alTop
+    BevelOuter = bvNone
+    ClientHeight = 26
+    ClientWidth = 896
+    TabOrder = 1
+    TabStop = True
+    object ToolBar: TToolBar
+      Left = 3
+      Height = 22
+      Top = 2
+      Width = 47
+      Align = alNone
+      Anchors = [akTop, akLeft, akBottom]
+      AutoSize = True
+      EdgeInner = esNone
+      EdgeOuter = esNone
+      TabOrder = 0
+      object SearchAgainButton: TToolButton
+        Left = 1
+        Top = 0
+        AutoSize = True
+        Caption = 'SearchAgainButton'
+        ImageIndex = 0
+        OnClick = SearchAgainButtonClick
+      end
+      object ClosePageButton: TToolButton
+        Left = 24
+        Top = 0
+        Caption = 'ClosePageButton'
+        ImageIndex = 1
+        OnClick = ClosePageButtonClick
+      end
     end
-    object ClosePageButton: TToolButton
-      Left = 24
-      Top = 0
-      Caption = 'ClosePageButton'
-      OnClick = ClosePageButtonClick
+    object SearchInListEdit: TTreeFilterEdit
+      Left = 53
+      Height = 23
+      Top = 2
+      Width = 716
+      ButtonWidth = 23
+      Anchors = [akTop, akLeft, akRight, akBottom]
+      BorderSpacing.Left = 2
+      BorderSpacing.Bottom = 1
+      BorderSpacing.Around = 2
+      AutoSize = False
+      NumGlyphs = 1
+      MaxLength = 0
+      TabOrder = 1
+      OnChange = SearchInListChange
     end
-    object ToolButton3: TToolButton
-      Left = 47
+    object CloseTabs: TToolBar
+      Left = 774
       Height = 22
-      Top = 0
-      Caption = 'ToolButton3'
-      Style = tbsSeparator
+      Top = 3
+      Width = 118
+      Align = alNone
+      Anchors = [akTop, akRight, akBottom]
+      BorderSpacing.Around = 2
+      EdgeBorders = []
+      EdgeInner = esNone
+      EdgeOuter = esNone
+      Indent = 2
+      TabOrder = 2
+      Wrapable = False
+      object tbbCloseLeft: TToolButton
+        AnchorSideRight.Control = ToolButton1
+        Left = 2
+        Top = 0
+        Action = actCloseLeft
+      end
+      object tbbCloseRight: TToolButton
+        Left = 64
+        Top = 0
+        Action = actCloseRight
+      end
+      object tbbCloseOthers: TToolButton
+        Left = 33
+        Top = 0
+        Action = actCloseOthers
+      end
+      object ToolButton1: TToolButton
+        Left = 25
+        Height = 22
+        Top = 0
+        Style = tbsSeparator
+      end
+      object ToolButton3: TToolButton
+        Left = 87
+        Height = 22
+        Top = 0
+        Style = tbsSeparator
+      end
+      object tbbCloseAll: TToolButton
+        Left = 95
+        Top = 0
+        Action = actCloseAll
+      end
+      object ToolButton2: TToolButton
+        Left = 56
+        Height = 22
+        Top = 0
+        Caption = 'ToolButton2'
+        Style = tbsSeparator
+      end
     end
   end
-  object SearchInListEdit: TTreeFilterEdit
-    AnchorSideLeft.Control = ToolBar
-    AnchorSideLeft.Side = asrBottom
-    AnchorSideTop.Control = ToolBar
-    AnchorSideTop.Side = asrCenter
-    AnchorSideRight.Control = Owner
-    AnchorSideRight.Side = asrBottom
-    Left = 61
-    Height = 23
-    Top = 0
-    Width = 738
-    ButtonWidth = 23
-    Anchors = [akTop, akLeft, akRight]
-    BorderSpacing.Left = 6
-    NumGlyphs = 1
-    MaxLength = 0
-    TabOrder = 0
-    OnChange = SearchInListChange
-  end
   object popList: TPopupMenu
-    Left = 190
-    Top = 133
+    left = 190
+    top = 133
     object mniCopyItem: TMenuItem
       Caption = 'Copy Item'
       OnClick = mniCopyItemClick
@@ -104,9 +166,10 @@
     end
   end
   object ActionList: TActionList
-    Left = 93
-    Top = 133
+    left = 93
+    top = 133
     object actClosePage: TAction
+      ImageIndex = 1
       OnExecute = ClosePageButtonClick
       ShortCut = 16499
     end
@@ -118,5 +181,22 @@
       OnExecute = actPrevPageExecute
       ShortCut = 24585
     end
+    object actCloseLeft: TAction
+      ImageIndex = 5
+      OnExecute = tbbCloseLeftClick
+    end
+    object actCloseOthers: TAction
+      ImageIndex = 6
+      OnExecute = tbbCloseOthersClick
+    end
+    object actCloseRight: TAction
+      ImageIndex = 7
+      OnExecute = tbbCloseRightClick
+    end
+    object actCloseAll: TAction
+      ImageIndex = 8
+      OnExecute = tbbCloseAllClick
+      ShortCut = 24691
+    end
   end
 end
Index: ide/searchresultview.pp
===================================================================
--- ide/searchresultview.pp	(revision 62137)
+++ ide/searchresultview.pp	(working copy)
@@ -40,7 +40,7 @@
   Classes, SysUtils, strutils, Laz_AVL_Tree,
   // LCL
   LCLProc, LCLType, LCLIntf, Forms, Controls, Graphics, ComCtrls, Menus, Clipbrd,
-  ActnList,
+  ActnList, ExtCtrls,
   // LazControls
   TreeFilterEdit,
   // LazUtils
@@ -53,7 +53,7 @@
 
 type
   { TLazSearchMatchPos }
-  
+
   TLazSearchMatchPos = class(TObject)
   private
     FFileEndPos: TPoint;
@@ -131,14 +131,19 @@
     function ItemsAsStrings: TStrings;
   end;
 
-
   { TSearchResultsView }
 
   TSearchResultsView = class(TForm)
     actClosePage: TAction;
+    actCloseLeft: TAction;
+    actCloseOthers: TAction;
+    actCloseRight: TAction;
+    actCloseAll: TAction;
     actNextPage: TAction;
     actPrevPage: TAction;
     ActionList: TActionList;
+    ClosePageButton1: TToolButton;
+    ControlBar1: TPanel;
     MenuItem1: TMenuItem;
     mniCollapseAll: TMenuItem;
     mniExpandAll: TMenuItem;
@@ -145,15 +150,28 @@
     mniCopySelected: TMenuItem;
     mniCopyAll: TMenuItem;
     mniCopyItem: TMenuItem;
+    pnlToolBars: TPanel;
     popList: TPopupMenu;
     ResultsNoteBook: TPageControl;
+    tbbCloseLeft: TToolButton;
+    tbbCloseOthers: TToolButton;
+    tbbCloseRight: TToolButton;
     ToolBar: TToolBar;
     SearchAgainButton: TToolButton;
-    ToolButton3: TToolButton;
+    CloseTabs: TToolBar;
+    ToolButton1: TToolButton;
     ClosePageButton: TToolButton;
     SearchInListEdit: TTreeFilterEdit;
+    ToolButton2: TToolButton;
+    ToolButton3: TToolButton;
+    tbbCloseAll: TToolButton;
     procedure actNextPageExecute(Sender: TObject);
     procedure actPrevPageExecute(Sender: TObject);
+    procedure ResultsNoteBookResize(Sender: TObject);
+    procedure tbbCloseAllClick(Sender: TObject);
+    procedure tbbCloseLeftClick(Sender: TObject);
+    procedure tbbCloseOthersClick(Sender: TObject);
+    procedure tbbCloseRightClick(Sender: TObject);
     procedure ClosePageButtonClick(Sender: TObject);
     procedure Form1Create(Sender: TObject);
     procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
@@ -183,6 +201,9 @@
     procedure TreeViewMouseDown(Sender: TObject; Button: TMouseButton;
       Shift: TShiftState; X, Y: Integer);
   private
+    type
+      TOnSide = (osLeft, osOthers, osRight); { Handling of multi tab closure }
+  private
     FMaxItems: integer;
     FFocusTreeViewInOnChange: Boolean;
     FFocusTreeViewInEndUpdate: Boolean;
@@ -196,6 +217,9 @@
     function GetItems(Index: integer): TStrings;
     procedure SetMaxItems(const AValue: integer);
     procedure UpdateToolbar;
+    function  GetPagesOnActiveLine:TFPlist;
+    procedure ClosePageOnSides(aOnSide : TOnSide);
+    procedure UpdateCloseTabs(aState : boolean);
   protected
     procedure Loaded; override;
     procedure ActivateControl(aWinControl: TWinControl);
@@ -268,7 +292,7 @@
   Dest.ShownFilename := Src.ShownFilename;
   Result := True;
 end;
-  
+
 function GetTreeSelectedItemsAsText(ATreeView: TCustomTreeView): string;
 var
   sl: TStringList;
@@ -285,6 +309,7 @@
   sl.Free;
 end;
 
+
 { TSearchResultsView }
 
 procedure TSearchResultsView.Form1Create(Sender: TObject);
@@ -301,6 +326,12 @@
   SearchAgainButton.Hint:=rsStartANewSearch;
   ClosePageButton.Hint := rsCloseCurrentPage;
   SearchInListEdit.Hint:=rsFilterTheListWithString;
+  { Close tabs buttons }
+  actCloseLeft.Hint:=rsCloseLeft;
+  actCloseRight.Hint:=rsCloseRight;
+  actCloseOthers.Hint:=rsCloseOthers;
+  actCloseAll.Hint:=rsCloseAll;
+
   CloseCommand := IDECommandList.FindIDECommand(ecClose);
   if CloseCommand <> nil then
   begin
@@ -325,6 +356,13 @@
   ClosePageButton.ImageIndex := IDEImages.LoadImage('menu_close');
   ActionList.Images := IDEImages.Images_16;
   actClosePage.ImageIndex := IDEImages.LoadImage('menu_close');
+  { Close tabs buttons }
+  CloseTabs.Images := IDEImages.Images_16;
+  actCloseLeft.ImageIndex   := IDEImages.LoadImage('tab_close_L');
+  actCloseOthers.ImageIndex := IDEImages.LoadImage('tab_close_LR');
+  actCloseRight.ImageIndex  := IDEImages.LoadImage('tab_close_R');
+  actCloseAll.ImageIndex    := IDEImages.LoadImage('tab_close_All');
+  UpdateCloseTabs(False);
 end;
 
 procedure TSearchResultsView.FormClose(Sender: TObject; var CloseAction: TCloseAction);
@@ -345,7 +383,7 @@
   end;
 end;
 
-procedure TSearchResultsView.FormKeyDown(Sender: TObject; var Key: Word; 
+procedure TSearchResultsView.FormKeyDown(Sender: TObject; var Key: Word;
   Shift: TShiftState);
 begin
   if (Key = VK_ESCAPE) then
@@ -352,7 +390,7 @@
   begin
     Key := VK_UNKNOWN;
     Close;
-  end;  
+  end;
 end;
 
 procedure TSearchResultsView.mniCopyAllClick(Sender: TObject);
@@ -422,13 +460,81 @@
 procedure TSearchResultsView.actNextPageExecute(Sender: TObject);
 begin
   ResultsNoteBook.SelectNextPage(True);
+  UpdateCloseTabs(True);
 end;
 
 procedure TSearchResultsView.actPrevPageExecute(Sender: TObject);
 begin
   ResultsNoteBook.SelectNextPage(False);
+  UpdateCloseTabs(True);
 end;
 
+procedure TSearchResultsView.ResultsNoteBookResize(Sender: TObject);
+begin
+  UpdateCloseTabs(ResultsNoteBook.PageCount>0);
+end;
+
+{ Handling of tabs closure. Only tabs on pages at the level of active page in
+  multiline ResultsNoteBook will be closed by Left / Others and Right }
+procedure TSearchResultsView.ClosePageOnSides(aOnSide: TOnSide);
+var
+  lvPageList: TFPList = nil;
+  lCurTabSheet: TTabSheet;
+  ix: integer;
+  lTabSheet: TTabSheet;
+  lNeedsRefresh : boolean = false;
+  lCurTV:TLazSearchResultTV;
+begin
+  lvPageList := GetPagesOnActiveLine;
+  with ResultsNoteBook do begin
+    lCurTabSheet := ActivePage;
+    if aOnSide = osLeft then
+      ix := lvPageList.IndexOf(lCurTabSheet)-1
+    else
+      ix := lvPageList.Count - 1;
+    while ix >= 0 do begin
+      lTabSheet := TTabSheet(lvPageList[ix]);
+      if lTabSheet = lCurTabSheet then begin
+        if aOnSide = osRight then
+          break;
+      end
+      else begin
+        ClosePage(lTabSheet.TabIndex);
+        lNeedsRefresh := True;
+      end;
+      Dec(ix);
+    end;
+  end;
+  lvPageList.Free;
+  if lNeedsRefresh then begin { ~bk force realign alClient }
+    lCurTV := GetTreeView(ResultsNoteBook.PageIndex);
+    if assigned(lCurTV) then
+      lCurTV.Height:=lCurTV.Height+1;
+  end;
+end;
+
+procedure TSearchResultsView.tbbCloseLeftClick(Sender: TObject);
+begin
+  ClosePageOnSides(osLeft);
+end;
+
+procedure TSearchResultsView.tbbCloseOthersClick(Sender: TObject);
+begin
+  ClosePageOnSides(osOthers);
+end;
+
+procedure TSearchResultsView.tbbCloseRightClick(Sender: TObject);
+begin
+  ClosePageOnSides(osRight);
+end;
+
+procedure TSearchResultsView.tbbCloseAllClick(Sender: TObject);
+begin
+  with ResultsNoteBook do
+    while PageCount>0 do
+      ClosePage(PageCount-1);
+end;
+
 {Keeps track of the Index of the Item the mouse is over, Sets ShowHint to true
 if the Item length is longer than the TreeView client width.}
 procedure TSearchResultsView.LazTVMousemove(Sender: TObject; Shift: TShiftState;
@@ -684,7 +790,9 @@
     ResultsNoteBook.Pages[PageIndex].Free;
   end;
   if ResultsNoteBook.PageCount = 0 then
-    Close;
+    Close
+  else
+    UpdateCloseTabs(True);
 end;
 
 {Sets the Items from the treeview on the currently selected page in the TNoteBook}
@@ -737,8 +845,73 @@
   SearchAgainButton.Enabled := state;
   ClosePageButton.Enabled := state;
   SearchInListEdit.Enabled := state;
+  if state then
+    UpdateCloseTabs(state);
 end;
 
+// <wp> a good tip as usual
+// https://forum.lazarus.freepascal.org/index.php/
+//   topic,39500.msg271287.html?PHPSESSID=t8cr445mhbtlnqm2jq0ckj2ul6#msg271287
+{ Returns a list of all pages on the same line of Tabs as the ActivaPage }
+function TSearchResultsView.GetPagesOnActiveLine: TFPlist;
+var
+  lRect: TRect;
+  lActiveMidX: integer;
+  lActiveMidY: integer;
+  ix: integer;
+begin
+  Result := nil;
+  with ResultsNoteBook do begin
+    if Assigned(ActivePage) then begin
+      Result := TFPList.Create;
+      lRect := TabRect(ActivePage.TabIndex);
+      { Problem with widgetsets (Gtk2) not returning the same coordinates as Windows }
+{$IFNDEF LCLGtk2}{General case until proven different }
+      lActiveMidX := (lRect.Right - lRect.Left) div 2;
+      lActiveMidY := (lRect.Top - lRect.Bottom) div 2;
+{$ELSE}
+      lActiveMidX := (lRect.Right - lRect.Left) div 2;
+      lActiveMidY := (lRect.Bottom - lRect.Top) div 2;
+{$endif}
+{    WriteLn('Width=',Width, ' MidX=',lActiveMidX, ' MidY=',lActiveMidY);
+     WriteLn('Rect(TabIndex)=[',dbgs(lRect), ']'); }
+      for ix := 0 to PageCount - 1 do begin
+        lRect := TabRect(ix);
+        // WriteLn('Rect=[',dbgs(lRect), ']');
+        if (lRect.Top < lActiveMidY) and (lRect.Bottom > lActiveMidY) and
+          (lRect.Right > lActiveMidX) and (lRect.Left < Width - lActiveMidX) then
+          Result.Add(Pages[ix]);
+      end;
+    end;
+  end;
+end;
+
+procedure TSearchResultsView.UpdateCloseTabs(aState: boolean);
+var
+  lPageList: TFPlist = nil;
+  lActiveIx : integer = -1;
+begin
+  with ResultsNoteBook do begin
+    if aState and (PageCount>0) then begin
+      lPageList := GetPagesOnActiveLine;
+      if lPageList.Count>0 then
+        repeat
+          inc(lActiveIx);
+          if lPageList[lActiveIx]=Pointer(ActivePage) then
+            Break;
+        until lActiveIx>=lPageList.Count -1
+      else
+        FreeAndNil(lPageList);
+    end;
+    aState := aState and Assigned(lPageList);
+    actCloseLeft.Enabled  := aState and (lActiveIx>0);
+    actCloseOthers.Enabled:= aState and (lPageList.Count>1);
+    actCloseRight.Enabled := aState and (lActiveIx<lPageList.Count-1);
+    actCloseAll.Enabled   := aState;
+  end;
+  lPageList.Free;
+end;
+
 procedure TSearchResultsView.ResultsNoteBookClosetabclicked(Sender: TObject);
 begin
   if (Sender is TTabSheet) then
@@ -769,7 +942,7 @@
     Key:=VK_UNKNOWN;
     if Assigned(FOnSelectionChanged) then
       FOnSelectionChanged(Self);
-  end;     
+  end;
 end;
 
 { Add Result will create a tab in the Results view window with an new
Index: images/laz_images_list.txt
===================================================================
--- images/laz_images_list.txt	(revision 62137)
+++ images/laz_images_list.txt	(working copy)
@@ -106,6 +106,18 @@
 actions/restore_defaults.png
 actions/restore_defaults_150.png
 actions/restore_defaults_200.png
+actions/tab_close_All.png
+actions/tab_close_All_150.png
+actions/tab_close_All_200.png
+actions/tab_close_L.png
+actions/tab_close_L_150.png
+actions/tab_close_L_200.png
+actions/tab_close_LR.png
+actions/tab_close_LR_150.png
+actions/tab_close_LR_200.png
+actions/tab_close_R.png
+actions/tab_close_R_150.png
+actions/tab_close_R_200.png
 codecompletion/cc_class.png
 codecompletion/cc_class_150.png
 codecompletion/cc_class_200.png
Index: languages/lazaruside.fr.po
===================================================================
--- languages/lazaruside.fr.po	(revision 62137)
+++ languages/lazaruside.fr.po	(working copy)
@@ -20854,10 +20854,26 @@
 msgid "Character set:"
 msgstr "Jeu de caractères :"
 
+#: lazarusidestrconsts.rscloseall
+msgid "Close all tabs"
+msgstr "Fermer tous les onglets"
+
 #: lazarusidestrconsts.rsclosecurrentpage
 msgid "Close current page"
-msgstr "Fermer la page en cours"
+msgstr "Fermer l'onglet actif"
 
+#: lazarusidestrconsts.rscloseleft
+msgid "Close tab(s) on the left"
+msgstr "Fermer le(s) onglet(s) de gauche"
+
+#: lazarusidestrconsts.rscloseothers
+msgid "Close other tab(s)"
+msgstr "Fermer tous les autre(s) onglet(s)"
+
+#: lazarusidestrconsts.rscloseright
+msgid "Close tab(s) on the right"
+msgstr "Fermer le(s) onglet(s) de droite"
+
 #: lazarusidestrconsts.rsconditionaldefines
 msgid "Conditional defines"
 msgstr "Définitions conditionnelles"
searchresultview.patch (19,309 bytes)

BrunoK

2019-12-01 13:48

reporter   ~0119567

> JM You could add all changes into one single patch instead of many separate ones. Creating the patch relative to main lazarus directory makes it possible.
Yesterday, on W10, TortoiseSVN didn't let me do that, today it seemed to go right, probably a chair to keyboard interface error, so except for the searchresultview_png.7z icons you'll find in searchresultview.patch an apparently complete patch file.

>> - Close all tabs
> JM Why would you want to do that? Closing the whole window makes more sense then IMO.
Because closing the whole window doesn't erase the tabs, you can do ctrl-alt-f after closing the search result form and see that all tabs are still in memory. The only way to close all the result tabs was to exit+restart lazarus. When I work with lazarus for many hours I don't want to exit lazarus just to release search results.

>JM This procedure always creates the aList. Logically it could be a function returning TFPlist.
Ok done.

> Why is the following test needed? It means there is a serious bug in GTK2 binding which should be fixed.
+{$IFNDEF LCLGtk2}{General case until proven different }
Basically I'm not interested in other widgetset's than windows. Windows has multi line tabs but gtk2 doesn't, that may be a reason.

@Juha Manninen : Just mark this as "wont resolve / close" and I'll resolve the issue at once.

Juha Manninen

2019-12-01 14:39

developer   ~0119569

> Just mark this as "wont resolve / close" and I'll resolve the issue at once.

You mean resolving as "won't fix"? No, I think you have a valid idea. I only told you my first impressions. It was meant as positive feedback.
I think the {$IFNDEF LCLGtk2} test must be fixed some other way. This TSearchResultsView should not contain widgetset dependent code.

Juha Manninen

2019-12-05 21:31

developer   ~0119643

Last edited: 2019-12-05 21:36

View 3 revisions

I am testing the feature now. It works well except that the search result window does not remember its size any more. After starting Lazarus it now opens as very small and must be resized to be useful. I don't see what part of the patch could cause it.

{$IFNDEF LCLGtk2} can be eliminated by calling Abs() :
    lActiveMidY := Abs(lRect.Bottom - lRect.Top) div 2;
BTW, "Bottom - Top" looks more logical than "Top - Bottom", but only GTK2 returns a positive value with it. Strange.

BrunoK

2019-12-06 16:37

reporter   ~0119664

I think that maybe there is something wrong in win32proc.GetLCLClientBoundsOffset. The Gtk2 looks more logical. Will take a look when I have time.

BrunoK

2019-12-09 12:44

reporter   ~0119706

Last edited: 2019-12-09 12:44

View 2 revisions

Improved version tested both win32/64 and linux_mint_gtk2.

Windows : requires patch as uploaded in 003641.

> JM I am testing the feature now. It works well except that the search result window does not remember its size any more.
>I don't see what part of the patch could cause it.
Yes, and I do not know why. Where is the code that stores and restores the sizes of IDE windows ? Couldn't find it and thinks it is an unrelated issue that surfaced recently.



searchresultview_191208.patch (16,870 bytes)
Index: ide/searchresultview.lfm
===================================================================
--- ide/searchresultview.lfm	(revision 62137)
+++ ide/searchresultview.lfm	(working copy)
@@ -1,84 +1,146 @@
 object SearchResultsView: TSearchResultsView
-  Left = 344
-  Height = 273
-  Top = 327
-  Width = 799
-  BorderIcons = [biSystemMenu]
+  Left = 250
+  Height = 300
+  Top = 250
+  Width = 896
+  AutoSize = True
   Caption = 'SearchResultsView'
-  ClientHeight = 273
-  ClientWidth = 799
+  ClientHeight = 300
+  ClientWidth = 896
+  Constraints.MinWidth = 400
   KeyPreview = True
   OnClose = FormClose
   OnCreate = Form1Create
   OnKeyDown = FormKeyDown
-  LCLVersion = '1.9.0.0'
+  Position = poDefault
+  LCLVersion = '2.1.0.0'
   object ResultsNoteBook: TPageControl
-    AnchorSideTop.Control = ToolBar
-    AnchorSideTop.Side = asrBottom
     Left = 0
-    Height = 251
-    Top = 22
-    Width = 799
-    Anchors = [akTop, akLeft, akRight, akBottom]
+    Height = 274
+    Top = 26
+    Width = 896
+    Align = alClient
     MultiLine = True
-    TabOrder = 1
+    TabOrder = 0
     OnChange = ResultsNoteBookPageChanged
-    OnChanging = ResultsNoteBookChanging
     OnCloseTabClicked = ResultsNoteBookClosetabclicked
     OnMouseDown = ResultsNoteBookMouseDown
+    OnResize = ResultsNoteBookResize
     Options = [nboShowCloseButtons, nboMultiLine]
   end
-  object ToolBar: TToolBar
+  object ControlBar1: TPanel
     Left = 0
-    Height = 22
+    Height = 26
     Top = 0
-    Width = 55
-    Align = alNone
-    AutoSize = True
-    EdgeBorders = []
-    TabOrder = 2
-    object SearchAgainButton: TToolButton
-      Left = 1
-      Top = 0
-      Caption = 'SearchAgainButton'
-      OnClick = SearchAgainButtonClick
+    Width = 896
+    Align = alTop
+    BevelOuter = bvNone
+    ClientHeight = 26
+    ClientWidth = 896
+    TabOrder = 1
+    TabStop = True
+    object ToolBar: TToolBar
+      Left = 3
+      Height = 22
+      Top = 2
+      Width = 47
+      Align = alNone
+      Anchors = [akTop, akLeft, akBottom]
+      AutoSize = True
+      EdgeInner = esNone
+      EdgeOuter = esNone
+      TabOrder = 0
+      object SearchAgainButton: TToolButton
+        Left = 1
+        Top = 0
+        AutoSize = True
+        Caption = 'SearchAgainButton'
+        ImageIndex = 0
+        OnClick = SearchAgainButtonClick
+      end
+      object ClosePageButton: TToolButton
+        Left = 24
+        Top = 0
+        Caption = 'ClosePageButton'
+        ImageIndex = 1
+        OnClick = ClosePageButtonClick
+      end
     end
-    object ClosePageButton: TToolButton
-      Left = 24
-      Top = 0
-      Caption = 'ClosePageButton'
-      OnClick = ClosePageButtonClick
+    object SearchInListEdit: TTreeFilterEdit
+      Left = 53
+      Height = 23
+      Top = 2
+      Width = 716
+      ButtonWidth = 23
+      Anchors = [akTop, akLeft, akRight, akBottom]
+      BorderSpacing.Left = 2
+      BorderSpacing.Bottom = 1
+      BorderSpacing.Around = 2
+      AutoSize = False
+      NumGlyphs = 1
+      MaxLength = 0
+      TabOrder = 1
+      OnChange = SearchInListChange
     end
-    object ToolButton3: TToolButton
-      Left = 47
+    object CloseTabs: TToolBar
+      Left = 774
       Height = 22
-      Top = 0
-      Caption = 'ToolButton3'
-      Style = tbsSeparator
+      Top = 3
+      Width = 118
+      Align = alNone
+      Anchors = [akTop, akRight, akBottom]
+      BorderSpacing.Around = 2
+      EdgeBorders = []
+      EdgeInner = esNone
+      EdgeOuter = esNone
+      Indent = 2
+      TabOrder = 2
+      Wrapable = False
+      object tbbCloseLeft: TToolButton
+        AnchorSideRight.Control = ToolButton1
+        Left = 2
+        Top = 0
+        Action = actCloseLeft
+      end
+      object tbbCloseRight: TToolButton
+        Left = 64
+        Top = 0
+        Action = actCloseRight
+      end
+      object tbbCloseOthers: TToolButton
+        Left = 33
+        Top = 0
+        Action = actCloseOthers
+      end
+      object ToolButton1: TToolButton
+        Left = 25
+        Height = 22
+        Top = 0
+        Style = tbsSeparator
+      end
+      object ToolButton3: TToolButton
+        Left = 87
+        Height = 22
+        Top = 0
+        Style = tbsSeparator
+      end
+      object tbbCloseAll: TToolButton
+        Left = 95
+        Top = 0
+        Action = actCloseAll
+      end
+      object ToolButton2: TToolButton
+        Left = 56
+        Height = 22
+        Top = 0
+        Caption = 'ToolButton2'
+        Style = tbsSeparator
+      end
     end
   end
-  object SearchInListEdit: TTreeFilterEdit
-    AnchorSideLeft.Control = ToolBar
-    AnchorSideLeft.Side = asrBottom
-    AnchorSideTop.Control = ToolBar
-    AnchorSideTop.Side = asrCenter
-    AnchorSideRight.Control = Owner
-    AnchorSideRight.Side = asrBottom
-    Left = 61
-    Height = 23
-    Top = 0
-    Width = 738
-    ButtonWidth = 23
-    Anchors = [akTop, akLeft, akRight]
-    BorderSpacing.Left = 6
-    NumGlyphs = 1
-    MaxLength = 0
-    TabOrder = 0
-    OnChange = SearchInListChange
-  end
   object popList: TPopupMenu
-    Left = 190
-    Top = 133
+    left = 190
+    top = 133
     object mniCopyItem: TMenuItem
       Caption = 'Copy Item'
       OnClick = mniCopyItemClick
@@ -104,9 +166,10 @@
     end
   end
   object ActionList: TActionList
-    Left = 93
-    Top = 133
+    left = 93
+    top = 133
     object actClosePage: TAction
+      ImageIndex = 1
       OnExecute = ClosePageButtonClick
       ShortCut = 16499
     end
@@ -118,5 +181,22 @@
       OnExecute = actPrevPageExecute
       ShortCut = 24585
     end
+    object actCloseLeft: TAction
+      ImageIndex = 5
+      OnExecute = tbbCloseLeftClick
+    end
+    object actCloseOthers: TAction
+      ImageIndex = 6
+      OnExecute = tbbCloseOthersClick
+    end
+    object actCloseRight: TAction
+      ImageIndex = 7
+      OnExecute = tbbCloseRightClick
+    end
+    object actCloseAll: TAction
+      ImageIndex = 8
+      OnExecute = tbbCloseAllClick
+      ShortCut = 24691
+    end
   end
 end
Index: ide/searchresultview.pp
===================================================================
--- ide/searchresultview.pp	(revision 62137)
+++ ide/searchresultview.pp	(working copy)
@@ -40,7 +40,7 @@
   Classes, SysUtils, strutils, Laz_AVL_Tree,
   // LCL
   LCLProc, LCLType, LCLIntf, Forms, Controls, Graphics, ComCtrls, Menus, Clipbrd,
-  ActnList,
+  ActnList, ExtCtrls,
   // LazControls
   TreeFilterEdit,
   // LazUtils
@@ -53,7 +53,7 @@
 
 type
   { TLazSearchMatchPos }
-  
+
   TLazSearchMatchPos = class(TObject)
   private
     FFileEndPos: TPoint;
@@ -131,14 +131,19 @@
     function ItemsAsStrings: TStrings;
   end;
 
-
   { TSearchResultsView }
 
   TSearchResultsView = class(TForm)
     actClosePage: TAction;
+    actCloseLeft: TAction;
+    actCloseOthers: TAction;
+    actCloseRight: TAction;
+    actCloseAll: TAction;
     actNextPage: TAction;
     actPrevPage: TAction;
     ActionList: TActionList;
+    ClosePageButton1: TToolButton;
+    ControlBar1: TPanel;
     MenuItem1: TMenuItem;
     mniCollapseAll: TMenuItem;
     mniExpandAll: TMenuItem;
@@ -145,15 +150,28 @@
     mniCopySelected: TMenuItem;
     mniCopyAll: TMenuItem;
     mniCopyItem: TMenuItem;
+    pnlToolBars: TPanel;
     popList: TPopupMenu;
     ResultsNoteBook: TPageControl;
+    tbbCloseLeft: TToolButton;
+    tbbCloseOthers: TToolButton;
+    tbbCloseRight: TToolButton;
     ToolBar: TToolBar;
     SearchAgainButton: TToolButton;
-    ToolButton3: TToolButton;
+    CloseTabs: TToolBar;
+    ToolButton1: TToolButton;
     ClosePageButton: TToolButton;
     SearchInListEdit: TTreeFilterEdit;
+    ToolButton2: TToolButton;
+    ToolButton3: TToolButton;
+    tbbCloseAll: TToolButton;
     procedure actNextPageExecute(Sender: TObject);
     procedure actPrevPageExecute(Sender: TObject);
+    procedure ResultsNoteBookResize(Sender: TObject);
+    procedure tbbCloseAllClick(Sender: TObject);
+    procedure tbbCloseLeftClick(Sender: TObject);
+    procedure tbbCloseOthersClick(Sender: TObject);
+    procedure tbbCloseRightClick(Sender: TObject);
     procedure ClosePageButtonClick(Sender: TObject);
     procedure Form1Create(Sender: TObject);
     procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
@@ -183,6 +201,9 @@
     procedure TreeViewMouseDown(Sender: TObject; Button: TMouseButton;
       Shift: TShiftState; X, Y: Integer);
   private
+    type
+      TOnSide = (osLeft, osOthers, osRight); { Handling of multi tab closure }
+  private
     FMaxItems: integer;
     FFocusTreeViewInOnChange: Boolean;
     FFocusTreeViewInEndUpdate: Boolean;
@@ -196,6 +217,9 @@
     function GetItems(Index: integer): TStrings;
     procedure SetMaxItems(const AValue: integer);
     procedure UpdateToolbar;
+    function  GetPagesOnActiveLine(aOnSide : TOnSide = osOthers):TFPlist;
+    procedure ClosePageOnSides(aOnSide : TOnSide);
+    procedure UpdateCloseTabs(aState : boolean);
   protected
     procedure Loaded; override;
     procedure ActivateControl(aWinControl: TWinControl);
@@ -268,7 +292,7 @@
   Dest.ShownFilename := Src.ShownFilename;
   Result := True;
 end;
-  
+
 function GetTreeSelectedItemsAsText(ATreeView: TCustomTreeView): string;
 var
   sl: TStringList;
@@ -301,6 +325,12 @@
   SearchAgainButton.Hint:=rsStartANewSearch;
   ClosePageButton.Hint := rsCloseCurrentPage;
   SearchInListEdit.Hint:=rsFilterTheListWithString;
+  { Close tabs buttons }
+  actCloseLeft.Hint:=rsCloseLeft;
+  actCloseRight.Hint:=rsCloseRight;
+  actCloseOthers.Hint:=rsCloseOthers;
+  actCloseAll.Hint:=rsCloseAll;
+
   CloseCommand := IDECommandList.FindIDECommand(ecClose);
   if CloseCommand <> nil then
   begin
@@ -325,6 +355,13 @@
   ClosePageButton.ImageIndex := IDEImages.LoadImage('menu_close');
   ActionList.Images := IDEImages.Images_16;
   actClosePage.ImageIndex := IDEImages.LoadImage('menu_close');
+  { Close tabs buttons }
+  CloseTabs.Images := IDEImages.Images_16;
+  actCloseLeft.ImageIndex   := IDEImages.LoadImage('tab_close_L');
+  actCloseOthers.ImageIndex := IDEImages.LoadImage('tab_close_LR');
+  actCloseRight.ImageIndex  := IDEImages.LoadImage('tab_close_R');
+  actCloseAll.ImageIndex    := IDEImages.LoadImage('tab_close_All');
+  UpdateCloseTabs(False);
 end;
 
 procedure TSearchResultsView.FormClose(Sender: TObject; var CloseAction: TCloseAction);
@@ -345,7 +382,7 @@
   end;
 end;
 
-procedure TSearchResultsView.FormKeyDown(Sender: TObject; var Key: Word; 
+procedure TSearchResultsView.FormKeyDown(Sender: TObject; var Key: Word;
   Shift: TShiftState);
 begin
   if (Key = VK_ESCAPE) then
@@ -352,7 +389,7 @@
   begin
     Key := VK_UNKNOWN;
     Close;
-  end;  
+  end;
 end;
 
 procedure TSearchResultsView.mniCopyAllClick(Sender: TObject);
@@ -422,13 +459,81 @@
 procedure TSearchResultsView.actNextPageExecute(Sender: TObject);
 begin
   ResultsNoteBook.SelectNextPage(True);
+  UpdateCloseTabs(True);
 end;
 
 procedure TSearchResultsView.actPrevPageExecute(Sender: TObject);
 begin
   ResultsNoteBook.SelectNextPage(False);
+  UpdateCloseTabs(True);
 end;
 
+procedure TSearchResultsView.ResultsNoteBookResize(Sender: TObject);
+begin
+  UpdateCloseTabs(ResultsNoteBook.PageCount>0);
+end;
+
+{ Handling of tabs closure. Only tabs on pages at the level of active page in
+  multiline ResultsNoteBook will be closed by Left / Others and Right }
+procedure TSearchResultsView.ClosePageOnSides(aOnSide: TOnSide);
+var
+  lvPageList: TFPList = nil;
+  lCurTabSheet: TTabSheet;
+  ix: integer;
+  lTabSheet: TTabSheet;
+  lNeedsRefresh : boolean = false;
+  lCurTV:TLazSearchResultTV;
+begin
+  lvPageList := GetPagesOnActiveLine(aOnSide);
+  with ResultsNoteBook do begin
+    lCurTabSheet := ActivePage;
+    if aOnSide = osLeft then
+      ix := lvPageList.IndexOf(lCurTabSheet)-1
+    else
+      ix := lvPageList.Count - 1;
+    while ix >= 0 do begin
+      lTabSheet := TTabSheet(lvPageList[ix]);
+      if lTabSheet = lCurTabSheet then begin
+        if aOnSide = osRight then
+          break;
+      end
+      else begin
+        ClosePage(lTabSheet.TabIndex);
+        lNeedsRefresh := True;
+      end;
+      Dec(ix);
+    end;
+  end;
+  lvPageList.Free;
+  if lNeedsRefresh then begin { ~bk force realign alClient }
+    lCurTV := GetTreeView(ResultsNoteBook.PageIndex);
+    if assigned(lCurTV) then
+      lCurTV.Height:=lCurTV.Height+1;
+  end;
+end;
+
+procedure TSearchResultsView.tbbCloseLeftClick(Sender: TObject);
+begin
+  ClosePageOnSides(osLeft);
+end;
+
+procedure TSearchResultsView.tbbCloseOthersClick(Sender: TObject);
+begin
+  ClosePageOnSides(osOthers);
+end;
+
+procedure TSearchResultsView.tbbCloseRightClick(Sender: TObject);
+begin
+  ClosePageOnSides(osRight);
+end;
+
+procedure TSearchResultsView.tbbCloseAllClick(Sender: TObject);
+begin
+  with ResultsNoteBook do
+    while PageCount>0 do
+      ClosePage(PageCount-1);
+end;
+
 {Keeps track of the Index of the Item the mouse is over, Sets ShowHint to true
 if the Item length is longer than the TreeView client width.}
 procedure TSearchResultsView.LazTVMousemove(Sender: TObject; Shift: TShiftState;
@@ -503,6 +608,7 @@
     if FFocusTreeViewInOnChange then
       ActivateControl(CurrentTV);
   end;
+  Application.ProcessMessages; // UI must be updated before updating the ToolBar
   UpdateToolbar;
 end;
 
@@ -684,7 +790,9 @@
     ResultsNoteBook.Pages[PageIndex].Free;
   end;
   if ResultsNoteBook.PageCount = 0 then
-    Close;
+    Close
+  else
+    UpdateCloseTabs(True);
 end;
 
 {Sets the Items from the treeview on the currently selected page in the TNoteBook}
@@ -737,8 +845,91 @@
   SearchAgainButton.Enabled := state;
   ClosePageButton.Enabled := state;
   SearchInListEdit.Enabled := state;
+  if state then
+    UpdateCloseTabs(state);
 end;
 
+{ Returns a list of all pages (visible tabs) on the same line of Tabs as
+  the ActivaPage }
+function TSearchResultsView.GetPagesOnActiveLine(aOnSide: TOnSide {=osOthers}): TFPlist;
+var
+  lActiveMidY: integer;
+  lActiveRect : TRect;
+  lRect, lLastRect: TRect;
+  lActiveIndex : integer;
+  ix: integer;
+begin
+  Result := nil;
+  with ResultsNoteBook do begin
+    if Assigned(ActivePage) then begin
+      Result := TFPList.Create;
+      lActiveIndex := ResultsNoteBook.ActivePageIndex;
+      lActiveRect := TabRect(lActiveIndex);
+      lActiveMidY := lActiveRect.Top + (lActiveRect.Bottom - lActiveRect.Top) div 2;
+      { Search closable tabs left of current tab }
+      if aOnSide in [osLeft, osOthers] then begin
+        lLastRect := lActiveRect;
+        for ix := lActiveIndex - 1 downto 0 do begin
+          lRect := TabRect(ix);
+          if (lRect.Top < lActiveMidY) and (lRect.Bottom > lActiveMidY) then begin
+            if lRect.Right<=lLastRect.Left then begin
+              Result.Insert(0, Pages[ix]);
+              lLastRect := lRect;
+            end
+            else
+              break;
+          end
+          else
+            break;
+        end;
+      end;
+      { Current tab }
+      Result.Add(Pages[lActiveIndex]);
+      { Search closable tabs right of current tab }
+      if aOnSide in [osOthers, osRight] then begin
+        lLastRect := lActiveRect;
+        for ix := lActiveIndex + 1 to PageCount - 1  do begin
+          lRect := TabRect(ix);
+          if (lRect.Top < lActiveMidY) and (lRect.Bottom > lActiveMidY) then begin
+            if lRect.Left>=lLastRect.Right then begin
+              Result.Add(Pages[ix]);
+              lLastRect := lRect;
+            end
+            else
+              break;
+          end
+          else
+            break;
+        end;
+      end;
+    end;
+  end;
+end;
+
+procedure TSearchResultsView.UpdateCloseTabs(aState: boolean);
+var
+  lPageList: TFPlist = nil;
+  lActiveIx : integer = -1;
+begin
+  with ResultsNoteBook do begin
+    if aState and (PageCount>0) then begin
+      lPageList := GetPagesOnActiveLine;
+      if Assigned(lPageList) and (lPageList.Count>0) then
+        repeat
+          inc(lActiveIx);
+          if lPageList[lActiveIx]=Pointer(ActivePage) then
+            break;
+        until lActiveIx>=lPageList.Count -1;
+    end;
+    aState := aState and Assigned(lPageList);
+    actCloseLeft.Enabled  := aState and (lActiveIx>0);
+    actCloseOthers.Enabled:= aState and (lPageList.Count>1);
+    actCloseRight.Enabled := aState and (lActiveIx<lPageList.Count-1);
+    actCloseAll.Enabled   := aState;
+  end;
+  lPageList.Free;
+end;
+
 procedure TSearchResultsView.ResultsNoteBookClosetabclicked(Sender: TObject);
 begin
   if (Sender is TTabSheet) then
@@ -769,7 +960,7 @@
     Key:=VK_UNKNOWN;
     if Assigned(FOnSelectionChanged) then
       FOnSelectionChanged(Self);
-  end;     
+  end;
 end;
 
 { Add Result will create a tab in the Results view window with an new

BrunoK

2019-12-09 14:04

reporter   ~0119708

> JM I am testing the feature now. It works well except that the search result window does not remember its size any more.
Maybe related to 0036127 and its Relationships friends.

Juha Manninen

2019-12-09 15:49

developer   ~0119709

Last edited: 2019-12-09 15:51

View 2 revisions

> Windows : requires patch as uploaded in 003641.
0003641 is a very old issue dealing with documentation. It cannot be right.

> Where is the code that stores and restores the sizes of IDE windows ?
In unit IDEWindowIntf. Please see my commit r62362 which added some debug code. I am puzzled with this issue, too, and started to debug it. Still no idea what is going on.

> Couldn't find it and thinks it is an unrelated issue that surfaced recently.
No, it is clearly caused by your patch. The window's size/position is remembered without your patch but not with your patch. You can easily test it yourself.

0036127 has nothing to do with this.

BrunoK

2019-12-09 16:44

reporter   ~0119710

> Windows : requires patch as uploaded in 003641.
>> 0003641 is a very old issue dealing with documentation. It cannot be right.
Sorry meant 0036412: TWin32WSCustomTabControl.GetTabRect doesn't return correct rectangle

>> In unit IDEWindowIntf. Please see my commit r62362 which added some debug code. I am puzzled with this issue, too, and started to debug it. Still no idea what is going on.
Yes, this afternoon did figure out where it was, but of course it is difficult to follow the code path.

>> No, it is clearly caused by your patch. The window's size/position is remembered without your patch but not with your patch. You can easily test it yourself.
Certainly believe you, but can't figure out what bit of code would cause the issue, will come back if I find the cause.

Juha Manninen

2019-12-09 18:45

developer   ~0119713

Oops, right. I was looking at the Pascal sources only. There was AutoSize=True in the .lfm file. I removed it and now it works.
Please test commit r62364.
I did not use your latest patch (searchresultview_191208.patch) because I had already modified the original one.
If changes are needed, please provide them against the latest trunk.

BrunoK

2019-12-10 15:44

reporter   ~0119728

A revised patch on r62364 that should correct some glitches and all out errors on windows. Mostly revised GetPagesOnActiveLine that was calculated in a bizarre way and did fail as soon there were more that 2 rows of tabs and buttons enable management when removing tabs / resizing the form.
Also some problem appeared on linux-gtk2 when using the left/right arrow on the tabsheet bar and are probably fixed.

You have given me some work, I didn't expect my stuff being so quickly evaluated ;-)

searchresultview_on_62364.patch (8,833 bytes)
Index: ide/searchresultview.lfm
===================================================================
--- ide/searchresultview.lfm	(revision 62372)
+++ ide/searchresultview.lfm	(working copy)
@@ -15,9 +15,9 @@
   LCLVersion = '2.1.0.0'
   object ResultsNoteBook: TPageControl
     Left = 0
-    Height = 274
+    Height = 247
     Top = 26
-    Width = 896
+    Width = 799
     Align = alClient
     MultiLine = True
     TabOrder = 0
@@ -25,17 +25,17 @@
     OnCloseTabClicked = ResultsNoteBookClosetabclicked
     OnMouseDown = ResultsNoteBookMouseDown
     OnResize = ResultsNoteBookResize
-    Options = [nboShowCloseButtons, nboMultiLine]
+    Options = [nboShowCloseButtons, nboMultiLine, nboDoChangeOnSetIndex]
   end
   object ControlBar1: TPanel
     Left = 0
     Height = 26
     Top = 0
-    Width = 896
+    Width = 799
     Align = alTop
     BevelOuter = bvNone
     ClientHeight = 26
-    ClientWidth = 896
+    ClientWidth = 799
     TabOrder = 1
     TabStop = True
     object ToolBar: TToolBar
@@ -69,7 +69,7 @@
       Left = 53
       Height = 23
       Top = 2
-      Width = 716
+      Width = 619
       ButtonWidth = 23
       Anchors = [akTop, akLeft, akRight, akBottom]
       BorderSpacing.Left = 2
@@ -82,7 +82,7 @@
       OnChange = SearchInListChange
     end
     object CloseTabs: TToolBar
-      Left = 774
+      Left = 677
       Height = 22
       Top = 3
       Width = 118
Index: ide/searchresultview.pp
===================================================================
--- ide/searchresultview.pp	(revision 62372)
+++ ide/searchresultview.pp	(working copy)
@@ -210,6 +210,7 @@
     FWorkedSearchText: string;
     FOnSelectionChanged: TNotifyEvent;
     FMouseOverIndex: integer;
+    FClosingTabs: boolean;
     function BeautifyPageName(const APageName: string): string;
     function GetPageIndex(const APageName: string): integer;
     function GetTreeView(APageIndex: integer): TLazSearchResultTV;
@@ -217,8 +218,10 @@
     function GetItems(Index: integer): TStrings;
     procedure SetMaxItems(const AValue: integer);
     procedure UpdateToolbar;
-    function  GetPagesOnActiveLine:TFPlist;
+    function  GetPagesOnActiveLine(aOnSide : TOnSide = osOthers):TFPlist;
     procedure ClosePageOnSides(aOnSide : TOnSide);
+    procedure ClosePageBegin;
+    procedure ClosePageEnd;
     procedure UpdateCloseTabs(aState : boolean);
   protected
     procedure Loaded; override;
@@ -309,7 +312,6 @@
   sl.Free;
 end;
 
-
 { TSearchResultsView }
 
 procedure TSearchResultsView.Form1Create(Sender: TObject);
@@ -320,7 +322,7 @@
   ResultsNoteBook.Options:= ResultsNoteBook.Options+[nboShowCloseButtons];
   ResultsNoteBook.Update;
 
-  Name:=NonModalIDEWindowNames[nmiwSearchResultsView];
+  Name:=NonModalIDEWindowNames[nmiwSearchResultsViewName];
   Caption:=lisMenuViewSearchResults;
 
   SearchAgainButton.Hint:=rsStartANewSearch;
@@ -460,13 +462,11 @@
 procedure TSearchResultsView.actNextPageExecute(Sender: TObject);
 begin
   ResultsNoteBook.SelectNextPage(True);
-  UpdateCloseTabs(True);
 end;
 
 procedure TSearchResultsView.actPrevPageExecute(Sender: TObject);
 begin
   ResultsNoteBook.SelectNextPage(False);
-  UpdateCloseTabs(True);
 end;
 
 procedure TSearchResultsView.ResultsNoteBookResize(Sender: TObject);
@@ -483,9 +483,9 @@
   ix: integer;
   lTabSheet: TTabSheet;
   lNeedsRefresh : boolean = false;
-  lCurTV:TLazSearchResultTV;
 begin
-  lvPageList := GetPagesOnActiveLine;
+  ClosePageBegin;
+  lvPageList := GetPagesOnActiveLine(aOnSide);
   with ResultsNoteBook do begin
     lCurTabSheet := ActivePage;
     if aOnSide = osLeft then
@@ -506,13 +506,22 @@
     end;
   end;
   lvPageList.Free;
-  if lNeedsRefresh then begin { ~bk force realign alClient }
-    lCurTV := GetTreeView(ResultsNoteBook.PageIndex);
-    if assigned(lCurTV) then
-      lCurTV.Height:=lCurTV.Height+1;
-  end;
+  ClosePageEnd;
+  if lNeedsRefresh then
+    lCurTabSheet.Height := lCurTabSheet.Height + 1;
+  UpdateToolBar;
 end;
 
+procedure TSearchResultsView.ClosePageBegin;
+begin
+  FClosingTabs := True;
+end;
+
+procedure TSearchResultsView.ClosePageEnd;
+begin
+  FClosingTabs := False;
+end;
+
 procedure TSearchResultsView.tbbCloseLeftClick(Sender: TObject);
 begin
   ClosePageOnSides(osLeft);
@@ -849,39 +858,60 @@
     UpdateCloseTabs(state);
 end;
 
-// <wp> a good tip as usual
-// https://forum.lazarus.freepascal.org/index.php/
-//   topic,39500.msg271287.html?PHPSESSID=t8cr445mhbtlnqm2jq0ckj2ul6#msg271287
-{ Returns a list of all pages on the same line of Tabs as the ActivaPage }
-function TSearchResultsView.GetPagesOnActiveLine: TFPlist;
+{ Returns a list of all pages (visible tabs) on the same line of Tabs as
+  the ActivaPage }
+function TSearchResultsView.GetPagesOnActiveLine(aOnSide: TOnSide {=osOthers}): TFPlist;
 var
-  lRect: TRect;
-  lActiveMidX: integer;
   lActiveMidY: integer;
+  lActiveRect : TRect;
+  lRect, lLastRect: TRect;
+  lActiveIndex : integer;
   ix: integer;
 begin
-  Result := TFPList.Create;
+  Result := nil;
   with ResultsNoteBook do begin
-    if ActivePage = Nil then Exit;
-    lRect := TabRect(ActivePage.TabIndex);
-    lActiveMidX := (lRect.Right - lRect.Left) div 2;
-    lActiveMidY := (lRect.Bottom - lRect.Top) div 2;
-    // Some widgetsets returned a negative value from Bottom-Top calculation
-    //  although they might be fixed now.
-    if lActiveMidY < 0 then begin
-      DebugLn(['TSearchResultsView.GetPagesOnActiveLine: TabRect Bottom-Top calculation'+
-               ' for ActivePage returned a negative value "', lActiveMidY, '".']);
-      lActiveMidY := -lActiveMidY;
+    if Assigned(ActivePage) then begin
+      Result := TFPList.Create;
+      lActiveIndex := ResultsNoteBook.ActivePageIndex;
+      lActiveRect := TabRect(lActiveIndex);
+      lActiveMidY := lActiveRect.Top + (lActiveRect.Bottom - lActiveRect.Top) div 2;
+      { Search closable tabs left of current tab }
+      if aOnSide in [osLeft, osOthers] then begin
+        lLastRect := lActiveRect;
+        for ix := lActiveIndex - 1 downto 0 do begin
+          lRect := TabRect(ix);
+          if (lRect.Top < lActiveMidY) and (lRect.Bottom > lActiveMidY) then begin
+            if lRect.Right<=lLastRect.Left then begin
+              Result.Insert(0, Pages[ix]);
+              lLastRect := lRect;
+            end
+            else
+              break;
+          end
+          else
+            break;
+        end;
+      end;
+      { Current tab }
+      Result.Add(Pages[lActiveIndex]);
+      { Search closable tabs right of current tab }
+      if aOnSide in [osOthers, osRight] then begin
+        lLastRect := lActiveRect;
+        for ix := lActiveIndex + 1 to PageCount - 1  do begin
+          lRect := TabRect(ix);
+          if (lRect.Top < lActiveMidY) and (lRect.Bottom > lActiveMidY) then begin
+            if lRect.Left>=lLastRect.Right then begin
+              Result.Add(Pages[ix]);
+              lLastRect := lRect;
+            end
+            else
+              break;
+          end
+          else
+            break;
+        end;
+      end;
     end;
-    //DebugLn(['Width=',Width, ' MidX=',lActiveMidX, ' MidY=',lActiveMidY]);
-    //DebugLn(['Rect(TabIndex)=[',dbgs(lRect), ']']);
-    for ix := 0 to PageCount - 1 do begin
-      lRect := TabRect(ix);
-      //DebugLn(['Rect=[',dbgs(lRect), ']']);
-      if (lRect.Top < lActiveMidY) and (lRect.Bottom > lActiveMidY)
-      and (lRect.Right > lActiveMidX) and (lRect.Left < Width - lActiveMidX) then
-        Result.Add(Pages[ix]);
-    end;
   end;
 end;
 
@@ -890,19 +920,25 @@
   lPageList: TFPlist = nil;
   lActiveIx : integer = -1;
 begin
-  if aState and (ResultsNoteBook.PageCount>0) then begin
-    lPageList := GetPagesOnActiveLine;
-    if lPageList.Count>0 then
-      repeat
-        inc(lActiveIx);
-        if lPageList[lActiveIx]=Pointer(ResultsNoteBook.ActivePage) then
-          Break;
-      until lActiveIx>=lPageList.Count -1
-    else
-      FreeAndNil(lPageList);
+  if FClosingTabs then
+    exit;
+
+  Application.ProcessMessages; // UI must be up to date before checking close buttons
+
+  with ResultsNoteBook do begin
+    if aState and (PageCount>0) then begin
+      lPageList := GetPagesOnActiveLine;
+      if Assigned(lPageList) and (lPageList.Count>0) then
+        repeat
+          inc(lActiveIx);
+          if lPageList[lActiveIx]=Pointer(ActivePage) then
+            break;
+        until lActiveIx>=lPageList.Count -1;
+    end;
   end;
   aState := aState and Assigned(lPageList);
   actCloseLeft.Enabled  := aState and (lActiveIx>0);
+  { !!! Left to right boolean evaluation -> first test aState !!! }
   actCloseOthers.Enabled:= aState and (lPageList.Count>1);
   actCloseRight.Enabled := aState and (lActiveIx<lPageList.Count-1);
   actCloseAll.Enabled   := aState;

BrunoK

2019-12-10 16:07

reporter   ~0119729

Just noticed that nmiwSearchResultsViewName got renamed to nmiwSearchResultsView so the last patch will show some problems.

Juha Manninen

2019-12-10 17:44

developer   ~0119731

The function TSearchResultsView.GetPagesOnActiveLine can now return Nil. I tried to avoid that with my changes. Can it cause trouble? What do you say?
Renamed nmiwSearchResultsView is no problem. Easily fixed.

BrunoK

2019-12-11 10:52

reporter   ~0119743

>The function TSearchResultsView.GetPagesOnActiveLine can now return Nil. I tried to avoid that with my changes. Can it cause trouble? What do you say?
It is correct because evaluation of boolean values is made from left to right, aEnable (ex aState) was set to false when the list is not assigned and hence the result of the and evaluation was cut as soon as the result is false.
ANYWAY : Your question is good and the revised patch makes the code less ambiguous in this respect. I knew it worked but I also did have difficulty rereading my code.

Apart that point, did some minor renaming and added MinHeigth to the form.

searchresultview_on_62364_1.patch (11,878 bytes)
Index: ide/searchresultview.lfm
===================================================================
--- ide/searchresultview.lfm	(revision 62372)
+++ ide/searchresultview.lfm	(working copy)
@@ -1,12 +1,13 @@
 object SearchResultsView: TSearchResultsView
   Left = 344
-  Height = 273
+  Height = 275
   Top = 327
-  Width = 799
+  Width = 722
   BorderIcons = [biSystemMenu]
   Caption = 'SearchResultsView'
-  ClientHeight = 273
-  ClientWidth = 799
+  ClientHeight = 275
+  ClientWidth = 722
+  Constraints.MinHeight = 100
   Constraints.MinWidth = 400
   KeyPreview = True
   OnClose = FormClose
@@ -15,9 +16,9 @@
   LCLVersion = '2.1.0.0'
   object ResultsNoteBook: TPageControl
     Left = 0
-    Height = 274
+    Height = 249
     Top = 26
-    Width = 896
+    Width = 722
     Align = alClient
     MultiLine = True
     TabOrder = 0
@@ -25,17 +26,17 @@
     OnCloseTabClicked = ResultsNoteBookClosetabclicked
     OnMouseDown = ResultsNoteBookMouseDown
     OnResize = ResultsNoteBookResize
-    Options = [nboShowCloseButtons, nboMultiLine]
+    Options = [nboShowCloseButtons, nboMultiLine, nboDoChangeOnSetIndex]
   end
   object ControlBar1: TPanel
     Left = 0
     Height = 26
     Top = 0
-    Width = 896
+    Width = 722
     Align = alTop
     BevelOuter = bvNone
     ClientHeight = 26
-    ClientWidth = 896
+    ClientWidth = 722
     TabOrder = 1
     TabStop = True
     object ToolBar: TToolBar
@@ -69,7 +70,7 @@
       Left = 53
       Height = 23
       Top = 2
-      Width = 716
+      Width = 542
       ButtonWidth = 23
       Anchors = [akTop, akLeft, akRight, akBottom]
       BorderSpacing.Left = 2
@@ -82,7 +83,7 @@
       OnChange = SearchInListChange
     end
     object CloseTabs: TToolBar
-      Left = 774
+      Left = 600
       Height = 22
       Top = 3
       Width = 118
@@ -138,8 +139,8 @@
     end
   end
   object popList: TPopupMenu
-    left = 190
-    top = 133
+    left = 129
+    top = 48
     object mniCopyItem: TMenuItem
       Caption = 'Copy Item'
       OnClick = mniCopyItemClick
@@ -165,8 +166,8 @@
     end
   end
   object ActionList: TActionList
-    left = 93
-    top = 133
+    left = 32
+    top = 48
     object actClosePage: TAction
       ImageIndex = 1
       OnExecute = ClosePageButtonClick
Index: ide/searchresultview.pp
===================================================================
--- ide/searchresultview.pp	(revision 62372)
+++ ide/searchresultview.pp	(working copy)
@@ -210,6 +210,7 @@
     FWorkedSearchText: string;
     FOnSelectionChanged: TNotifyEvent;
     FMouseOverIndex: integer;
+    FClosingTabs: boolean;
     function BeautifyPageName(const APageName: string): string;
     function GetPageIndex(const APageName: string): integer;
     function GetTreeView(APageIndex: integer): TLazSearchResultTV;
@@ -217,9 +218,11 @@
     function GetItems(Index: integer): TStrings;
     procedure SetMaxItems(const AValue: integer);
     procedure UpdateToolbar;
-    function  GetPagesOnActiveLine:TFPlist;
+    function  GetPagesOnActiveLine(aOnSide : TOnSide = osOthers):TFPlist;
     procedure ClosePageOnSides(aOnSide : TOnSide);
-    procedure UpdateCloseTabs(aState : boolean);
+    procedure ClosePageBegin;
+    procedure ClosePageEnd;
+    procedure UpdateCloseButtons(aEnable : boolean);
   protected
     procedure Loaded; override;
     procedure ActivateControl(aWinControl: TWinControl);
@@ -309,7 +312,6 @@
   sl.Free;
 end;
 
-
 { TSearchResultsView }
 
 procedure TSearchResultsView.Form1Create(Sender: TObject);
@@ -362,7 +364,7 @@
   actCloseOthers.ImageIndex := IDEImages.LoadImage('tab_close_LR');
   actCloseRight.ImageIndex  := IDEImages.LoadImage('tab_close_R');
   actCloseAll.ImageIndex    := IDEImages.LoadImage('tab_close_All');
-  UpdateCloseTabs(False);
+  UpdateCloseButtons(False);
 end;
 
 procedure TSearchResultsView.FormClose(Sender: TObject; var CloseAction: TCloseAction);
@@ -460,18 +462,16 @@
 procedure TSearchResultsView.actNextPageExecute(Sender: TObject);
 begin
   ResultsNoteBook.SelectNextPage(True);
-  UpdateCloseTabs(True);
 end;
 
 procedure TSearchResultsView.actPrevPageExecute(Sender: TObject);
 begin
   ResultsNoteBook.SelectNextPage(False);
-  UpdateCloseTabs(True);
 end;
 
 procedure TSearchResultsView.ResultsNoteBookResize(Sender: TObject);
 begin
-  UpdateCloseTabs(ResultsNoteBook.PageCount>0);
+  UpdateCloseButtons(ResultsNoteBook.PageCount>0);
 end;
 
 { Handling of tabs closure. Only tabs on pages at the level of active page in
@@ -483,36 +483,47 @@
   ix: integer;
   lTabSheet: TTabSheet;
   lNeedsRefresh : boolean = false;
-  lCurTV:TLazSearchResultTV;
 begin
-  lvPageList := GetPagesOnActiveLine;
-  with ResultsNoteBook do begin
-    lCurTabSheet := ActivePage;
-    if aOnSide = osLeft then
-      ix := lvPageList.IndexOf(lCurTabSheet)-1
-    else
-      ix := lvPageList.Count - 1;
-    while ix >= 0 do begin
-      lTabSheet := TTabSheet(lvPageList[ix]);
-      if lTabSheet = lCurTabSheet then begin
-        if aOnSide = osRight then
-          break;
-      end
-      else begin
-        ClosePage(lTabSheet.TabIndex);
-        lNeedsRefresh := True;
+  lvPageList := GetPagesOnActiveLine(aOnSide);
+  if Assigned(lvPageList) then begin
+    ClosePageBegin;
+    with ResultsNoteBook do begin
+      lCurTabSheet := ActivePage;
+      if aOnSide = osLeft then
+        ix := lvPageList.IndexOf(lCurTabSheet)-1
+      else
+        ix := lvPageList.Count - 1;
+      while ix >= 0 do begin
+        lTabSheet := TTabSheet(lvPageList[ix]);
+        if lTabSheet = lCurTabSheet then begin
+          if aOnSide = osRight then
+            break;
+        end
+        else begin
+          ClosePage(lTabSheet.TabIndex);
+          lNeedsRefresh := True;
+        end;
+        Dec(ix);
       end;
-      Dec(ix);
     end;
+    lvPageList.Free;
+    ClosePageEnd;
+    if lNeedsRefresh then { Force resizing of the active TabSheet }
+      lCurTabSheet.Height := lCurTabSheet.Height + 1;
+    UpdateToolBar;
   end;
-  lvPageList.Free;
-  if lNeedsRefresh then begin { ~bk force realign alClient }
-    lCurTV := GetTreeView(ResultsNoteBook.PageIndex);
-    if assigned(lCurTV) then
-      lCurTV.Height:=lCurTV.Height+1;
-  end;
 end;
 
+procedure TSearchResultsView.ClosePageBegin;
+begin
+  FClosingTabs := True;
+end;
+
+procedure TSearchResultsView.ClosePageEnd;
+begin
+  FClosingTabs := False;
+end;
+
 procedure TSearchResultsView.tbbCloseLeftClick(Sender: TObject);
 begin
   ClosePageOnSides(osLeft);
@@ -792,7 +803,7 @@
   if ResultsNoteBook.PageCount = 0 then
     Close
   else
-    UpdateCloseTabs(True);
+    UpdateCloseButtons(True);
 end;
 
 {Sets the Items from the treeview on the currently selected page in the TNoteBook}
@@ -846,66 +857,98 @@
   ClosePageButton.Enabled := state;
   SearchInListEdit.Enabled := state;
   if state then
-    UpdateCloseTabs(state);
+    UpdateCloseButtons(state);
 end;
 
-// <wp> a good tip as usual
-// https://forum.lazarus.freepascal.org/index.php/
-//   topic,39500.msg271287.html?PHPSESSID=t8cr445mhbtlnqm2jq0ckj2ul6#msg271287
-{ Returns a list of all pages on the same line of Tabs as the ActivaPage }
-function TSearchResultsView.GetPagesOnActiveLine: TFPlist;
+{ Returns a list of all pages (visible tabs) on the same line of Tabs as
+  the ActivaPage }
+function TSearchResultsView.GetPagesOnActiveLine(aOnSide: TOnSide {=osOthers}): TFPlist;
 var
-  lRect: TRect;
-  lActiveMidX: integer;
   lActiveMidY: integer;
+  lActiveRect : TRect;
+  lRect, lLastRect: TRect;
+  lActiveIndex : integer;
   ix: integer;
 begin
-  Result := TFPList.Create;
+  Result := nil;
   with ResultsNoteBook do begin
-    if ActivePage = Nil then Exit;
-    lRect := TabRect(ActivePage.TabIndex);
-    lActiveMidX := (lRect.Right - lRect.Left) div 2;
-    lActiveMidY := (lRect.Bottom - lRect.Top) div 2;
-    // Some widgetsets returned a negative value from Bottom-Top calculation
-    //  although they might be fixed now.
-    if lActiveMidY < 0 then begin
-      DebugLn(['TSearchResultsView.GetPagesOnActiveLine: TabRect Bottom-Top calculation'+
-               ' for ActivePage returned a negative value "', lActiveMidY, '".']);
-      lActiveMidY := -lActiveMidY;
+    if Assigned(ActivePage) then begin
+      Result := TFPList.Create;
+      lActiveIndex := ResultsNoteBook.ActivePageIndex;
+      lActiveRect := TabRect(lActiveIndex);
+      lActiveMidY := lActiveRect.Top + (lActiveRect.Bottom - lActiveRect.Top) div 2;
+      { Search closable tabs left of current tab }
+      if aOnSide in [osLeft, osOthers] then begin
+        lLastRect := lActiveRect;
+        for ix := lActiveIndex - 1 downto 0 do begin
+          lRect := TabRect(ix);
+          if (lRect.Top < lActiveMidY) and (lRect.Bottom > lActiveMidY) then begin
+            if lRect.Right<=lLastRect.Left then begin
+              Result.Insert(0, Pages[ix]);
+              lLastRect := lRect;
+            end
+            else
+              break;
+          end
+          else
+            break;
+        end;
+      end;
+      { Current tab }
+      Result.Add(Pages[lActiveIndex]);
+      { Search closable tabs right of current tab }
+      if aOnSide in [osOthers, osRight] then begin
+        lLastRect := lActiveRect;
+        for ix := lActiveIndex + 1 to PageCount - 1  do begin
+          lRect := TabRect(ix);
+          if (lRect.Top < lActiveMidY) and (lRect.Bottom > lActiveMidY) then begin
+            if lRect.Left>=lLastRect.Right then begin
+              Result.Add(Pages[ix]);
+              lLastRect := lRect;
+            end
+            else
+              break;
+          end
+          else
+            break;
+        end;
+      end;
     end;
-    //DebugLn(['Width=',Width, ' MidX=',lActiveMidX, ' MidY=',lActiveMidY]);
-    //DebugLn(['Rect(TabIndex)=[',dbgs(lRect), ']']);
-    for ix := 0 to PageCount - 1 do begin
-      lRect := TabRect(ix);
-      //DebugLn(['Rect=[',dbgs(lRect), ']']);
-      if (lRect.Top < lActiveMidY) and (lRect.Bottom > lActiveMidY)
-      and (lRect.Right > lActiveMidX) and (lRect.Left < Width - lActiveMidX) then
-        Result.Add(Pages[ix]);
-    end;
   end;
 end;
 
-procedure TSearchResultsView.UpdateCloseTabs(aState: boolean);
+procedure TSearchResultsView.UpdateCloseButtons(aEnable: boolean);
 var
   lPageList: TFPlist = nil;
   lActiveIx : integer = -1;
 begin
-  if aState and (ResultsNoteBook.PageCount>0) then begin
-    lPageList := GetPagesOnActiveLine;
-    if lPageList.Count>0 then
-      repeat
-        inc(lActiveIx);
-        if lPageList[lActiveIx]=Pointer(ResultsNoteBook.ActivePage) then
-          Break;
-      until lActiveIx>=lPageList.Count -1
-    else
-      FreeAndNil(lPageList);
+  if FClosingTabs then
+    exit;
+
+  Application.ProcessMessages; { UI must be up to date before searching candidate tabs }
+
+  with ResultsNoteBook do begin
+    if aEnable and (PageCount>0) then begin
+      lPageList := GetPagesOnActiveLine;
+      if Assigned(lPageList) and (lPageList.Count>0) then
+        repeat
+          inc(lActiveIx);
+          if lPageList[lActiveIx]=Pointer(ActivePage) then
+            break;
+        until lActiveIx>=lPageList.Count -1;
+    end;
   end;
-  aState := aState and Assigned(lPageList);
-  actCloseLeft.Enabled  := aState and (lActiveIx>0);
-  actCloseOthers.Enabled:= aState and (lPageList.Count>1);
-  actCloseRight.Enabled := aState and (lActiveIx<lPageList.Count-1);
-  actCloseAll.Enabled   := aState;
+  aEnable := aEnable and Assigned(lPageList);
+  actCloseLeft.Enabled  := aEnable and (lActiveIx>0);
+  if aEnable then begin
+    actCloseOthers.Enabled:= lPageList.Count>1;
+    actCloseRight.Enabled := lActiveIx<(lPageList.Count-1);
+  end
+  else begin
+    actCloseOthers.Enabled:= False;
+    actCloseRight.Enabled := False;
+  end;
+  actCloseAll.Enabled   := aEnable;
   lPageList.Free;
 end;
 

Issue History

Date Modified Username Field Change
2019-11-30 16:33 BrunoK New Issue
2019-11-30 16:33 BrunoK File Added: searchresultview_new.png
2019-11-30 16:33 BrunoK File Added: searchresultview.pp.patch
2019-11-30 16:33 BrunoK File Added: lazarusidestrconsts.pas.patch
2019-11-30 16:33 BrunoK File Added: laz_images_list.txt.patch
2019-11-30 16:33 BrunoK File Added: searchresultview_png.7z
2019-11-30 16:33 BrunoK File Added: patch.po.fr.txt
2019-12-01 12:10 Juha Manninen Note Added: 0119563
2019-12-01 12:14 Juha Manninen Note Edited: 0119563 View Revisions
2019-12-01 13:48 BrunoK File Added: searchresultview.patch
2019-12-01 13:48 BrunoK Note Added: 0119567
2019-12-01 14:39 Juha Manninen Note Added: 0119569
2019-12-05 21:31 Juha Manninen Note Added: 0119643
2019-12-05 21:35 Juha Manninen Note Edited: 0119643 View Revisions
2019-12-05 21:36 Juha Manninen Note Edited: 0119643 View Revisions
2019-12-06 16:37 BrunoK Note Added: 0119664
2019-12-09 12:44 BrunoK File Added: searchresultview_191208.patch
2019-12-09 12:44 BrunoK Note Added: 0119706
2019-12-09 12:44 BrunoK Note Edited: 0119706 View Revisions
2019-12-09 14:04 BrunoK Note Added: 0119708
2019-12-09 15:35 Juha Manninen Assigned To => Juha Manninen
2019-12-09 15:35 Juha Manninen Status new => assigned
2019-12-09 15:49 Juha Manninen Note Added: 0119709
2019-12-09 15:51 Juha Manninen Note Edited: 0119709 View Revisions
2019-12-09 16:44 BrunoK Note Added: 0119710
2019-12-09 17:38 Juha Manninen Relationship added related to 0036412
2019-12-09 18:45 Juha Manninen Status assigned => feedback
2019-12-09 18:45 Juha Manninen LazTarget => -
2019-12-09 18:45 Juha Manninen Note Added: 0119713
2019-12-09 18:45 Juha Manninen Fixed in Revision => r62364
2019-12-09 18:45 Juha Manninen Widgetset Win32/Win64 => Win32/Win64
2019-12-10 15:44 BrunoK File Added: searchresultview_on_62364.patch
2019-12-10 15:44 BrunoK Note Added: 0119728
2019-12-10 15:44 BrunoK Status feedback => assigned
2019-12-10 16:07 BrunoK Note Added: 0119729
2019-12-10 17:44 Juha Manninen Note Added: 0119731
2019-12-11 10:52 BrunoK File Added: searchresultview_on_62364_1.patch
2019-12-11 10:52 BrunoK Note Added: 0119743