mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-06 02:37:55 +02:00
3860 lines
120 KiB
ObjectPascal
3860 lines
120 KiB
ObjectPascal
{
|
|
***************************************************************************
|
|
* *
|
|
* This source is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This code is distributed in the hope that it will be useful, but *
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
* General Public License for more details. *
|
|
* *
|
|
* A copy of the GNU General Public License is available on the World *
|
|
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
|
|
* obtain it by writing to the Free Software Foundation, *
|
|
* Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
|
|
* *
|
|
***************************************************************************
|
|
|
|
Author: Mattias Gaertner
|
|
|
|
Abstract:
|
|
Frame for messages - output lines for (compiler) messages.
|
|
}
|
|
unit etMessageFrame;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
{$I ide.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
Math, StrUtils, Classes, SysUtils, AVL_Tree,
|
|
// LCL
|
|
Forms, Buttons, ExtCtrls, Controls, LMessages, LCLType, LCLIntf,
|
|
Graphics, Themes, ImgList, Menus, Clipbrd, Dialogs, StdCtrls,
|
|
// LazUtils
|
|
GraphType, UTF8Process, LazUTF8, LazFileCache, LazFileUtils, IntegerList, LazLoggerBase,
|
|
// SynEdit
|
|
SynEdit, SynEditMarks,
|
|
// BuildIntf
|
|
ProjectIntf, PackageIntf, CompOptsIntf, IDEExternToolIntf,
|
|
// IDEIntf
|
|
IDEImagesIntf, MenuIntf, IDECommands, IDEDialogs, LazIDEIntf, IdeIntfStrConsts,
|
|
// IdeUtils
|
|
IdeUtilsPkgStrConsts,
|
|
// IdeConfig
|
|
EnvironmentOpts, IDEOptionDefs, CompilerOptions,
|
|
// IDE
|
|
LazarusIDEStrConsts, HelpFPCMessages, etSrcEditMarks,
|
|
MsgWnd_Options, etQuickFixes, ExtTools, EnvGuiOptions;
|
|
|
|
const
|
|
CustomViewCaption = '------------------------------';
|
|
|
|
type
|
|
TMessagesCtrl = class;
|
|
|
|
{ TLMsgWndView }
|
|
|
|
TLMsgWndView = class(TLazExtToolView)
|
|
private
|
|
FAsyncQueued: boolean;
|
|
FControl: TMessagesCtrl;
|
|
FFilter: TLMsgViewFilter;
|
|
fPaintBottom: integer; // only valid if FPaintStamp=Control.FPaintStamp
|
|
FPaintStamp: int64;
|
|
fPaintTop: integer; // only valid if FPaintStamp=Control.FPaintStamp
|
|
FPendingChanges: TETMultiSrcChanges;
|
|
procedure SetFilter(AValue: TLMsgViewFilter);
|
|
procedure MarksFixed(ListOfTMessageLine: TFPList); // (main thread) called after mlfFixed was added to these messages
|
|
procedure CallOnChangedInMainThread({%H-}Data: PtrInt); // (main thread)
|
|
function AsHintString(const aHintLastLine: integer): string;
|
|
protected
|
|
procedure SetToolState(AValue: TLMVToolState); override;
|
|
procedure FetchAllPending; override; // (main thread)
|
|
procedure ToolExited; override; // (main thread)
|
|
procedure QueueAsyncOnChanged; override; // (worker thread)
|
|
procedure RemoveAsyncOnChanged; override; // (worker thread)
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
function LineFits(Line: TMessageLine): boolean; override; // (worker thread)
|
|
property Control: TMessagesCtrl read FControl;
|
|
function HasContent: boolean;
|
|
function GetShownLineCount(WithHeader, WithProgressLine: boolean): integer;
|
|
procedure RebuildLines; // (main thread)
|
|
function ApplySrcChanges(Changes: TETSingleSrcChanges): boolean; // true if something changed
|
|
public
|
|
// requires Enter/LeaveCriticalSection, write only via main thread
|
|
property Filter: TLMsgViewFilter read FFilter write SetFilter;
|
|
property PendingChanges: TETMultiSrcChanges read FPendingChanges;// src changes for messages adding to view
|
|
end;
|
|
|
|
{ TMsgCtrlUrgencyStyle }
|
|
|
|
TMsgCtrlUrgencyStyle = class
|
|
private
|
|
FColor: TColor;
|
|
FControl: TMessagesCtrl;
|
|
FImageIndex: integer;
|
|
FTranslated: string;
|
|
FUrgency: TMessageLineUrgency;
|
|
procedure SetColor(AValue: TColor);
|
|
procedure SetImageIndex(AValue: integer);
|
|
procedure SetTranslated(AValue: string);
|
|
procedure Changed;
|
|
public
|
|
constructor Create(AControl: TMessagesCtrl; TheUrgency: TMessageLineUrgency);
|
|
function Equals(Obj: TObject): boolean; override;
|
|
procedure Assign(Src: TMsgCtrlUrgencyStyle);
|
|
procedure SetValues(TheTranslated: string; TheImageIndex: integer = -1; TheColor: TColor = clDefault);
|
|
property Control: TMessagesCtrl read FControl;
|
|
property Urgency: TMessageLineUrgency read FUrgency;
|
|
property Translated: string read FTranslated write SetTranslated;
|
|
property ImageIndex: integer read FImageIndex write SetImageIndex default -1;
|
|
property Color: TColor read FColor write SetColor default clDefault;
|
|
end;
|
|
|
|
type
|
|
TOnOpenMessageLine = function(Sender: TObject; Msg: TMessageLine): boolean of object;
|
|
|
|
TMsgCtrlState = (
|
|
mcsFocused
|
|
);
|
|
TMsgCtrlStates = set of TMsgCtrlState;
|
|
|
|
TMsgCtrlOption = (
|
|
mcoSingleClickOpensFile, // otherwise double click
|
|
mcoShowStats, // show numbers of errors, warnings and hints in view header line
|
|
mcoShowTranslated, // show translation (e.g. messages from German message file)
|
|
mcoShowMessageID, // show message ID
|
|
mcoShowMsgIcons,
|
|
mcoAutoOpenFirstError, // when all views stopped, open first error
|
|
mcoSrcEditPopupSelect, // when user right clicks on gutter mark,
|
|
// scroll and select message of the quickfixes
|
|
mcoWndStayOnTop, // use fsStayOnTop
|
|
mcoAlwaysDrawFocused // draw selected item as focused, even if the window is not
|
|
);
|
|
TMsgCtrlOptions = set of TMsgCtrlOption;
|
|
const
|
|
MCDefaultOptions = [mcoShowStats,mcoShowTranslated,
|
|
mcoAutoOpenFirstError,mcoShowMsgIcons,
|
|
mcoSrcEditPopupSelect];
|
|
|
|
type
|
|
|
|
{ TMessagesCtrl }
|
|
|
|
TMessagesCtrl = class(TCustomControl)
|
|
private
|
|
FActiveFilter: TLMsgViewFilter;
|
|
FBackgroundColor: TColor;
|
|
FFilenameStyle: TMsgWndFileNameStyle;
|
|
FHeaderBackground: array[TLMVToolState] of TColor;
|
|
FIdleConnected: boolean;
|
|
FImageChangeLink: TChangeLink;
|
|
FImages: TCustomImageList;
|
|
FItemHeight: integer;
|
|
FOnAllViewsStopped: TNotifyEvent;
|
|
FOnOpenMessage: TOnOpenMessageLine;
|
|
FOnOptionsChanged: TNotifyEvent;
|
|
FOptions: TMsgCtrlOptions;
|
|
FScrollLeft: integer;
|
|
FScrollTop: integer;
|
|
fScrollTopMax: integer;
|
|
FSearchText: string;
|
|
FSelectedLines: TIntegerList;
|
|
FSelectedView: TLMsgWndView;
|
|
FSourceMarks: TETMarks;
|
|
FTextColor: TColor;
|
|
fUpdateLock: integer;
|
|
FUpdateTimer: TTimer;
|
|
fSomeViewsRunning: boolean;
|
|
fHasHeaderHint: boolean;
|
|
fUrgencyStyles: array[TMessageLineUrgency] of TMsgCtrlUrgencyStyle;
|
|
FAutoHeaderBackground: TColor;
|
|
FHintLastLine: integer;
|
|
FHintLastView: TLMsgWndView;
|
|
procedure CreateSourceMark(MsgLine: TMessageLine; aSynEdit: TSynEdit);
|
|
procedure CreateSourceMarks(View: TLMsgWndView; StartLineNumber: Integer);
|
|
function GetActiveFilter: TLMsgViewFilter; inline;
|
|
function GetHeaderBackground(aToolState: TLMVToolState): TColor;
|
|
function GetSelectedLine: integer;
|
|
function GetUrgencyStyles(Urgency: TMessageLineUrgency): TMsgCtrlUrgencyStyle;
|
|
function GetViews(Index: integer): TLMsgWndView;
|
|
procedure ViewChanged(Sender: TObject); // (main thread)
|
|
procedure MsgCtrlMouseMove(Sender: TObject; {%H-}Shift: TShiftState; {%H-}X,Y: Integer);
|
|
procedure MsgUpdateTimerTimer(Sender: TObject);
|
|
procedure SetActiveFilter(AValue: TLMsgViewFilter); inline;
|
|
procedure SetBackgroundColor(AValue: TColor);
|
|
procedure SetFilenameStyle(AValue: TMsgWndFileNameStyle);
|
|
procedure SetHeaderBackground(aToolState: TLMVToolState; AValue: TColor);
|
|
procedure SetIdleConnected(AValue: boolean);
|
|
procedure SetImages(AValue: TCustomImageList);
|
|
procedure SetItemHeight(AValue: integer);
|
|
procedure SetOptions(NewOptions: TMsgCtrlOptions);
|
|
procedure SetScrollLeft(AValue: integer);
|
|
procedure SetScrollTop(AValue: integer);
|
|
procedure SetSearchText(AValue: string);
|
|
procedure SetSelectedLine(AValue: integer);
|
|
procedure SetSelectedView(AValue: TLMsgWndView);
|
|
procedure SetSourceMarks(AValue: TETMarks);
|
|
procedure SetTextColor(AValue: TColor);
|
|
procedure SetUrgencyStyles(Urgency: TMessageLineUrgency;
|
|
AValue: TMsgCtrlUrgencyStyle);
|
|
procedure SetAutoHeaderBackground(AValue: TColor);
|
|
procedure WMHScroll(var Msg: TLMScroll); message LM_HSCROLL;
|
|
procedure WMVScroll(var Msg: TLMScroll); message LM_VSCROLL;
|
|
procedure WMMouseWheel(var Message: TLMMouseEvent); message LM_MOUSEWHEEL;
|
|
procedure WMSetFocus(var Message: TLMSetFocus); message LM_SETFOCUS;
|
|
procedure WMKillFocus(var Message: TLMKillFocus); message LM_KILLFOCUS;
|
|
procedure ImageListChange(Sender: TObject);
|
|
procedure OnIdle(Sender: TObject; var {%H-}Done: Boolean);
|
|
procedure FilterChanged(Sender: TObject);
|
|
function GetPageScroll: integer;
|
|
protected
|
|
FViews: TFPList;// list of TMessagesViewMap
|
|
FStates: TMsgCtrlStates;
|
|
FPaintStamp: int64;
|
|
fLastSearchStartView: TLMsgWndView;
|
|
fLastSearchStartLine: integer;
|
|
fLastLoSearchText: string; // lower case search text
|
|
procedure FetchNewMessages;
|
|
function FetchNewMessages(View: TLMsgWndView): boolean; // true if new lines
|
|
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
|
|
procedure Paint; override;
|
|
procedure UpdateScrollBar(InvalidateScrollMax: boolean);
|
|
procedure CreateWnd; override;
|
|
procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); override;
|
|
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
|
|
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
|
|
procedure DoAllViewsStopped;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
procedure BeginUpdate;
|
|
procedure EndUpdate;
|
|
procedure EraseBackground({%H-}DC: HDC); override;
|
|
procedure ApplyEnvironmentOptions;
|
|
function UrgencyToStr(Urgency: TMessageLineUrgency): string;
|
|
|
|
// views
|
|
function ViewCount: integer; inline;
|
|
property Views[Index: integer]: TLMsgWndView read GetViews;
|
|
function IndexOfView(View: TLMsgWndView): integer;
|
|
procedure ClearViews(OnlyFinished: boolean); // deletes/frees all views
|
|
procedure RemoveView(View: TLMsgWndView); // remove without free
|
|
function GetView(aCaption: string; CreateIfNotExist: boolean): TLMsgWndView;
|
|
function GetLineAt(Y: integer; out View: TLMsgWndView; out Line: integer): boolean;
|
|
function GetLineText(Line: TMessageLine): string;
|
|
function GetHeaderText(View: TLMsgWndView): string;
|
|
function FindUnfinishedView: TLMsgWndView; // running or waiting for run
|
|
function GetLastViewWithContent: TLMsgWndView;
|
|
|
|
// filter
|
|
property ActiveFilter: TLMsgViewFilter read GetActiveFilter write SetActiveFilter;
|
|
function Filters: TLMsgViewFilters; inline;
|
|
|
|
// select, search
|
|
procedure ToggleSelectedLine(View: TLMsgWndView; LineNumber: integer);
|
|
procedure ExtendSelection(View: TLMsgWndView; LineNumber: integer);
|
|
function SearchNext(StartView: TLMsgWndView; StartLine: integer;
|
|
SkipStart, Downwards: boolean;
|
|
out View: TLMsgWndView; out LineNumber: integer): boolean;
|
|
procedure Select(View: TLMsgWndView; LineNumber: integer; DoScroll, FullyVisible: boolean);
|
|
procedure Select(Msg: TMessageLine; DoScroll: boolean);
|
|
function SelectNextOccurrence(Downwards: boolean): boolean;
|
|
function SelectNextShown(Offset: integer): boolean;
|
|
function SelectLast(DoScroll, FullyVisible: boolean): boolean;
|
|
function SelectFirst(DoScroll, FullyVisible: boolean): boolean;
|
|
function GetSelectedMsg: TMessageLine;
|
|
function SearchNextUrgent(StartView: TLMsgWndView; StartLine: integer;
|
|
SkipStart, Downwards: boolean;
|
|
aMinUrgency: TMessageLineUrgency; WithSrcPos: boolean;
|
|
out View: TLMsgWndView; out LineNumber: integer): boolean;
|
|
function SelectFirstUrgentMessage(aMinUrgency: TMessageLineUrgency;
|
|
WithSrcPos: boolean): boolean;
|
|
function SelectNextUrgentMessage(aMinUrgency: TMessageLineUrgency;
|
|
WithSrcPos: boolean; Downwards: boolean): boolean;
|
|
|
|
// scroll
|
|
function IsLineVisible(View: TLMsgWndView; LineNumber: integer): boolean;
|
|
function IsLastLineVisible(View: TLMsgWndView): boolean;
|
|
procedure ScrollToLine(View: TLMsgWndView; LineNumber: integer; FullyVisible: boolean);
|
|
function GetLineTop(View: TLMsgWndView; LineNumber: integer; Scrolled: boolean): integer;
|
|
property ScrollLeft: integer read FScrollLeft write SetScrollLeft;
|
|
property ScrollTop: integer read FScrollTop write SetScrollTop;
|
|
function ScrollLeftMax: integer;
|
|
function ScrollTopMax: integer;
|
|
procedure StoreSelectedAsSearchStart;
|
|
|
|
// file
|
|
function OpenSelection: boolean;
|
|
procedure CreateMarksForFile(aSynEdit: TSynEdit; aFilename: string; DeleteOld: boolean);
|
|
function ApplySrcChanges(Changes: TETSingleSrcChanges): boolean; // true if something changed
|
|
procedure MsgCtrlShowHint(Sender: TObject; {%H-}HintInfo: PHintInfo);
|
|
public
|
|
// properties
|
|
property AutoHeaderBackground: TColor read FAutoHeaderBackground write SetAutoHeaderBackground default MsgWndDefAutoHeaderBackground;
|
|
property BackgroundColor: TColor read FBackgroundColor write SetBackgroundColor default MsgWndDefBackgroundColor;
|
|
property Color default clWindow;
|
|
property FilenameStyle: TMsgWndFileNameStyle read FFilenameStyle write SetFilenameStyle;
|
|
property HeaderBackground[aToolState: TLMVToolState]: TColor read GetHeaderBackground write SetHeaderBackground;
|
|
property IdleConnected: boolean read FIdleConnected write SetIdleConnected;
|
|
property Images: TCustomImageList read FImages write SetImages;
|
|
property ItemHeight: integer read FItemHeight write SetItemHeight;
|
|
property OnAllViewsStopped: TNotifyEvent read FOnAllViewsStopped write FOnAllViewsStopped;
|
|
property OnOpenMessage: TOnOpenMessageLine read FOnOpenMessage write FOnOpenMessage;
|
|
Property OnOptionsChanged: TNotifyEvent read FOnOptionsChanged write FOnOptionsChanged;
|
|
property Options: TMsgCtrlOptions read FOptions write SetOptions default MCDefaultOptions;
|
|
property SearchText: string read FSearchText write SetSearchText;
|
|
// First initially selected line, -1=header line, can be on progress line (=View.Count)
|
|
property SelectedLine1: integer read GetSelectedLine write SetSelectedLine;
|
|
property SelectedView: TLMsgWndView read FSelectedView write SetSelectedView;
|
|
property ShowHint default true;
|
|
property SourceMarks: TETMarks read FSourceMarks write SetSourceMarks;
|
|
property TextColor: TColor read FTextColor write SetTextColor default MsgWndDefTextColor;
|
|
property UrgencyStyles[Urgency: TMessageLineUrgency]: TMsgCtrlUrgencyStyle read GetUrgencyStyles write SetUrgencyStyles;
|
|
end;
|
|
|
|
{ TMessagesFrame }
|
|
|
|
TMessagesFrame = class(TFrame)
|
|
HideSearchSpeedButton: TSpeedButton;
|
|
MsgCtrlPopupMenu: TPopupMenu;
|
|
SearchEdit: TEdit;
|
|
SearchNextSpeedButton: TSpeedButton;
|
|
SearchPanel: TPanel;
|
|
SearchPrevSpeedButton: TSpeedButton;
|
|
procedure HideSearchSpeedButtonClick(Sender: TObject);
|
|
procedure MsgCtrlPopupMenuPopup(Sender: TObject);
|
|
procedure SearchEditChange(Sender: TObject);
|
|
procedure SearchEditKeyDown(Sender: TObject; var Key: Word; {%H-}Shift: TShiftState);
|
|
procedure SearchNextSpeedButtonClick(Sender: TObject);
|
|
procedure SearchPrevSpeedButtonClick(Sender: TObject);
|
|
private
|
|
// Event handlers
|
|
procedure AboutToolMenuItemClick(Sender: TObject);
|
|
procedure AddFilterMenuItemClick(Sender: TObject);
|
|
procedure ClearFilterMsgTypesMenuItemClick(Sender: TObject);
|
|
procedure ClearMenuItemClick(Sender: TObject);
|
|
procedure CopyAllMenuItemClick(Sender: TObject);
|
|
procedure CopyFilenameMenuItemClick(Sender: TObject);
|
|
procedure CopyMsgMenuItemClick(Sender: TObject);
|
|
procedure CopyShownMenuItemClick(Sender: TObject);
|
|
procedure EditHelpMenuItemClick(Sender: TObject);
|
|
procedure FileStyleMenuItemClick(Sender: TObject);
|
|
procedure FindMenuItemClick(Sender: TObject);
|
|
procedure HelpMenuItemClick(Sender: TObject);
|
|
procedure FilterHintsWithoutPosMenuItemClick(Sender: TObject);
|
|
procedure FilterMsgOfTypeMenuItemClick(Sender: TObject);
|
|
procedure FilterUrgencyMenuItemClick(Sender: TObject);
|
|
procedure MoreOptionsMenuItemClick(Sender: TObject);
|
|
procedure SelectFilterClick(Sender: TObject);
|
|
procedure OpenToolsOptionsMenuItemClick(Sender: TObject);
|
|
procedure RemoveCompOptHideMsgClick(Sender: TObject);
|
|
procedure SaveAllToFileMenuItemClick(Sender: TObject);
|
|
procedure SaveShownToFileMenuItemClick(Sender: TObject);
|
|
procedure ShowIDMenuItemClick(Sender: TObject);
|
|
procedure SrcEditLinesChanged(Sender: TObject);
|
|
procedure TranslateMenuItemClick(Sender: TObject);
|
|
procedure RemoveFilterMsgTypeClick(Sender: TObject);
|
|
procedure WndStayOnTopMenuItemClick(Sender: TObject);
|
|
private
|
|
FImages: TLCLGlyphs;
|
|
function AllMessagesAsString(const OnlyShown: boolean): String;
|
|
function GetAboutView: TLMsgWndView;
|
|
function GetViews(Index: integer): TLMsgWndView;
|
|
procedure HideSearch;
|
|
procedure ImagesGetWidthForPPI(Sender: TCustomImageList; {%H-}AImageWidth,
|
|
{%H-}APPI: Integer; var AResultWidth: Integer);
|
|
procedure SaveClicked(OnlyShown: boolean);
|
|
procedure CopyAllClicked(OnlyShown: boolean);
|
|
procedure CopyMsgToClipboard(OnlyFilename: boolean);
|
|
function GetMsgPattern(SubTool: string; MsgId: integer;
|
|
WithUrgency: boolean; MaxLen: integer): string;
|
|
protected
|
|
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
|
|
public
|
|
MessagesCtrl: TMessagesCtrl;
|
|
constructor Create(TheOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
|
|
procedure ApplyIDEOptions;
|
|
|
|
// Views
|
|
function ViewCount: integer;
|
|
property Views[Index: integer]: TLMsgWndView read GetViews;
|
|
function GetView(aCaption: string; CreateIfNotExist: boolean): TLMsgWndView;
|
|
function FindUnfinishedView: TLMsgWndView;
|
|
procedure DeleteView(View: TLMsgWndView); // free view
|
|
function IndexOfView(View: TLMsgWndView): integer;
|
|
procedure ClearViews(OnlyFinished: boolean); // deletes/frees all views
|
|
|
|
// source marks
|
|
procedure CreateMarksForFile(aSynEdit: TSynEdit; aFilename: string;
|
|
DeleteOld: boolean);
|
|
procedure ApplySrcChanges(Changes: TETSingleSrcChanges);
|
|
procedure ApplyMultiSrcChanges(Changes: TETMultiSrcChanges);
|
|
procedure SourceEditorPopup(MarkLine: TSynEditMarkLine;
|
|
const LogicalCaretXY: TPoint);
|
|
procedure SourceEditorHint(MarkLine: TSynEditMarkLine;
|
|
var HintStr: string);
|
|
|
|
// message lines
|
|
procedure SelectMsgLine(Msg: TMessageLine; DoScroll: boolean);
|
|
function SelectFirstUrgentMessage(aMinUrgency: TMessageLineUrgency;
|
|
WithSrcPos: boolean): boolean;
|
|
function SelectNextUrgentMessage(aMinUrgency: TMessageLineUrgency;
|
|
WithSrcPos, Downwards: boolean): boolean;
|
|
procedure ClearCustomMessages(const ViewCaption: string='');
|
|
function AddCustomMessage(TheUrgency: TMessageLineUrgency; Msg: string;
|
|
aFilename: string = ''; LineNumber: integer = 0; Column: integer = 0;
|
|
const ViewCaption: string = CustomViewCaption): TMessageLine;
|
|
end;
|
|
|
|
const
|
|
MessagesMenuRootName = 'Messages';
|
|
var
|
|
MsgFindMenuItem: TIDEMenuCommand;
|
|
MsgQuickFixMenuSection: TIDEMenuSection;
|
|
MsgAboutSection: TIDEMenuSection;
|
|
MsgAboutToolMenuItem: TIDEMenuCommand;
|
|
MsgOpenToolOptionsMenuItem: TIDEMenuCommand;
|
|
MsgFilterMsgOfTypeMenuItem: TIDEMenuCommand;
|
|
MsgRemoveCompOptHideMenuSection: TIDEMenuSection;
|
|
MsgRemoveMsgTypeFilterMenuSection: TIDEMenuSection;
|
|
MsgRemoveFilterMsgOneTypeMenuSection: TIDEMenuSection;
|
|
MsgRemoveFilterAllMsgTypesMenuItem: TIDEMenuCommand;
|
|
MsgFilterBelowMenuSection: TIDEMenuSection;
|
|
MsgFilterWarningsMenuItem: TIDEMenuCommand;
|
|
MsgFilterNotesMenuItem: TIDEMenuCommand;
|
|
MsgFilterHintsMenuItem: TIDEMenuCommand;
|
|
MsgFilterVerboseMenuItem: TIDEMenuCommand;
|
|
MsgFilterDebugMenuItem: TIDEMenuCommand;
|
|
MsgFilterNoneMenuItem: TIDEMenuCommand;
|
|
MsgFilterHintsWithoutPosMenuItem: TIDEMenuCommand;
|
|
MsgFiltersMenuSection: TIDEMenuSection;
|
|
MsgSelectFilterMenuSection: TIDEMenuSection;
|
|
MsgAddFilterMenuItem: TIDEMenuCommand;
|
|
MsgCopyMenuSection: TIDEMenuSection;
|
|
MsgCopyFilenameMenuItem: TIDEMenuCommand;
|
|
MsgCopyMsgMenuItem: TIDEMenuCommand;
|
|
MsgCopyShownMenuItem: TIDEMenuCommand;
|
|
MsgCopyAllMenuItem: TIDEMenuCommand;
|
|
MsgSaveToFileMenuSection: TIDEMenuSection;
|
|
MsgSaveAllToFileMenuItem: TIDEMenuCommand;
|
|
MsgSaveShownToFileMenuItem: TIDEMenuCommand;
|
|
MsgHelpMenuItem: TIDEMenuCommand;
|
|
MsgEditHelpMenuItem: TIDEMenuCommand;
|
|
MsgClearMenuItem: TIDEMenuCommand;
|
|
MsgOptionsMenuSection: TIDEMenuSection;
|
|
MsgWndStayOnTopMenuItem: TIDEMenuCommand;
|
|
MsgFilenameStyleMenuSection: TIDEMenuSection;
|
|
MsgFileStyleShortMenuItem: TIDEMenuCommand;
|
|
MsgFileStyleRelativeMenuItem: TIDEMenuCommand;
|
|
MsgFileStyleFullMenuItem: TIDEMenuCommand;
|
|
MsgTranslateMenuItem: TIDEMenuCommand;
|
|
MsgShowIDMenuItem: TIDEMenuCommand;
|
|
MsgMoreOptionsMenuItem: TIDEMenuCommand;
|
|
|
|
procedure RegisterStandardMessagesViewMenuItems;
|
|
|
|
implementation
|
|
|
|
const
|
|
cNotALineHint=low(integer);
|
|
|
|
procedure RegisterStandardMessagesViewMenuItems;
|
|
var
|
|
Parent: TIDEMenuSection;
|
|
Root: TIDEMenuSection;
|
|
begin
|
|
MessagesMenuRoot := RegisterIDEMenuRoot(MessagesMenuRootName);
|
|
Root:=MessagesMenuRoot;
|
|
MsgFindMenuItem := RegisterIDEMenuCommand(Root, 'Find', lisFind);
|
|
MsgQuickFixMenuSection := RegisterIDEMenuSection(Root, 'Quick Fix');
|
|
MsgAboutSection:=RegisterIDEMenuSection(Root,'About');
|
|
Parent:=MsgAboutSection;
|
|
Parent.ChildrenAsSubMenu:=true;
|
|
Parent.Caption:=lisAbout;
|
|
MsgAboutToolMenuItem:=RegisterIDEMenuCommand(Parent, 'About', lisAbout);
|
|
MsgOpenToolOptionsMenuItem:=RegisterIDEMenuCommand(Parent, 'Open Tool '
|
|
+'Options', lisOpenToolOptions);
|
|
MsgFilterMsgOfTypeMenuItem:=RegisterIDEMenuCommand(Root,'FilterMsgOfType',lisFilterAllMessagesOfCertainType);
|
|
MsgRemoveCompOptHideMenuSection:=RegisterIDEMenuSection(Root,'RemoveCompOptHideMsg');
|
|
Parent:=MsgRemoveCompOptHideMenuSection;
|
|
Parent.ChildrenAsSubMenu:=true;
|
|
Parent.Caption:=lisRemoveCompilerOptionHideMessage;
|
|
MsgRemoveMsgTypeFilterMenuSection:=RegisterIDEMenuSection(Root,'RemoveMsgTypeFilters');
|
|
Parent:=MsgRemoveMsgTypeFilterMenuSection;
|
|
Parent.ChildrenAsSubMenu:=true;
|
|
Parent.Caption:=lisRemoveMessageTypeFilter;
|
|
MsgRemoveFilterMsgOneTypeMenuSection:=RegisterIDEMenuSection(Parent,'RemoveOneMsgTypeFilterSection');
|
|
MsgRemoveFilterAllMsgTypesMenuItem:=RegisterIDEMenuCommand(Parent, 'Remove'
|
|
+' all message type filters', lisRemoveAllMessageTypeFilters);
|
|
MsgFilterBelowMenuSection:=RegisterIDEMenuSection(Root,'Filter Below Section');
|
|
Parent:=MsgFilterBelowMenuSection;
|
|
Parent.ChildrenAsSubMenu:=true;
|
|
Parent.Caption:=lisFilterNonUrgentMessages;
|
|
MsgFilterWarningsMenuItem:=RegisterIDEMenuCommand(Parent,
|
|
'Filter Warnings', lisFilterWarningsAndBelow);
|
|
MsgFilterWarningsMenuItem.RadioItem:=true;
|
|
MsgFilterWarningsMenuItem.GroupIndex:=2;
|
|
MsgFilterNotesMenuItem:=RegisterIDEMenuCommand(Parent, 'Filter Notes',
|
|
lisFilterNotesAndBelow);
|
|
MsgFilterNotesMenuItem.RadioItem:=true;
|
|
MsgFilterNotesMenuItem.GroupIndex:=2;
|
|
MsgFilterHintsMenuItem:=RegisterIDEMenuCommand(Parent, 'Filter Hints',
|
|
lisFilterHintsAndBelow);
|
|
MsgFilterHintsMenuItem.RadioItem:=true;
|
|
MsgFilterHintsMenuItem.GroupIndex:=2;
|
|
MsgFilterVerboseMenuItem:=RegisterIDEMenuCommand(Parent, 'Filter Verbose '
|
|
+'Messages', lisFilterVerboseMessagesAndBelow);
|
|
MsgFilterVerboseMenuItem.RadioItem:=true;
|
|
MsgFilterVerboseMenuItem.GroupIndex:=2;
|
|
MsgFilterDebugMenuItem:=RegisterIDEMenuCommand(Parent, 'Filter Debug '
|
|
+'Messages', lisFilterDebugMessagesAndBelow);
|
|
MsgFilterDebugMenuItem.RadioItem:=true;
|
|
MsgFilterDebugMenuItem.GroupIndex:=2;
|
|
MsgFilterNoneMenuItem:=RegisterIDEMenuCommand(Parent, 'Filter None, do not'
|
|
+' filter by urgency', lisFilterNoneDoNotFilterByUrgency);
|
|
MsgFilterNoneMenuItem.RadioItem:=true;
|
|
MsgFilterNoneMenuItem.GroupIndex:=2;
|
|
MsgFilterHintsWithoutPosMenuItem:=RegisterIDEMenuCommand(Root, 'Filter Hints'
|
|
+' without Source Position', lisFilterHintsWithoutSourcePosition);
|
|
MsgFiltersMenuSection:=RegisterIDEMenuSection(Root,'Switch Filter Section');
|
|
Parent:=MsgFiltersMenuSection;
|
|
Parent.ChildrenAsSubMenu:=true;
|
|
Parent.Caption:=lisSwitchFilterSettings;
|
|
MsgSelectFilterMenuSection:=RegisterIDEMenuSection(Parent,'Filters');
|
|
MsgAddFilterMenuItem:=RegisterIDEMenuCommand(Parent, 'Add Filter',
|
|
lisAddFilter);
|
|
MsgCopyMenuSection:=RegisterIDEMenuSection(Root,'Copy');
|
|
Parent:=MsgCopyMenuSection;
|
|
Parent.ChildrenAsSubMenu:=true;
|
|
Parent.Caption:=lisCopy;
|
|
MsgCopyFilenameMenuItem:=RegisterIDEMenuCommand(Parent, 'Filename',
|
|
lisCopyFileNameToClipboard);
|
|
MsgCopyMsgMenuItem := RegisterIDEMenuCommand(Parent, 'Selected',lisCopySelectedMessagesToClipboard);
|
|
MsgCopyShownMenuItem := RegisterIDEMenuCommand(Parent, 'Shown', lisCopyAllShownMessagesToClipboard);
|
|
MsgCopyAllMenuItem:=RegisterIDEMenuCommand(Parent, 'All',
|
|
lisCopyAllOriginalMessagesToClipboard);
|
|
MsgSaveToFileMenuSection:=RegisterIDEMenuSection(Root,'Save');
|
|
Parent:=MsgSaveToFileMenuSection;
|
|
Parent.ChildrenAsSubMenu:=true;
|
|
Parent.Caption:=lisSave;
|
|
MsgSaveShownToFileMenuItem:=RegisterIDEMenuCommand(Parent, 'Save Shown '
|
|
+'Messages to File', lisSaveShownMessagesToFile);
|
|
MsgSaveAllToFileMenuItem:=RegisterIDEMenuCommand(Parent, 'Save All '
|
|
+'Messages to File', lisSaveAllOriginalMessagesToFile);
|
|
MsgHelpMenuItem := RegisterIDEMenuCommand(Root, 'Help for this message',lisHelp);
|
|
MsgEditHelpMenuItem := RegisterIDEMenuCommand(Root, 'Edit help for messages',lisEditHelp);
|
|
MsgClearMenuItem := RegisterIDEMenuCommand(Root, 'Clear', lisClear);
|
|
MsgOptionsMenuSection:=RegisterIDEMenuSection(Root,'Option Section');
|
|
Parent:=MsgOptionsMenuSection;
|
|
Parent.ChildrenAsSubMenu:=true;
|
|
Parent.Caption:=lisOptions;
|
|
MsgWndStayOnTopMenuItem:=RegisterIDEMenuCommand(Parent,
|
|
'Window stay on top', lisWindowStaysOnTop);
|
|
MsgFilenameStyleMenuSection:=RegisterIDEMenuSection(Parent,'Filename Styles');
|
|
Parent:=MsgFilenameStyleMenuSection;
|
|
Parent.ChildrenAsSubMenu:=true;
|
|
Parent.Caption:=lisFilenameStyle;
|
|
MsgFileStyleShortMenuItem:=RegisterIDEMenuCommand(Parent, 'Short',
|
|
lisShortNoPath);
|
|
MsgFileStyleRelativeMenuItem:=RegisterIDEMenuCommand(Parent, 'Relative',
|
|
lisRelative);
|
|
MsgFileStyleFullMenuItem:=RegisterIDEMenuCommand(Parent, 'Full', lisFull);
|
|
Parent:=MsgOptionsMenuSection;
|
|
MsgTranslateMenuItem:=RegisterIDEMenuCommand(Parent, 'Translate',
|
|
lisTranslateTheEnglishMessages);
|
|
MsgShowIDMenuItem:=RegisterIDEMenuCommand(Parent, 'ShowID',
|
|
lisShowMessageTypeID);
|
|
MsgMoreOptionsMenuItem:=RegisterIDEMenuCommand(Parent, 'More Options',
|
|
lisDlgMore);
|
|
end;
|
|
|
|
{$R *.lfm}
|
|
|
|
{ TLMsgWndView }
|
|
|
|
procedure TLMsgWndView.MarksFixed(ListOfTMessageLine: TFPList);
|
|
var
|
|
i: Integer;
|
|
ViewLine: TMessageLine;
|
|
j: Integer;
|
|
WorkerMsg: TMessageLine;
|
|
begin
|
|
//debugln(['TLMsgWndView.OnMarksFixed START ',ListOfTMessageLine.Count]);
|
|
// apply marks to WorkerMessages
|
|
if Tool<>nil then begin
|
|
Tool.EnterCriticalSection;
|
|
try
|
|
for i:=0 to ListOfTMessageLine.Count-1 do begin
|
|
ViewLine:=TMessageLine(ListOfTMessageLine[i]);
|
|
j:=Tool.WorkerMessages.IndexOfOutputIndex(ViewLine.OutputIndex);
|
|
if j<0 then continue;
|
|
WorkerMsg:=Tool.WorkerMessages[j];
|
|
WorkerMsg.Flags:=ViewLine.Flags;
|
|
//debugln(['TLMsgWndView.OnMarksFixed j=',j,' ',dbgs(WorkerMsg.Flags),' ',dbgs(Pointer(WorkerMsg)),' WorkerMsg.OutputIndex=',WorkerMsg.OutputIndex,' ViewLine.OutputIndex=',ViewLine.OutputIndex]);
|
|
end;
|
|
finally
|
|
Tool.LeaveCriticalSection;
|
|
end;
|
|
end;
|
|
|
|
// delete messages from view
|
|
for i:=ListOfTMessageLine.Count-1 downto 0 do begin
|
|
ViewLine:=TMessageLine(ListOfTMessageLine[i]);
|
|
Lines.Delete(ViewLine);
|
|
end;
|
|
ListOfTMessageLine.Clear;
|
|
|
|
// update control
|
|
Control.UpdateScrollBar(true);
|
|
Control.Invalidate;
|
|
end;
|
|
|
|
procedure TLMsgWndView.SetToolState(AValue: TLMVToolState);
|
|
begin
|
|
if ToolState=AValue then Exit;
|
|
inherited;
|
|
Control.Invalidate;
|
|
end;
|
|
|
|
procedure TLMsgWndView.SetFilter(AValue: TLMsgViewFilter);
|
|
begin
|
|
FFilter.Assign(AValue);
|
|
end;
|
|
|
|
procedure TLMsgWndView.FetchAllPending;
|
|
var
|
|
OldLineCount: Integer;
|
|
i: Integer;
|
|
OldUpdateSortedSrcPos: Boolean;
|
|
MsgLine: TMessageLine;
|
|
Line: Integer;
|
|
Col: Integer;
|
|
begin
|
|
OldLineCount:=Lines.Count;
|
|
inherited FetchAllPending;
|
|
if OldLineCount=Lines.Count then exit;
|
|
|
|
// apply pending src changes
|
|
OldUpdateSortedSrcPos:=Lines.UpdateSortedSrcPos;
|
|
if FPendingChanges.Count>0 then begin
|
|
Lines.UpdateSortedSrcPos:=false;
|
|
try
|
|
for i:=OldLineCount to Lines.Count-1 do begin
|
|
MsgLine:=Lines[i];
|
|
//debugln(['TLMsgWndView.FetchAllPending ',i,' ',MsgLine.Msg]);
|
|
Line:=MsgLine.Line;
|
|
Col:=MsgLine.Column;
|
|
FPendingChanges.AdaptCaret(MsgLine.GetFullFilename,Line,Col,
|
|
mlfLeftToken in MsgLine.Flags);
|
|
MsgLine.SetSourcePosition(MsgLine.Filename,Line,Col);
|
|
end;
|
|
finally
|
|
Lines.UpdateSortedSrcPos:=OldUpdateSortedSrcPos;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TLMsgWndView.ToolExited;
|
|
var
|
|
ErrCount: Integer;
|
|
u: TMessageLineUrgency;
|
|
MsgLine: TMessageLine;
|
|
i: Integer;
|
|
StartLine: Integer;
|
|
sl: TStringList;
|
|
begin
|
|
inherited ToolExited;
|
|
if Tool.Terminated then begin
|
|
ToolState:=lmvtsFailed;
|
|
end else if (ExitStatus<>0) or (ExitCode<>0) then begin
|
|
// tool stopped with errors
|
|
ErrCount:=0;
|
|
EnterCriticalSection;
|
|
try
|
|
for u:=mluError to high(TMessageLineUrgency) do
|
|
inc(ErrCount,Lines.UrgencyCounts[u]+PendingLines.UrgencyCounts[u]);
|
|
finally
|
|
LeaveCriticalSection;
|
|
end;
|
|
if ErrCount=0 then begin
|
|
// parser did not add an error message
|
|
// => add an error message
|
|
// add up the last 100 lines of output with panic urgency
|
|
Tool.EnterCriticalSection; // Note: always lock Tool *before* View
|
|
try
|
|
EnterCriticalSection;
|
|
try
|
|
StartLine:=Max(0,Tool.WorkerOutput.Count-100);
|
|
if PendingLines.Count>0 then
|
|
StartLine:=Max(StartLine,PendingLines[PendingLines.Count-1].OutputIndex+1);
|
|
if Lines.Count>0 then
|
|
StartLine:=Max(StartLine,Lines[Lines.Count-1].OutputIndex+1);
|
|
for i:=StartLine to Tool.WorkerOutput.Count-1 do
|
|
begin
|
|
MsgLine:=PendingLines.CreateLine(-1);
|
|
MsgLine.Msg:=Tool.WorkerOutput[i];
|
|
MsgLine.Urgency:=mluPanic;
|
|
PendingLines.Add(MsgLine);
|
|
end;
|
|
MsgLine:=PendingLines.CreateLine(-1);
|
|
MsgLine.Urgency:=mluPanic;
|
|
if ExitCode<>0 then
|
|
MsgLine.Msg:=Format(
|
|
lisToolStoppedWithExitCodeUseContextMenuToGetMoreInfo, [IntToStr(
|
|
ExitCode)])
|
|
else
|
|
MsgLine.Msg:=Format(
|
|
lisToolStoppedWithExitStatusUseContextMenuToGetMoreInfo, [
|
|
IntToStr(ExitStatus)]);
|
|
PendingLines.Add(MsgLine);
|
|
finally
|
|
LeaveCriticalSection;
|
|
end;
|
|
finally
|
|
Tool.LeaveCriticalSection;
|
|
end;
|
|
end;
|
|
ToolState:=lmvtsFailed;
|
|
end else if Tool.ErrorMessage<>'' then begin
|
|
// error executing the tool
|
|
EnterCriticalSection;
|
|
try
|
|
sl:=TStringList.Create;
|
|
try
|
|
sl.Text:=Tool.ErrorMessage;
|
|
for i:=0 to sl.Count-1 do begin
|
|
if sl[i]='' then continue;
|
|
MsgLine:=PendingLines.CreateLine(-1);
|
|
MsgLine.Urgency:=mluPanic;
|
|
MsgLine.Msg:=Format(lisInternalError, [sl[i]]);
|
|
PendingLines.Add(MsgLine);
|
|
end;
|
|
finally
|
|
sl.Free;
|
|
end;
|
|
finally
|
|
LeaveCriticalSection;
|
|
end;
|
|
ToolState:=lmvtsFailed;
|
|
end else
|
|
ToolState:=lmvtsSuccess;
|
|
end;
|
|
|
|
procedure TLMsgWndView.CallOnChangedInMainThread(Data: PtrInt);
|
|
begin
|
|
FAsyncQueued:=false;
|
|
if csDestroying in ComponentState then exit;
|
|
if Assigned(OnChanged) then
|
|
OnChanged(Self);
|
|
end;
|
|
|
|
function TLMsgWndView.AsHintString(const aHintLastLine: integer): string;
|
|
var
|
|
MsgLine: TMessageLine;
|
|
s: string;
|
|
begin
|
|
Result := '';
|
|
if Self=nil then Exit;
|
|
MsgLine := nil;
|
|
if aHintLastLine<0 then
|
|
Result := Control.GetHeaderText(Self)
|
|
else if aHintLastLine < Lines.Count then
|
|
MsgLine := Lines[aHintLastLine]
|
|
else
|
|
MsgLine := ProgressLine;
|
|
if MsgLine<>nil then begin
|
|
Result := Control.GetLineText(MsgLine);
|
|
s := ExternalToolList.GetMsgHint(MsgLine.SubTool, MsgLine.MsgID);
|
|
if s<>'' then
|
|
Result += LineEnding+LineEnding + s;
|
|
end;
|
|
end;
|
|
|
|
procedure TLMsgWndView.QueueAsyncOnChanged;
|
|
begin
|
|
if FAsyncQueued then exit;
|
|
FAsyncQueued:=true;
|
|
if Application<>nil then
|
|
Application.QueueAsyncCall(@CallOnChangedInMainThread,0);
|
|
end;
|
|
|
|
procedure TLMsgWndView.RemoveAsyncOnChanged;
|
|
begin
|
|
if not FAsyncQueued then exit;
|
|
FAsyncQueued:=false;
|
|
if Application<>nil then
|
|
Application.RemoveAsyncCalls(Self);
|
|
end;
|
|
|
|
constructor TLMsgWndView.Create(AOwner: TComponent);
|
|
begin
|
|
fMessageLineClass:=TLMsgViewLine;
|
|
inherited Create(AOwner);
|
|
Lines.OnMarksFixed:=@MarksFixed;
|
|
FFilter:=TLMsgViewFilter.Create;
|
|
fPendingChanges:=TETMultiSrcChanges.Create(nil);
|
|
end;
|
|
|
|
destructor TLMsgWndView.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
FreeAndNil(FPendingChanges);
|
|
FreeAndNil(FFilter);
|
|
end;
|
|
|
|
function TLMsgWndView.LineFits(Line: TMessageLine): boolean;
|
|
begin
|
|
if FFilter<>nil then
|
|
Result:=FFilter.LineFits(Line)
|
|
else
|
|
Result:=inherited LineFits(Line);
|
|
end;
|
|
|
|
function TLMsgWndView.HasContent: boolean;
|
|
begin
|
|
Result:=GetShownLineCount(true,true)>0;
|
|
end;
|
|
|
|
function TLMsgWndView.GetShownLineCount(WithHeader, WithProgressLine: boolean): integer;
|
|
begin
|
|
Result:=Lines.Count;
|
|
// the header is only shown if there SummaryMsg<>'' or ProgressLine.Msg<>'' or Lines.Count>0
|
|
if ProgressLine.Msg<>'' then begin
|
|
if WithHeader then
|
|
inc(Result);
|
|
if WithProgressLine then
|
|
inc(Result);
|
|
end else if Caption<>'' then begin
|
|
if WithHeader then
|
|
inc(Result);
|
|
end else if (Result>0) and WithHeader then
|
|
inc(Result);
|
|
end;
|
|
|
|
procedure TLMsgWndView.RebuildLines;
|
|
// called by main thread
|
|
var
|
|
i: Integer;
|
|
SrcMsg: TMessageLine;
|
|
NewProgressLine: TMessageLine;
|
|
NewMsg: TMessageLine;
|
|
Line: Integer;
|
|
Col: Integer;
|
|
begin
|
|
if Tool=nil then exit;
|
|
Tool.EnterCriticalSection; // lock Tool before View
|
|
try
|
|
EnterCriticalSection;
|
|
try
|
|
ClearLines;
|
|
NewProgressLine:=nil;
|
|
for i:=0 to Tool.WorkerMessages.Count-1 do begin
|
|
SrcMsg:=Tool.WorkerMessages[i];
|
|
//if Pos('"db"',SrcMsg.Msg)>0 then
|
|
// debugln(['TLMsgWndView.RebuildLines i=',i,' Msg="',SrcMsg.Msg,'" Fits=',LineFits(SrcMsg),' ',dbgs(SrcMsg.Flags),' ',SrcMsg.OutputIndex]);
|
|
if LineFits(SrcMsg) then begin
|
|
NewProgressLine:=nil;
|
|
NewMsg:=Lines.CreateLine(-1);
|
|
NewMsg.Assign(SrcMsg);
|
|
// adapt line,col due to src changes
|
|
Line:=NewMsg.Line;
|
|
Col:=NewMsg.Column;
|
|
FPendingChanges.AdaptCaret(NewMsg.GetFullFilename,Line,Col,
|
|
mlfLeftToken in NewMsg.Flags);
|
|
NewMsg.SetSourcePosition(NewMsg.Filename,Line,Col);
|
|
//debugln(['TLMsgWndView.RebuildLines NewMsg=',Lines.Count,'="',NewMsg.Msg,'"']);
|
|
Lines.Add(NewMsg);
|
|
end else begin
|
|
NewProgressLine:=SrcMsg;
|
|
end;
|
|
end;
|
|
FLastWorkerMessageCount:=Tool.WorkerMessages.Count-1;
|
|
if (NewProgressLine<>nil) and Running then begin
|
|
ProgressLine.Assign(NewProgressLine);
|
|
end
|
|
else if ProgressLine.Msg<>'' then begin
|
|
ProgressLine.Clear;
|
|
end;
|
|
finally
|
|
LeaveCriticalSection;
|
|
end;
|
|
finally
|
|
Tool.LeaveCriticalSection;
|
|
end;
|
|
end;
|
|
|
|
function TLMsgWndView.ApplySrcChanges(Changes: TETSingleSrcChanges): boolean;
|
|
|
|
function ApplyChanges(CurChanges: TETSingleSrcChanges;
|
|
CurLines: TMessageLines): boolean;
|
|
var
|
|
FromY: integer;
|
|
MaxY: integer;
|
|
LineDiffBehindMaxY: integer;
|
|
ToY: Integer;
|
|
MsgLine: TMessageLine;
|
|
Line: Integer;
|
|
Col: Integer;
|
|
OldUpdateSortedSrcPos: Boolean;
|
|
begin
|
|
Result:=false;
|
|
if CurChanges.First=nil then exit;
|
|
CurChanges.GetRange(FromY,MaxY,LineDiffBehindMaxY);
|
|
if LineDiffBehindMaxY=0 then
|
|
ToY:=MaxY
|
|
else
|
|
ToY:=High(Integer);
|
|
OldUpdateSortedSrcPos:=Lines.UpdateSortedSrcPos;
|
|
CurLines.UpdateSortedSrcPos:=false;
|
|
try
|
|
{if CurLines=Lines then begin
|
|
debugln(['ApplyChanges MinY=',FromY,' MaxY=',MaxY,' LineDiffBehindMaxY=',LineDiffBehindMaxY]);
|
|
CurChanges.WriteDebugReport('Changes:');
|
|
end;}
|
|
for MsgLine in CurLines.EnumerateFile(CurChanges.Filename,FromY,ToY)
|
|
do begin
|
|
Line:=MsgLine.Line;
|
|
Col:=MsgLine.Column;
|
|
if Line>MaxY then
|
|
inc(Line,LineDiffBehindMaxY)
|
|
else
|
|
CurChanges.AdaptCaret(Line,Col,mlfLeftToken in MsgLine.Flags);
|
|
//if CurLines=Lines then
|
|
// debugln(['ApplyChanges ',MsgLine.Msg,' Old=',MsgLine.Line,',',MsgLine.Column,' New=',Line,',',Col]);
|
|
|
|
if (Line=MsgLine.Line) and (MsgLine.Column=Col) then continue;
|
|
MsgLine.SetSourcePosition(MsgLine.Filename,Line,Col);
|
|
Result:=true;
|
|
end;
|
|
finally
|
|
CurLines.UpdateSortedSrcPos:=OldUpdateSortedSrcPos;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
Queue: TETSingleSrcChanges;
|
|
Change: TETSrcChange;
|
|
Node: TAvlTreeNode;
|
|
aFilename: String;
|
|
begin
|
|
Result:=false;
|
|
//debugln(['TLMsgWndView.ApplySrcChanges START ',Changes.Filename,' ',Changes.First<>nil]);
|
|
// check if there are changes
|
|
if Changes.First=nil then exit;
|
|
aFilename:=Changes.Filename;
|
|
if aFilename='' then exit;
|
|
|
|
// update visible lines
|
|
Result:=ApplyChanges(Changes,Lines);
|
|
|
|
// update pending lines
|
|
if Tool<>nil then begin
|
|
Tool.EnterCriticalSection; // lock Tool before View
|
|
try
|
|
EnterCriticalSection;
|
|
try
|
|
Queue:=PendingChanges.GetChanges(aFilename,true);
|
|
Change:=Changes.First;
|
|
while Change<>nil do begin
|
|
Queue.Add(Change.Action,Change.FromPos,Change.ToPos);
|
|
Change:=Change.Next;
|
|
end;
|
|
if not Running then begin
|
|
// apply all pending changes to Tool.WorkerMessages
|
|
Node:=PendingChanges.AllChanges.FindLowest;
|
|
while Node<>nil do begin
|
|
ApplyChanges(TETSingleSrcChanges(Node.Data),Tool.WorkerMessages);
|
|
Node:=Node.Successor;
|
|
end;
|
|
PendingChanges.Clear;
|
|
end;
|
|
finally
|
|
LeaveCriticalSection;
|
|
end;
|
|
finally
|
|
Tool.LeaveCriticalSection;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{ TMsgCtrlUrgencyStyle }
|
|
|
|
procedure TMsgCtrlUrgencyStyle.SetColor(AValue: TColor);
|
|
begin
|
|
if FColor=AValue then Exit;
|
|
FColor:=AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TMsgCtrlUrgencyStyle.SetImageIndex(AValue: integer);
|
|
begin
|
|
if FImageIndex=AValue then Exit;
|
|
FImageIndex:=AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TMsgCtrlUrgencyStyle.SetTranslated(AValue: string);
|
|
begin
|
|
if FTranslated=AValue then Exit;
|
|
FTranslated:=AValue;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TMsgCtrlUrgencyStyle.Changed;
|
|
begin
|
|
Control.Invalidate;
|
|
end;
|
|
|
|
constructor TMsgCtrlUrgencyStyle.Create(AControl: TMessagesCtrl;
|
|
TheUrgency: TMessageLineUrgency);
|
|
begin
|
|
FControl:=AControl;
|
|
fUrgency:=TheUrgency;
|
|
FImageIndex:=-1;
|
|
FColor:=clDefault;
|
|
end;
|
|
|
|
function TMsgCtrlUrgencyStyle.Equals(Obj: TObject): boolean;
|
|
var
|
|
Src: TMsgCtrlUrgencyStyle;
|
|
begin
|
|
if Obj is TMsgCtrlUrgencyStyle then begin
|
|
Src:=TMsgCtrlUrgencyStyle(Obj);
|
|
Result:=(ImageIndex=Src.ImageIndex)
|
|
and (Color=Src.Color)
|
|
and (Translated=Src.Translated);
|
|
end else
|
|
Result:=inherited Equals(Obj);
|
|
end;
|
|
|
|
procedure TMsgCtrlUrgencyStyle.Assign(Src: TMsgCtrlUrgencyStyle);
|
|
begin
|
|
if Equals(Src) then exit;
|
|
fImageIndex:=Src.ImageIndex;
|
|
fColor:=Src.Color;
|
|
fTranslated:=Src.Translated;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TMsgCtrlUrgencyStyle.SetValues(TheTranslated: string;
|
|
TheImageIndex: integer; TheColor: TColor);
|
|
begin
|
|
Translated:=TheTranslated;
|
|
ImageIndex:=TheImageIndex;
|
|
Color:=TheColor;
|
|
end;
|
|
|
|
{ TMessagesCtrl }
|
|
|
|
// inline
|
|
function TMessagesCtrl.ViewCount: integer;
|
|
begin
|
|
Result:=FViews.Count;
|
|
end;
|
|
|
|
// inline
|
|
function TMessagesCtrl.Filters: TLMsgViewFilters;
|
|
begin
|
|
Result:=EnvironmentGuiOpts.MsgViewFilters;
|
|
end;
|
|
|
|
// inline
|
|
function TMessagesCtrl.GetActiveFilter: TLMsgViewFilter;
|
|
begin
|
|
Result:=Filters.ActiveFilter;
|
|
end;
|
|
|
|
// inline
|
|
procedure TMessagesCtrl.SetActiveFilter(AValue: TLMsgViewFilter);
|
|
begin
|
|
Filters.ActiveFilter:=AValue;
|
|
end;
|
|
|
|
function TMessagesCtrl.GetViews(Index: integer): TLMsgWndView;
|
|
|
|
procedure RaiseOutOfBounds;
|
|
begin
|
|
raise Exception.Create('TMessagesCtrl.GetViews '+IntToStr(Index)+' out of bounds '+IntToStr(ViewCount));
|
|
end;
|
|
|
|
begin
|
|
if (Index<0) or (Index>=ViewCount) then
|
|
RaiseOutOfBounds;
|
|
Result:=TLMsgWndView(FViews[Index]);
|
|
end;
|
|
|
|
procedure TMessagesCtrl.ViewChanged(Sender: TObject);
|
|
var
|
|
AllViewsStopped: Boolean;
|
|
i: Integer;
|
|
begin
|
|
for i:=0 to ViewCount-1 do begin
|
|
if Views[i].Running then begin
|
|
// the views may change many times
|
|
// reduce the update of the control to a few per second by using a timer
|
|
fSomeViewsRunning:=true;
|
|
FUpdateTimer.Enabled:=true;
|
|
exit;
|
|
end;
|
|
end;
|
|
// no views are running
|
|
// The variable fSomeViewsRunning contains the last state
|
|
// if fSomeViewsRunning was true, then all views have stopped
|
|
AllViewsStopped:=fSomeViewsRunning;
|
|
fSomeViewsRunning:=false;
|
|
// no views running => update immediately
|
|
FetchNewMessages;
|
|
|
|
if AllViewsStopped then
|
|
DoAllViewsStopped;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.FetchNewMessages;
|
|
// called when new messages are available from the worker threads
|
|
// calls Views to fetch and filter new messages
|
|
// scrolls to new message
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if csDestroying in ComponentState then exit;
|
|
BeginUpdate;
|
|
try
|
|
for i:=0 to ViewCount-1 do
|
|
FetchNewMessages(Views[i]);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
UpdateScrollBar(true);
|
|
end;
|
|
|
|
function TMessagesCtrl.FetchNewMessages(View: TLMsgWndView): boolean;
|
|
var
|
|
OldLineCount: Integer;
|
|
i: Integer;
|
|
OtherView: TLMsgWndView;
|
|
MaxY: Integer;
|
|
y: Integer;
|
|
begin
|
|
Result:=false;
|
|
if csDestroying in ComponentState then exit;
|
|
if IndexOfView(View)<0 then exit;
|
|
|
|
OldLineCount:=View.Lines.Count;
|
|
if not View.ApplyPending then
|
|
exit; // no new lines
|
|
Result:=true;
|
|
CreateSourceMarks(View,OldLineCount);
|
|
UpdateScrollBar(true);
|
|
Invalidate;
|
|
|
|
// auto scroll
|
|
if SelectedView<>nil then
|
|
exit; // user has selected a non progress line -> do not auto scroll
|
|
|
|
for i:=0 to ViewCount-1 do
|
|
begin
|
|
OtherView:=Views[i];
|
|
if OtherView=View then break;
|
|
if OtherView.Running then begin
|
|
// there is still a prior View running
|
|
// -> keep the last line of the other View visible
|
|
MaxY:=GetLineTop(OtherView,OtherView.GetShownLineCount(true,true),false);
|
|
y:=GetLineTop(View,View.GetShownLineCount(false,true),false);
|
|
ScrollTop:=Min(MaxY,y);
|
|
exit;
|
|
end;
|
|
end;
|
|
// scroll to last line
|
|
ScrollToLine(View,View.GetShownLineCount(false,true),true);
|
|
end;
|
|
|
|
procedure TMessagesCtrl.MsgUpdateTimerTimer(Sender: TObject);
|
|
begin
|
|
FUpdateTimer.Enabled:=false;
|
|
FetchNewMessages;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.SetBackgroundColor(AValue: TColor);
|
|
begin
|
|
if FBackgroundColor=AValue then Exit;
|
|
FBackgroundColor:=AValue;
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.SetFilenameStyle(AValue: TMsgWndFileNameStyle);
|
|
begin
|
|
if FFilenameStyle=AValue then Exit;
|
|
FFilenameStyle:=AValue;
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.SetHeaderBackground(aToolState: TLMVToolState;
|
|
AValue: TColor);
|
|
begin
|
|
if FHeaderBackground[aToolState]=AValue then exit;
|
|
FHeaderBackground[aToolState]:=AValue;
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.SetIdleConnected(AValue: boolean);
|
|
begin
|
|
if FIdleConnected=AValue then Exit;
|
|
FIdleConnected:=AValue;
|
|
if IdleConnected then
|
|
Application.AddOnIdleHandler(@OnIdle)
|
|
else
|
|
Application.RemoveOnIdleHandler(@OnIdle);
|
|
end;
|
|
|
|
procedure TMessagesCtrl.SetImages(AValue: TCustomImageList);
|
|
begin
|
|
if FImages=AValue then Exit;
|
|
if Images <> nil then
|
|
Images.UnRegisterChanges(FImageChangeLink);
|
|
FImages:=AValue;
|
|
if Images <> nil then begin
|
|
Images.RegisterChanges(FImageChangeLink);
|
|
Images.FreeNotification(Self);
|
|
if ItemHeight<Images.Height+2 then
|
|
ItemHeight:=Images.Height+2;
|
|
end;
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.SetItemHeight(AValue: integer);
|
|
begin
|
|
FItemHeight:=Max(0,FItemHeight);
|
|
if FItemHeight=AValue then Exit;
|
|
FItemHeight:=AValue;
|
|
UpdateScrollBar(true);
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.SetOptions(NewOptions: TMsgCtrlOptions);
|
|
var
|
|
ChangedOptions: TMsgCtrlOptions;
|
|
begin
|
|
if FOptions=NewOptions then Exit;
|
|
ChangedOptions:=(FOptions-NewOptions)+(NewOptions-FOptions);
|
|
FOptions:=NewOptions;
|
|
if [mcoShowStats,mcoShowTranslated,mcoShowMessageID,mcoShowMsgIcons,
|
|
mcoAlwaysDrawFocused]*ChangedOptions<>[]
|
|
then
|
|
Invalidate;
|
|
if Assigned(OnOptionsChanged) then
|
|
OnOptionsChanged(Self);
|
|
end;
|
|
|
|
procedure TMessagesCtrl.SetScrollLeft(AValue: integer);
|
|
begin
|
|
AValue:=Max(0,Min(AValue,ScrollLeftMax));
|
|
if FScrollLeft=AValue then Exit;
|
|
FScrollLeft:=AValue;
|
|
UpdateScrollBar(false);
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.SetScrollTop(AValue: integer);
|
|
begin
|
|
AValue:=Max(0,Min(AValue,ScrollTopMax));
|
|
if FScrollTop=AValue then Exit;
|
|
FScrollTop:=AValue;
|
|
UpdateScrollBar(false);
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.SetSearchText(AValue: string);
|
|
begin
|
|
if FSearchText=AValue then Exit;
|
|
FSearchText:=AValue;
|
|
IdleConnected:=true;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.SetSelectedLine(AValue: integer);
|
|
// Select the given line, clear possibly existing selections.
|
|
begin
|
|
Assert(AValue>=-1, 'TMessagesCtrl.SetSelectedLine: AValue < -1.');
|
|
Assert(Assigned(SelectedView), 'TMessagesCtrl.SetSelectedLine: View = Nil.');
|
|
AValue:=Min(AValue, SelectedView.GetShownLineCount(false,true)-1);
|
|
if (FSelectedLines.Count>0) and (FSelectedLines[0]=AValue) then begin
|
|
{$IFDEF VerboseMsgCtrlSelection}
|
|
DebugLn(['TMessagesCtrl.SetSelectedLine: Value ', AValue, ' already selected.']);
|
|
{$ENDIF}
|
|
Exit;
|
|
end;
|
|
FSelectedLines.Count:=1; // One line.
|
|
FSelectedLines[0]:=AValue;
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.SetSelectedView(AValue: TLMsgWndView);
|
|
begin
|
|
if FSelectedView=AValue then Exit;
|
|
FSelectedView:=AValue;
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.SetSourceMarks(AValue: TETMarks);
|
|
begin
|
|
if FSourceMarks=AValue then Exit;
|
|
FSourceMarks:=AValue;
|
|
if SourceMarks<>nil then
|
|
FreeNotification(SourceMarks);
|
|
end;
|
|
|
|
procedure TMessagesCtrl.SetTextColor(AValue: TColor);
|
|
begin
|
|
if FTextColor=AValue then Exit;
|
|
FTextColor:=AValue;
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.SetUrgencyStyles(Urgency: TMessageLineUrgency;
|
|
AValue: TMsgCtrlUrgencyStyle);
|
|
begin
|
|
fUrgencyStyles[Urgency].Assign(AValue);
|
|
end;
|
|
|
|
procedure TMessagesCtrl.SetAutoHeaderBackground(AValue: TColor);
|
|
begin
|
|
if FAutoHeaderBackground=AValue then Exit;
|
|
FAutoHeaderBackground:=AValue;
|
|
Invalidate;
|
|
end;
|
|
|
|
function TMessagesCtrl.UrgencyToStr(Urgency: TMessageLineUrgency): string;
|
|
begin
|
|
if (mcoShowTranslated in Options)
|
|
and (fUrgencyStyles[Urgency].Translated<>'') then
|
|
Result:=fUrgencyStyles[Urgency].Translated
|
|
else
|
|
Result:=MessageLineUrgencyNames[Urgency];
|
|
end;
|
|
|
|
procedure TMessagesCtrl.WMHScroll(var Msg: TLMScroll);
|
|
begin
|
|
case Msg.ScrollCode of
|
|
// Scrolls to start / end of the line
|
|
SB_TOP: ScrollLeft := 1;
|
|
SB_BOTTOM: ScrollLeft := ScrollLeftMax;
|
|
// Scrolls one char left / right
|
|
SB_LINEDOWN: ScrollLeft := ScrollLeft + 1;
|
|
SB_LINEUP: ScrollLeft := ScrollLeft - 1;
|
|
// Scrolls one page of chars left / right
|
|
SB_PAGEDOWN: ScrollLeft := ScrollLeft + (ClientWidth div 2);
|
|
SB_PAGEUP: ScrollLeft := ScrollLeft - (ClientHeight div 2);
|
|
// Scrolls to the current scroll bar position
|
|
SB_THUMBPOSITION,
|
|
SB_THUMBTRACK: ScrollLeft := Msg.Pos;
|
|
end;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.WMVScroll(var Msg: TLMScroll);
|
|
begin
|
|
case Msg.ScrollCode of
|
|
// Scrolls to start / end of the text
|
|
SB_TOP: ScrollTop := 0;
|
|
SB_BOTTOM: ScrollTop := ScrollTopMax;
|
|
{$IFDEF EnableMsgWndLineWrap}
|
|
// Scrolls one line up / down
|
|
SB_LINEDOWN: ScrollTop := ScrollTop + 1;
|
|
SB_LINEUP: ScrollTop := ScrollTop - 1;
|
|
{$ELSE}
|
|
// Scrolls one line up / down
|
|
SB_LINEDOWN: ScrollTop := ScrollTop + ItemHeight div 2;
|
|
SB_LINEUP: ScrollTop := ScrollTop - ItemHeight div 2;
|
|
{$ENDIF}
|
|
// Scrolls one page of lines up / down
|
|
SB_PAGEDOWN: ScrollTop := ScrollTop + GetPageScroll;
|
|
SB_PAGEUP: ScrollTop := ScrollTop - GetPageScroll;
|
|
// Scrolls to the current scroll bar position
|
|
SB_THUMBPOSITION,
|
|
SB_THUMBTRACK: ScrollTop := Msg.Pos;
|
|
// Ends scrolling
|
|
SB_ENDSCROLL: SetCaptureControl(nil); // release scrollbar capture
|
|
end;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.WMMouseWheel(var Message: TLMMouseEvent);
|
|
begin
|
|
if Mouse.WheelScrollLines=-1 then
|
|
begin
|
|
// -1 : scroll by page
|
|
ScrollTop := ScrollTop - (Message.WheelDelta * GetPageScroll) div 120;
|
|
end else begin
|
|
{$IFDEF EnableMsgWndLineWrap}
|
|
// scrolling one line -> see SB_LINEDOWN and SB_LINEUP handler in WMVScroll
|
|
ScrollTop := ScrollTop -
|
|
(Message.WheelDelta * Mouse.WheelScrollLines) div 240;
|
|
{$ELSE}
|
|
// scrolling one line -> scroll half an item, see SB_LINEDOWN and SB_LINEUP
|
|
// handler in WMVScroll
|
|
ScrollTop := ScrollTop -
|
|
(Message.WheelDelta * Mouse.WheelScrollLines*ItemHeight) div 240;
|
|
{$ENDIF}
|
|
end;
|
|
Message.Result := 1;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.WMSetFocus(var Message: TLMSetFocus);
|
|
begin
|
|
Invalidate;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.WMKillFocus(var Message: TLMKillFocus);
|
|
begin
|
|
Invalidate;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.ImageListChange(Sender: TObject);
|
|
begin
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.OnIdle(Sender: TObject; var Done: Boolean);
|
|
var
|
|
View: TLMsgWndView;
|
|
LineNumber: integer;
|
|
i: Integer;
|
|
begin
|
|
//debugln(['TMessagesCtrl.OnIdle fLastLoSearchText=',fLastLoSearchText,' ',UTF8LowerCase(fSearchText)]);
|
|
for i:=0 to ViewCount-1 do begin
|
|
View:=Views[i];
|
|
if not View.Filter.IsEqual(ActiveFilter) then begin
|
|
View.EnterCriticalSection;
|
|
try
|
|
View.Filter:=ActiveFilter;
|
|
finally
|
|
View.LeaveCriticalSection;
|
|
end;
|
|
View.RebuildLines;
|
|
CreateSourceMarks(View,0);
|
|
UpdateScrollBar(true);
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
if fLastLoSearchText<>UTF8LowerCase(fSearchText) then begin
|
|
fLastLoSearchText:=UTF8LowerCase(FSearchText);
|
|
if SearchNext(fLastSearchStartView,fLastSearchStartLine,false,true,
|
|
View,LineNumber)
|
|
then begin
|
|
//debugln(['TMessagesCtrl.OnIdle search text found ',LineNumber]);
|
|
Select(View,LineNumber,true,true);
|
|
end else begin
|
|
//debugln(['TMessagesCtrl.OnIdle search text not found']);
|
|
end;
|
|
Invalidate;
|
|
end;
|
|
IdleConnected:=false;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.FilterChanged(Sender: TObject);
|
|
begin
|
|
FSelectedLines.Clear;
|
|
IdleConnected:=true;
|
|
end;
|
|
|
|
function TMessagesCtrl.GetPageScroll: integer;
|
|
begin
|
|
{$IFDEF EnableMsgWndLineWrap}
|
|
Result:=Max(1,((ClientHeight-BorderWidth) div ItemHeight));
|
|
{$ELSE}
|
|
Result:=ClientHeight - ItemHeight;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TMessagesCtrl.GetSelectedLine: integer;
|
|
// Return the first selected line number.
|
|
begin
|
|
if FSelectedLines.Count>0 then
|
|
Result:=FSelectedLines[0]
|
|
else
|
|
Result:=-1; // No selection.
|
|
end;
|
|
|
|
procedure TMessagesCtrl.CreateSourceMarks(View: TLMsgWndView;
|
|
StartLineNumber: Integer);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if SourceMarks=nil then exit;
|
|
for i:=StartLineNumber to View.Lines.Count-1 do
|
|
CreateSourceMark(View.Lines[i],nil);
|
|
end;
|
|
|
|
function TMessagesCtrl.GetHeaderBackground(aToolState: TLMVToolState): TColor;
|
|
begin
|
|
Result:=FHeaderBackground[aToolState];
|
|
end;
|
|
|
|
procedure TMessagesCtrl.CreateSourceMark(MsgLine: TMessageLine;
|
|
aSynEdit: TSynEdit);
|
|
var
|
|
SourceMark: TETMark;
|
|
begin
|
|
if TLMsgViewLine(MsgLine).Mark<>nil then exit;
|
|
if ord(MsgLine.Urgency)<ord(mluHint) then exit;
|
|
SourceMark:=SourceMarks.CreateMark(MsgLine,aSynEdit);
|
|
if SourceMark=nil then exit;
|
|
TLMsgViewLine(MsgLine).Mark:=SourceMark;
|
|
end;
|
|
|
|
function TMessagesCtrl.GetUrgencyStyles(Urgency: TMessageLineUrgency
|
|
): TMsgCtrlUrgencyStyle;
|
|
begin
|
|
Result:=fUrgencyStyles[Urgency];
|
|
end;
|
|
|
|
procedure TMessagesCtrl.Notification(AComponent: TComponent;
|
|
Operation: TOperation);
|
|
begin
|
|
inherited Notification(AComponent, Operation);
|
|
if Operation=opRemove then begin
|
|
if (AComponent is TLMsgWndView) and (FViews.IndexOf(AComponent)>=0) then begin
|
|
if fLastSearchStartView=AComponent then
|
|
fLastSearchStartView:=nil;
|
|
if SelectedView=AComponent then
|
|
FSelectedView:=nil;
|
|
RemoveView(TLMsgWndView(AComponent));
|
|
end
|
|
else if AComponent=Images then
|
|
Images:=nil
|
|
else if AComponent=SourceMarks then
|
|
SourceMarks:=nil;
|
|
end;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.Paint;
|
|
var
|
|
LoSearchText: string;
|
|
|
|
procedure DrawText(ARect: TRect; aTxt: string; IsSelected: boolean;
|
|
TxtColor: TColor);
|
|
var
|
|
Details: TThemedElementDetails;
|
|
TextRect: TRect;
|
|
p: SizeInt;
|
|
LoTxt: String;
|
|
aLeft: Integer;
|
|
aRight: Integer;
|
|
LastP: Integer;
|
|
begin
|
|
Canvas.Font.Color:=Font.Color;
|
|
TextRect:=ARect;
|
|
TextRect.Right:=TextRect.Left+Canvas.TextWidth(aTxt)+2;
|
|
if IsSelected then begin
|
|
if (mcsFocused in FStates) or (mcoAlwaysDrawFocused in Options) then
|
|
Details:=ThemeServices.GetElementDetails(ttItemSelected)
|
|
else
|
|
Details:=ThemeServices.GetElementDetails(ttItemSelectedNotFocus);
|
|
ThemeServices.DrawElement(Canvas.Handle, Details, TextRect, nil);
|
|
TxtColor:=clDefault;
|
|
end else
|
|
Details:=ThemeServices.GetElementDetails(ttItemNormal);
|
|
if LoSearchText<>'' then begin
|
|
LoTxt:=UTF8LowerCase(aTxt);
|
|
p:=1;
|
|
LastP:=1;
|
|
while p<=length(LoTxt) do begin
|
|
p:=PosEx(LoSearchText,LoTxt,LastP);
|
|
if p<1 then break;
|
|
Canvas.Brush.Color:=clHighlight;
|
|
aLeft:=TextRect.Left+Canvas.TextWidth(copy(ATxt,1,p-1));
|
|
aRight:=aLeft+Canvas.TextWidth(copy(ATxt,p,length(LoSearchText)));
|
|
Canvas.FillRect(aLeft,TextRect.Top+1,aRight,TextRect.Bottom-1);
|
|
LastP:=p+length(LoSearchText);
|
|
end;
|
|
Canvas.Brush.Color:=BackgroundColor;
|
|
end;
|
|
if TxtColor=clDefault then
|
|
ThemeServices.DrawText(Canvas, Details, ATxt, TextRect,
|
|
DT_CENTER or DT_VCENTER or DT_SINGLELINE or DT_NOPREFIX, 0)
|
|
else begin
|
|
p:=(TextRect.Top+TextRect.Bottom-Canvas.TextHeight('Mg')) div 2;
|
|
Canvas.Font.Color:=TxtColor;
|
|
Canvas.TextOut(TextRect.Left+2,p,ATxt);
|
|
end;
|
|
end;
|
|
|
|
var
|
|
i: Integer;
|
|
View: TLMsgWndView;
|
|
y: Integer;
|
|
j: Integer;
|
|
Line: TMessageLine;
|
|
Indent: Integer;
|
|
NodeRect: TRect;
|
|
ImgIndex: LongInt;
|
|
IsSelected: Boolean;
|
|
FirstLineIsNotSelectedMessage: Boolean;
|
|
SecondLineIsNotSelectedMessage: Boolean;
|
|
col: TColor;
|
|
ImgRes: TScaledImageListResolution;
|
|
begin
|
|
if Focused then
|
|
Include(FStates,mcsFocused)
|
|
else
|
|
Exclude(FStates,mcsFocused);
|
|
//debugln(['TMessagesCtrl.Paint ',Focused,' CanFocus=',CanFocus,' TabStop=',TabStop]);
|
|
LUIncreaseChangeStamp64(FPaintStamp);
|
|
|
|
// paint background
|
|
Canvas.Brush.Color:=BackgroundColor;
|
|
Canvas.FillRect(0,0,ClientWidth,ClientHeight);
|
|
Indent:=BorderWidth+2;
|
|
LoSearchText:=fLastLoSearchText;
|
|
fHasHeaderHint:=False;
|
|
|
|
// paint from top to bottom
|
|
{$IFDEF EnableMsgWndLineWrap}
|
|
y:=-ScrollTop*ItemHeight;
|
|
{$ELSE}
|
|
y:=-ScrollTop;
|
|
{$ENDIF}
|
|
for i:=0 to ViewCount-1 do begin
|
|
if y>ClientHeight then break;
|
|
View:=Views[i];
|
|
if not View.HasContent then continue;
|
|
|
|
View.FPaintStamp:=FPaintStamp;
|
|
View.fPaintTop:=y;
|
|
|
|
// draw header
|
|
if (y+ItemHeight>0) and (y<ClientHeight) then begin
|
|
// header text
|
|
NodeRect:=Rect(0,y,ClientWidth,y+ItemHeight);
|
|
Canvas.Brush.Color:=HeaderBackground[View.ToolState];
|
|
Canvas.FillRect(NodeRect);
|
|
Canvas.Pen.Style:=psDash;
|
|
Canvas.Line(NodeRect.Left,NodeRect.Top,NodeRect.Right,NodeRect.Top);
|
|
Canvas.Pen.Style:=psSolid;
|
|
DrawText(NodeRect,GetHeaderText(View),
|
|
(fSelectedView=View) and (FSelectedLines.IndexOf(-1)>=0),TextColor);
|
|
Canvas.Brush.Color:=BackgroundColor;
|
|
end;
|
|
inc(y,ItemHeight);
|
|
|
|
// draw lines
|
|
j:=0;
|
|
if y<0 then begin
|
|
j:=Min((-y) div ItemHeight,View.Lines.Count);
|
|
inc(y,j*ItemHeight);
|
|
end;
|
|
FirstLineIsNotSelectedMessage:=false;
|
|
SecondLineIsNotSelectedMessage:=false;
|
|
while (j<View.Lines.Count) and (y<ClientHeight) do begin
|
|
Line:=View.Lines[j];
|
|
NodeRect:=Rect(Indent,y,ClientWidth,y+ItemHeight);
|
|
IsSelected:=(fSelectedView=View) and (FSelectedLines.IndexOf(j)>=0);
|
|
if not IsSelected then begin
|
|
if (y>-ItemHeight) and (y<=0) then
|
|
FirstLineIsNotSelectedMessage:=true
|
|
else if (y>0) and (y<=ItemHeight) then
|
|
SecondLineIsNotSelectedMessage:=true;
|
|
end;
|
|
ImgIndex:=fUrgencyStyles[Line.Urgency].ImageIndex;
|
|
if (Images<>nil) and (mcoShowMsgIcons in Options)
|
|
and (ImgIndex>=0) and (ImgIndex<Images.Count) then begin
|
|
ImgRes := Images.ResolutionForControl[0, Self];
|
|
ImgRes.Draw(Canvas,
|
|
NodeRect.Left + 1, (NodeRect.Top + NodeRect.Bottom - Images.Height) div 2,
|
|
ImgIndex, gdeNormal);
|
|
inc(NodeRect.Left, ImgRes.Width+2);
|
|
end;
|
|
// message text
|
|
col:=UrgencyStyles[Line.Urgency].Color;
|
|
if col=clDefault then
|
|
col:=TextColor;
|
|
DrawText(NodeRect,GetLineText(Line),IsSelected,col);
|
|
inc(y,ItemHeight);
|
|
inc(j);
|
|
end;
|
|
if FirstLineIsNotSelectedMessage and SecondLineIsNotSelectedMessage then begin
|
|
// the first two lines are normal messages, not selected
|
|
// => paint view header hint
|
|
fHasHeaderHint:=True;
|
|
NodeRect:=Rect(0,0,ClientWidth,ItemHeight div 2);
|
|
Canvas.Brush.Color:=HeaderBackground[View.ToolState];
|
|
Canvas.Brush.Style:=bsSolid;
|
|
Canvas.FillRect(NodeRect);
|
|
NodeRect:=Rect(0,NodeRect.Bottom,ClientWidth,ItemHeight);
|
|
Canvas.GradientFill(NodeRect,HeaderBackground[View.ToolState],
|
|
AutoHeaderBackground,gdVertical);
|
|
Canvas.Pen.Style:=psDash;
|
|
NodeRect:=Rect(0,0,ClientWidth,ItemHeight);
|
|
Canvas.Line(NodeRect.Left,NodeRect.Bottom,NodeRect.Right,NodeRect.Bottom);
|
|
Canvas.Pen.Style:=psSolid;
|
|
DrawText(NodeRect,'...'+GetHeaderText(View),false,TextColor);
|
|
Canvas.Brush.Color:=BackgroundColor;
|
|
end;
|
|
inc(y,ItemHeight*(View.Lines.Count-j));
|
|
|
|
// draw progress line
|
|
if View.ProgressLine.Msg<>'' then begin
|
|
if (y+ItemHeight>0) and (y<ClientHeight) then begin
|
|
// progress text
|
|
NodeRect:=Rect(Indent,y,ClientWidth,y+ItemHeight);
|
|
col:=UrgencyStyles[View.ProgressLine.Urgency].Color;
|
|
if col=clDefault then
|
|
col:=TextColor;
|
|
DrawText(NodeRect,View.ProgressLine.Msg,
|
|
(fSelectedView=View) and (FSelectedLines.IndexOf(View.Lines.Count)>=0),col);
|
|
end;
|
|
inc(y,ItemHeight);
|
|
end;
|
|
|
|
View.fPaintBottom:=y;
|
|
end;
|
|
|
|
// call OnPaint
|
|
inherited Paint;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.UpdateScrollBar(InvalidateScrollMax: boolean);
|
|
var
|
|
ScrollInfo: TScrollInfo;
|
|
begin
|
|
if InvalidateScrollMax then begin
|
|
fScrollTopMax:=-1;
|
|
end;
|
|
if not HandleAllocated then exit;
|
|
|
|
ScrollInfo.cbSize := SizeOf(ScrollInfo);
|
|
ScrollInfo.fMask := SIF_ALL or SIF_DISABLENOSCROLL;
|
|
ScrollInfo.nMin := 0;
|
|
ScrollInfo.nTrackPos := 0;
|
|
ScrollInfo.nMax := ScrollTopMax+ClientHeight-1;
|
|
if ClientHeight < 2 then
|
|
ScrollInfo.nPage := 1
|
|
else
|
|
ScrollInfo.nPage := ClientHeight-1;
|
|
if ScrollTop > ScrollTopMax then
|
|
ScrollTop := ScrollTopMax;
|
|
ScrollInfo.nPos := ScrollTop;
|
|
//debugln(['TMessagesCtrl.UpdateScrollBar ScrollTop=',ScrollTop,' ScrollTopMax=',ScrollTopMax]);
|
|
ShowScrollBar(Handle, SB_VERT, True);
|
|
SetScrollInfo(Handle, SB_VERT, ScrollInfo, false);
|
|
end;
|
|
|
|
procedure TMessagesCtrl.CreateWnd;
|
|
begin
|
|
inherited CreateWnd;
|
|
ItemHeight:=Canvas.TextHeight('Mg')+2;
|
|
UpdateScrollBar(false);
|
|
end;
|
|
|
|
procedure TMessagesCtrl.DoSetBounds(ALeft, ATop, AWidth, AHeight: integer);
|
|
begin
|
|
inherited DoSetBounds(ALeft, ATop, AWidth, AHeight);
|
|
UpdateScrollBar(true);
|
|
end;
|
|
|
|
procedure TMessagesCtrl.MsgCtrlMouseMove(Sender: TObject; Shift: TShiftState;
|
|
X, Y: Integer);
|
|
var
|
|
lLineFound: boolean;
|
|
loLine: integer;
|
|
begin
|
|
lLineFound := GetLineAt(Y,{out}FHintLastView, loLine);
|
|
if lLineFound then begin
|
|
if loLine<>FHintLastLine then
|
|
Application.CancelHint;
|
|
FHintLastLine := loLine;
|
|
end
|
|
else begin
|
|
if FHintLastLine>cNotALineHint then
|
|
Application.CancelHint;
|
|
FHintLastLine := cNotALineHint;
|
|
end;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.MouseDown(Button: TMouseButton; Shift: TShiftState;
|
|
X, Y: Integer);
|
|
var
|
|
View: TLMsgWndView;
|
|
LineNumber: integer;
|
|
begin
|
|
if not Focused and CanFocus then
|
|
SetFocus;
|
|
inherited MouseDown(Button, Shift, X, Y);
|
|
if GetLineAt(Y,View,LineNumber) then begin
|
|
if not (Button in [mbLeft,mbRight]) then Exit;
|
|
if (Button=mbLeft) and (ssCtrl in Shift) then begin
|
|
ToggleSelectedLine(View,LineNumber);
|
|
end
|
|
else if (Button=mbLeft) and (ssShift in Shift) then
|
|
ExtendSelection(View,LineNumber)
|
|
else if (Button=mbLeft) and (ssAlt in Shift) then
|
|
ToggleSelectedLine(View,LineNumber)
|
|
else begin
|
|
if (Button=mbLeft)
|
|
or (View<>SelectedView) or (FSelectedLines.IndexOf(LineNumber)=-1) then
|
|
begin
|
|
if fHasHeaderHint and (Y<ItemHeight) then
|
|
// The header is drawn on top as a hint. Select the actual header line.
|
|
Select(View,-1,true,true)
|
|
else begin
|
|
Select(View,LineNumber,true,true);
|
|
StoreSelectedAsSearchStart;
|
|
end;
|
|
end;
|
|
if (Button=mbLeft) then begin
|
|
if ((ssDouble in Shift) and (not (mcoSingleClickOpensFile in FOptions)))
|
|
or ((mcoSingleClickOpensFile in FOptions) and ([ssDouble,ssTriple,ssQuad]*Shift=[]))
|
|
then
|
|
OpenSelection;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.KeyDown(var Key: Word; Shift: TShiftState);
|
|
begin
|
|
inherited KeyDown(Key, Shift);
|
|
|
|
case Key of
|
|
VK_DOWN:
|
|
begin
|
|
SelectNextShown(+1);
|
|
Key:=VK_UNKNOWN;
|
|
end;
|
|
|
|
VK_UP:
|
|
begin
|
|
SelectNextShown(-1);
|
|
Key:=VK_UNKNOWN;
|
|
end;
|
|
|
|
VK_HOME:
|
|
begin
|
|
SelectFirst(true,true);
|
|
Key:=VK_UNKNOWN;
|
|
end;
|
|
|
|
VK_END:
|
|
begin
|
|
SelectLast(true,true);
|
|
Key:=VK_UNKNOWN;
|
|
end;
|
|
|
|
VK_PRIOR: // Page Up
|
|
begin
|
|
SelectNextShown(-Max(1,ClientHeight div ItemHeight));
|
|
Key:=VK_UNKNOWN;
|
|
end;
|
|
|
|
VK_NEXT: // Page Down
|
|
begin
|
|
SelectNextShown(Max(1,ClientHeight div ItemHeight));
|
|
Key:=VK_UNKNOWN;
|
|
end;
|
|
VK_C: // Ctrl+'C' -> copy HintData to clipboard
|
|
if (Shift = [ssCtrl]) and Assigned(FHintLastView) then begin
|
|
ClipBoard.AsText := FHintLastView.AsHintString(Self.FHintLastLine);
|
|
Key := VK_UNKNOWN;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.DoAllViewsStopped;
|
|
{off $DEFINE VerboseMsgFrame}
|
|
|
|
{$IFDEF VerboseMsgFrame}
|
|
procedure DbgViews;
|
|
var
|
|
i, j: Integer;
|
|
View: TLMsgWndView;
|
|
Tool: TAbstractExternalTool;
|
|
SrcMsg: TMessageLine;
|
|
begin
|
|
debugln(['TMessagesCtrl.DoAllViewsStopped ']);
|
|
debugln(['DbgViews===START========================================']);
|
|
for i:=0 to ViewCount-1 do
|
|
begin
|
|
View:=Views[i];
|
|
View.RebuildLines;
|
|
Tool:=View.Tool;
|
|
if Tool=nil then continue;
|
|
debugln(['DbgViews ',i,'/',ViewCount,' Tool.Title=',Tool.Title]);
|
|
Tool.EnterCriticalSection; // lock Tool before View
|
|
try
|
|
View.EnterCriticalSection;
|
|
try
|
|
for j:=0 to Tool.WorkerMessages.Count-1 do begin
|
|
SrcMsg:=Tool.WorkerMessages[j];
|
|
debugln(['WorkerMsg ',SrcMsg.Filename,'(',SrcMsg.Line,',',SrcMsg.Column,') ',UrgencyToStr(SrcMsg.Urgency),'/',SrcMsg.SubTool,': ',SrcMsg.Msg]);
|
|
end;
|
|
for j:=0 to View.Lines.Count-1 do begin
|
|
SrcMsg:=View.Lines[j];
|
|
debugln(['ViewMsg ',SrcMsg.Filename,'(',SrcMsg.Line,',',SrcMsg.Column,') ',UrgencyToStr(SrcMsg.Urgency),'/',SrcMsg.SubTool,': ',SrcMsg.Msg]);
|
|
end;
|
|
finally
|
|
View.LeaveCriticalSection;
|
|
end;
|
|
finally
|
|
Tool.LeaveCriticalSection;
|
|
end;
|
|
end;
|
|
debugln(['DbgViews===END==========================================']);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
var
|
|
CurLine: TMessageLine;
|
|
begin
|
|
if Assigned(OnAllViewsStopped) then
|
|
OnAllViewsStopped(Self);
|
|
if mcoAutoOpenFirstError in Options then begin
|
|
CurLine:=GetSelectedMsg;
|
|
if (CurLine<>nil) and (CurLine.Urgency>=mluError)
|
|
and CurLine.HasSourcePosition then
|
|
exit;
|
|
if SelectFirstUrgentMessage(mluError,true) then
|
|
OpenSelection;
|
|
end;
|
|
{$IFDEF VerboseMsgFrame}
|
|
DbgViews;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TMessagesCtrl.SearchNext(StartView: TLMsgWndView; StartLine: integer;
|
|
SkipStart, Downwards: boolean; out View: TLMsgWndView; out LineNumber: integer
|
|
): boolean;
|
|
var
|
|
CurView: TLMsgWndView;
|
|
CurLine: Integer;
|
|
CurViewLineCnt: integer;
|
|
Txt: String;
|
|
|
|
function Next: boolean;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if Downwards then begin
|
|
inc(CurLine);
|
|
if CurLine>=CurViewLineCnt then begin
|
|
i:=IndexOfView(CurView);
|
|
repeat
|
|
inc(i);
|
|
if i>=ViewCount then exit(false);
|
|
CurView:=Views[i];
|
|
until CurView.HasContent;
|
|
CurLine:=-1;
|
|
CurViewLineCnt:=CurView.GetShownLineCount(true,true);
|
|
end;
|
|
end else begin
|
|
dec(CurLine);
|
|
if CurLine<-1 then begin
|
|
i:=IndexOfView(CurView);
|
|
repeat
|
|
dec(i);
|
|
if i<0 then exit(false);
|
|
CurView:=Views[i];
|
|
until CurView.HasContent;
|
|
CurViewLineCnt:=CurView.GetShownLineCount(true,true);
|
|
CurLine:=CurViewLineCnt-1;
|
|
end;
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
begin
|
|
Result:=false;
|
|
View:=nil;
|
|
LineNumber:=-1;
|
|
if ViewCount=0 then exit;
|
|
if StartView=nil then begin
|
|
// use default start
|
|
if Downwards then begin
|
|
StartView:=Views[0];
|
|
StartLine:=-1;
|
|
end else begin
|
|
StartView:=Views[ViewCount-1];
|
|
StartLine:=StartView.GetShownLineCount(true,true);
|
|
end;
|
|
end;
|
|
CurView:=StartView;
|
|
CurLine:=StartLine;
|
|
CurViewLineCnt:=CurView.GetShownLineCount(true,true);
|
|
// skip invalid line numbers
|
|
if CurLine<-1 then begin
|
|
SkipStart:=false;
|
|
if Downwards then
|
|
CurLine:=-1
|
|
else if not Next then
|
|
exit;
|
|
end else if CurLine>=CurViewLineCnt then begin
|
|
SkipStart:=false;
|
|
if Downwards then begin
|
|
if not Next then exit;
|
|
end else
|
|
CurLine:=CurViewLineCnt-1;
|
|
end;
|
|
// skip invalid views
|
|
if not CurView.HasContent then begin
|
|
SkipStart:=false;
|
|
if not Next then exit;
|
|
end;
|
|
// skip start
|
|
if SkipStart then
|
|
if not Next then exit;
|
|
// search
|
|
repeat
|
|
if CurLine<0 then
|
|
Txt:=GetHeaderText(CurView)
|
|
else if CurLine<CurView.Lines.Count then
|
|
Txt:=GetLineText(CurView.Lines[CurLine])
|
|
else
|
|
Txt:=CurView.ProgressLine.Msg;
|
|
Txt:=UTF8LowerCase(Txt);
|
|
if Pos(fLastLoSearchText,Txt)>0 then begin
|
|
View:=CurView;
|
|
LineNumber:=CurLine;
|
|
exit(true);
|
|
end;
|
|
until not Next;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.ToggleSelectedLine(View: TLMsgWndView; LineNumber: integer);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
BeginUpdate;
|
|
SelectedView:=View;
|
|
if FSelectedLines.Count=0 then // No existing selection.
|
|
i:=-1
|
|
else
|
|
i:=FSelectedLines.IndexOf(LineNumber);
|
|
if i=-1 then
|
|
FSelectedLines.Add(LineNumber)
|
|
else
|
|
FSelectedLines.Delete(i); // Was already selected -> toggle.
|
|
Invalidate;
|
|
EndUpdate;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.ExtendSelection(View: TLMsgWndView; LineNumber: integer);
|
|
var
|
|
i: Integer;
|
|
Empty: Boolean;
|
|
begin
|
|
BeginUpdate;
|
|
SelectedView:=View;
|
|
Empty:=FSelectedLines.Count=0;
|
|
FSelectedLines.Count:=1; // Delete possible earlier selections except first one.
|
|
if Empty then
|
|
FSelectedLines[0]:=LineNumber // No earlier selection.
|
|
else if LineNumber<FSelectedLines[0] then
|
|
for i:=LineNumber to FSelectedLines[0]-1 do
|
|
FSelectedLines.Add(i)
|
|
else if LineNumber>FSelectedLines[0] then
|
|
for i:=FSelectedLines[0]+1 to LineNumber do
|
|
FSelectedLines.Add(i);
|
|
// if LineNumber=FSelectedLines[0] then do nothing.
|
|
Invalidate;
|
|
EndUpdate;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.Select(View: TLMsgWndView; LineNumber: integer;
|
|
DoScroll, FullyVisible: boolean);
|
|
begin
|
|
BeginUpdate;
|
|
SelectedView:=View;
|
|
SelectedLine1:=LineNumber;
|
|
if DoScroll then
|
|
ScrollToLine(SelectedView,LineNumber,FullyVisible);
|
|
EndUpdate;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.Select(Msg: TMessageLine; DoScroll: boolean);
|
|
begin
|
|
BeginUpdate;
|
|
if (Msg=nil) or (Msg.Lines=nil) or not (Msg.Lines.Owner is TLMsgWndView) then
|
|
begin
|
|
SelectedView:=nil;
|
|
FSelectedLines.Clear;
|
|
Invalidate;
|
|
end else begin
|
|
SelectedView:=TLMsgWndView(Msg.Lines.Owner);
|
|
SelectedLine1:=Msg.Index;
|
|
if DoScroll then
|
|
ScrollToLine(SelectedView,Msg.Index,true);
|
|
end;
|
|
EndUpdate;
|
|
end;
|
|
|
|
function TMessagesCtrl.SelectNextOccurrence(Downwards: boolean): boolean;
|
|
var
|
|
View: TLMsgWndView;
|
|
LineNumber: integer;
|
|
begin
|
|
StoreSelectedAsSearchStart;
|
|
Result:=SearchNext(SelectedView,SelectedLine1,true,Downwards,View,LineNumber);
|
|
if not Result then exit;
|
|
Select(View,LineNumber,true,true);
|
|
end;
|
|
|
|
function TMessagesCtrl.SelectNextShown(Offset: integer): boolean;
|
|
// returns true if selection changed
|
|
var
|
|
View: TLMsgWndView;
|
|
Line: Integer;
|
|
i: Integer;
|
|
begin
|
|
Result:=false;
|
|
{$IFDEF VerboseMsgCtrlSelection}
|
|
DebugLn(['TMessagesCtrl.SelectNextShown START']);
|
|
{$ENDIF}
|
|
while Offset<>0 do begin
|
|
{$IFDEF VerboseMsgCtrlSelection}
|
|
DebugLn(['TMessagesCtrl.SelectNextShown LOOP Offset=',Offset,
|
|
' ViewIndex=',IndexOfView(SelectedView),' Line=',SelectedLine1]);
|
|
{$ENDIF}
|
|
if SelectedView=nil then begin
|
|
if Offset>0 then begin
|
|
SelectFirst(true,true);
|
|
dec(Offset);
|
|
end else begin
|
|
SelectLast(true,true);
|
|
Inc(Offset);
|
|
end;
|
|
Result:=true;
|
|
end else begin
|
|
View:=SelectedView;
|
|
Line:=SelectedLine1;
|
|
if Offset>0 then begin
|
|
{$IFDEF VerboseMsgCtrlSelection}
|
|
DebugLn(['TMessagesCtrl.SelectNextShown NEXT View.GetShownLineCount(false,true)=',
|
|
View.GetShownLineCount(false,true),' ViewIndex=',IndexOfView(View),' Line=',Line]);
|
|
{$ENDIF}
|
|
inc(Line,Offset);
|
|
if Line<View.GetShownLineCount(false,true) then
|
|
Offset:=0
|
|
else begin
|
|
// next view
|
|
Offset:=Line-View.GetShownLineCount(false,true);
|
|
i:=IndexOfView(View);
|
|
{$IFDEF VerboseMsgCtrlSelection}
|
|
DebugLn(['TMessagesCtrl.SelectNextShown Line=',Line,' Offset=',Offset,' ViewIndex=',i]);
|
|
{$ENDIF}
|
|
repeat
|
|
inc(i);
|
|
if i>=ViewCount then begin
|
|
{$IFDEF VerboseMsgCtrlSelection}
|
|
DebugLn(['TMessagesCtrl.SelectNextShown can not go further down']);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
View:=Views[i];
|
|
until View.HasContent;
|
|
Line:=-1;
|
|
end;
|
|
end else begin
|
|
inc(Line,Offset);
|
|
if Line>=-1 then
|
|
Offset:=0
|
|
else begin
|
|
// previous view
|
|
Offset:=Line+2;
|
|
i:=IndexOfView(View);
|
|
repeat
|
|
dec(i);
|
|
if i<0 then begin
|
|
{$IFDEF VerboseMsgCtrlSelection}
|
|
DebugLn(['TMessagesCtrl.SelectNextShown can not go further up']);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
View:=Views[i];
|
|
until View.HasContent;
|
|
Line:=View.GetShownLineCount(false,false)-1;
|
|
end;
|
|
end;
|
|
{$IFDEF VerboseMsgCtrlSelection}
|
|
DebugLn(['TMessagesCtrl.SelectNextShown SELECT Offset=',Offset,
|
|
' ViewIndex=',IndexOfView(View),' Line=',Line]);
|
|
{$ENDIF}
|
|
Select(View,Line,true,true);
|
|
Result:=true;
|
|
end;
|
|
end;
|
|
{$IFDEF VerboseMsgCtrlSelection}
|
|
DebugLn(['TMessagesCtrl.SelectNextShown END ViewIndex=',IndexOfView(SelectedView),
|
|
' Line=',SelectedLine1]);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TMessagesCtrl.SelectLast(DoScroll, FullyVisible: boolean): boolean;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
i:=ViewCount-1;
|
|
while (i>=0) do begin
|
|
if Views[i].HasContent then begin
|
|
Select(Views[i],Views[i].GetShownLineCount(true,true)-1,DoScroll,FullyVisible);
|
|
exit(true);
|
|
end;
|
|
dec(i);
|
|
end;
|
|
Result:=false;
|
|
end;
|
|
|
|
function TMessagesCtrl.SelectFirst(DoScroll, FullyVisible: boolean): boolean;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
i:=0;
|
|
while (i<ViewCount) do begin
|
|
if Views[i].HasContent then begin
|
|
Select(Views[i],-1,DoScroll,FullyVisible);
|
|
exit(true);
|
|
end;
|
|
inc(i);
|
|
end;
|
|
Result:=false;
|
|
end;
|
|
|
|
function TMessagesCtrl.GetSelectedMsg: TMessageLine;
|
|
// Return the first selected message.
|
|
var
|
|
View: TLMsgWndView;
|
|
Line: Integer;
|
|
begin
|
|
Result:=nil;
|
|
View:=SelectedView;
|
|
if View=nil then exit;
|
|
Line:=SelectedLine1;
|
|
if (Line<0) then exit;
|
|
if Line<View.Lines.Count then
|
|
Result:=View.Lines[Line]
|
|
else if View.ProgressLine.Msg<>'' then begin
|
|
Assert((Line=View.Lines.Count), 'TMessagesCtrl.GetSelectedMsg: Line is too big.');
|
|
Result:=View.ProgressLine;
|
|
end;
|
|
end;
|
|
|
|
function TMessagesCtrl.SearchNextUrgent(StartView: TLMsgWndView;
|
|
StartLine: integer; SkipStart, Downwards: boolean;
|
|
aMinUrgency: TMessageLineUrgency; WithSrcPos: boolean; out
|
|
View: TLMsgWndView; out LineNumber: integer): boolean;
|
|
var
|
|
CurView: TLMsgWndView;
|
|
CurLine: Integer;
|
|
CurViewLineCnt: integer;
|
|
MsgLine: TMessageLine;
|
|
|
|
function Next: boolean;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if Downwards then begin
|
|
inc(CurLine);
|
|
if CurLine>=CurViewLineCnt then begin
|
|
i:=IndexOfView(CurView);
|
|
repeat
|
|
inc(i);
|
|
if i>=ViewCount then exit(false);
|
|
CurView:=Views[i];
|
|
until CurView.HasContent;
|
|
CurLine:=-1;
|
|
CurViewLineCnt:=CurView.GetShownLineCount(true,true);
|
|
end;
|
|
end else begin
|
|
dec(CurLine);
|
|
if CurLine<-1 then begin
|
|
i:=IndexOfView(CurView);
|
|
repeat
|
|
dec(i);
|
|
if i<0 then exit(false);
|
|
CurView:=Views[i];
|
|
until CurView.HasContent;
|
|
CurViewLineCnt:=CurView.GetShownLineCount(true,true);
|
|
CurLine:=CurViewLineCnt-1;
|
|
end;
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
begin
|
|
Result:=false;
|
|
View:=nil;
|
|
LineNumber:=-1;
|
|
if ViewCount=0 then exit;
|
|
if StartView=nil then begin
|
|
// use default start
|
|
if Downwards then begin
|
|
StartView:=Views[0];
|
|
StartLine:=-1;
|
|
end else begin
|
|
StartView:=Views[ViewCount-1];
|
|
StartLine:=StartView.GetShownLineCount(true,true);
|
|
end;
|
|
end;
|
|
CurView:=StartView;
|
|
CurLine:=StartLine;
|
|
CurViewLineCnt:=CurView.GetShownLineCount(true,true);
|
|
// skip invalid line numbers
|
|
if CurLine<-1 then begin
|
|
SkipStart:=false;
|
|
if Downwards then
|
|
CurLine:=-1
|
|
else if not Next then
|
|
exit;
|
|
end else if CurLine>=CurViewLineCnt then begin
|
|
SkipStart:=false;
|
|
if Downwards then begin
|
|
if not Next then exit;
|
|
end else
|
|
CurLine:=CurViewLineCnt-1;
|
|
end;
|
|
// skip invalid views
|
|
if not CurView.HasContent then begin
|
|
SkipStart:=false;
|
|
if not Next then exit;
|
|
end;
|
|
// skip start
|
|
if SkipStart then
|
|
if not Next then exit;
|
|
// search
|
|
repeat
|
|
if (CurLine>=0) and (CurLine<CurView.Lines.Count) then begin
|
|
MsgLine:=CurView.Lines[CurLine];
|
|
if MsgLine.Urgency>=aMinUrgency then begin
|
|
if (not WithSrcPos) or MsgLine.HasSourcePosition then begin
|
|
View:=CurView;
|
|
LineNumber:=CurLine;
|
|
exit(true);
|
|
end;
|
|
end;
|
|
end;
|
|
until not Next;
|
|
end;
|
|
|
|
function TMessagesCtrl.SelectFirstUrgentMessage(
|
|
aMinUrgency: TMessageLineUrgency; WithSrcPos: boolean): boolean;
|
|
var
|
|
View: TLMsgWndView;
|
|
LineNumber: integer;
|
|
begin
|
|
Result:=false;
|
|
if ViewCount=0 then exit;
|
|
if not SearchNextUrgent(nil,0,false,true,aMinUrgency,WithSrcPos,View,LineNumber)
|
|
then exit;
|
|
Select(View,LineNumber,true,true);
|
|
Result:=true;
|
|
end;
|
|
|
|
function TMessagesCtrl.SelectNextUrgentMessage(aMinUrgency: TMessageLineUrgency;
|
|
WithSrcPos: boolean; Downwards: boolean): boolean;
|
|
var
|
|
View: TLMsgWndView;
|
|
LineNumber: integer;
|
|
begin
|
|
Result:=false;
|
|
if not SearchNextUrgent(SelectedView,SelectedLine1,true,Downwards,
|
|
aMinUrgency,WithSrcPos,View,LineNumber)
|
|
then exit;
|
|
Select(View,LineNumber,true,true);
|
|
Result:=true;
|
|
end;
|
|
|
|
function TMessagesCtrl.IsLineVisible(View: TLMsgWndView; LineNumber: integer): boolean;
|
|
var
|
|
y: Integer;
|
|
begin
|
|
Result:=false;
|
|
if View=nil then exit;
|
|
y:=GetLineTop(View,LineNumber,true);
|
|
if (y+ItemHeight>0) and (y<ClientHeight) then
|
|
Result:=true;
|
|
end;
|
|
|
|
function TMessagesCtrl.IsLastLineVisible(View: TLMsgWndView): boolean;
|
|
var
|
|
LineNumber: Integer;
|
|
begin
|
|
LineNumber:=View.GetShownLineCount(false,true)-1;
|
|
Result:=IsLineVisible(View,LineNumber);
|
|
end;
|
|
|
|
function TMessagesCtrl.GetLineText(Line: TMessageLine): string;
|
|
begin
|
|
// 'filename(line,column) '
|
|
case FilenameStyle of
|
|
mwfsShort: Result:=Line.GetShortFilename;
|
|
mwfsRelative: Result:=Line.GetRelativeFilename
|
|
else Result:=Line.GetFullFilename;
|
|
end;
|
|
if Line.Line>0 then begin
|
|
Result+='('+IntToStr(Line.Line)+','+IntToStr(Line.Column)+')';
|
|
end;
|
|
if Result<>'' then
|
|
Result+=' ';
|
|
|
|
// 'error: '
|
|
if Line.Urgency<>mluImportant then
|
|
Result+=UrgencyToStr(Line.Urgency)+': ';
|
|
|
|
// message id
|
|
if (mcoShowMessageID in Options) and (Line.MsgID<>0) then
|
|
Result+='('+IntToStr(Line.MsgID)+') ';
|
|
|
|
// message
|
|
if (mcoShowTranslated in Options) and (Line.TranslatedMsg<>'') then
|
|
Result+=Line.TranslatedMsg
|
|
else
|
|
Result+=Line.Msg;
|
|
end;
|
|
|
|
function GetStats(Lines: TMessageLines): string;
|
|
var
|
|
ErrCnt, WarnCnt, HintCnt: Integer;
|
|
c: TMessageLineUrgency;
|
|
begin
|
|
Result:='';
|
|
ErrCnt:=0;
|
|
WarnCnt:=0;
|
|
HintCnt:=0;
|
|
for c:=Low(Lines.UrgencyCounts) to high(Lines.UrgencyCounts) do begin
|
|
//debugln(['GetStats cat=',dbgs(c),' count=',Lines.UrgencyCounts[c]]);
|
|
if c>=mluError then
|
|
inc(ErrCnt,Lines.UrgencyCounts[c])
|
|
else if c=mluWarning then
|
|
inc(WarnCnt,Lines.UrgencyCounts[c])
|
|
else if c in [mluHint,mluNote] then
|
|
inc(HintCnt,Lines.UrgencyCounts[c]);
|
|
end;
|
|
if ErrCnt>0 then
|
|
Result+=Format(lisErrors2, [IntToStr(ErrCnt)]);
|
|
if WarnCnt>0 then
|
|
Result+=Format(lisWarnings, [IntToStr(WarnCnt)]);
|
|
if HintCnt>0 then
|
|
Result+=Format(lisHints, [IntToStr(HintCnt)]);
|
|
end;
|
|
|
|
function TMessagesCtrl.GetHeaderText(View: TLMsgWndView): string;
|
|
begin
|
|
Result:=View.Caption;
|
|
if Result='' then
|
|
Result:=lisMenuViewMessages;
|
|
if View.SummaryMsg<>'' then
|
|
Result+=': '+View.SummaryMsg;
|
|
if mcoShowStats in Options then begin
|
|
Result+=GetStats(View.Lines);
|
|
end;
|
|
end;
|
|
|
|
function TMessagesCtrl.FindUnfinishedView: TLMsgWndView;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i:=0 to ViewCount-1 do begin
|
|
Result:=Views[i];
|
|
//debugln(['TMessagesCtrl.FindUnfinishedView ',i,' ',ViewCount,' caption="',Result.Caption,'" Result.Tool=',dbgsname(Result.Tool)]);
|
|
if not Result.HasFinished then exit;
|
|
end;
|
|
Result:=nil;
|
|
end;
|
|
|
|
function TMessagesCtrl.GetLastViewWithContent: TLMsgWndView;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
i:=ViewCount-1;
|
|
while i>=0 do begin
|
|
Result:=Views[i];
|
|
if Result.HasContent then exit;
|
|
dec(i);
|
|
end;
|
|
Result:=nil;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.ScrollToLine(View: TLMsgWndView; LineNumber: integer;
|
|
FullyVisible: boolean);
|
|
var
|
|
y: Integer;
|
|
MinScrollTop: integer;
|
|
MaxScrollTop: Integer;
|
|
begin
|
|
{$IFDEF EnableMsgWndLineWrap}
|
|
{$ELSE}
|
|
y:=GetLineTop(View,LineNumber,false);
|
|
if FullyVisible then begin
|
|
MinScrollTop:=Max(0,y+ItemHeight-ClientHeight);
|
|
MaxScrollTop:=y;
|
|
end else begin
|
|
MinScrollTop:=Max(0,y-1-ClientHeight);
|
|
MaxScrollTop:=y+ItemHeight-1;
|
|
end;
|
|
{$ENDIF}
|
|
//debugln(['TMessagesCtrl.ScrollToLine ',LineNumber,' y=',y,' Min=',MinScrollTop,' Max=',MaxScrollTop]);
|
|
y:=Max(Min(ScrollTop,MaxScrollTop),MinScrollTop);
|
|
//debugln(['TMessagesCtrl.ScrollToLine y=',y,' ScrollTopMax=',ScrollTopMax]);
|
|
ScrollTop:=y;
|
|
end;
|
|
|
|
function TMessagesCtrl.GetLineTop(View: TLMsgWndView; LineNumber: integer;
|
|
Scrolled: boolean): integer;
|
|
var
|
|
i: Integer;
|
|
CurView: TLMsgWndView;
|
|
begin
|
|
Result:=0;
|
|
if View=nil then exit;
|
|
for i:=0 to ViewCount-1 do begin
|
|
CurView:=Views[i];
|
|
if CurView=View then break;
|
|
inc(Result,ItemHeight*CurView.GetShownLineCount(true,true));
|
|
end;
|
|
if LineNumber<0 then begin
|
|
// header
|
|
end else if LineNumber<View.Lines.Count then begin
|
|
// normal messages
|
|
inc(Result,(LineNumber+1)*ItemHeight);
|
|
end else begin
|
|
// last line
|
|
inc(Result,(View.Lines.Count+1)*ItemHeight);
|
|
end;
|
|
if Scrolled then
|
|
dec(Result,ScrollTop);
|
|
end;
|
|
|
|
constructor TMessagesCtrl.Create(AOwner: TComponent);
|
|
var
|
|
u: TMessageLineUrgency;
|
|
begin
|
|
inherited Create(AOwner);
|
|
ControlStyle:=ControlStyle-[csCaptureMouse]+[csReflector];
|
|
FOptions:=MCDefaultOptions;
|
|
Filters.OnChanged:=@FilterChanged;
|
|
FActiveFilter:=Filters[0];
|
|
FViews:=TFPList.Create;
|
|
FSelectedLines:=TIntegerList.Create;
|
|
FUpdateTimer:=TTimer.Create(Self);
|
|
FUpdateTimer.Name:='MsgUpdateTimer';
|
|
FUpdateTimer.Interval:=200;
|
|
FUpdateTimer.OnTimer:=@MsgUpdateTimerTimer;
|
|
FItemHeight:=20;
|
|
FSelectedView:=nil;
|
|
FHintLastLine:=cNotALineHint;
|
|
BorderWidth:=0;
|
|
fBackgroundColor:=MsgWndDefBackgroundColor;
|
|
FHeaderBackground[lmvtsRunning]:=MsgWndDefHeaderBackgroundRunning;
|
|
FHeaderBackground[lmvtsSuccess]:=MsgWndDefHeaderBackgroundSuccess;
|
|
FHeaderBackground[lmvtsFailed]:=MsgWndDefHeaderBackgroundFailed;
|
|
FAutoHeaderBackground:=MsgWndDefAutoHeaderBackground;
|
|
FTextColor:=MsgWndDefTextColor;
|
|
TabStop:=True;
|
|
ParentColor:=False;
|
|
FImageChangeLink:=TChangeLink.Create;
|
|
FImageChangeLink.OnChange:=@ImageListChange;
|
|
for u:=Low(TMessageLineUrgency) to high(TMessageLineUrgency) do
|
|
fUrgencyStyles[u]:=TMsgCtrlUrgencyStyle.Create(Self,u);
|
|
ShowHint:= True;
|
|
OnMouseMove:=@MsgCtrlMouseMove;
|
|
OnShowHint:=@MsgCtrlShowHint;
|
|
end;
|
|
|
|
destructor TMessagesCtrl.Destroy;
|
|
var
|
|
u: TMessageLineUrgency;
|
|
begin
|
|
IdleConnected:=false;
|
|
Images:=nil;
|
|
ClearViews(false);
|
|
|
|
FreeAndNil(FSelectedLines);
|
|
FreeAndNil(FViews);
|
|
FreeAndNil(FUpdateTimer);
|
|
FreeAndNil(FImageChangeLink);
|
|
for u:=Low(TMessageLineUrgency) to high(TMessageLineUrgency) do
|
|
FreeAndNil(fUrgencyStyles[u]);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.BeginUpdate;
|
|
begin
|
|
inc(fUpdateLock);
|
|
end;
|
|
|
|
procedure TMessagesCtrl.EndUpdate;
|
|
begin
|
|
if fUpdateLock=0 then
|
|
raise Exception.Create('');
|
|
dec(fUpdateLock);
|
|
end;
|
|
|
|
procedure TMessagesCtrl.EraseBackground(DC: HDC);
|
|
begin
|
|
// everything is painted, so erasing the background is not needed
|
|
end;
|
|
|
|
procedure TMessagesCtrl.ApplyEnvironmentOptions;
|
|
var
|
|
NewOptions: TMsgCtrlOptions;
|
|
u: TMessageLineUrgency;
|
|
|
|
procedure SetOption(Option: TMsgCtrlOption; State: boolean);
|
|
begin
|
|
if State then
|
|
NewOptions:=NewOptions+[Option]
|
|
else
|
|
NewOptions:=NewOptions-[Option];
|
|
end;
|
|
|
|
begin
|
|
for u in TMessageLineUrgency do
|
|
UrgencyStyles[u].Color:=EnvironmentGuiOpts.MsgColors[u];
|
|
BackgroundColor:=EnvironmentGuiOpts.MsgViewColors[mwBackground];
|
|
AutoHeaderBackground:=EnvironmentGuiOpts.MsgViewColors[mwAutoHeader];
|
|
HeaderBackground[lmvtsRunning]:=EnvironmentGuiOpts.MsgViewColors[mwRunning];
|
|
HeaderBackground[lmvtsSuccess]:=EnvironmentGuiOpts.MsgViewColors[mwSuccess];
|
|
HeaderBackground[lmvtsFailed]:=EnvironmentGuiOpts.MsgViewColors[mwFailed];
|
|
TextColor:=EnvironmentGuiOpts.MsgViewColors[mwTextColor];
|
|
NewOptions:=Options;
|
|
SetOption(mcoSingleClickOpensFile,not EnvironmentGuiOpts.MsgViewDblClickJumps);
|
|
SetOption(mcoShowMsgIcons,EnvironmentGuiOpts.ShowMessagesIcons);
|
|
SetOption(mcoShowTranslated,EnvironmentGuiOpts.MsgViewShowTranslations);
|
|
SetOption(mcoAlwaysDrawFocused,EnvironmentGuiOpts.MsgViewAlwaysDrawFocused);
|
|
Options:=NewOptions;
|
|
FilenameStyle:=EnvironmentGuiOpts.MsgViewFilenameStyle;
|
|
end;
|
|
|
|
function TMessagesCtrl.IndexOfView(View: TLMsgWndView): integer;
|
|
begin
|
|
Result:=FViews.IndexOf(View);
|
|
end;
|
|
|
|
procedure TMessagesCtrl.ClearViews(OnlyFinished: boolean);
|
|
var
|
|
i: Integer;
|
|
View: TLMsgWndView;
|
|
begin
|
|
if OnlyFinished then begin
|
|
for i:=ViewCount-1 downto 0 do begin
|
|
if i>=ViewCount then continue;
|
|
View:=Views[i];
|
|
if View.HasFinished then
|
|
View.Free;
|
|
end;
|
|
end else begin
|
|
while ViewCount>0 do
|
|
Views[0].Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.RemoveView(View: TLMsgWndView);
|
|
begin
|
|
if FViews.IndexOf(View)<0 then exit;
|
|
FViews.Remove(View);
|
|
View.FControl:=nil;
|
|
View.OnChanged:=nil;
|
|
if fLastSearchStartView=View then
|
|
fLastSearchStartView:=nil;
|
|
if SelectedView=View then
|
|
SelectedView:=nil;
|
|
UpdateScrollBar(true);
|
|
Invalidate;
|
|
end;
|
|
|
|
function TMessagesCtrl.GetView(aCaption: string; CreateIfNotExist: boolean
|
|
): TLMsgWndView;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i:=0 to ViewCount-1 do begin
|
|
Result:=Views[i];
|
|
if UTF8CompareStr(aCaption,Result.Caption)=0 then exit;
|
|
end;
|
|
if not CreateIfNotExist then
|
|
exit(nil);
|
|
Result:=TLMsgWndView.Create(Self);
|
|
Result.FControl:=Self;
|
|
Result.Caption:=aCaption;
|
|
Result.Filter.Assign(ActiveFilter);
|
|
FViews.Add(Result);
|
|
FreeNotification(Result);
|
|
Result.OnChanged:=@ViewChanged;
|
|
fSomeViewsRunning:=true;
|
|
end;
|
|
|
|
function TMessagesCtrl.GetLineAt(Y: integer; out View: TLMsgWndView;
|
|
out Line: integer): boolean;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i:=0 to ViewCount-1 do begin
|
|
View:=Views[i];
|
|
if View.FPaintStamp<>FPaintStamp then continue;
|
|
if (View.fPaintTop>Y) or (View.fPaintBottom<Y) then continue;
|
|
Line:=((Y-View.fPaintTop) div ItemHeight)-1;
|
|
Result:=true;
|
|
exit;
|
|
end;
|
|
View:=nil;
|
|
Line:=-1;
|
|
Result:=false;
|
|
end;
|
|
|
|
function TMessagesCtrl.ScrollLeftMax: integer;
|
|
begin
|
|
Result:=0;
|
|
end;
|
|
|
|
function TMessagesCtrl.ScrollTopMax: integer;
|
|
var
|
|
i: Integer;
|
|
View: TLMsgWndView;
|
|
begin
|
|
if fScrollTopMax<0 then begin
|
|
fScrollTopMax:=0;
|
|
for i:=0 to ViewCount-1 do begin
|
|
View:=Views[i];
|
|
inc(fScrollTopMax,View.GetShownLineCount(true,true)*ItemHeight);
|
|
end;
|
|
fScrollTopMax:=Max(0,fScrollTopMax-ClientHeight);
|
|
end;
|
|
Result:=fScrollTopMax;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.StoreSelectedAsSearchStart;
|
|
begin
|
|
fLastLoSearchText:=UTF8LowerCase(FSearchText);
|
|
fLastSearchStartView:=FSelectedView;
|
|
fLastSearchStartLine:=SelectedLine1;
|
|
end;
|
|
|
|
function TMessagesCtrl.OpenSelection: boolean;
|
|
var
|
|
Msg: TMessageLine;
|
|
begin
|
|
Result:=false;
|
|
if not Assigned(OnOpenMessage) then exit;
|
|
Msg:=GetSelectedMsg;
|
|
if Msg=nil then exit;
|
|
Result:=OnOpenMessage(Self,Msg);
|
|
end;
|
|
|
|
procedure TMessagesCtrl.CreateMarksForFile(aSynEdit: TSynEdit;
|
|
aFilename: string; DeleteOld: boolean);
|
|
var
|
|
i: Integer;
|
|
Lines: TMessageLines;
|
|
Line: TMessageLine;
|
|
begin
|
|
if DeleteOld then
|
|
SourceMarks.RemoveMarks(aSynEdit);
|
|
for i:=0 to ViewCount-1 do begin
|
|
Lines:=Views[i].Lines;
|
|
for Line in Lines.EnumerateFile(aFilename,0,High(Integer)) do begin
|
|
//debugln(['TMessagesCtrl.CreateMarksForFile ',GetLineText(Line)]);
|
|
CreateSourceMark(Line,aSynEdit);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TMessagesCtrl.ApplySrcChanges(Changes: TETSingleSrcChanges): boolean;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result:=false;
|
|
//debugln(['TMessagesCtrl.ApplySrcChanges ViewCount=',ViewCount]);
|
|
for i:=0 to ViewCount-1 do
|
|
if Views[i].ApplySrcChanges(Changes) then
|
|
Result:=true;
|
|
if Result then
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TMessagesCtrl.MsgCtrlShowHint(Sender: TObject; HintInfo: PHintInfo);
|
|
begin
|
|
if fUpdateLock > 0 then
|
|
exit;
|
|
{ No selected 'view' or not specified line }
|
|
if not Assigned(FHintLastView) or (FHintLastLine = cNotALineHint) then begin
|
|
Application.CancelHint;
|
|
Exit;
|
|
end;
|
|
with HintInfo^ do begin
|
|
HintStr := FHintLastView.AsHintString(Self.FHintLastLine);
|
|
ReshowTimeout := 0;
|
|
HideTimeout := 5000;
|
|
end;
|
|
end;
|
|
|
|
{ TMessagesFrame }
|
|
|
|
procedure TMessagesFrame.MsgCtrlPopupMenuPopup(Sender: TObject);
|
|
|
|
procedure UpdateRemoveCompOptHideMsgItems;
|
|
var
|
|
i: Integer;
|
|
View: TLMsgWndView;
|
|
ToolData: TIDEExternalToolData;
|
|
IDETool: TObject;
|
|
CompOpts: TBaseCompilerOptions;
|
|
Flag: PCompilerMsgIdFlag;
|
|
Pattern: String;
|
|
Pkg: TIDEPackage;
|
|
Cnt: Integer;
|
|
Item: TIDEMenuCommand;
|
|
ModuleName: String;
|
|
begin
|
|
// create one menuitem per compiler option
|
|
Cnt:=0;
|
|
for i:=0 to ViewCount-1 do begin
|
|
View:=Views[i];
|
|
if View.Tool=nil then continue;
|
|
ToolData:=TIDEExternalToolData(View.Tool.Data);
|
|
if not (ToolData is TIDEExternalToolData) then continue;
|
|
IDETool:=ExternalToolList.GetIDEObject(ToolData);
|
|
if IDETool=nil then continue;
|
|
if IDETool is TLazProject then begin
|
|
CompOpts:=TLazProject(IDETool).LazCompilerOptions as TBaseCompilerOptions;
|
|
ModuleName:=lisProjectOption;
|
|
end else if IDETool is TIDEPackage then begin
|
|
Pkg:=TIDEPackage(IDETool);
|
|
CompOpts:=Pkg.LazCompilerOptions as TBaseCompilerOptions;
|
|
ModuleName:=Format(lisPackageOption, [Pkg.Name]);
|
|
end else
|
|
continue;
|
|
for Flag in CompOpts.IDEMessageFlags do begin
|
|
if Flag^.Flag<>cfvHide then continue;
|
|
if Cnt>=MsgRemoveCompOptHideMenuSection.Count then begin
|
|
Item:=RegisterIDEMenuCommand(MsgRemoveCompOptHideMenuSection,'RemoveCompOptHideMsg'+IntToStr(Cnt),'');
|
|
Item.OnClick:=@RemoveCompOptHideMsgClick;
|
|
end else begin
|
|
Item:=MsgRemoveCompOptHideMenuSection.Items[Cnt] as TIDEMenuCommand;
|
|
end;
|
|
Item.Tag:=Flag^.MsgId;
|
|
Item.UserTag:=PtrUInt(ToolData);
|
|
Pattern:=GetMsgPattern(SubToolFPC,Flag^.MsgID,true,40);
|
|
Item.Caption:=ModuleName+': '+Pattern;
|
|
inc(Cnt);
|
|
end;
|
|
end;
|
|
MsgRemoveCompOptHideMenuSection.Visible:=Cnt>0;
|
|
// delete old menu items
|
|
while MsgRemoveCompOptHideMenuSection.Count>Cnt do
|
|
MsgRemoveCompOptHideMenuSection[Cnt].Free;
|
|
end;
|
|
|
|
procedure UpdateRemoveMsgTypeFilterItems;
|
|
var
|
|
FilterItem: TLMVFilterMsgType;
|
|
i: Integer;
|
|
Item: TIDEMenuCommand;
|
|
Cnt: Integer;
|
|
begin
|
|
// create one menuitem per filter item
|
|
Cnt:=MessagesCtrl.ActiveFilter.FilterMsgTypeCount;
|
|
MsgRemoveMsgTypeFilterMenuSection.Visible:=Cnt>0;
|
|
for i:=0 to Cnt-1 do begin
|
|
if i>=MsgRemoveFilterMsgOneTypeMenuSection.Count then begin
|
|
Item:=RegisterIDEMenuCommand(MsgRemoveFilterMsgOneTypeMenuSection,'MsgRemoveMsgOfTypeFilter'+IntToStr(i),'');
|
|
Item.Tag:=i;
|
|
Item.OnClick:=@RemoveFilterMsgTypeClick;
|
|
end else
|
|
Item:=MsgRemoveFilterMsgOneTypeMenuSection.Items[i] as TIDEMenuCommand;
|
|
FilterItem:=MessagesCtrl.ActiveFilter.FilterMsgTypes[i];
|
|
Item.Caption:=GetMsgPattern(FilterItem.SubTool,FilterItem.MsgID,true,40);
|
|
end;
|
|
// delete old menu items
|
|
while MsgRemoveFilterMsgOneTypeMenuSection.Count>Cnt do
|
|
MsgRemoveFilterMsgOneTypeMenuSection[Cnt].Free;
|
|
MsgRemoveFilterAllMsgTypesMenuItem.OnClick:=@ClearFilterMsgTypesMenuItemClick;
|
|
end;
|
|
|
|
procedure UpdateFilterItems;
|
|
var
|
|
i: Integer;
|
|
Filter: TLMsgViewFilter;
|
|
Item: TIDEMenuCommand;
|
|
Cnt: Integer;
|
|
begin
|
|
Cnt:=MessagesCtrl.Filters.Count;
|
|
for i:=0 to Cnt-1 do begin
|
|
Filter:=MessagesCtrl.Filters[i];
|
|
if i>=MsgSelectFilterMenuSection.Count then begin
|
|
Item:=RegisterIDEMenuCommand(MsgSelectFilterMenuSection,'MsgSelectFilter'+IntToStr(i),'');
|
|
Item.Tag:=i;
|
|
Item.OnClick:=@SelectFilterClick;
|
|
end else
|
|
Item:=MsgSelectFilterMenuSection[i] as TIDEMenuCommand;
|
|
Item.Caption:=Filter.Caption;
|
|
Item.Checked:=Filter=MessagesCtrl.ActiveFilter;
|
|
end;
|
|
// delete old menu items
|
|
while MsgSelectFilterMenuSection.Count>Cnt do
|
|
MsgSelectFilterMenuSection[Cnt].Free;
|
|
|
|
MsgAddFilterMenuItem.OnClick:=@AddFilterMenuItemClick;
|
|
end;
|
|
|
|
procedure UpdateQuickFixes(CurLine: TMessageLine);
|
|
begin
|
|
// delete old
|
|
MsgQuickFixMenuSection.Clear;
|
|
// create items
|
|
if CurLine<>nil then begin
|
|
IDEQuickFixes.SetMsgLines(CurLine);
|
|
IDEQuickFixes.OnPopupMenu(MsgQuickFixMenuSection);
|
|
end;
|
|
MsgQuickFixMenuSection.Visible:=MsgQuickFixMenuSection.Count>0;
|
|
end;
|
|
|
|
var
|
|
View: TLMsgWndView;
|
|
MinUrgency: TMessageLineUrgency;
|
|
ToolData: TIDEExternalToolData;
|
|
Line: TMessageLine;
|
|
i, LineNumber, VisibleCnt: Integer;
|
|
HasText, HasFilename, HasViewContent, Running, CanFilterMsgType: Boolean;
|
|
MsgType, ToolOptionsCaption: String;
|
|
begin
|
|
MessagesMenuRoot.MenuItem:=MsgCtrlPopupMenu.Items;
|
|
//MessagesMenuRoot.BeginUpdate;
|
|
try
|
|
HasText:=false;
|
|
HasFilename:=false;
|
|
MsgType:='';
|
|
CanFilterMsgType:=false;
|
|
Line:=nil;
|
|
HasViewContent:=false;
|
|
Running:=false;
|
|
|
|
// check all
|
|
for i:=0 to MessagesCtrl.ViewCount-1 do begin
|
|
View:=MessagesCtrl.Views[i];
|
|
if View.HasContent then
|
|
HasViewContent:=true;
|
|
if View.Running then
|
|
Running:=true;
|
|
end;
|
|
|
|
MsgFindMenuItem.OnClick:=@FindMenuItemClick;
|
|
|
|
// check selection
|
|
View:=MessagesCtrl.SelectedView;
|
|
if View<>nil then begin
|
|
for i:=0 to MessagesCtrl.FSelectedLines.Count-1 do begin
|
|
LineNumber:=MessagesCtrl.FSelectedLines[i];
|
|
if LineNumber=-1 then Continue; // header
|
|
if LineNumber=View.Lines.Count then
|
|
Line:=View.ProgressLine // progress line
|
|
else
|
|
Line:=View.Lines[LineNumber]; // normal messages
|
|
if Line.Filename<>'' then
|
|
HasFilename:=True;
|
|
if Line.Msg<>'' then
|
|
HasText:=True;
|
|
if (Line.SubTool<>'') and (Line.MsgID<>0) then begin
|
|
MsgType:=GetMsgPattern(Line.SubTool,Line.MsgID,true,40);
|
|
CanFilterMsgType:=ord(Line.Urgency)<ord(mluError);
|
|
end;
|
|
end;
|
|
end else begin
|
|
// no line selected => use last visible View
|
|
View:=MessagesCtrl.GetLastViewWithContent;
|
|
end;
|
|
ToolOptionsCaption:='';
|
|
|
|
// About
|
|
if View<>nil then
|
|
begin
|
|
MsgAboutToolMenuItem.Caption:=Format(lisAbout2, [View.Caption]);
|
|
MsgAboutSection.Visible:=true;
|
|
if (View.Tool<>nil) and (View.Tool.Data is TIDEExternalToolData) then begin
|
|
ToolData:=TIDEExternalToolData(View.Tool.Data);
|
|
if ToolData.Kind=IDEToolCompilePackage then
|
|
ToolOptionsCaption:=Format(lisCPOpenPackage, [ToolData.ModuleName]);
|
|
end;
|
|
end else
|
|
MsgAboutSection.Visible:=false;
|
|
MsgAboutToolMenuItem.OnClick:=@AboutToolMenuItemClick;
|
|
VisibleCnt:=1;
|
|
MsgOpenToolOptionsMenuItem.Visible:=ToolOptionsCaption<>'';
|
|
if MsgOpenToolOptionsMenuItem.Visible then
|
|
begin
|
|
inc(VisibleCnt);
|
|
//only assign caption if it is not empty to avoid its "unlocalizing",
|
|
//this is visible e.g. in EditorToolBar menu tree
|
|
MsgOpenToolOptionsMenuItem.Caption:=ToolOptionsCaption;
|
|
end
|
|
else
|
|
//assign default caption if item is not visible (needed for EditorToolBar)
|
|
MsgOpenToolOptionsMenuItem.Caption:=lisOpenToolOptions;
|
|
MsgOpenToolOptionsMenuItem.OnClick:=@OpenToolsOptionsMenuItemClick;
|
|
MsgAboutSection.ChildrenAsSubMenu:=VisibleCnt>1;
|
|
|
|
// Filtering
|
|
if CanFilterMsgType then begin
|
|
MsgFilterMsgOfTypeMenuItem.Caption:=Format(lisFilterAllMessagesOfType, [MsgType]);
|
|
MsgFilterMsgOfTypeMenuItem.Visible:=true;
|
|
end else begin
|
|
//assign default caption if item is not visible (needed for EditorToolBar)
|
|
MsgFilterMsgOfTypeMenuItem.Caption:=lisFilterAllMessagesOfCertainType;
|
|
MsgFilterMsgOfTypeMenuItem.Visible:=false;
|
|
end;
|
|
MsgFilterMsgOfTypeMenuItem.OnClick:=@FilterMsgOfTypeMenuItemClick;
|
|
MsgFilterHintsWithoutPosMenuItem.Checked:=MessagesCtrl.ActiveFilter.FilterNotesWithoutPos;
|
|
MsgFilterHintsWithoutPosMenuItem.OnClick:=@FilterHintsWithoutPosMenuItemClick;
|
|
|
|
MinUrgency:=MessagesCtrl.ActiveFilter.MinUrgency;
|
|
MsgFilterNoneMenuItem.Checked:=MinUrgency in [mluNone..mluDebug];
|
|
MsgFilterNoneMenuItem.OnClick:=@FilterUrgencyMenuItemClick;
|
|
MsgFilterDebugMenuItem.Checked:=MinUrgency in [mluVerbose3..mluVerbose];
|
|
MsgFilterDebugMenuItem.OnClick:=@FilterUrgencyMenuItemClick;
|
|
MsgFilterVerboseMenuItem.Checked:=MinUrgency=mluHint;
|
|
MsgFilterVerboseMenuItem.OnClick:=@FilterUrgencyMenuItemClick;
|
|
MsgFilterHintsMenuItem.Checked:=MinUrgency=mluNote;
|
|
MsgFilterHintsMenuItem.OnClick:=@FilterUrgencyMenuItemClick;
|
|
MsgFilterNotesMenuItem.Checked:=MinUrgency in [mluWarning..mluImportant];
|
|
MsgFilterNotesMenuItem.OnClick:=@FilterUrgencyMenuItemClick;
|
|
MsgFilterWarningsMenuItem.Checked:=MinUrgency>=mluError;
|
|
MsgFilterWarningsMenuItem.OnClick:=@FilterUrgencyMenuItemClick;
|
|
|
|
// Copying
|
|
MsgCopyMsgMenuItem.Enabled:=HasText;
|
|
MsgCopyMsgMenuItem.OnClick:=@CopyMsgMenuItemClick;
|
|
MsgCopyFilenameMenuItem.Enabled:=HasFilename;
|
|
MsgCopyFilenameMenuItem.OnClick:=@CopyFilenameMenuItemClick;
|
|
MsgCopyAllMenuItem.Enabled:=not Running;
|
|
MsgCopyAllMenuItem.OnClick:=@CopyAllMenuItemClick;
|
|
MsgCopyShownMenuItem.Enabled:=HasViewContent;
|
|
MsgCopyShownMenuItem.OnClick:=@CopyShownMenuItemClick;
|
|
|
|
// Saving
|
|
MsgSaveAllToFileMenuItem.Enabled:=not Running;
|
|
MsgSaveAllToFileMenuItem.OnClick:=@SaveAllToFileMenuItemClick;
|
|
MsgSaveShownToFileMenuItem.Enabled:=HasViewContent;
|
|
MsgSaveShownToFileMenuItem.OnClick:=@SaveShownToFileMenuItemClick;
|
|
MsgHelpMenuItem.Enabled:=HasText;
|
|
MsgHelpMenuItem.OnClick:=@HelpMenuItemClick;
|
|
MsgEditHelpMenuItem.OnClick:=@EditHelpMenuItemClick;
|
|
MsgClearMenuItem.OnClick:=@ClearMenuItemClick;
|
|
MsgClearMenuItem.Enabled:=View<>nil;
|
|
|
|
// Options
|
|
MsgWndStayOnTopMenuItem.Checked:=mcoWndStayOnTop in MessagesCtrl.Options;
|
|
MsgWndStayOnTopMenuItem.OnClick:=@WndStayOnTopMenuItemClick;
|
|
MsgFileStyleShortMenuItem.Checked:=MessagesCtrl.FilenameStyle=mwfsShort;
|
|
MsgFileStyleShortMenuItem.OnClick:=@FileStyleMenuItemClick;
|
|
MsgFileStyleRelativeMenuItem.Checked:=MessagesCtrl.FilenameStyle=mwfsRelative;
|
|
MsgFileStyleRelativeMenuItem.OnClick:=@FileStyleMenuItemClick;
|
|
MsgFileStyleFullMenuItem.Checked:=MessagesCtrl.FilenameStyle=mwfsFull;
|
|
MsgFileStyleFullMenuItem.OnClick:=@FileStyleMenuItemClick;
|
|
|
|
MsgTranslateMenuItem.Checked:=mcoShowTranslated in MessagesCtrl.Options;
|
|
MsgTranslateMenuItem.OnClick:=@TranslateMenuItemClick;
|
|
MsgShowIDMenuItem.Checked:=mcoShowMessageID in MessagesCtrl.Options;
|
|
MsgShowIDMenuItem.OnClick:=@ShowIDMenuItemClick;
|
|
MsgMoreOptionsMenuItem.OnClick:=@MoreOptionsMenuItemClick;
|
|
|
|
UpdateRemoveCompOptHideMsgItems;
|
|
UpdateRemoveMsgTypeFilterItems;
|
|
UpdateFilterItems;
|
|
|
|
UpdateQuickFixes(Line);
|
|
finally
|
|
//MessagesMenuRoot.EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TMessagesFrame.SelectFilterClick(Sender: TObject);
|
|
var
|
|
Filter: TLMsgViewFilter;
|
|
Item: TIDEMenuCommand;
|
|
begin
|
|
Item:=Sender as TIDEMenuCommand;
|
|
Filter:=MessagesCtrl.Filters.GetFilter(Item.Caption,false);
|
|
if Filter=nil then exit;
|
|
MessagesCtrl.ActiveFilter:=Filter;
|
|
end;
|
|
|
|
procedure TMessagesFrame.OpenToolsOptionsMenuItemClick(Sender: TObject);
|
|
var
|
|
View: TLMsgWndView;
|
|
ToolData: TIDEExternalToolData;
|
|
begin
|
|
View:=GetAboutView;
|
|
if (View=nil) or (View.Tool=nil) then exit;
|
|
ToolData:=TIDEExternalToolData(View.Tool.Data);
|
|
if not (ToolData is TIDEExternalToolData) then exit;
|
|
if ToolData.Kind=IDEToolCompilePackage then begin
|
|
PackageEditingInterface.DoOpenPackageFile(ToolData.Filename,
|
|
[pofAddToRecent],false);
|
|
end;
|
|
end;
|
|
|
|
procedure TMessagesFrame.RemoveCompOptHideMsgClick(Sender: TObject);
|
|
var
|
|
Item: TIDEMenuCommand;
|
|
MsgId: Integer;
|
|
ToolData: TIDEExternalToolData;
|
|
IDETool: TObject;
|
|
CompOpts: TLazCompilerOptions;
|
|
Pkg: TIDEPackage;
|
|
begin
|
|
if not (Sender is TIDEMenuCommand) then exit;
|
|
Item:=TIDEMenuCommand(Sender);
|
|
MsgId:=Item.Tag;
|
|
ToolData:=TIDEExternalToolData(Item.UserTag);
|
|
IDETool:=ExternalToolList.GetIDEObject(ToolData);
|
|
if IDETool=nil then exit;
|
|
if IDETool is TLazProject then begin
|
|
CompOpts:=TLazProject(IDETool).LazCompilerOptions;
|
|
CompOpts.MessageFlags[MsgID]:=cfvNone;
|
|
end else if IDETool is TIDEPackage then begin
|
|
if PackageEditingInterface.DoOpenPackageFile(ToolData.Filename,
|
|
[pofAddToRecent],false)<>mrOk then exit;
|
|
Pkg:=PackageEditingInterface.FindPackageWithName(ToolData.ModuleName);
|
|
if Pkg=nil then exit;
|
|
CompOpts:=Pkg.LazCompilerOptions;
|
|
CompOpts.MessageFlags[MsgID]:=cfvNone;
|
|
end;
|
|
end;
|
|
|
|
procedure TMessagesFrame.SaveAllToFileMenuItemClick(Sender: TObject);
|
|
begin
|
|
SaveClicked(false);
|
|
end;
|
|
|
|
procedure TMessagesFrame.SaveShownToFileMenuItemClick(Sender: TObject);
|
|
begin
|
|
SaveClicked(true);
|
|
end;
|
|
|
|
procedure TMessagesFrame.SearchEditChange(Sender: TObject);
|
|
var
|
|
s: TCaption;
|
|
begin
|
|
s:=SearchEdit.Text;
|
|
MessagesCtrl.SearchText:=s;
|
|
end;
|
|
|
|
procedure TMessagesFrame.SearchEditKeyDown(Sender: TObject; var Key: Word;
|
|
Shift: TShiftState);
|
|
begin
|
|
if (Key=VK_ESCAPE) then
|
|
HideSearch;
|
|
end;
|
|
|
|
procedure TMessagesFrame.SearchNextSpeedButtonClick(Sender: TObject);
|
|
begin
|
|
MessagesCtrl.SelectNextOccurrence(true);
|
|
end;
|
|
|
|
procedure TMessagesFrame.SearchPrevSpeedButtonClick(Sender: TObject);
|
|
begin
|
|
MessagesCtrl.SelectNextOccurrence(false);
|
|
end;
|
|
|
|
procedure TMessagesFrame.ShowIDMenuItemClick(Sender: TObject);
|
|
begin
|
|
if mcoShowMessageID in MessagesCtrl.Options then
|
|
MessagesCtrl.Options:=MessagesCtrl.Options-[mcoShowMessageID]
|
|
else
|
|
MessagesCtrl.Options:=MessagesCtrl.Options+[mcoShowMessageID];
|
|
end;
|
|
|
|
procedure TMessagesFrame.SrcEditLinesChanged(Sender: TObject);
|
|
begin
|
|
//debugln(['TMessagesFrame.SrcEditLinesChanged ',DbgSName(Sender)]);
|
|
if Sender is TETSynPlugin then
|
|
ApplySrcChanges(TETSynPlugin(Sender).Changes);
|
|
end;
|
|
|
|
procedure TMessagesFrame.TranslateMenuItemClick(Sender: TObject);
|
|
begin
|
|
if mcoShowTranslated in MessagesCtrl.Options then
|
|
MessagesCtrl.Options:=MessagesCtrl.Options-[mcoShowTranslated]
|
|
else
|
|
MessagesCtrl.Options:=MessagesCtrl.Options+[mcoShowTranslated];
|
|
EnvironmentGuiOpts.MsgViewShowTranslations:=mcoShowTranslated in MessagesCtrl.Options;
|
|
end;
|
|
|
|
procedure TMessagesFrame.RemoveFilterMsgTypeClick(Sender: TObject);
|
|
var
|
|
i: PtrInt;
|
|
begin
|
|
i:=TIDEMenuCommand(Sender).Tag;
|
|
if i<MessagesCtrl.ActiveFilter.FilterMsgTypeCount then
|
|
MessagesCtrl.ActiveFilter.DeleteFilterMsgType(i);
|
|
end;
|
|
|
|
procedure TMessagesFrame.WndStayOnTopMenuItemClick(Sender: TObject);
|
|
begin
|
|
if mcoWndStayOnTop in MessagesCtrl.Options then
|
|
MessagesCtrl.Options:=MessagesCtrl.Options-[mcoWndStayOnTop]
|
|
else
|
|
MessagesCtrl.Options:=MessagesCtrl.Options+[mcoWndStayOnTop];
|
|
EnvironmentGuiOpts.MsgViewStayOnTop:=mcoWndStayOnTop in MessagesCtrl.Options;
|
|
end;
|
|
|
|
function TMessagesFrame.AllMessagesAsString(const OnlyShown: boolean): String;
|
|
var
|
|
Tool: TAbstractExternalTool;
|
|
View: TLMsgWndView;
|
|
s: String;
|
|
i, j: Integer;
|
|
begin
|
|
s:='';
|
|
for i:=0 to MessagesCtrl.ViewCount-1 do begin
|
|
View:=MessagesCtrl.Views[i];
|
|
if OnlyShown or (View.Tool=nil) then begin
|
|
// save shown messages
|
|
if not View.HasContent then continue;
|
|
s+=MessagesCtrl.GetHeaderText(View)+LineEnding;
|
|
for j:=0 to View.Lines.Count-1 do
|
|
s+=MessagesCtrl.GetLineText(View.Lines[j])+LineEnding;
|
|
end else begin
|
|
// save raw data
|
|
if View.Running then continue;
|
|
Tool:=View.Tool;
|
|
Tool.EnterCriticalSection;
|
|
try
|
|
for j:=0 to Tool.WorkerOutput.Count-1 do
|
|
s+=Tool.WorkerOutput[j]+LineEnding;
|
|
finally
|
|
Tool.LeaveCriticalSection;
|
|
end;
|
|
end;
|
|
end;
|
|
Result:=s;
|
|
end;
|
|
|
|
function TMessagesFrame.GetAboutView: TLMsgWndView;
|
|
begin
|
|
Result:=MessagesCtrl.SelectedView;
|
|
if Result=nil then
|
|
Result:=MessagesCtrl.GetLastViewWithContent;
|
|
end;
|
|
|
|
procedure TMessagesFrame.CopyFilenameMenuItemClick(Sender: TObject);
|
|
begin
|
|
CopyMsgToClipboard(true);
|
|
end;
|
|
|
|
procedure TMessagesFrame.CopyMsgMenuItemClick(Sender: TObject);
|
|
begin
|
|
CopyMsgToClipboard(false);
|
|
end;
|
|
|
|
procedure TMessagesFrame.CopyAllMenuItemClick(Sender: TObject);
|
|
begin
|
|
CopyAllClicked(false);
|
|
end;
|
|
|
|
procedure TMessagesFrame.CopyShownMenuItemClick(Sender: TObject);
|
|
begin
|
|
CopyAllClicked(true);
|
|
end;
|
|
|
|
procedure TMessagesFrame.EditHelpMenuItemClick(Sender: TObject);
|
|
begin
|
|
ShowMessageHelpEditor;
|
|
end;
|
|
|
|
procedure TMessagesFrame.FileStyleMenuItemClick(Sender: TObject);
|
|
begin
|
|
if Sender=MsgFileStyleShortMenuItem then
|
|
MessagesCtrl.FilenameStyle:=mwfsShort
|
|
else if Sender=MsgFileStyleRelativeMenuItem then
|
|
MessagesCtrl.FilenameStyle:=mwfsRelative
|
|
else if Sender=MsgFileStyleFullMenuItem then
|
|
MessagesCtrl.FilenameStyle:=mwfsFull;
|
|
EnvironmentGuiOpts.MsgViewFilenameStyle:=MessagesCtrl.FilenameStyle;
|
|
end;
|
|
|
|
procedure TMessagesFrame.FindMenuItemClick(Sender: TObject);
|
|
begin
|
|
MessagesCtrl.StoreSelectedAsSearchStart;
|
|
SearchPanel.Visible:=true;
|
|
SearchEdit.SetFocus;
|
|
end;
|
|
|
|
procedure TMessagesFrame.HelpMenuItemClick(Sender: TObject);
|
|
begin
|
|
ExecuteIDECommand(Self, ecContextHelp);
|
|
end;
|
|
|
|
procedure TMessagesFrame.FilterHintsWithoutPosMenuItemClick(Sender: TObject);
|
|
begin
|
|
MessagesCtrl.ActiveFilter.FilterNotesWithoutPos:=not MessagesCtrl.ActiveFilter.FilterNotesWithoutPos;
|
|
end;
|
|
|
|
procedure TMessagesFrame.FilterMsgOfTypeMenuItemClick(Sender: TObject);
|
|
var
|
|
Line: TMessageLine;
|
|
begin
|
|
Line:=MessagesCtrl.GetSelectedMsg;
|
|
if (Line=nil) or (ord(Line.Urgency)>=ord(mluError)) then exit;
|
|
MessagesCtrl.ActiveFilter.AddFilterMsgType(Line.SubTool,Line.MsgID);
|
|
end;
|
|
|
|
procedure TMessagesFrame.FilterUrgencyMenuItemClick(Sender: TObject);
|
|
var
|
|
MinUrgency: TMessageLineUrgency;
|
|
begin
|
|
//debugln(['TMessagesFrame.FilterUrgencyMenuItemClick ',DbgSName(Sender),' ',(Sender as TIDEMenuCommand).Caption,' ',(Sender as TIDEMenuCommand).Checked]);
|
|
if Sender=MsgFilterWarningsMenuItem then
|
|
MinUrgency:=mluError
|
|
else if Sender=MsgFilterNotesMenuItem then
|
|
MinUrgency:=mluWarning
|
|
else if Sender=MsgFilterHintsMenuItem then
|
|
MinUrgency:=mluNote
|
|
else if Sender=MsgFilterVerboseMenuItem then
|
|
MinUrgency:=mluHint
|
|
else if Sender=MsgFilterDebugMenuItem then
|
|
MinUrgency:=mluVerbose3
|
|
else {if Sender=MsgFilterNoneMenuItem then}
|
|
MinUrgency:=mluNone;
|
|
MessagesCtrl.ActiveFilter.MinUrgency:=MinUrgency;
|
|
//debugln(['TMessagesFrame.FilterUrgencyMenuItemClick ',MessageLineUrgencyNames[MinUrgency]]);
|
|
end;
|
|
|
|
procedure TMessagesFrame.HideSearchSpeedButtonClick(Sender: TObject);
|
|
begin
|
|
HideSearch;
|
|
end;
|
|
|
|
procedure TMessagesFrame.ImagesGetWidthForPPI(Sender: TCustomImageList;
|
|
AImageWidth, APPI: Integer; var AResultWidth: Integer);
|
|
begin
|
|
if (16<=AResultWidth) and (AResultWidth<=20) then
|
|
AResultWidth := 16;
|
|
end;
|
|
|
|
procedure TMessagesFrame.MoreOptionsMenuItemClick(Sender: TObject);
|
|
begin
|
|
LazarusIDE.DoOpenIDEOptions(TMsgWndOptionsFrame);
|
|
end;
|
|
|
|
procedure TMessagesFrame.AboutToolMenuItemClick(Sender: TObject);
|
|
var
|
|
View: TLMsgWndView;
|
|
Form: TForm;
|
|
s: String;
|
|
Tool: TAbstractExternalTool;
|
|
Proc: TProcessUTF8;
|
|
Memo: TMemo;
|
|
i: Integer;
|
|
begin
|
|
View:=GetAboutView;
|
|
if View=nil then exit;
|
|
s:=View.Caption+LineEnding;
|
|
s+=LineEnding;
|
|
Tool:=View.Tool;
|
|
if Tool<>nil then begin
|
|
if Tool.Hint<>'' then
|
|
s+=Tool.Hint+LineEnding+LineEnding;
|
|
Proc:=Tool.Process;
|
|
if Proc<>nil then begin
|
|
if Proc.Executable<>'' then
|
|
s+='Executable: '+LineEnding+Proc.Executable+LineEnding+LineEnding;
|
|
if Proc.CurrentDirectory<>'' then begin
|
|
if Tool.CurrentDirectoryIsTestDir then
|
|
s+='CurrentDirectory is test build directory:'
|
|
else
|
|
s+='CurrentDirectory:';
|
|
s+=LineEnding+Proc.CurrentDirectory+LineEnding+LineEnding;
|
|
end;
|
|
if Proc.Desktop<>'' then
|
|
s+='Desktop: '+Proc.Desktop+LineEnding;
|
|
if Tool.EnvironmentOverrides.Text<>'' then
|
|
s+='Environment Overrides:'+LineEnding
|
|
+Tool.EnvironmentOverrides.Text+LineEnding;
|
|
s+='Parameters:'+LineEnding;
|
|
s+=Proc.Parameters.Text+LineEnding;
|
|
s+='Command Line:'+LineEnding;
|
|
s+=StrToCmdLineParam(Tool.Process.Executable)+' '+MergeCmdLineParams(Tool.Process.Parameters)+LineEnding+LineEnding;
|
|
s+='Parsers: ';
|
|
if Tool.ParserCount=0 then
|
|
s+='none'
|
|
else begin
|
|
for i:=0 to Tool.ParserCount-1 do begin
|
|
if i>0 then s+=', ';
|
|
s+=Tool.Parsers[i].GetLocalizedParserName;
|
|
end;
|
|
end;
|
|
s+=LineEnding+LineEnding;
|
|
|
|
s+='ProcessID:'+LineEnding+IntToStr(Proc.ProcessID)+LineEnding+LineEnding;
|
|
if Tool.Terminated then
|
|
s+='Terminated'+LineEnding+LineEnding
|
|
else begin
|
|
s+='ExitCode:'+LineEnding+IntToStr(Proc.ExitCode)+LineEnding;
|
|
s+='ExitStatus:'+LineEnding+IntToStr(Proc.ExitStatus)+LineEnding+LineEnding;
|
|
end;
|
|
end;
|
|
if Tool.ErrorMessage<>'' then
|
|
s+=lisError+Tool.ErrorMessage+LineEnding+LineEnding;
|
|
end;
|
|
|
|
Form:=TForm.CreateNew(Self);
|
|
try
|
|
with Form do begin
|
|
Name:='AboutExtToolDlg';
|
|
Width:=500;
|
|
Height:=300;
|
|
Position:=poScreenCenter;
|
|
Caption:=Format(lisAbout2, [View.Caption]);
|
|
end;
|
|
|
|
Memo:=TMemo.Create(Form);
|
|
with Memo do begin
|
|
Name:='Memo';
|
|
Lines.Text:=s;
|
|
Align:=alClient;
|
|
WordWrap:=true; // carbon requires this and it is a good idea in general
|
|
ScrollBars:=ssVertical;
|
|
ReadOnly:=true;
|
|
Parent:=Form;
|
|
end;
|
|
Form.ShowModal;
|
|
finally
|
|
Form.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TMessagesFrame.AddFilterMenuItemClick(Sender: TObject);
|
|
var
|
|
aCaption: String;
|
|
i: Integer;
|
|
NewFilter: TLMsgViewFilter;
|
|
Filters: TLMsgViewFilters;
|
|
begin
|
|
aCaption:=lisFilter;
|
|
i:=1;
|
|
Filters:=MessagesCtrl.Filters;
|
|
while Filters.GetFilter(aCaption+IntToStr(i),false)<>nil do
|
|
inc(i);
|
|
if not InputQuery(lisCreateFilter, lisCodeToolsDefsName, aCaption) then exit;
|
|
aCaption:=UTF8Trim(aCaption,[]);
|
|
if aCaption='' then exit;
|
|
if Filters.GetFilter(aCaption,false)<>nil then begin
|
|
IDEMessageDialog(lisFilterAlreadyExists, Format(
|
|
lisAFilterWithTheNameAlreadyExists, [aCaption]), mtError, [mbCancel], '');
|
|
exit;
|
|
end;
|
|
NewFilter:=Filters.GetFilter(aCaption,true);
|
|
NewFilter.Assign(MessagesCtrl.ActiveFilter);
|
|
MessagesCtrl.ActiveFilter:=NewFilter;
|
|
end;
|
|
|
|
procedure TMessagesFrame.ClearFilterMsgTypesMenuItemClick(Sender: TObject);
|
|
begin
|
|
MessagesCtrl.ActiveFilter.ClearFilterMsgTypes;
|
|
end;
|
|
|
|
procedure TMessagesFrame.ClearMenuItemClick(Sender: TObject);
|
|
begin
|
|
MessagesCtrl.ClearViews(true);
|
|
end;
|
|
|
|
function TMessagesFrame.GetViews(Index: integer): TLMsgWndView;
|
|
begin
|
|
Result:=MessagesCtrl.Views[Index];
|
|
end;
|
|
|
|
procedure TMessagesFrame.HideSearch;
|
|
begin
|
|
SearchPanel.Visible:=false;
|
|
MessagesCtrl.SearchText:='';
|
|
end;
|
|
|
|
procedure TMessagesFrame.SaveClicked(OnlyShown: boolean);
|
|
var
|
|
Dlg: TSaveDialog;
|
|
s: String;
|
|
Filename: String;
|
|
fs: TFileStream;
|
|
begin
|
|
Dlg:=IDESaveDialogClass.Create(nil);
|
|
try
|
|
Dlg.Title:=lisSaveMessages;
|
|
Dlg.FileName:='messages.txt';
|
|
Dlg.Options:=Dlg.Options+[ofPathMustExist,ofCreatePrompt];
|
|
InitIDEFileDialog(Dlg);
|
|
if not Dlg.Execute then exit;
|
|
Filename:=TrimAndExpandFilename(Dlg.FileName);
|
|
if DirPathExistsCached(Filename) then exit;
|
|
|
|
s:=AllMessagesAsString(OnlyShown);
|
|
|
|
try
|
|
fs:=TFileStream.Create(Filename,fmCreate);
|
|
try
|
|
if s<>'' then
|
|
fs.Write(s[1],length(s));
|
|
finally
|
|
fs.Free;
|
|
end;
|
|
except
|
|
on E: Exception do begin
|
|
IDEMessageDialog(lisWriteError, Format(lisUnableToWriteFile2, [Filename]
|
|
),
|
|
mtError, [mbCancel]);
|
|
end;
|
|
end;
|
|
|
|
finally
|
|
StoreIDEFileDialog(Dlg);
|
|
Dlg.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TMessagesFrame.CopyAllClicked(OnlyShown: boolean);
|
|
var
|
|
s: String;
|
|
Msg: String;
|
|
begin
|
|
s:=AllMessagesAsString(OnlyShown);
|
|
if length(s)>1000000 then begin
|
|
if length(s)<10000 then
|
|
Msg:=Format(lisByte, [IntToStr(length(s))])
|
|
else if Length(s)<10000000 then
|
|
Msg:=Format(lisKB, [IntToStr(length(s) div 1000)])
|
|
else
|
|
Msg:=Format(lisMB, [IntToStr(length(s) div 1000)]);
|
|
if IDEMessageDialog(lisCCOWarningCaption, Format(
|
|
lisThisWillPutALotOfTextOnTheClipboardProceed, [Msg, #13]),
|
|
mtConfirmation,[mbYes,mbNo])<>mrYes then exit;
|
|
end;
|
|
Clipboard.AsText:=s;
|
|
end;
|
|
|
|
procedure TMessagesFrame.CopyMsgToClipboard(OnlyFilename: boolean);
|
|
var
|
|
View: TLMsgWndView;
|
|
Line: TMessageLine;
|
|
OrderedSelection: TIntegerList;
|
|
i, LineNumber: Integer;
|
|
Txt: String;
|
|
begin
|
|
Txt:='';
|
|
View:=MessagesCtrl.SelectedView;
|
|
if View=nil then exit;
|
|
OrderedSelection:=TIntegerList.Create;
|
|
try
|
|
// The initially selected line is first in the list. The list is not sorted.
|
|
// Here we need the line numbers sorted.
|
|
OrderedSelection.Assign(MessagesCtrl.FSelectedLines);
|
|
OrderedSelection.Sort;
|
|
for i:=0 to OrderedSelection.Count-1 do begin
|
|
LineNumber:=OrderedSelection[i];
|
|
Assert(LineNumber<=View.Lines.Count, 'TMessagesFrame.CopyMsgToClipboard: LineNumber is too big.');
|
|
if LineNumber=-1 then begin
|
|
if OnlyFilename then
|
|
Txt:=rsResourceFileName
|
|
else
|
|
Txt:=MessagesCtrl.GetHeaderText(View); // header
|
|
end
|
|
else begin
|
|
if LineNumber=View.Lines.Count then
|
|
Line:=View.ProgressLine // progress line
|
|
else
|
|
Line:=View.Lines[LineNumber]; // normal messages
|
|
if OnlyFilename then
|
|
Txt+=Line.Filename
|
|
else
|
|
Txt+=MessagesCtrl.GetLineText(Line);
|
|
end;
|
|
if i<OrderedSelection.Count-1 then
|
|
Txt+=LineEnding;
|
|
end;
|
|
finally
|
|
OrderedSelection.Free;
|
|
end;
|
|
Clipboard.AsText:=Txt;
|
|
end;
|
|
|
|
function TMessagesFrame.GetMsgPattern(SubTool: string; MsgId: integer;
|
|
WithUrgency: boolean; MaxLen: integer): string;
|
|
var
|
|
Pattern: String;
|
|
Urgency: TMessageLineUrgency;
|
|
begin
|
|
Result:=SubTool;
|
|
if Result=SubToolFPC then
|
|
Result:='';
|
|
if (MsgID<>0) then
|
|
Result+='('+IntToStr(MsgID)+')';
|
|
Pattern:=ExternalToolList.GetMsgPattern(SubTool,MsgID,Urgency);
|
|
if Pattern<>'' then
|
|
Result+=' '+Pattern;
|
|
if WithUrgency and (not (Urgency in [mluNone,mluImportant])) then
|
|
Result:=MessagesCtrl.UrgencyToStr(Urgency)+': '+Result;
|
|
if UTF8Length(Result)>MaxLen then
|
|
Result:=UTF8Copy(Result,1,MaxLen)+'...';
|
|
end;
|
|
|
|
procedure TMessagesFrame.Notification(AComponent: TComponent;
|
|
Operation: TOperation);
|
|
begin
|
|
inherited Notification(AComponent, Operation);
|
|
if Operation=opRemove then begin
|
|
if AComponent=MessagesCtrl then
|
|
MessagesCtrl:=nil;
|
|
end;
|
|
end;
|
|
|
|
constructor TMessagesFrame.Create(TheOwner: TComponent);
|
|
var
|
|
ImgIDInfo: Integer;
|
|
ImgIDHint: Integer;
|
|
ImgIDNote: Integer;
|
|
ImgIDWarning: Integer;
|
|
ImgIDError: Integer;
|
|
ImgIDFatal: Integer;
|
|
begin
|
|
inherited Create(TheOwner);
|
|
|
|
MessagesCtrl:=TMessagesCtrl.Create(Self);
|
|
FImages := TLCLGlyphs.Create(Self);
|
|
FImages.Width := 12;
|
|
FImages.Height := 12;
|
|
FImages.RegisterResolutions([12, 16, 24]);
|
|
FImages.SetWidth100Suffix(16);
|
|
FImages.OnGetWidthForPPI := @ImagesGetWidthForPPI;
|
|
ImgIDInfo:=FImages.GetImageIndex('state_information');
|
|
ImgIDHint:=FImages.GetImageIndex('state_hint');
|
|
ImgIDNote:=FImages.GetImageIndex('state_note');
|
|
ImgIDWarning:=FImages.GetImageIndex('state_warning');
|
|
ImgIDError:=FImages.GetImageIndex('state_error');
|
|
ImgIDFatal:=FImages.GetImageIndex('state_fatal');
|
|
with MessagesCtrl do begin
|
|
Name:='MessagesCtrl';
|
|
Align:=alClient;
|
|
Parent:=Self;
|
|
|
|
UrgencyStyles[mluNone].SetValues('?',ImgIDInfo,EnvironmentGuiOpts.MsgColors[mluNone]);
|
|
UrgencyStyles[mluProgress].SetValues(lisPDProgress, ImgIDInfo,
|
|
EnvironmentGuiOpts.MsgColors[mluProgress]);
|
|
UrgencyStyles[mluDebug].SetValues(lisDebug, ImgIDInfo,
|
|
EnvironmentGuiOpts.MsgColors[mluDebug]);
|
|
UrgencyStyles[mluVerbose3].SetValues(lisExtremelyVerbose, ImgIDInfo,
|
|
EnvironmentGuiOpts.MsgColors[mluVerbose3]);
|
|
UrgencyStyles[mluVerbose2].SetValues(lisVeryVerbose, ImgIDInfo,
|
|
EnvironmentGuiOpts.MsgColors[mluVerbose2]);
|
|
UrgencyStyles[mluVerbose].SetValues(lisVerbose, ImgIDInfo,
|
|
EnvironmentGuiOpts.MsgColors[mluVerbose]);
|
|
UrgencyStyles[mluHint].SetValues(lisHint, ImgIDHint,
|
|
EnvironmentGuiOpts.MsgColors[mluHint]);
|
|
UrgencyStyles[mluNote].SetValues(lisNote, ImgIDNote,
|
|
EnvironmentGuiOpts.MsgColors[mluNote]);
|
|
UrgencyStyles[mluWarning].SetValues(lisCCOWarningCaption, ImgIDWarning,
|
|
EnvironmentGuiOpts.MsgColors[mluWarning]);
|
|
UrgencyStyles[mluImportant].SetValues(lisImportant, ImgIDInfo,
|
|
EnvironmentGuiOpts.MsgColors[mluImportant]);
|
|
UrgencyStyles[mluError].SetValues(lisCCOErrorCaption, ImgIDError,
|
|
EnvironmentGuiOpts.MsgColors[mluError]);
|
|
UrgencyStyles[mluFatal].SetValues(lisFatal, ImgIDFatal,
|
|
EnvironmentGuiOpts.MsgColors[mluFatal]);
|
|
UrgencyStyles[mluPanic].SetValues(lisPanic, ImgIDFatal,
|
|
EnvironmentGuiOpts.MsgColors[mluPanic]);
|
|
Images:=Self.FImages;
|
|
PopupMenu:=MsgCtrlPopupMenu;
|
|
end;
|
|
MessagesCtrl.SourceMarks:=ExtToolsMarks;
|
|
|
|
// search
|
|
SearchPanel.Visible:=false; // by default the search is hidden
|
|
HideSearchSpeedButton.Hint:=lisHideSearch;
|
|
IDEImages.AssignImage(HideSearchSpeedButton, 'debugger_power');
|
|
SearchNextSpeedButton.Hint:=lisUDSearchNextOccurrenceOfThisPhrase;
|
|
IDEImages.AssignImage(SearchNextSpeedButton, 'callstack_bottom');
|
|
SearchPrevSpeedButton.Hint:=lisUDSearchPreviousOccurrenceOfThisPhrase;
|
|
IDEImages.AssignImage(SearchPrevSpeedButton, 'callstack_top');
|
|
SearchEdit.TextHint:=lisUDSearch;
|
|
end;
|
|
|
|
destructor TMessagesFrame.Destroy;
|
|
begin
|
|
MessagesCtrl.BeginUpdate;
|
|
ClearViews(false);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TMessagesFrame.ApplyIDEOptions;
|
|
begin
|
|
MessagesCtrl.ApplyEnvironmentOptions;
|
|
end;
|
|
|
|
function TMessagesFrame.ViewCount: integer;
|
|
begin
|
|
Result:=MessagesCtrl.ViewCount;
|
|
end;
|
|
|
|
function TMessagesFrame.GetView(aCaption: string; CreateIfNotExist: boolean
|
|
): TLMsgWndView;
|
|
begin
|
|
Result:=MessagesCtrl.GetView(aCaption,CreateIfNotExist);
|
|
end;
|
|
|
|
function TMessagesFrame.FindUnfinishedView: TLMsgWndView;
|
|
begin
|
|
Result:=MessagesCtrl.FindUnfinishedView;
|
|
end;
|
|
|
|
procedure TMessagesFrame.DeleteView(View: TLMsgWndView);
|
|
begin
|
|
View.Free;
|
|
end;
|
|
|
|
function TMessagesFrame.IndexOfView(View: TLMsgWndView): integer;
|
|
begin
|
|
Result:=MessagesCtrl.IndexOfView(View);
|
|
end;
|
|
|
|
procedure TMessagesFrame.ClearViews(OnlyFinished: boolean);
|
|
begin
|
|
MessagesCtrl.ClearViews(OnlyFinished);
|
|
end;
|
|
|
|
procedure TMessagesFrame.CreateMarksForFile(aSynEdit: TSynEdit;
|
|
aFilename: string; DeleteOld: boolean);
|
|
begin
|
|
MessagesCtrl.CreateMarksForFile(aSynEdit,aFilename,DeleteOld);
|
|
end;
|
|
|
|
procedure TMessagesFrame.ApplySrcChanges(Changes: TETSingleSrcChanges);
|
|
begin
|
|
MessagesCtrl.ApplySrcChanges(Changes);
|
|
end;
|
|
|
|
procedure TMessagesFrame.ApplyMultiSrcChanges(Changes: TETMultiSrcChanges);
|
|
var
|
|
Node: TAvlTreeNode;
|
|
begin
|
|
for Node in Changes.PendingChanges do
|
|
ApplySrcChanges(TETSingleSrcChanges(Node.Data));
|
|
end;
|
|
|
|
procedure TMessagesFrame.SourceEditorPopup(MarkLine: TSynEditMarkLine;
|
|
const LogicalCaretXY: TPoint);
|
|
var
|
|
i: Integer;
|
|
CurMark: TETMark;
|
|
BestMark: TETMark;
|
|
begin
|
|
//debugln(['TMessagesFrame.SourceEditorPopup ']);
|
|
// show quickfixes for the first TETMark in editor line
|
|
if MarkLine=nil then exit;
|
|
IDEQuickFixes.ClearLines;
|
|
BestMark:=nil;
|
|
for i:=0 to MarkLine.Count-1 do begin
|
|
CurMark:=TETMark(MarkLine[i]);
|
|
if not (CurMark is TETMark) then continue;
|
|
//debugln(['TMessagesFrame.SourceEditorPopup ',CurMark.Line,',',CurMark.Column,' ID=',CurMark.MsgLine.MsgID,' Msg=',CurMark.MsgLine.Msg,' EditorXY=',dbgs(LogicalCaretXY)]);
|
|
if (BestMark=nil) then
|
|
BestMark:=CurMark
|
|
else begin
|
|
// there are multiple marks in the line
|
|
if (LogicalCaretXY.Y=MarkLine.LineNum)
|
|
and (LogicalCaretXY.X=CurMark.Column) then begin
|
|
// mark at cursor position
|
|
BestMark:=CurMark;
|
|
break;
|
|
end else begin
|
|
// default: use first in line
|
|
if CurMark.Column<BestMark.Column then
|
|
BestMark:=CurMark;
|
|
end;
|
|
end;
|
|
end;
|
|
if BestMark=nil then
|
|
exit;
|
|
IDEQuickFixes.AddMsgLine(BestMark.MsgLine);
|
|
// create items
|
|
if IDEQuickFixes.Count>0 then begin
|
|
IDEQuickFixes.OnPopupMenu(SrcEditMenuSectionFirstDynamic);
|
|
if mcoSrcEditPopupSelect in MessagesCtrl.Options then
|
|
MessagesCtrl.Select(BestMark.MsgLine,true);
|
|
end;
|
|
end;
|
|
|
|
procedure TMessagesFrame.SourceEditorHint(MarkLine: TSynEditMarkLine;
|
|
var HintStr: string);
|
|
var
|
|
i: Integer;
|
|
CurMark: TETMark;
|
|
Msg: TMessageLine;
|
|
CurHint: String;
|
|
begin
|
|
if MarkLine=nil then exit;
|
|
for i:=0 to MarkLine.Count-1 do begin
|
|
CurMark:=TETMark(MarkLine[i]);
|
|
if not (CurMark is TETMark) then continue;
|
|
Msg:=CurMark.MsgLine;
|
|
CurHint:=MessagesCtrl.UrgencyToStr(Msg.Urgency)+': '+Msg.Msg;
|
|
if HintStr<>'' then
|
|
HintStr:=HintStr+LineEnding;
|
|
HintStr:=HintStr+CurHint;
|
|
end;
|
|
end;
|
|
|
|
procedure TMessagesFrame.SelectMsgLine(Msg: TMessageLine; DoScroll: boolean);
|
|
begin
|
|
MessagesCtrl.Select(Msg,DoScroll);
|
|
end;
|
|
|
|
function TMessagesFrame.SelectFirstUrgentMessage(
|
|
aMinUrgency: TMessageLineUrgency; WithSrcPos: boolean): boolean;
|
|
begin
|
|
Result:=MessagesCtrl.SelectFirstUrgentMessage(aMinUrgency,WithSrcPos);
|
|
end;
|
|
|
|
function TMessagesFrame.SelectNextUrgentMessage(
|
|
aMinUrgency: TMessageLineUrgency; WithSrcPos, Downwards: boolean): boolean;
|
|
begin
|
|
Result:=MessagesCtrl.SelectNextUrgentMessage(aMinUrgency,WithSrcPos,Downwards);
|
|
end;
|
|
|
|
procedure TMessagesFrame.ClearCustomMessages(const ViewCaption: string);
|
|
var
|
|
View: TLMsgWndView;
|
|
begin
|
|
View:=GetView(ViewCaption,false);
|
|
if (View=nil) or (View.Lines.Count=0) then exit;
|
|
View.Lines.Clear;
|
|
MessagesCtrl.UpdateScrollBar(true);
|
|
MessagesCtrl.Invalidate;
|
|
end;
|
|
|
|
function TMessagesFrame.AddCustomMessage(TheUrgency: TMessageLineUrgency;
|
|
Msg: string; aFilename: string; LineNumber: integer; Column: integer;
|
|
const ViewCaption: string): TMessageLine;
|
|
var
|
|
View: TLMsgWndView;
|
|
begin
|
|
Result:=nil;
|
|
View:=GetView(ViewCaption,true);
|
|
View.Running:=false;
|
|
Result:=View.Lines.CreateLine(-1);
|
|
Result.Msg:=Msg;
|
|
Result.Urgency:=TheUrgency;
|
|
Result.SetSourcePosition(aFilename,LineNumber,Column);
|
|
View.Lines.Add(Result);
|
|
MessagesCtrl.UpdateScrollBar(true);
|
|
MessagesCtrl.Invalidate;
|
|
end;
|
|
|
|
end.
|
|
|