diff --git a/ide/keymapping.pp b/ide/keymapping.pp index d154bcbb48..79d8862cff 100644 --- a/ide/keymapping.pp +++ b/ide/keymapping.pp @@ -471,6 +471,8 @@ begin ecGotoEditor9: SetResult(VK_9,[ssAlt],VK_UNKNOWN,[]); ecGotoEditor0: SetResult(VK_0,[ssAlt],VK_UNKNOWN,[]); + ecLockEditor: SetResult(VK_UNKNOWN,[],VK_UNKNOWN,[]); + EcFoldLevel1: SetResult(VK_1,[ssAlt,ssShift],VK_UNKNOWN,[]); EcFoldLevel2: SetResult(VK_2,[ssAlt,ssShift],VK_UNKNOWN,[]); EcFoldLevel3: SetResult(VK_3,[ssAlt,ssShift],VK_UNKNOWN,[]); @@ -903,6 +905,8 @@ begin ecGotoEditor9: SetResult(VK_9,[ssAlt],VK_UNKNOWN,[],VK_UNKNOWN,[],VK_UNKNOWN,[]); ecGotoEditor0: SetResult(VK_0,[ssAlt],VK_UNKNOWN,[],VK_UNKNOWN,[],VK_UNKNOWN,[]); + ecLockEditor: SetResult(VK_UNKNOWN,[],VK_UNKNOWN,[]); + EcFoldLevel1: SetResult(VK_1,[ssAlt,ssShift],VK_UNKNOWN,[],VK_UNKNOWN,[],VK_UNKNOWN,[]); EcFoldLevel2: SetResult(VK_2,[ssAlt,ssShift],VK_UNKNOWN,[],VK_UNKNOWN,[],VK_UNKNOWN,[]); EcFoldLevel3: SetResult(VK_3,[ssAlt,ssShift],VK_UNKNOWN,[],VK_UNKNOWN,[],VK_UNKNOWN,[]); @@ -1515,6 +1519,8 @@ begin ecGotoEditor9: SetResult(VK_UNKNOWN,[],VK_UNKNOWN,[]); ecGotoEditor0: SetResult(VK_UNKNOWN,[],VK_UNKNOWN,[]); + ecLockEditor: SetResult(VK_UNKNOWN,[],VK_UNKNOWN,[]); + (* EcFoldLevel1: SetResult(VK_1,[ssMeta,ssShift],VK_UNKNOWN,[]); EcFoldLevel2: SetResult(VK_2,[ssMeta,ssShift],VK_UNKNOWN,[]); @@ -1967,6 +1973,8 @@ begin ecCopyEditorPrevWindow: Result := srkmecCopyEditorPrevWindow; ecCopyEditorNewWindow: Result := srkmecCopyEditorNewWindow; + ecLockEditor: Result := srkmecLockEditor; + ecGotoEditor1.. ecGotoEditor0 : Result:= Format(srkmecGotoEditor,[cmd-ecGotoEditor1]); EcFoldLevel1.. @@ -2722,6 +2730,8 @@ begin AddDefault(C, 'Copy to prior window', srkmecCopyEditorPrevWindow, ecCopyEditorPrevWindow); AddDefault(C, 'Copy to new window', srkmecCopyEditorNewWindow, ecCopyEditorNewWindow); + AddDefault(C, 'Lock editor', srkmecLockEditor, ecLockEditor); + // file menu C:=Categories[AddCategory('FileMenu',srkmCatFileMenu,nil)]; AddDefault(C, 'New', lisMenuTemplateNew, ecNew); diff --git a/ide/lazarusidestrconsts.pas b/ide/lazarusidestrconsts.pas index 01d530dea1..407c46604b 100644 --- a/ide/lazarusidestrconsts.pas +++ b/ide/lazarusidestrconsts.pas @@ -2009,6 +2009,7 @@ resourcestring uemProcedureJump = 'Procedure Jump'; uemClosePage = '&Close Page'; uemCloseOtherPages = 'Close All &Other Pages'; + uemLockPage = '&Lock Page'; uemCopyToNewWindow = 'Copy to new Window'; uemCopyToOtherWindow = 'Copy to other Window'; uemCopyToOtherWindowNew = 'New Window'; @@ -2068,6 +2069,7 @@ resourcestring ueFileROText1='The file "'; ueFileROText2='" is not writable.'; ueModified='Modified'; + ueLocked='Locked'; uepReadonly= 'Readonly'; uepIns='INS'; uepOvr='OVR'; @@ -2303,6 +2305,7 @@ resourcestring srkmecCopyEditorNextWindow = 'Copy editor to next free window'; srkmecCopyEditorPrevWindow = 'Copy editor to prior free window'; srkmecCopyEditorNewWindow = 'Copy editor to new window'; + srkmecLockEditor = 'Lock Editor'; lisKMGoToSourceEditor1 = 'Go to source editor 1'; lisKMGoToSourceEditor2 = 'Go to source editor 2'; @@ -2956,7 +2959,7 @@ resourcestring lisUEReplaceThisOccurrenceOfWith = 'Replace this occurrence of %s%s%s%s ' +'with %s%s%s?'; lisUESearching = 'Searching: %s'; - lisUEReadOnly = '%s/ReadOnly'; + lisUEModeSeparator = '/'; lisUEGotoLine = 'Goto line:'; lisGotoLine = 'Goto line'; diff --git a/ide/main.pp b/ide/main.pp index ff2c1eb823..43be9df5d1 100644 --- a/ide/main.pp +++ b/ide/main.pp @@ -809,6 +809,9 @@ type ): string; override; procedure MarkUnitsModifiedUsingSubComponent(SubComponent: TComponent); + function GetAvailableUnitEditorInfo(AnUnitInfo: TUnitInfo; + ACaretPoint: TPoint; WantedTopLine: integer = -1): TUnitEditorInfo; + // project(s) procedure DoLoadDefaultCompilerOptions(AProject: TProject); function DoNewProject(ProjectDesc: TProjectDescriptor): TModalResult; override; @@ -7692,7 +7695,10 @@ begin //DebugLn(['TMainIDE.DoOpenFileInSourceEditor NewCaretXY=',dbgs(NewCaretXY),' NewTopLine=',NewTopLine]); end; + NewSrcEdit.IsLocked := AnEditorInfo.IsLocked; AnEditorInfo.EditorComponent := NewSrcEdit; + if (not (ofProjectLoading in Flags)) then + OnSrcNotebookEditorVisibleChanged(NewSrcEdit.SourceNotebook); //debugln(['TMainIDE.DoOpenFileInSourceEditor ',AnUnitInfo.Filename,' ',AnUnitInfo.EditorIndex]); // restore source editor settings @@ -7722,8 +7728,8 @@ begin // update statusbar and focus editor if (not (ofProjectLoading in Flags)) then begin - SrcNotebook.ShowOnTop; - SrcNotebook.FocusEditor; + SourceEditorManager.ActiveEditor := NewSrcEdit; + SourceEditorManager.ShowActiveWindowOnTop(True); end; SrcNoteBook.UpdateStatusBar; SrcNotebook.BringToFront; @@ -7853,6 +7859,7 @@ begin MainIDEBar.itmFileCloseAll.Enabled:=True; NewSrcEdit.SyntaxHighlighterType:=NewUnitInfo.EditorInfo[0].SyntaxHighlighter; NewUnitInfo.GetClosedOrNewEditorInfo.EditorComponent := NewSrcEdit; + OnSrcNotebookEditorVisibleChanged(NewSrcEdit.SourceNotebook); // create component AncestorType:=NewFileDescriptor.ResourceClass; @@ -9031,6 +9038,51 @@ begin UnitList.Free; end; +function TMainIDE.GetAvailableUnitEditorInfo(AnUnitInfo: TUnitInfo; + ACaretPoint: TPoint; WantedTopLine: integer = -1): TUnitEditorInfo; +var + i: Integer; +begin + Result := nil; + if AnUnitInfo.OpenEditorInfoCount = 0 then + exit; + // find caret on screen, rather than last focused + i := 0; + while (i < AnUnitInfo.OpenEditorInfoCount) do begin + Result := AnUnitInfo.OpenEditorInfo[i]; + if TSourceEditor(Result.EditorComponent).IsCaretOnScreen(ACaretPoint, + not TSourceEditor(Result.EditorComponent).IsLocked) + or (TSourceEditor(Result.EditorComponent).TopLine = WantedTopLine) + then + exit; + inc(i); + end; + // find unlocked + i := 0; + while (i < AnUnitInfo.OpenEditorInfoCount) do begin + Result := AnUnitInfo.OpenEditorInfo[i]; + if (not TSourceEditor(Result.EditorComponent).IsLocked) or + TSourceEditor(Result.EditorComponent).IsCaretOnScreen(ACaretPoint) + then + exit; + inc(i); + end; + // open new copy + i := 0; + if AnUnitInfo.OpenEditorInfoCount > 0 then + while (i < SourceEditorManager.SourceWindowCount) and + (SourceEditorManager.SourceWindowByLastFocused[i].IndexOfEditorInShareWith + (TSourceEditor(AnUnitInfo.OpenEditorInfo[0].EditorComponent)) >= 0) + do + inc(i); + if i < SourceEditorManager.SourceWindowCount then + i := SourceEditorManager.IndexOfSourceWindow(SourceEditorManager.SourceWindowByLastFocused[i]); + if DoOpenFileInSourceEditor(AnUnitInfo.GetClosedOrNewEditorInfo, -1, i, []) = mrOk then + Result := AnUnitInfo.OpenEditorInfo[0] + else + Result := nil; +end; + function TMainIDE.LoadIDECodeBuffer(var ACodeBuffer: TCodeBuffer; const AFilename: string; Flags: TLoadBufferFlags; ShowAbort: boolean ): TModalResult; @@ -12363,9 +12415,10 @@ var AFileName: string; SearchedFilename: string; LogCaretXY: TPoint; - TopLine: integer; OpenFlags: TOpenFlags; SrcEdit: TSourceEditor; + AnUnitInfo: TUnitInfo; + AnEditorInfo: TUnitEditorInfo; begin Result:=false; if pos('(',SearchResultsView.GetSelectedText) > 0 then @@ -12382,21 +12435,28 @@ begin end; if SearchedFilename<>'' then begin // open the file in the source editor - Result:=(DoOpenEditorFile(SearchedFilename,-1,-1,OpenFlags)=mrOk); + AnUnitInfo := Project1.UnitInfoWithFilename(SearchedFilename); + AnEditorInfo := nil; + if AnUnitInfo <> nil then + AnEditorInfo := GetAvailableUnitEditorInfo(AnUnitInfo, LogCaretXY); + if AnEditorInfo <> nil then begin + SourceEditorManager.ActiveEditor := TSourceEditor(AnEditorInfo.EditorComponent); + Result := True; + end else + Result:=(DoOpenEditorFile(SearchedFilename,-1,-1,OpenFlags)=mrOk); if Result then begin // set caret position SourceEditorManager.AddJumpPointClicked(Self); SrcEdit:=SourceEditorManager.ActiveEditor; if LogCaretXY.Y>SrcEdit.EditorComponent.Lines.Count then LogCaretXY.Y:=SrcEdit.EditorComponent.Lines.Count; - TopLine:=LogCaretXY.Y-(SrcEdit.EditorComponent.LinesInWindow div 2); - if TopLine<1 then TopLine:=1; if FocusEditor then begin SearchResultsView.ShowOnTop; SourceEditorManager.ShowActiveWindowOnTop(True); end; SrcEdit.EditorComponent.LogicalCaretXY:=LogCaretXY; - SrcEdit.EditorComponent.TopLine:=TopLine; + if not SrcEdit.IsLocked then + SrcEdit.CenterCursor(True); with SrcEdit.EditorComponent do begin LeftChar:= Math.Max(LogCaretXY.X-CharsInWindow,1); end; @@ -13638,7 +13698,7 @@ function TMainIDE.DoJumpToCodePos( AddJumpPoint: boolean; FocusEditor: boolean; MarkLine: Boolean): TModalResult; var NewSrcEdit: TSourceEditor; - LinesInWin, MinLines, CurTopLine: Integer; + AnEditorInfo: TUnitEditorInfo; begin Result:=mrCancel; if NewSource=nil then begin @@ -13661,7 +13721,16 @@ begin if (ActiveUnitInfo = nil) or (NewSource<>ActiveUnitInfo.Source) then begin // jump to other file -> open it - Result:=DoOpenEditorFile(NewSource.Filename,-1,-1,[ofOnlyIfExists,ofRegularFile]); + ActiveUnitInfo := Project1.UnitInfoWithFilename(NewSource.Filename); + AnEditorInfo := nil; + if ActiveUnitInfo <> nil then + AnEditorInfo := GetAvailableUnitEditorInfo(ActiveUnitInfo, Point(NewX,NewY), NewTopLine); + if AnEditorInfo <> nil then begin + SourceEditorManager.ActiveEditor := TSourceEditor(AnEditorInfo.EditorComponent); + Result := mrOK; + end + else + Result:=DoOpenEditorFile(NewSource.Filename,-1,-1,[ofOnlyIfExists,ofRegularFile]); if Result<>mrOk then begin UpdateSourceNames; exit; @@ -13669,31 +13738,26 @@ begin NewSrcEdit := SourceEditorManager.ActiveEditor; end else begin - NewSrcEdit:=ActiveSrcEdit; + AnEditorInfo := GetAvailableUnitEditorInfo(ActiveUnitInfo, Point(NewX,NewY), NewTopLine); + if AnEditorInfo <> nil then begin + NewSrcEdit := TSourceEditor(AnEditorInfo.EditorComponent); + SourceEditorManager.ActiveEditor := NewSrcEdit; + end + else + NewSrcEdit:=ActiveSrcEdit; end; if NewX<1 then NewX:=1; if NewY<1 then NewY:=1; - if NewTopLine<1 then begin - CurTopLine := NewSrcEdit.EditorComponent.TopLine; - LinesInWin := NewSrcEdit.EditorComponent.LinesInWindow; - MinLines := Min(Max(LinesInWin div 5, 2), LinesInWin div 3); - if (NewY <= CurTopLine) or (NewY >= CurTopLine + LinesInWin) - then - NewTopLine := Max(1, NewY - (LinesInWin div 2)) - else - if NewY < CurTopLine + MinLines then - NewTopLine := Max(1, NewY - MinLines) - else - if NewY > CurTopLine + LinesInWin - MinLines then - NewTopLine := Max(1, NewY - LinesInWin + MinLines) - else - NewTopLine := CurTopLine; - end; //debugln(['[TMainIDE.DoJumpToCodePos] ',NewX,',',NewY,',',NewTopLine]); with NewSrcEdit.EditorComponent do begin MoveLogicalCaretIgnoreEOL(Point(NewX,NewY)); - TopLine:=NewTopLine; + if not NewSrcEdit.IsLocked then begin + if NewTopLine < 1 then + NewSrcEdit.CenterCursor(True) + else + TopLine:=NewTopLine; + end; //DebugLn('TMainIDE.DoJumpToCodePos NewY=',dbgs(NewY),' ',dbgs(TopLine),' ',dbgs(NewTopLine)); LeftChar:=Max(NewX-CharsInWindow,1); end; @@ -14641,14 +14705,24 @@ var function GetSrcEdit(AMark: TProjectBookmark): TSourceEditor; var UInf: TUnitInfo; - i: Integer; + i, j: Integer; begin if AMark.UnitInfo is TSourceEditor then Result := TSourceEditor(AMark.UnitInfo) else begin // find the nearest open View UInf := TUnitInfo(AMark.UnitInfo); Result := TSourceEditor(UInf.OpenEditorInfo[0].EditorComponent); - for i := 1 to UInf.OpenEditorInfoCount - 1 do + j := 0; + while (j < UInf.OpenEditorInfoCount) and + (Result.IsLocked) and (not Result.IsCaretOnScreen(AMark.CursorPos)) + do begin + inc(j); + if j < UInf.OpenEditorInfoCount then + Result := TSourceEditor(UInf.OpenEditorInfo[j].EditorComponent); + end; + if j >= UInf.OpenEditorInfoCount then + exit(nil); + for i := j + 1 to UInf.OpenEditorInfoCount - 1 do begin if (not Backward) and (GetWinForEdit(Result) > GetWinForEdit(TSourceEditor(UInf.OpenEditorInfo[i].EditorComponent)) ) @@ -14697,6 +14771,7 @@ var i: Integer; CurPos, CurFound: TProjectBookmark; AnUnitInfo: TUnitInfo; + AnEditorInfo: TUnitEditorInfo; begin if ID < 0 then begin // ID < 0 => next/prev @@ -14713,7 +14788,8 @@ begin CurFound := nil; i := 0; while (i < Project1.Bookmarks.Count) and - (CompareBookmarkEditorPos(CurPos, Project1.Bookmarks[i]) = 0) + ( (GetSrcEdit(Project1.Bookmarks[i]) = nil) or + (CompareBookmarkEditorPos(CurPos, Project1.Bookmarks[i]) = 0) ) do inc(i); if i >= Project1.Bookmarks.Count then @@ -14722,15 +14798,17 @@ begin CurFound := Project1.Bookmarks[i]; inc(i); while (i < Project1.Bookmarks.Count) do begin - if (CompareBookmarkEditorPos(CurPos, Project1.Bookmarks[i]) <> 0) then begin - if (not Backward) and - (CompareBookmarkEditorPos(Project1.Bookmarks[i], CurFound) > 0) - then - CurFound := Project1.Bookmarks[i]; - if (Backward) and - (CompareBookmarkEditorPos(Project1.Bookmarks[i], CurFound) < 0) - then - CurFound := Project1.Bookmarks[i]; + if (GetSrcEdit(Project1.Bookmarks[i]) <> nil) then begin + if (CompareBookmarkEditorPos(CurPos, Project1.Bookmarks[i]) <> 0) then begin + if (not Backward) and + (CompareBookmarkEditorPos(Project1.Bookmarks[i], CurFound) > 0) + then + CurFound := Project1.Bookmarks[i]; + if (Backward) and + (CompareBookmarkEditorPos(Project1.Bookmarks[i], CurFound) < 0) + then + CurFound := Project1.Bookmarks[i]; + end; end; inc(i); end; @@ -14744,17 +14822,22 @@ begin AnEditor := GetSrcEdit(CurFound); end else begin - AnEditor := nil; - AnUnitInfo := TUnitInfo(Project1.Bookmarks.UnitInfoForBookmarkWithIndex(ID)); - if (AnUnitInfo <> nil) and (AnUnitInfo.OpenEditorInfoCount > 0) then - AnEditor := TSourceEditor(AnUnitInfo.OpenEditorInfo[0].EditorComponent); - if AnEditor = nil then exit; + AnEditor := nil; + AnUnitInfo := TUnitInfo(Project1.Bookmarks.UnitInfoForBookmarkWithIndex(ID)); + AnEditorInfo := nil; + if (AnUnitInfo <> nil) and (AnUnitInfo.OpenEditorInfoCount > 0) then + AnEditorInfo := GetAvailableUnitEditorInfo(AnUnitInfo, + Project1.Bookmarks.BookmarkWithID(ID).CursorPos); + if AnEditorInfo <> nil then + AnEditor := TSourceEditor(AnEditorInfo.EditorComponent); + if AnEditor = nil then exit; end; SourceEditorManager.ActiveEditor := AnEditor; SourceEditorManager.ShowActiveWindowOnTop(True); AnEditor.EditorComponent.GotoBookMark(ID); - AnEditor.CenterCursor; + if not AnEditor.IsLocked then + AnEditor.CenterCursor(True); end; //this is fired when the editor is focused, changed, ?. Anything that causes the status change @@ -14775,6 +14858,7 @@ begin if p <> nil then begin p.PageIndex := SrcEdit.PageIndex; p.WindowIndex := SourceEditorManager.IndexOfSourceWindow(SrcEdit.SourceNotebook); + p.IsLocked := SrcEdit.IsLocked; end else if SrcEdit.IsNewSharedEditor then begin // attach to UnitInfo @@ -15360,6 +15444,7 @@ var DestIndex, UnitIndex: integer; DestJumpPoint: TProjectJumpHistoryPosition; CursorPoint, NewJumpPoint: TProjectJumpHistoryPosition; JumpHistory : TProjectJumpHistory; + AnEditorInfo: TUnitEditorInfo; begin DestEditor := nil; NewCaretXY.Y:=-1; @@ -15423,7 +15508,9 @@ begin JumpHistory.HistoryIndex:=DestIndex; NewCaretXY:=DestJumpPoint.CaretXY; NewTopLine:=DestJumpPoint.TopLine; - DestEditor:=TSourceEditor(Project1.Units[UnitIndex].OpenEditorInfo[0].EditorComponent); + AnEditorInfo := GetAvailableUnitEditorInfo(Project1.Units[UnitIndex], NewCaretXY); + if AnEditorInfo <> nil then + DestEditor:=TSourceEditor(AnEditorInfo.EditorComponent); {$IFDEF VerboseJumpHistory} writeln('[TMainIDE.OnSrcNotebookJumpToHistoryPoint] Result Line=',NewCaretXY.Y,' Col=',NewCaretXY.X); {$ENDIF} diff --git a/ide/project.pp b/ide/project.pp index 9c8d494020..956cb31a83 100644 --- a/ide/project.pp +++ b/ide/project.pp @@ -171,6 +171,7 @@ type FUnitInfo: TUnitInfo; procedure SetEditorComponent(const AValue: TSourceEditorInterface); private + FIsLocked: Boolean; FIsVisibleTab: Boolean; FPageIndex: integer; FWindowIndex: integer; @@ -200,6 +201,7 @@ type property TopLine: Integer read FTopLine write FTopLine; property CursorPos: TPoint read FCursorPos write FCursorPos; property FoldState: String read FFoldState write FFoldState; + property IsLocked: Boolean read FIsLocked write FIsLocked; property CustomHighlighter: Boolean read FCustomHighlighter write FCustomHighlighter; // SetCustomHighlighter property SyntaxHighlighter: TLazSyntaxHighlighter read FSyntaxHighlighter write FSyntaxHighlighter; // SetSyntaxHighlighter end; @@ -1093,6 +1095,7 @@ begin FEditorComponent := AValue; UnitInfo.FEditorInfoList.MakeUnUsedEditorInfo(Self); PageIndex := -1; // calls UnitInfo.UpdatePageIndex + IsLocked := False; end else begin PageIndex := -1; @@ -1166,6 +1169,7 @@ begin CursorPos := Point(XMLConfig.GetValue(Path+'CursorPos/X',-1), XMLConfig.GetValue(Path+'CursorPos/Y',-1)); FFoldState := XMLConfig.GetValue(Path+'FoldState/Value', ''); + FIsLocked := XMLConfig.GetValue(Path+'IsLocked/Value', False); FSyntaxHighlighter := StrToLazSyntaxHighlighter( XMLConfig.GetValue(Path+'SyntaxHighlighter/Value', LazSyntaxHighlighterNames[UnitInfo.DefaultSyntaxHighlighter])); @@ -1179,6 +1183,7 @@ begin XMLConfig.SetDeleteValue(Path+'TopLine/Value', FTopLine, -1); XMLConfig.SetDeleteValue(Path+'CursorPos/X', FCursorPos.X, -1); XMLConfig.SetDeleteValue(Path+'CursorPos/Y', FCursorPos.Y, -1); + XMLConfig.SetDeleteValue(Path+'IsLocked/Value', FIsLocked, False); XMLConfig.SetDeleteValue(Path+'FoldState/Value', FoldState, ''); XMLConfig.SetDeleteValue(Path+'SyntaxHighlighter/Value', LazSyntaxHighlighterNames[fSyntaxHighlighter], diff --git a/ide/sourceeditor.pp b/ide/sourceeditor.pp index 6865e56c05..181d710152 100644 --- a/ide/sourceeditor.pp +++ b/ide/sourceeditor.pp @@ -201,6 +201,7 @@ type private //FAOwner is normally a TSourceNotebook. This is set in the Create constructor. FAOwner: TComponent; + FIsLocked: Boolean; FIsNewSharedEditor: Boolean; FSharedValues: TSourceEditorSharedValues; FEditor: TIDESynEditor; @@ -245,6 +246,7 @@ type function GetSharedEditors(Index: Integer): TSourceEditor; procedure SetCodeBuffer(NewCodeBuffer: TCodeBuffer); function GetSource: TStrings; + procedure SetIsLocked(const AValue: Boolean); procedure SetPageName(const AValue: string); procedure UpdateExecutionSourceMark; procedure UpdatePageName; @@ -392,7 +394,7 @@ type function CaretInSelection(const ACaretPos: TPoint): Boolean; // cursor - procedure CenterCursor; + procedure CenterCursor(SoftCenter: Boolean = False); function TextToScreenPosition(const Position: TPoint): TPoint; override; function ScreenToTextPosition(const Position: TPoint): TPoint; override; function ScreenToPixelPosition(const Position: TPoint): TPoint; override; @@ -407,6 +409,7 @@ type function GetTopLine: Integer; override; procedure SetTopLine(const AValue: Integer); override; function CursorInPixel: TPoint; override; + function IsCaretOnScreen(ACaret: TPoint; UseSoftCenter: Boolean = False): Boolean; // text function SearchReplace(const ASearch, AReplace: string; @@ -468,6 +471,7 @@ type function SharedEditorCount: Integer; property SharedEditors[Index: Integer]: TSourceEditor read GetSharedEditors; property IsNewSharedEditor: Boolean read FIsNewSharedEditor write FIsNewSharedEditor; + property IsLocked: Boolean read FIsLocked write SetIsLocked; end; //============================================================================ @@ -596,6 +600,7 @@ type procedure SrcEditMenuCopyToExistingWindowClicked(Sender: TObject); procedure SrcEditMenuMoveToNewWindowClicked(Sender: TObject); procedure SrcEditMenuMoveToExistingWindowClicked(Sender: TObject); + procedure EditorLockClicked(Sender: TObject); public procedure DeleteBreakpointClicked(Sender: TObject); procedure ToggleBreakpointClicked(Sender: TObject); @@ -668,7 +673,6 @@ type procedure EditorChanged(Sender: TObject); procedure DoClose(var CloseAction: TCloseAction); override; - function IndexOfEditorInShareWith(AnOtherEditor: TSourceEditor): Integer; protected function GetActiveCompletionPlugin: TSourceEditorCompletionPlugin; override; function GetCompletionPlugins(Index: integer): TSourceEditorCompletionPlugin; override; @@ -781,6 +785,7 @@ type EditorComp: TComponent): TSourceEditor; function GetActiveSE: TSourceEditor; { $note deprecate and use SetActiveEditor} procedure CheckCurrentCodeBufferChanged; + function IndexOfEditorInShareWith(AnOtherEditor: TSourceEditor): Integer; procedure UpdateStatusBar; procedure ClearErrorLines; override; @@ -826,11 +831,13 @@ type private FActiveWindow: TSourceNotebook; FSourceWindowList: TFPList; + FSourceWindowByFocusList: TFPList; FUpdateLock: Integer; FShowWindowOnTop: Boolean; FShowWindowOnTopFocus: Boolean; procedure FreeSourceWindows; function GetActiveSourceWindowIndex: integer; + function GetSourceWindowByLastFocused(Index: Integer): TSourceEditorWindowInterface; procedure SetActiveSourceWindowIndex(const AValue: integer); protected FChangeNotifyLists: Array [TsemChangeReason] of TMethodList; @@ -849,6 +856,9 @@ type function IndexOfSourceWindow(AWindow: TSourceEditorWindowInterface): integer; property ActiveSourceWindowIndex: integer read GetActiveSourceWindowIndex write SetActiveSourceWindowIndex; + function IndexOfSourceWindowByLastFocused(AWindow: TSourceEditorWindowInterface): integer; + property SourceWindowByLastFocused[Index: Integer]: TSourceEditorWindowInterface + read GetSourceWindowByLastFocused; // Editors function SourceEditorIntfWithFilename(const Filename: string): TSourceEditorInterface; override; @@ -910,6 +920,7 @@ type function GetActiveSrcEditor: TSourceEditor; function GetSourceEditorsByPage(WindowIndex, PageIndex: integer ): TSourceEditor; + function GetSourceNbByLastFocused(Index: Integer): TSourceNotebook; function GetSrcEditors(Index: integer): TSourceEditor; procedure SetActiveSourceNotebook(const AValue: TSourceNotebook); function GetSourceNotebook(Index: integer): TSourceNotebook; @@ -924,6 +935,8 @@ type function ActiveOrNewSourceWindow: TSourceNotebook; function NewSourceWindow: TSourceNotebook; function SourceWindowWithPage(const APage: TPage): TSourceNotebook; + property SourceWindowByLastFocused[Index: Integer]: TSourceNotebook + read GetSourceNbByLastFocused; // Editors function SourceEditorCount: integer; override; function GetActiveSE: TSourceEditor; { $note deprecate and use ActiveEditor} @@ -1174,6 +1187,8 @@ var SrcEditMenuCopyToOtherWindow: TIDEMenuSection; SrcEditMenuCopyToOtherWindowNew: TIDEMenuCommand; SrcEditMenuCopyToOtherWindowList: TIDEMenuSection; + // EditorLocks + SrcEditMenuEditorLock: TIDEMenuCommand; {$ENDIF} @@ -1242,6 +1257,10 @@ begin 'Close All Other Pages',uemCloseOtherPages, nil, nil, nil); {$IFnDEF SingleSrcWindow} + // Lock Editor + SrcEditMenuEditorLock := RegisterIDEMenuCommand(SrcEditMenuSectionPages, + 'LockEditor', uemLockPage); + SrcEditMenuEditorLock.ShowAlwaysCheckable := True; // Move to other Window SrcEditMenuMoveToNewWindow := RegisterIDEMenuCommand(SrcEditMenuSectionPages, 'MoveToNewWindow', uemMoveToNewWindow); @@ -2901,6 +2920,9 @@ Begin ecInsertCVSSource: InsertCVSKeyword('Source'); + ecLockEditor: + IsLocked := not IsLocked; + else begin Handled:=false; @@ -3857,6 +3879,15 @@ Begin Result := FEditor.Lines; end; +procedure TSourceEditor.SetIsLocked(const AValue: Boolean); +begin + if FIsLocked = AValue then exit; + FIsLocked := AValue; + UpdatePageName; + SourceNotebook.UpdateStatusBar; + UpdateProjectFile; +end; + procedure TSourceEditor.SetPageName(const AValue: string); begin if FPageName=AValue then exit; @@ -3875,6 +3906,7 @@ begin NewPageName:=Format('%s:%d', [FPageName, (p+1) mod 10]) else NewPageName:=FPageName; + if IsLocked then NewPageName:='!'+NewPageName; if Modified then NewPageName:='*'+NewPageName; if SourceNotebook.NoteBookPages[p] <> NewPageName then begin SourceNotebook.NoteBookPages[p] := NewPageName; @@ -4182,10 +4214,30 @@ end; Center the current cursor line in editor. -------------------------------------------------------------------------------} -procedure TSourceEditor.CenterCursor; -var NewTopLine: integer; +procedure TSourceEditor.CenterCursor(SoftCenter: Boolean = False); +var + NewTopLine: integer; + Y, CurTopLine, LinesInWin, MinLines: Integer; begin - NewTopLine:=EditorComponent.CaretY-((EditorComponent.LinesInWindow-1) div 2); + if SoftCenter then begin + CurTopLine := EditorComponent.TopLine; + LinesInWin := EditorComponent.LinesInWindow; + MinLines := Min(Max(LinesInWin div 5, 1), 5); + Y := EditorComponent.CaretY; + if (Y <= CurTopLine) or (Y >= CurTopLine + LinesInWin) + then + NewTopLine := Max(1, Y - (LinesInWin div 2)) + else + if Y < CurTopLine + MinLines then + NewTopLine := Max(1, Y - MinLines) + else + if Y > CurTopLine + LinesInWin - MinLines then + NewTopLine := Max(1, Y - LinesInWin + MinLines) + else + NewTopLine := CurTopLine; + end + else + NewTopLine:=EditorComponent.CaretY-((EditorComponent.LinesInWindow-1) div 2); if NewTopLine<1 then NewTopLine:=1; EditorComponent.TopLine:=NewTopLine; end; @@ -4316,6 +4368,22 @@ begin Result:=Point(FEditor.CaretXPix,FEditor.CaretYPix); end; +function TSourceEditor.IsCaretOnScreen(ACaret: TPoint; UseSoftCenter: Boolean = False): Boolean; +var + LinesInWin, MinLines: Integer; +begin + if UsesoftCenter then begin + LinesInWin := EditorComponent.LinesInWindow; + MinLines := Min(Max(LinesInWin div 5, 1), 5); + end + else + MinLines := 0; + Result := (ACaret.y >= FEditor.TopLine + MinLines) and + (ACaret.Y <= FEditor.TopLine + FEditor.LinesInWindow - MinLines) and + (ACaret.X >= FEditor.LeftChar) and + (ACaret.X <= FEditor.LeftChar + FEditor.CharsInWindow); +end; + function TSourceEditor.SearchReplace(const ASearch, AReplace: string; SearchOptions: TSrcEditSearchOptions): integer; const @@ -5002,11 +5070,14 @@ begin end; {$IFnDEF SingleSrcWindow} + // Editor locks + SrcEditMenuEditorLock.Checked := ASrcEdit.IsLocked; + // Multi win SrcEditMenuMoveToOtherWindowList.Clear; NBAvail := False; for i := 0 to Manager.SourceWindowCount - 1 do if (i <> Manager.IndexOfSourceWindow(self)) and - (Manager.SourceWindows[i].IndexOfEditorInShareWith(GetActiveSE) < 0) + (Manager.SourceWindows[i].IndexOfEditorInShareWith(ASrcEdit) < 0) then begin NBAvail := True; with RegisterIDEMenuCommand(SrcEditMenuMoveToOtherWindowList, @@ -5024,7 +5095,7 @@ begin NBAvail := False; SrcEditMenuCopyToOtherWindowList.Clear; for i := 0 to Manager.SourceWindowCount - 1 do - if (Manager.SourceWindows[i].IndexOfEditorInShareWith(GetActiveSE) < 0) and + if (Manager.SourceWindows[i].IndexOfEditorInShareWith(ASrcEdit) < 0) and (i <> Manager.IndexOfSourceWindow(self)) then begin NBAvail := True; @@ -5148,6 +5219,9 @@ begin SrcEditMenuEditorProperties.OnClick:=@EditorPropertiesClicked; {$IFnDEF SingleSrcWindow} + // EditorLocks + SrcEditMenuEditorLock.OnClick := @EditorLockClicked; + // MultiWin SrcEditMenuMoveToNewWindow.OnClick := @SrcEditMenuMoveToNewWindowClicked; SrcEditMenuMoveToOtherWindowNew.OnClick := @SrcEditMenuMoveToNewWindowClicked; @@ -6500,6 +6574,11 @@ begin MoveEditor(PageIndex, (Sender as TIDEMenuItem).Tag, -1) end; +procedure TSourceNotebook.EditorLockClicked(Sender: TObject); +begin + GetActiveSE.IsLocked := not GetActiveSE.IsLocked; +end; + Procedure TSourceNotebook.UpdateStatusBar; var tempEditor: TSourceEditor; @@ -6543,11 +6622,17 @@ begin else PanelFileMode := ''; - If TempEditor.ReadOnly then + If TempEditor.ReadOnly then begin if PanelFileMode <> '' then - PanelFileMode := Format(lisUEReadOnly, [PanelFileMode]) - else - PanelFileMode := uepReadonly; + PanelFileMode := PanelFileMode + lisUEModeSeparator; + PanelFileMode := PanelFileMode + uepReadonly; + end; + + If TempEditor.IsLocked then begin + if PanelFileMode <> '' then + PanelFileMode := PanelFileMode + lisUEModeSeparator; + PanelFileMode := PanelFileMode + ueLocked; + end; PanelXY := Format(' %6d:%4d', [TempEditor.CurrentCursorYLine,TempEditor.CurrentCursorXLine]); @@ -7529,6 +7614,7 @@ procedure TSourceEditorManagerBase.FreeSourceWindows; var s: TSourceEditorWindowInterface; begin + FSourceWindowByFocusList.Clear; while FSourceWindowList.Count > 0 do begin s := TSourceEditorWindowInterface(FSourceWindowList[0]); FSourceWindowList.Delete(0); @@ -7542,6 +7628,11 @@ begin Result := IndexOfSourceWindow(ActiveSourceWindow); end; +function TSourceEditorManagerBase.GetSourceWindowByLastFocused(Index: Integer): TSourceEditorWindowInterface; +begin + Result := TSourceEditorWindowInterface(FSourceWindowByFocusList[Index]); +end; + procedure TSourceEditorManagerBase.SetActiveSourceWindowIndex( const AValue: integer); begin @@ -7561,6 +7652,8 @@ begin AValue.SetFocus; FActiveWindow := AValue as TSourceNotebook; + FSourceWindowByFocusList.Remove(AValue); + FSourceWindowByFocusList.Insert(0, AValue); // Todo: Each synEdit needs it's own beautifier if SourceEditorCount > 0 then @@ -7667,6 +7760,11 @@ begin end; end; +function TSourceEditorManagerBase.IndexOfSourceWindowByLastFocused(AWindow: TSourceEditorWindowInterface): integer; +begin + Result := FSourceWindowByFocusList.IndexOf(AWindow); +end; + function TSourceEditorManagerBase.SourceEditorIntfWithFilename( const Filename: string): TSourceEditorInterface; var @@ -7860,6 +7958,7 @@ begin FChangeNotifyLists[i] := TMethodList.Create; SrcEditorIntf.SourceEditorManagerIntf := Self; FSourceWindowList := TFPList.Create; + FSourceWindowByFocusList := TFPList.Create; FCompletionPlugins := TFPList.Create; FUpdateLock := 0; inherited; @@ -7875,6 +7974,7 @@ begin SrcEditorIntf.SourceEditorManagerIntf := nil; // xx move down FreeAndNil(FCompletionPlugins); FreeAndNil(FSourceWindowList); + FreeAndNil(FSourceWindowByFocusList); for i := low(TsemChangeReason) to high(TsemChangeReason) do FChangeNotifyLists[i].Free;; inherited Destroy; @@ -7963,6 +8063,11 @@ begin Result := nil; end; +function TSourceEditorManager.GetSourceNbByLastFocused(Index: Integer): TSourceNotebook; +begin + Result := TSourceNotebook(inherited SourceWindowByLastFocused[Index]); +end; + function TSourceEditorManager.GetSrcEditors(Index: integer): TSourceEditor; begin Result := TSourceEditor(inherited SourceEditors[Index]); @@ -8431,7 +8536,8 @@ begin ActiveEditor := NewEditor; ShowActiveWindowOnTop(True); with NewEditor.EditorComponent do begin - TopLine:=NewTopLine; + if not NewEditor.IsLocked then + TopLine:=NewTopLine; LogicalCaretXY:=NewCaretXY; end; end; @@ -8698,6 +8804,7 @@ begin Result.IncUpdateLock; FSourceWindowList.Add(Result); FSourceWindowList.Sort(TListSortCompare(@SortSourceWindows)); + FSourceWindowByFocusList.Add(Result); if Activate then begin ActiveSourceWindow := Result; ShowActiveWindowOnTop(False); @@ -8712,6 +8819,7 @@ begin if FSourceWindowList = nil then exit; i := FSourceWindowList.IndexOf(AWindow); FSourceWindowList.Remove(AWindow); + FSourceWindowByFocusList.Remove(AWindow); if SourceWindowCount = 0 then ActiveSourceWindow := nil else if ActiveSourceWindow = AWindow then diff --git a/ideintf/idecommands.pas b/ideintf/idecommands.pas index 0be8a6f7d6..d2680cf5a0 100644 --- a/ideintf/idecommands.pas +++ b/ideintf/idecommands.pas @@ -220,6 +220,8 @@ const ecGotoEditor9 = ecGotoEditor8 + 1; ecGotoEditor0 = ecGotoEditor9 + 1; + ecLockEditor = ecFirstLazarus + 370; + // marker ecSetFreeBookmark = ecFirstLazarus + 381; ecPrevBookmark = ecFirstLazarus + 382;