fpc/utils/debugsvr/gtk/frmmain.pp
fpc 790a4fe2d3 * log and id tags removed
git-svn-id: trunk@42 -
2005-05-21 09:42:41 +00:00

746 lines
19 KiB
ObjectPascal

{
This file is part of the Free Pascal run time library.
Copyright (c) 2003 by the Free Pascal development team
Main form of GTK debugserver
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program 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.
**********************************************************************}
{$mode objfpc}
{$h+}
unit frmmain;
Interface
uses
fpgtk,fpgtkext,glib,gtk,gdk,debugserverintf,sysutils,msgintf,classes,inifiles;
Type
TClientCList = Class(TFPGtkScrollClist)
Protected
FTabLabel : TFPgtkLabel;
FClient : TClient;
Public
Constructor Create (AClient : TClient);
Property Client : TClient Read FClient Write FClient;
Property TabLabel : TFPgtkLabel Read FTabLabel Write FTabLabel;
end;
TMainForm = Class (TFPGtkWindow)
Private
// 'real' Variables
FCreating,
FPaused,
FInsertNew,
FCloseOnDisconnect,
FLoadSinglePage,
FSinglePage : Boolean;
FIdleHandle : gint;
FMsgImages : Array[-1..3] of PgdkPixmap;
FMsgMasks : Array[-1..3] of Pgdkbitmap;
// Widgets
FVBox : TFPGtkVBox;
FMainMenu : TFpGtkMenuBar;
FFile, // File menu
FFileClear,
FFileSave,
FFileExit,
FFileClose,
FOptions,
FHelp, // Help menu
FHelpAbout : TFPGtkMenuItem;
FOptionsInsertNew,
FOptionsSinglePage,
FOptionsCloseOnDisconnect,
FFilePause : TFPGtkCheckMenuItem;
FConnectImage,
FDisconnectImage,
FInfoImage,
FWarningImage,
FErrorImage,
FClearImage,
FSaveImage,
FCloseImage,
FPauseImage,
FRunImage,
FRunButtonImage : TFPGtkPixmap;
FMainToolbar : TFPGtkToolbar;
FToolbarFrame : TFPGtkFrame;
FPages : TfpGTKNotebook;
FList : TClientCList;
FRunButton : TFPgtkButton;
FRunImages : Array[Boolean] of PgdkPixmap;
FRunMasks : Array[Boolean] of Pgdkbitmap;
Public
// General
Constructor Create;
Procedure CreateWindow;
Procedure LoadSettings;
Procedure SaveSettings;
Function SettingsFilename : String;
Procedure SetupDebugServer;
Procedure ShutdownDebugServer;
// List/Client management
Procedure SaveToFile(C : TClientCList; FN : String);
Function NewList(Client : TClient) : TClientCList;
Function GetList(Client : TClient) : TClientClist;
Procedure HaveData(Handle : Gint);
Procedure AddEventToList(Event : TDebugEvent);
Function CheckForNewClient : TClient;
Function CurrentList : TClientCList;
Function GetClientPageIndex(Client : TClient) : Integer;
Procedure ConnectionClosed(C : TCLient);
Procedure SetRunButtonBitmaps;
Function GetFileName(ATitle : String) : String;
Procedure DialogSetFilename(Sender : TFPGtkWindow;Data : Pointer; Action : Integer;Initiator : TFPGtkObject);
// User callbacks
Procedure FileClearClick(Sender : TFPGtkObject; Data : Pointer);
Procedure FileSaveClick(Sender : TFPGtkObject; Data : Pointer);
Procedure FileCopyClick(Sender : TFPGtkObject; Data : Pointer);
Procedure FilePauseClick(Sender : TFPGtkObject; Data : Pointer);
Procedure FileCloseClick(Sender : TFPGtkObject; Data : Pointer);
Procedure FileExitClick(Sender : TFPGtkObject; Data : Pointer);
Procedure OptionsClick(Sender : TFPGtkObject; Data : Pointer);
Procedure HelpAboutClick(Sender : TFPGtkObject; Data : Pointer);
Function OnDeleteEvent(Sender:TFPgtkWidget; Event:PGdkEvent; data:pointer): boolean;
end;
Implementation
uses frmabout,bitmapdata;
ResourceString
SCaption = 'FPC Debug server';
SMenuFile = '_File';
SMenuFileClear = 'Clea_r';
SMenuFileSave = '_Save';
SMenuFileCopy = '_Copy';
SMenuFilePause = '_Pause';
SMenuFileClose = 'C_lose';
SMenuFileExit = 'E_xit';
SMenuOptions = '_Options';
SMenuOptionsInsertNew = '_New messages first';
SMenuOptionsSinglePage = '_All clients on 1 page';
SMenuOptionsCloseOnDisconnect = '_Close client on disconnect';
SMenuHelp = '_Help';
SMenuHelpAbout = '_About';
SHintFileClear = 'Clear current list';
SHintFileSave = 'Save current list to disk';
SHintFilePause = 'Discard new messages';
SHintFileClose = 'Close current list';
SNewClient = 'New client';
SMessageFrom = '[%s] : %s';
SSaveFileTitle = 'Save list to file:';
SNeedsRestart = 'Restart debugserver for this option to take effect.';
Const
SKeyParameters = 'Parameters';
SKeySinglePage = 'SinglePage';
SKeyCloseOnDisconnect = 'CloseOnDisconnect';
SkeyInsertNew = 'InsertNewMessages';
SConfigFile = 'debugserver.cnf';
DefaultSinglePage = True;
DefaultCloseOnConnect = False;
DefaultInsertNew = True;
Const
DefaultTooltips = True;
DefaultToolBarStyle = GTK_TOOLBAR_ICONS;
DefaultToolbarRelief = GTK_RELIEF_NONE;
{ ---------------------------------------------------------------------
TClientCList
---------------------------------------------------------------------}
Constructor TClientCList.Create(Aclient : TClient);
begin
Inherited Create(3);
FClient:=AClient;
Clist.ShowTitles;
CList.PassiveTitles;
Clist.ColumnTitle[0]:='*';
Clist.ColumnTitle[1]:='Time';
Clist.ColumnTitle[2]:='Message';
CLIST.SetColumnWidth(0,20);
CList.SetColumnResizeable(0,False);
CLIST.SetColumnWidth(1,120);
end;
{ ---------------------------------------------------------------------
MainForm - General
---------------------------------------------------------------------}
// Cludge to go around TFPgtkBin.Child not being public...
Type
TMyBin = Class(TFPgtkBin)
Public
Property Child;
end;
Procedure TMainForm.CreateWindow;
Var
V : TFPGtkHBox;
FAccelGroup: Integer;
begin
{ File Menu }
Title:=SCaption;
FAccelGroup:=AccelGroupNew;
FFileClear := NewMenuItem(SMenuFileClear ,'','',MakeAccelKeyDef(Self,FaccelGroup,GDK_L,[amcontrol]),@FileClearClick,Nil);
FFileSave := NewMenuItem(SMenuFileSave ,'','',MakeAccelKeyDef(Self,FaccelGroup,GDK_S,[amcontrol]),@FileSaveClick,Nil);
FFilePause := NewCheckMenuItem(SMenuFilePause ,'','',MakeAccelKeyDef(Self,FaccelGroup,GDK_P,[amcontrol]),@FilePauseClick,Nil);
If Not FSinglePage then
FFileClose := NewMenuItem(SMenuFileClose ,'','',MakeAccelKeyDef(Self,FaccelGroup,GDK_W,[amcontrol]),@FileCloseClick,Nil);
FFileExit := NewMenuItem(SMenuFileExit ,'','',MakeAccelKeyDef(Self,FaccelGroup,GDK_Q,[amcontrol]),@FileExitClick,Nil);
If FSinglePage then
FFile := NewSubMenu(SMenuFile,'','',[FFileClear,FFileSave,FFilePause,NewLine,FFileExit])
else
FFile := NewSubMenu(SMenuFile,'','',[FFileClear,FFileSave,FFilePause,FFileClose,NewLine,FFileExit]);
{ Options menu }
FOptionsInsertNew := NewCheckMenuItem(SMenuOptionsInsertNew,'','', @OptionsClick,@FInsertNew);
FOptionsInsertNew.Active:=FInsertNew;
FOptionsSinglePage := NewCheckMenuItem(SMenuOptionsSinglePage,'','', @OptionsClick,@FLoadSinglePage);
FOptionsSinglePage.Active:=FSinglePage;
FOptionsCloseOnDisconnect := NewCheckMenuItem(SMenuOptionsCloseOnDisconnect,'','', @OptionsClick,@FCloseOnDisconnect);
FOptionsCloseOnDisconnect.active:=FCloseOnDisconnect;
FOptions := NewSubMenu(SMenuOptions,'','',[FOptionsInsertNew,FOptionsSinglePage,FOptionsCloseOnDisconnect]);
{ Help Menu }
FHelpAbout:=NewMenuItem(SMenuHelpAbout ,'','',@HelpAboutClick,Nil);
FHelp := NewSubMenu(SMenuHelp,'','',[FHelpAbout]);
{ Menu bar }
FMainMenu:=NewMenuBar([FFile,FOptions,FHelp]);
{ Toolbar images }
FClearImage:=PixmapFromFile('clear');
FPauseImage:=PixmapFromFile('pause');
FRunImage:=PixmapFromFile('run');
FCloseImage:=PixMapFromFile('close');
FSaveImage:=PixmapFromFile('save');
FRunButtonImage:=TFPGtkPixmap.Create;
FRunImages[False]:=FPauseImage.PixMap;
FRunMasks[False]:=FPauseImage.Mask;
FRunImages[True]:=FRunImage.PixMap;
FRunMasks[True]:=FRunImage.Mask;
SetRunButtonBitmaps;
{ Message images }
FConnectImage:=PixMapFromFile('connect');
FDisConnectImage:=PixMapFromFile('disconnect');
FinfoImage:=PixmapFromFile('info');
FWarningImage:=PixmapFromFile('warning');
FErrorImage:=PixmapFromFile('error');
{ Save references for quick lookup}
FMsgImages[-1]:=FDisconnectImage.PixMap;
FMsgImages[0]:=FinfoImage.PixMap;
FMsgImages[1]:=FWarningImage.PixMap;
FMsgImages[2]:=FErrorImage.PixMap;
FMsgImages[3]:=FConnectImage.PixMap;
FMsgMasks[-1]:=FDisconnectImage.Mask;
FMsgMasks[0]:=FinfoImage.Mask;
FMsgMasks[1]:=FWarningImage.Mask;
FMsgMasks[2]:=FErrorImage.Mask;
FMsgMasks[3]:=FConnectImage.Mask;
{ Toolbar }
FMainToolBar:=TFPGtkToolbar.Create;
With FMainToolbar do
begin
Style:=DefaultToolbarStyle;
ButtonRelief:=DefaultToolbarRelief;
EnableTooltips:=DefaultTooltips;
AppendItem(SMenuFileClear,SHintFileClear,'',FClearImage,@FileClearClick,Nil);
AppendItem(SMenuFileSave,SHintFileSave,'',FSaveImage,@FileSaveClick,Nil);
FRunButton:=AppendItem(SMenuFilePause,SHintFilePause,'',FRunButtonImage,@FilePauseClick,Nil) as TFPgtkButton;
AppendItem(SMenuFileClose,SHintFileClose,'',FCloseImage,@FileCloseClick,Nil);
end;
{ place left aligned on frame }
V:=TFPGtkHBox.Create;
V.PackStart(FMainToolbar,False,False,2);
FToolbarFrame:=TFPGtkFrame.Create;
FtoolbarFrame.Add(V);
{ For Pages }
// FPages:=TFPGtkNoteBook.Create;
If FSinglePage then
FList:=NewList(Nil)
else
FPages:=TFPGtkNoteBook.Create;
{ Place on form }
FVBox:=TFPGTKVBox.create;
FVBox.PackStart(FMainMenu,false, true, 0);
FVBox.PackStart(FToolBarFrame,false, true, 0);
If FSinglePage then
FVBox.PackStart(FList,true, true, 0)
else
FVBox.PackStart(FPages,true, true, 0);
Self.add(FVBox);
Setusize(640,480);
ConnectDeleteEvent(@OnDeleteEvent,Nil);
end;
Constructor TMainForm.Create;
begin
Inherited Create(gtk_window_dialog);
LoadSettings;
FSinglePage:=FLoadSinglePage;
FPaused:=False;
FCreating:=True;
Try
CreateWindow;
Finally
FCreating:=False;
end;
SetUpDebugServer;
end;
Procedure TMainForm.SetRunButtonBitmaps;
begin
FRunButtonImage.SetPixMap(FRunImages[FPaused],FRunMasks[FPaused]);
end;
Function TMainForm.SettingsFileName : String;
Var
Home : String;
begin
Home:=GetEnvironmentVariable('HOME');
If (Home<>'') then
Result:=IncludeTrailingBackslash(Home)+'.'+SConfigFile
else
Result:=SConfigFile;
end;
Procedure TMainForm.LoadSettings;
begin
With TIniFile.Create(SettingsFileName) do
Try
FLoadSinglePage:=ReadBool(SKeyParameters,SkeySinglePage,DefaultSinglePage);
FCloseOnDisconnect:=ReadBool(SKeyParameters,SkeyCloseOnDisconnect,DefaultCloseOnConnect);
FInsertNew:=ReadBool(SKeyParameters,SKeyInsertNew,DefaultInsertNew);
finally
Free;
end;
end;
Procedure TMainForm.SaveSettings;
begin
With TIniFile.Create(SettingsFileName) do
Try
WriteBool(SKeyParameters,SkeySinglePage,FLoadSinglePage);
WriteBool(SKeyParameters,SkeyCloseOnDisconnect,FCloseOnDisconnect);
WriteBool(SKeyParameters,SKeyInsertNew,FinsertNew);
UpdateFile;
finally
Free;
end;
end;
{ ---------------------------------------------------------------------
Event handling.
---------------------------------------------------------------------}
// GTK idle callback, to check for new connections during idle time.
function GtkIdle (Data:pointer) : gint; Cdecl;
begin
With (TObject(Data) as TMainForm) do
CheckForNewClient;
end;
// When input appears on one of the handles, we come here through the gtk
// input watch.
Procedure GtkHaveInput (Data : GPointer; Source : gint; Condition : TGDKInputCondition);cdecl;
begin
With (TObject(Data) as TMainForm) do
HaveData(Source);
end;
// Real methods.
Procedure TMainForm.SetupDebugServer;
begin
OpenDebugServer;
FIdleHandle:=gtk_idle_add(@GtkIdle,Self);
CloseObjConnectionCallBack:=@Self.ConnectionClosed;
end;
Procedure TMainForm.ShutdownDebugServer;
begin
CloseObjConnectionCallBack:=Nil;
gtk_idle_remove(FIdleHandle);
CloseDebugServer;
end;
// tell gdk not to watch this handle any more.
// Clean up of the associated page happens in log event.
Procedure TMainForm.ConnectionClosed(C : TCLient);
begin
gdk_input_remove(gint(C.Data));
end;
// We get here when data is present on socket.
Procedure TMainForm.HaveData(Handle : Gint);
Var
Event : TDebugEvent;
begin
ReadMessageEvent(Handle,Event);
AddEventToList(Event);
end;
// Check if a new client has reported ?
Function TMainForm.CheckForNewClient : TClient;
Var
IHandle : gint;
L : TClientCList;
begin
// Writeln('Checking new client');
Result:=CheckNewConnection;
If Result<>Nil then
begin
If Not FSinglePage then
begin
L:=NewList(Result);
FPages.AppendPage(L,L.TabLabel);
FPages.Page:=L;
// Force draw.
Fpages.Draw(Nil);
L.ShowNow;
L.Draw(Nil);
L.Clist.draw(Nil);
end;
IHandle:=gdk_input_add(Result.Handle,GDK_INPUT_READ,@GtkHaveInput,self);
Result.Data:=Pointer(IHandle);
end;
end;
// Add event data to the appropriate list.
Procedure TMainForm.AddEventToList(Event : TDebugEvent);
Const
MsgTypeStrings : Array[-1..3] of String = ('*','?','!','!','.');
Var
T,D,S : String;
L : TClientCList;
TL : TFPgtkWidget;
SL : TStringList;
Index : Integer;
begin
With Event do
begin
T:=MsgTypeStrings[LogCode];
D:=DateTimeToStr(TimeStamp);
If FSinglePage or (logcode<>lctIdentify) then
S:=Format(SMessageFrom,[Client.Peer,Event])
else
S:=Event;
L:=Nil;
If Assigned(Client) then
L:=GetList(Client);
If L=Nil then
Writeln('No list found for event ',s)
else
begin
If (LogCode=lctIdentify) then
If Not FSinglePage then
L.TabLabel.Text:=Event;
If Not FPaused then
begin
If FInsertNew then
begin
Index:=0;
L.CList.Prepend([T,D,S]);
end
else
Index:=L.CList.Append([T,D,S]);
L.Clist.SetPixMap(Index,0,FMsgImages[logCode],FmsgMasks[LogCode]);
end;
If LogCode=lctStop then
begin
L.Client:=Nil; // New clients MAY have the same addresspointer as existing;
If (Not FSinglePage) and FCloseOnDisconnect then
L.Free;
end;
end;
end;
CheckForNewClient;
end;
// Create new list.
Function TMainForm.NewList(Client : TClient) : TClientCList;
begin
Result:=TClientCList.Create(Client);
If Client<>Nil then
Result.TabLabel:=TfpGtkLabel.Create(SNewClient);
end;
// Get page index on which messages for client are shown.
// Only call when FSinglePage is not true
Function TMainForm.GetClientPageIndex(Client : TClient) : Integer;
Var
P : TFPgtkWidget;
begin
With FPages,Children do
begin
Result:=Count-1;
While Result>=0 do
begin
P:=GetChildOnPage(Result);
if Not(P is TClientClist) or
(TClientClist(P).Client<>Client) Then
Dec(Result)
end
end;
end;
// Get list on which messages for client are shown.
// Only call when FSinglePage is not true
Function TMainForm.GetList(Client : TClient) : TClientCList;
Var
I : Integer;
P : TFPgtkWidget;
begin
If FSinglePage then
Result:=FList
else
begin
With FPages,Children do
For I:=0 to Count-1 do
begin
P:=GetChildOnPage(i);
if P is TClientClist then
If TClientClist(P).Client=Client Then
begin
Result:=TClientClist(P);
exit
end;
end;
end;
end;
// Return current list.
Function TMainForm.CurrentList : TClientClist;
begin
If FSinglePage then
Result:=FList
else
Result:=FPages.Page as TClientCList;
end;
// Write list contents to file.
Procedure TMainForm.SaveToFile(C : TClientCList; FN : String);
Var
F : System.text;
I,J : Integer;
S,T : String;
P : PGdkPixmap;
M : PGdkBitmap;
begin
Assign(F,FN);
rewrite(F);
Try
With C.Clist do
For I:=0 To count-1 do
begin
GetPixmap(I,0,P,M);
For J:=-1 to 3 do
If (FMsgImages[J]=P) then
T:=MsgTypes[J];
S:=T+' '+CellText[i,1]+' '+Celltext[I,2];
Writeln(F,S);
end;
finally
System.Close(f);
end;
end;
{ ---------------------------------------------------------------------
Callbacks for user events.
---------------------------------------------------------------------}
Procedure TMainForm.FileClearClick(Sender : TFPGtkObject; Data : Pointer);
Var
L:TClientClist;
begin
L:=CurrentList;
If L<>Nil then
L.Clist.Clear;
end;
Procedure TMainForm.DialogSetFilename(Sender : TFPGtkWindow;Data : Pointer; Action : Integer;Initiator : TFPGtkObject);
type
PString = ^AnsiString;
begin
PString(Data)^:=(Sender as TFPgtkFileSelection).Filename;
end;
Function TMainForm.GetFileName(ATitle : String) : String;
var
FS : TFPgtkFileSelection;
begin
FS := TFPgtkFileSelection.Create (gtk_window_dialog);
Result:='';
with FS do
begin
Title:=ATitle;
OKButton.ConnectClicked (@(CloseWithResult), inttopointer(drOk));
CancelButton.ConnectClicked (@(CloseWindow), nil);
if Not execute (nil, @Result, @DialogSetFilename) = drOk then
Result:='';
end;
end;
Procedure TMainForm.FileSaveClick(Sender : TFPGtkObject; Data : Pointer);
Var
L:TClientClist;
FN : String;
begin
L:=CurrentList;
If L<>Nil then
begin
FN:=GetFileName(SSaveFileTitle);
If (FN<>'') then
SaveToFile(L,Fn);
end;
end;
Procedure TMainForm.FileCopyClick(Sender : TFPGtkObject; Data : Pointer);
// not used for the moment; need to figure out a way to do this.
begin
end;
Procedure TMainForm.FilePauseClick(Sender : TFPGtkObject; Data : Pointer);
begin
If (Sender<>FFilePause) then
With FFilePause do
Active:=Not Active;
FPaused:=FFilePause.Active;
SetRunButtonBitmaps;
end;
Procedure TMainForm.FileCloseClick(Sender : TFPGtkObject; Data : Pointer);
Var
L : TClientCList;
begin
L:=CurrentList;
If (L<>Nil) and (L.Client=Nil) then
L.Free;
end;
Procedure TMainForm.FileExitClick(Sender : TFPGtkObject; Data : Pointer);
begin
ShutdownDebugServer;
Close;
end;
Procedure TMainForm.OptionsClick(Sender : TFPGtkObject; Data : Pointer);
Type
PBoolean = ^boolean;
begin
If not FCreating then
begin
PBoolean(data)^:=Not PBoolean(data)^;
SaveSettings;
If (@FLoadSinglePage=Data) then
ShowMessage(SCaption,SNeedsRestart);
end;
end;
Procedure TMainForm.HelpAboutClick(Sender : TFPGtkObject; Data : Pointer);
begin
With TAboutForm.Create do
Execute(Nil,Nil,Nil);
end;
Function TMainForm.OnDeleteEvent(Sender:TFPgtkWidget; Event:PGdkEvent; data:pointer): boolean;
begin
ShutdownDebugServer;
Result:=True;
end;
end.