Files
HeidiSQL/source/main.pas
Ansgar Becker 41074d211e Convert database tree from TTreeView to TVirtualStringTree.
- Fixes bug #271 "F5 shortcut in database tree not working"
- Fullfills rfe #519 "Highlight selected item in tree view when focus is elsewhere"
- Fixes inconsistent expansion state of nodes after refreshing
- Fixes plus sign staying when a db node with 0 tables was selected
- Slightly enhances painting speed within the dbtree
- Removes temporary storage of a rightclicked node (DBRightClickSelectItem). VirtualTree handles its right clicked selection exactly like the left clicked one, which makes makes coding a bit cleaner as we can always use the selected item instead of hacking with the right and left clicked node. On the other side it removes a quite user friendly feature. Well, we can reimplement this feature later, though it's not user UNfriendly currently.
- Reasonably rename the StringList "OnlyDBs" to "DatabasesWanted" and "OnlyDBs2" to "Databases"
2008-06-13 23:05:51 +00:00

1353 lines
43 KiB
ObjectPascal

unit Main;
// -------------------------------------
// Main-window
// -------------------------------------
{$I compilers.inc}
interface
uses
Synchronization,
Communication,
Windows, SysUtils, Classes, Graphics, Forms, Controls, Menus,
StdCtrls, Dialogs, Buttons, Messages, ExtCtrls, ComCtrls, StdActns,
ActnList, ImgList, Registry, ShellApi, ToolWin, Clipbrd, db, DBCtrls,
SynMemo, synedit, SynEditTypes, ZDataSet, ZSqlProcessor,
HeidiComp, sqlhelp, MysqlQueryThread, Childwin, VirtualTrees, TntDBGrids,
StrUtils, DateUtils, PngImageList, OptimizeTables, View, Usermanager,
SelectDBObject;
type
TMainForm = class(TForm)
MainMenu1: TMainMenu;
File1: TMenuItem;
FileNewItem: TMenuItem;
FileCloseItem: TMenuItem;
Help1: TMenuItem;
N1: TMenuItem;
FileExitItem: TMenuItem;
menuAbout: TMenuItem;
Edit1: TMenuItem;
CopyItem: TMenuItem;
PasteItem: TMenuItem;
StatusBar: TStatusBar;
ActionList1: TActionList;
EditCopy1: TEditCopy;
EditPaste1: TEditPaste;
FileNew1: TAction;
FileExit1: TAction;
FileClose1: TWindowClose;
Extra1: TMenuItem;
FlushUserPrivileges1: TMenuItem;
MenuCopyCSV: TMenuItem;
N3: TMenuItem;
MenuRefresh: TMenuItem;
MenuExport: TMenuItem;
MenuCreateDatabase: TMenuItem;
MenuCreateTable: TMenuItem;
N4: TMenuItem;
MenuDropDatabase: TMenuItem;
MenuDropTable: TMenuItem;
ResetWindowOptions1: TMenuItem;
N5: TMenuItem;
MenuImportTextFile: TMenuItem;
Flush1: TMenuItem;
MenuFlushLogs: TMenuItem;
MenuFlushHosts: TMenuItem;
MenuFlushTables: TMenuItem;
MenuFlushTableswithreadlock: TMenuItem;
MenuFlushStatus: TMenuItem;
N6: TMenuItem;
MenuUserManager: TMenuItem;
MenuPreferences: TMenuItem;
N7: TMenuItem;
menuReadme: TMenuItem;
UserManager: TAction;
ShowAboutBox: TAction;
actMaintenance: TAction;
menuMaintenance: TMenuItem;
ImExport1: TMenuItem;
CopyContentsasHTMLTable1: TMenuItem;
CopyHTMLtable: TAction;
Copy2CSV: TAction;
menuWebsite: TMenuItem;
N9: TMenuItem;
N11: TMenuItem;
PrintList: TAction;
CopyTable: TAction;
ControlBar1: TControlBar;
ToolBarStandard: TToolBar;
ToolButton9: TToolButton;
FileCloseItem2: TToolButton;
tlbSep1: TToolButton;
ToolButton5: TToolButton;
ToolButton6: TToolButton;
ToolButton12: TToolButton;
tlbSep2: TToolButton;
ButtonCreateDatabase: TToolButton;
ButtonCreateTable: TToolButton;
ButtonDropDatabase: TToolButton;
ButtonDropTable: TToolButton;
tlbSep4: TToolButton;
ButtonRefresh: TToolButton;
tlbSep5: TToolButton;
ButtonImportTextfile: TToolButton;
ButtonExport: TToolButton;
ButtonUserManager: TToolButton;
ToolBarData: TToolBar;
DBNavigator1: TDBNavigator;
PanelLimit: TPanel;
CheckBoxLimit: TCheckBox;
EditLimitStart: TEdit;
EditLimitEnd: TEdit;
ButtonOK: TButton;
UpDownLimitStart: TUpDown;
UpDownLimitEnd: TUpDown;
EditUndo1: TEditUndo;
ToolButton14: TToolButton;
ExecuteQuery: TAction;
ExecuteSelection: TAction;
SaveDialog2: TSaveDialog;
ExportSettings1: TMenuItem;
Importsettings1: TMenuItem;
OpenDialog2: TOpenDialog;
menuSupportForum: TMenuItem;
Copy2XML: TAction;
ExportData: TAction;
Exportdata1: TMenuItem;
CopyasXMLdata1: TMenuItem;
ExecuteLine: TAction;
HTMLview: TAction;
InsertFiles: TAction;
InsertfilesintoBLOBfields1: TMenuItem;
ExportTables: TAction;
DataSearch: TAction;
DropTablesAndViews: TAction;
LoadSQL: TAction;
ImportSQL1: TMenuItem;
menuConnections: TPopupMenu;
miNewConnection: TMenuItem;
menuWindow: TMenuItem;
miFake: TMenuItem;
menuBugtracker: TMenuItem;
menuFeaturetracker: TMenuItem;
menuDownload: TMenuItem;
btnSQLHelp: TToolButton;
menuSQLHelp: TMenuItem;
N8: TMenuItem;
Import1: TMenuItem;
tlbSep6: TToolButton;
menuUpdateCheck: TMenuItem;
PngImageListMain: TPngImageList;
tlbSep3: TToolButton;
actEditView: TAction;
actCreateView: TAction;
ToolButton1: TToolButton;
ToolButton2: TToolButton;
Createview1: TMenuItem;
procedure actCreateViewExecute(Sender: TObject);
procedure btnSQLHelpClick(Sender: TObject);
procedure menuWindowClick(Sender: TObject);
procedure focusWindow(Sender: TObject);
procedure menuConnectionsPopup(Sender: TObject);
procedure ShowConnections(Sender: TObject);
procedure FileExit1Execute(Sender: TObject);
procedure FlushClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormCreate(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure ButtonRefreshClick(Sender: TObject);
procedure ButtonCreateDatabaseClick(Sender: TObject);
procedure ButtonCreateTableClick(Sender: TObject);
procedure ButtonDropDatabaseClick(Sender: TObject);
procedure ResetWindowOptions1Click(Sender: TObject);
procedure ButtonImportTextfileClick(Sender: TObject);
procedure MenuPreferencesClick(Sender: TObject);
procedure menuReadmeClick(Sender: TObject);
procedure FormResize(Sender: TObject);
procedure UserManagerExecute(Sender: TObject);
procedure ShowAboutBoxExecute(Sender: TObject);
procedure actMaintenanceExecute(Sender: TObject);
procedure actEditViewExecute(Sender: TObject);
procedure CopyHTMLtableExecute(Sender: TObject);
procedure Copy2CSVExecute(Sender: TObject);
procedure PrintListExecute(Sender: TObject);
procedure CopyTableExecute(Sender: TObject);
procedure showstatus(msg: string=''; panel: Integer=0);
procedure ButtonOKClick(Sender: TObject);
procedure LimitPanelEnter(Sender: TObject);
procedure LimitPanelExit(Sender: TObject);
procedure OpenURL(Sender: TObject);
function mask(str: String) : String;
procedure ExportSettings1Click(Sender: TObject);
procedure Importsettings1Click(Sender: TObject);
procedure ExecuteQueryExecute(Sender: TObject);
procedure ExecuteSelectionExecute(Sender: TObject);
procedure Copy2XMLExecute(Sender: TObject);
procedure DBNavigator1BeforeAction(Sender: TObject;
Button: TNavigateBtn);
procedure ExportDataExecute(Sender: TObject);
procedure ExecuteLineExecute(Sender: TObject);
procedure HTMLviewExecute(Sender: TObject);
procedure InsertFilesExecute(Sender: TObject);
procedure ExportTablesExecute(Sender: TObject);
procedure DataSearchExecute(Sender: TObject);
procedure DropTablesAndViewsExecute(Sender: TObject);
procedure LoadSQLExecute(Sender: TObject);
procedure EnsureConnected;
function ExecuteRemoteQuery(sender: THandle; query: string): TDataSet;
procedure ExecuteRemoteNonQuery(sender: THandle; query: string);
procedure HandleWMComplete(var msg: TMessage); message WM_COMPLETED;
procedure HandleWMCopyData(var msg: TWMCopyData); message WM_COPYDATA;
procedure HandleWMProcessLog(var msg: TMessage); message WM_PROCESSLOG;
procedure menuUpdateCheckClick(Sender: TObject);
private
regMain : TRegistry;
function GetChildwin: TMDIChild;
function GetParamValue(const paramChar: Char; const paramName:
string; var curIdx: Byte; out paramValue: string): Boolean;
public
MaintenanceForm: TOptimize;
ViewForm: TfrmView;
UserManagerForm: TUserManagerForm;
SelectDBObjectForm: TfrmSelectDBObject;
procedure OpenRegistry(Session: String = '');
function GetRegValue( valueName: String; defaultValue: Integer; Session: String = '' ) : Integer; Overload;
function GetRegValue( valueName: String; defaultValue: Boolean; Session: String = '' ) : Boolean; Overload;
function GetRegValue( valueName: String; defaultValue: String; Session: String = '' ) : String; Overload;
function CreateMDIChild(parHost, parPort, parUser, parPass, parDatabase, parTimeout, parCompress, parSortDatabases, parDescription: String): Boolean;
// Reference to currently active childwindow:
property Childwin: TMDIChild read GetChildwin;
end;
var
MainForm : TMainForm;
appstarted : Boolean = false; // see connections.pas
loadsqlfile : boolean = true; // load sql-file into query-memo at startup?
AppVersion : String = 'x.y';
AppRevision : String = '$Rev$';
FullAppVersion : String;
DirnameCommonAppData,
DirnameUserAppData,
DIRNAME_SNIPPETS,
DirnameSessionLogs : String;
const
discname = 'not connected';
ICON_MYSELF_CONNECTED = 38;
ICON_MYSELF_DISCONNECTED = -1;
ICON_OTHER_CONNECTED = 36;
ICON_OTHER_DISCONNECTED = -1;
{$I const.inc}
type TMyKey = record
Name : String;
_type : String;
Columns : TStringList;
SubParts : TStringList;
end;
implementation
uses
About,
connections,
exportsql,
tbl_properties,
loaddata,
options,
printlist,
copytable,
insertfiles,
Helpers,
Threading,
mysql_structures,
MysqlConn,
UpdateCheck;
{$R *.DFM}
procedure TMainForm.ShowConnections(Sender: TObject);
begin
if ActiveMDIChild = nil then
ConnectionWindow(Self)
else begin
debug('perf: new connection clicked.');
ShellExec( ExtractFileName(paramstr(0)), ExtractFilePath(paramstr(0)) );
end;
end;
procedure TMainForm.HandleWMComplete(var msg: TMessage);
begin
HandleWMCompleteMessage(msg);
end;
procedure TMainForm.HandleWMCopyData(var msg: TWMCopyData);
begin
HandleWMCopyDataMessage(msg);
end;
procedure TMainForm.HandleWMProcessLog(var msg: TMessage);
begin
ChildWin.ProcessSqlLog;
end;
procedure TMainForm.EnsureConnected;
begin
if ActiveMDIChild = nil then raise Exception.Create('Not connected.');
end;
function TMainForm.ExecuteRemoteQuery(sender: THandle; query: string): TDataSet;
//var
//tab: THandle;
begin
EnsureConnected;
// tab := TMDIChild(ActiveMDIChild).CreateOrGetRemoteQueryTab(sender);
// TQueryTab(tab).AddText(query);
// tab.ExecOrQueueQuery(query);
result := ChildWin.ExecuteQuery(query);
end;
procedure TMainForm.ExecuteRemoteNonQuery(sender: THandle; query: string);
//var
//tab: THandle;
begin
EnsureConnected;
// tab := TMDIChild(ActiveMDIChild).CreateOrGetRemoteQueryTab(sender);
// TQueryTab(tab).AddText(query);
// tab.ExecOrQueueQuery(query);
ChildWin.ExecuteNonQuery(query);
end;
procedure TMainForm.showstatus(msg: string=''; panel: Integer=0);
begin
// show Message in statusbar
StatusBar.Panels[panel].Text := msg;
StatusBar.Repaint;
end;
procedure TMainForm.FileExit1Execute(Sender: TObject);
begin
Close;
end;
procedure TMainForm.FlushClick(Sender: TObject);
var
flushwhat : String;
begin
if sender is TMenuItem then
flushwhat := UpperCase((sender as TMenuItem).Caption)
else if sender is TToolButton then
flushwhat := 'PRIVILEGES';
delete(flushwhat, pos('&', flushwhat), 1);
ChildWin.ExecUpdateQuery('FLUSH ' + flushwhat);
if sender = MenuFlushTableswithreadlock then begin
MessageDlg(
'Tables have been flushed and read lock acquired.'#10 +
'Perform backup or snapshot of table data files now.'#10 +
'Press OK to unlock when done...',
mtInformation, [mbOk], 0
);
ChildWin.ExecUpdateQuery('UNLOCK TABLES');
end;
end;
procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
var
ws : String;
filename : String;
buffer : array[0..MAX_PATH] of char;
begin
if ActiveMDIChild <> nil then
ActiveMDIChild.Close;
if windowstate = wsNormal
then ws := 'Normal' else
if windowstate = wsMinimized
then ws := 'Minimized' else
if windowstate = wsMaximized
then ws := 'Maximized';
with TRegistry.Create do
begin
if OpenKey(REGPATH, true) then
begin
WriteString(REGNAME_WINDOWSTATE, ws);
WriteInteger(REGNAME_WINDOWLEFT, left);
WriteInteger(REGNAME_WINDOWTOP, top);
WriteInteger(REGNAME_WINDOWWIDTH, width);
WriteInteger(REGNAME_WINDOWHEIGHT, height);
// Position of Toolbars
WriteInteger(REGNAME_TOOLBAR2LEFT, ToolBarStandard.Left);
WriteInteger(REGNAME_TOOLBARDATALEFT, ToolBarData.Left);
WriteInteger(REGNAME_TOOLBAR2TOP, ToolBarStandard.Top);
WriteInteger(REGNAME_TOOLBARDATATOP, ToolBarData.Top);
end;
CloseKey;
Free;
end;
GetTempPath(MAX_PATH, buffer);
filename := buffer;
filename := '\'+filename+APPNAME+'-preview.';
if FileExists(filename+'html') then
deletefile(filename+'html');
if FileExists(filename+'jpg') then
deletefile(filename+'jpg');
if FileExists(filename+'gif') then
deletefile(filename+'gif');
if FileExists(filename+'bmp') then
deletefile(filename+'bmp');
if regMain <> nil then begin
regMain.CloseKey;
regMain.Free;
end;
end;
{***
OnCreate Event
Important to set the windowstate here instead of in OnShow
because possible windowstate-switching is done with an animation
if set in Windows. This animation takes some milliseconds
to complete and can be annoying.
}
procedure TMainForm.FormCreate(Sender: TObject);
var
ws : String;
Monitor: TMonitor;
const
MoveWinThreshold: Byte = 80;
begin
caption := APPNAME;
setLocales;
ws := GetRegValue('windowstate', 'Normal');
if ws = 'Minimized'
then windowstate := wsMinimized else
if ws = 'Normal' then begin
windowstate := wsNormal;
Left := GetRegValue(REGNAME_WINDOWLEFT, Left);
Top := GetRegValue(REGNAME_WINDOWTOP, Top);
Width := GetRegValue(REGNAME_WINDOWWIDTH, Width);
Height := GetRegValue(REGNAME_WINDOWHEIGHT, Height);
// Ensure main window (that means: the upper left corner) is placed inside
// current monitor resolution, important e.g. after having plugged off a second monitor
// Ensures a minimum visible area of 80x80 pixels, so the user can manually resize and move it
Monitor := Screen.MonitorFromWindow(Self.Handle);
if Left > Monitor.Left + Monitor.Width - MoveWinThreshold then
Left := Monitor.Left + Monitor.Width - MoveWinThreshold; // Set minimal visible width
if Top > Monitor.Top + Monitor.Height - MoveWinThreshold then
Top := Monitor.Top + Monitor.Height - MoveWinThreshold; // Set minimal visible height
end else
if ws = 'Maximized'
then windowstate := wsMaximized;
// Position of Toolbars
ToolBarStandard.Left := GetRegValue(REGNAME_TOOLBAR2LEFT, ToolBarStandard.Left);
ToolBarData.Left := GetRegValue(REGNAME_TOOLBARDATALEFT, ToolBarData.Left);
ToolBarStandard.Top := GetRegValue(REGNAME_TOOLBAR2TOP, ToolBarStandard.Top);
ToolBarData.Top := GetRegValue(REGNAME_TOOLBARDATATOP, ToolBarData.Top);
// Beautify AppRevision
if Pos('$Rev: WC', AppRevision) < 1 then
AppRevision := 'unknown'
else begin
AppRevision := StringReplace( AppRevision, '$Rev: WC', '', [rfIgnoreCase] );
AppRevision := StringReplace( AppRevision, '$', '', [] );
AppRevision := Trim( AppRevision );
end;
// Compose full version string
FullAppVersion := 'Version ' + AppVersion + ', Revision ' + AppRevision;
// "All users" folder for HeidiSQL's data (All Users\Application Data)
DirnameCommonAppData := GetShellFolder(CSIDL_COMMON_APPDATA) + '\' + APPNAME + '\';
// User folder for HeidiSQL's data (<user name>\Application Data)
DirnameUserAppData := GetShellFolder(CSIDL_APPDATA) + '\' + APPNAME + '\';
// Folder which contains snippet-files
DIRNAME_SNIPPETS := DirnameCommonAppData + 'Snippets\';
// Folder for session logfiles
DirnameSessionLogs := DirnameUserAppData + 'Sessionlogs\';
end;
{**
Check for connection parameters on commandline or show connections form.
}
procedure TMainForm.FormShow(Sender: TObject);
var
curParam : Byte;
sValue,
parHost, parPort, parUser, parPass, parDatabase,
parTimeout, parCompress, parSortDatabases, parDescription : String;
reg : TRegistry;
LastUpdatecheck : TDateTime;
UpdatecheckInterval : Integer;
DefaultLastrunDate : String;
frm : TfrmUpdateCheck;
begin
// Do an updatecheck if checked in settings
if GetRegValue(REGNAME_DO_UPDATECHECK, DEFAULT_DO_UPDATECHECK) then begin
DefaultLastrunDate := '2000-01-01';
try
LastUpdatecheck := StrToDateTime( GetRegValue(REGNAME_LAST_UPDATECHECK, DefaultLastrunDate) );
except
LastUpdatecheck := StrToDateTime( DefaultLastrunDate );
end;
UpdatecheckInterval := GetRegValue(REGNAME_UPDATECHECK_INTERVAL, DEFAULT_UPDATECHECK_INTERVAL);
if DaysBetween(Now, LastUpdatecheck) >= UpdatecheckInterval then begin
frm := TfrmUpdateCheck.Create(Self);
frm.AutoClose := True;
frm.CheckForBuildsInAutoMode := GetRegValue(REGNAME_DO_UPDATECHECK_BUILDS, DEFAULT_DO_UPDATECHECK_BUILDS);
frm.ShowModal;
FreeAndNil(frm);
end;
end;
// Check commandline if parameters were passed. Otherwise show connections windows
curParam := 1;
while curParam <= ParamCount do
begin
// -M and -d are choosen not to conflict with mysql.exe
// http://dev.mysql.com/doc/refman/5.0/en/mysql-command-options.html
//
// To test all supported variants, set Run > Parameters > Parameters option to:
// --host=192.168.0.1 --user=root --password -d "My session name" -D"test db" -C -P 2200
if GetParamValue('h', 'host', curParam, sValue) then
parHost := sValue
else if GetParamValue('P', 'port', curParam, sValue) then
parPort := sValue
else if GetParamValue('C', 'compress', curParam, sValue) then
parCompress := sValue
else if GetParamValue('M', 'timeout', curParam, sValue) then
parTimeout := sValue
else if GetParamValue('u', 'user', curParam, sValue) then
parUser := sValue
else if GetParamValue('p', 'password', curParam, sValue) then
parPass := sValue
else if GetParamValue('D', 'database', curParam, sValue) then
parDatabase := sValue
else if GetParamValue('d', 'description', curParam, sValue) then
parDescription := sValue;
Inc(curParam);
end;
// Minimal parameter is hostname. If given, user commandline parameters.
if parHost <> '' then
begin
// Parameters belong to connection, not to a SQL file which should get opened
loadsqlfile := False;
// Take care for empty description - it gets used to read/write session settings to registry!
if parDescription = '' then
parDescription := parHost;
if CreateMDIChild(
parHost,
parPort,
parUser,
parPass,
parDatabase,
parTimeout,
parCompress,
parSortDatabases,
parDescription) then
begin
// Save session parameters to registry
reg := TRegistry.Create;
if reg.OpenKey(REGPATH + REGKEY_SESSIONS + parDescription, true) then
begin
reg.WriteString(REGNAME_HOST, parHost);
reg.WriteString(REGNAME_USER, parUser);
reg.WriteString(REGNAME_PASSWORD, encrypt(parPass));
reg.WriteString(REGNAME_PORT, parPort);
reg.WriteString(REGNAME_TIMEOUT, parTimeout);
reg.WriteBool(REGNAME_COMPRESSED, Boolean(StrToIntDef(parCompress, 0)) );
reg.WriteString(REGNAME_ONLYDBS, parDatabase);
reg.WriteBool(REGNAME_ONLYDBSSORTED, Boolean(StrToIntDef(parSortDatabases, 0)) );
reg.CloseKey;
reg.Free;
end;
end;
end else
// Cannot be done in OnCreate because we need ready forms here:
ShowConnections(self);
end;
procedure TMainForm.ButtonRefreshClick(Sender: TObject);
begin
// Refresh
// Force data tab update when appropriate.
Childwin.dataselected := false;
if ChildWin.PageControlMain.ActivePage = ChildWin.tabHost then
ChildWin.ShowVariablesAndProcesses(self)
else if ChildWin.PageControlMain.ActivePage = ChildWin.tabDatabase then
ChildWin.MenuRefreshClick(self)
else if ChildWin.PageControlMain.ActivePage = ChildWin.tabTable then
ChildWin.ShowTableProperties(ChildWin.SelectedTable)
else if ChildWin.PageControlMain.ActivePage = ChildWin.tabData then
ChildWin.viewdata(self)
else
ChildWin.RefreshTree(True);
end;
procedure TMainForm.ButtonCreateDatabaseClick(Sender: TObject);
begin
// create database
ChildWin.CreateDatabase(self);
end;
procedure TMainForm.ButtonCreateTableClick(Sender: TObject);
begin
// create table
ChildWin.CreateTable(self);
end;
procedure TMainForm.ButtonDropDatabaseClick(Sender: TObject);
begin
// drop db
if ChildWin.ActiveDatabase <> '' then
ChildWin.DropDB(self);
end;
procedure TMainForm.btnSQLHelpClick(Sender: TObject);
begin
// SQL help
ChildWin.CallSQLHelp( Sender );
end;
procedure TMainForm.ResetWindowOptions1Click(Sender: TObject);
var
reg : TRegistry;
begin
// reset all options for window-size, height ...
if ActiveMDIChild <> nil then
begin
MessageDlg('Close all open windows before you do this.', mtError, [mbok], 0);
exit;
end;
reg := TRegistry.Create;
with reg do
begin
Access := KEY_ALL_ACCESS;
if OpenKey(REGPATH, false) then
begin
DeleteValue(REGNAME_CHILDWINSTATE);
DeleteValue(REGNAME_CHILDWINLEFT);
DeleteValue(REGNAME_CHILDWINTOP);
DeleteValue(REGNAME_CHILDWINWIDTH);
DeleteValue(REGNAME_CHILDWINHEIGHT);
DeleteValue(REGNAME_QUERYMEMOHEIGHT);
DeleteValue(REGNAME_DBTREEWIDTH);
DeleteValue(REGNAME_SQLOUTHEIGHT);
CloseKey;
MessageDlg('All Window-Settings were reset to default values.', mtInformation, [mbok], 0);
end;
Free;
end;
end;
procedure TMainForm.ButtonImportTextfileClick(Sender: TObject);
begin
// Import Textfile
loaddataWindow(self);
end;
procedure TMainForm.MenuPreferencesClick(Sender: TObject);
begin
// Preferences
OptionsWindow (Self);
end;
procedure TMainForm.menuReadmeClick(Sender: TObject);
begin
// show readme.txt
ShellExec( 'readme.txt', ExtractFilePath(paramstr(0)) );
end;
procedure TMainForm.FormResize(Sender: TObject);
begin
StatusBar.Panels[0].Width := (width - StatusBar.Panels[1].Width) - StatusBar.Panels[2].Width;
end;
procedure TMainForm.UserManagerExecute(Sender: TObject);
begin
if UserManagerForm = nil then
UserManagerForm := TUserManagerForm.Create(Self);
UserManagerForm.ShowModal;
end;
procedure TMainForm.menuWindowClick(Sender: TObject);
var
i: integer;
list: TWindowDataArray;
item: TMenuItem;
begin
// Delete dynamically added connection menu items.
// NOTE: The menu doesn't like having 0 items, so we keep one which we delete later.
for i := menuWindow.Count - 1 downto 1 do menuWindow.Delete(i);
// Check if all the heidisql windows are still alive.
CheckForCrashedWindows;
// Fetch the list of windows.
list := GetWindowList;
// TODO: Load "all" array with all connections
// Re-create dynamic menu items.
for i := 0 to High(list) do with list[i] do begin
// TODO: Remove connection with this UID from "all" array
item := TMenuItem.Create(self);
if namePostfix <> 0 then name := name + Format(' (%d)', [namePostFix]);
item.Caption := name;
if (appHandle = Handle) and (connected) then item.ImageIndex := ICON_MYSELF_CONNECTED
else if (appHandle = Handle) and (not connected) then item.ImageIndex := ICON_MYSELF_DISCONNECTED
else if (appHandle <> Handle) and (connected) then item.ImageIndex := ICON_OTHER_CONNECTED
else if (appHandle <> Handle) and (not connected) then item.ImageIndex := ICON_OTHER_DISCONNECTED;
item.Tag := appHandle;
item.OnClick := focusWindow;
menuWindow.Add(item);
end;
// NOTE: The menu breaks if it has 0 items at any point. Therefore we delete item 0 as the last thing.
// Perhaps later the Window menu will contain more items, for now it's initially filled with a fake menu item.
menuWindow.Delete(0);
end;
procedure TMainForm.ShowAboutBoxExecute(Sender: TObject);
begin
// Info-Box
AboutWindow (Self);
end;
procedure TMainForm.actMaintenanceExecute(Sender: TObject);
begin
// optimize / repair... tables
if MaintenanceForm = nil then
MaintenanceForm := TOptimize.Create(Self);
MaintenanceForm.ShowModal;
end;
{**
Create a view
}
procedure TMainForm.actCreateViewExecute(Sender: TObject);
var
NodeData: PVTreeData;
ds: TDataset;
begin
if ViewForm = nil then
ViewForm := TfrmView.Create(Self);
ViewForm.EditViewName := '';
if (Sender as TAction) = actEditView then begin
// Edit mode
if Assigned(Childwin.ListTables.FocusedNode) then begin
// "Edit view" was clicked in ListTables' context menu
NodeData := Childwin.ListTables.GetNodeData(Childwin.ListTables.FocusedNode);
ViewForm.EditViewName := NodeData.Captions[0];
end else if Childwin.DBtree.GetFirstSelected <> nil then begin
// "Edit view" was clicked in DBTree's context menu
ds := Childwin.FetchDbTableList(Childwin.ActiveDatabase);
ds.RecNo := Childwin.DBtree.GetFirstSelected.Index+1;
ViewForm.EditViewName := ds.Fields[0].AsString;
end else
// If we're here, there's a menu item "Edit/Create view" in an unknown location
raise Exception.Create('Internal error in actCreateViewExexute.');
end else begin
// Create mode. Nothing special here.
end;
ViewForm.ShowModal;
end;
{**
Edit view
}
procedure TMainForm.actEditViewExecute(Sender: TObject);
begin
actCreateViewExecute(Sender);
end;
procedure TMainForm.Copy2CSVExecute(Sender: TObject);
begin
// Copy data in actual dataset as CSV
if ChildWin.PageControlMain.ActivePage = ChildWin.tabData then
dataset2csv(ChildWin.GetVisualDataset(), ChildWin.prefCSVSeparator, ChildWin.prefCSVEncloser, ChildWin.prefCSVTerminator)
else if ChildWin.PageControlMain.ActivePage = ChildWin.tabQuery then
dataset2csv(ChildWin.GetVisualDataset(), ChildWin.prefCSVSeparator, ChildWin.prefCSVEncloser, ChildWin.prefCSVTerminator);
end;
procedure TMainForm.CopyHTMLtableExecute(Sender: TObject);
begin
// Copy data in actual dataset as HTML
if ChildWin.PageControlMain.ActivePage = ChildWin.tabData then
dataset2html(ChildWin.GetVisualDataset(), TZQuery(ChildWin.GetVisualDataset()).Sql.Text, '', ChildWin.prefConvertHTMLEntities, APPNAME + ' ' + FullAppVersion )
else if ChildWin.PageControlMain.ActivePage = ChildWin.tabQuery then
dataset2html(ChildWin.GetVisualDataset(), TZReadOnlyQuery(ChildWin.GetVisualDataset()).Sql.Text, '', ChildWin.prefConvertHTMLEntities, APPNAME + ' ' + FullAppVersion);
end;
procedure TMainForm.PrintListExecute(Sender: TObject);
var
page : TTabSheet;
begin
// print
page := ChildWin.PageControlMain.ActivePage;
if page.Name = 'SheetData' then
begin
// TODO: Print data
end
else if (page.Name = 'SheetQuery') then
begin
// TODO: Print data
end
else
printlistWindow(self);
end;
procedure TMainForm.CopyTableExecute(Sender: TObject);
begin
// copy table
CopyTableWindow(self);
end;
procedure TMainForm.ButtonOKClick(Sender: TObject);
var
reg: TRegistry;
begin
// Set auto limit option after manual checking "Limit"
reg := TRegistry.Create;
if reg.OpenKey( REGPATH, true ) then begin
reg.WriteBool( REGNAME_DATALIMIT, CheckboxLimit.Checked );
end;
FreeAndNil(reg);
ChildWin.DBTree.SetFocus;
ChildWin.viewdata(self);
end;
procedure TMainForm.LimitPanelEnter(Sender: TObject);
begin
// Entering Data-Toolbar
ButtonOK.Default := true;
end;
procedure TMainForm.LimitPanelExit(Sender: TObject);
begin
// Exiting Data-Toolbar
ButtonOK.Default := false;
end;
procedure TMainForm.focusWindow(Sender: TObject);
begin
ActivateWindow((Sender as TMenuItem).Tag);
end;
procedure TMainForm.menuConnectionsPopup(Sender: TObject);
var
i: integer;
s: string;
keep: boolean;
list: TWindowDataArray;
item: TMenuItem;
begin
// Delete dynamically added connection menu items.
for i := menuConnections.Items.Count - 1 downto 0 do begin
s := menuConnections.Items[i].Name;
SetLength(s, 2);
keep := false;
if s = 'mi' then keep := true;
if s = 'se' then keep := true;
if not keep then menuConnections.Items.Delete(i);
end;
// Check if all the heidisql windows are still alive.
CheckForCrashedWindows;
// Fetch list of heidisql windows.
list := GetWindowList;
// Add separator before 'open heidisql windows' section.
item := TMenuItem.Create(self);
item.Caption := '-';
menuConnections.Items.Add(item);
// TODO: Load "all" array with all connections
// Re-create dynamic menu items.
for i := 0 to High(list) do with list[i] do begin
// TODO: Remove connection with this UID from "all" array
item := TMenuItem.Create(self);
if namePostfix <> 0 then name := name + Format(' (%d)', [namePostFix]);
item.Caption := name;
if (appHandle = Handle) and (connected) then item.ImageIndex := ICON_MYSELF_CONNECTED
else if (appHandle = Handle) and (not connected) then item.ImageIndex := ICON_MYSELF_DISCONNECTED
else if (appHandle <> Handle) and (connected) then item.ImageIndex := ICON_OTHER_CONNECTED
else if (appHandle <> Handle) and (not connected) then item.ImageIndex := ICON_OTHER_DISCONNECTED;
item.Tag := appHandle;
item.OnClick := focusWindow;
menuConnections.Items.Add(item);
end;
end;
procedure TMainForm.OpenURL(Sender: TObject);
begin
// open url (hint)
if sender is TMenuItem then
ShellExec( TMenuItem(Sender).Hint )
else
ShellExec( TControl(Sender).Hint );
end;
// Escape database, table, field, index or key name.
function TMainform.mask(str: String) : String;
begin
if ActiveMDIChild = nil then
raise Exception.Create('Cannot mask SQL without active MDI');
result := ChildWin.mask(str);
end;
procedure TMainForm.ExportSettings1Click(Sender: TObject);
begin
// Export settings to .reg-file
if SaveDialog2.Execute then begin
if winexec(pchar('regedit.exe /e "'+SaveDialog2.FileName+'" HKEY_CURRENT_USER'+REGPATH), SW_SHOW) = ERROR_FILE_NOT_FOUND then
MessageDlg('File not found: regedit.exe', mtError, [mbOK], 0);
end;
end;
procedure TMainForm.Importsettings1Click(Sender: TObject);
begin
// Import settings from .reg-file
if OpenDialog2.Execute then begin
if winexec(pchar('regedit.exe "'+OpenDialog2.FileName+'"'), SW_SHOW) = ERROR_FILE_NOT_FOUND then
MessageDlg('File not found: regedit.exe', mtError, [mbOK], 0);
end;
end;
procedure TMainForm.ExecuteQueryExecute(Sender: TObject);
begin
ChildWin.ExecSqlClick(sender, false);
end;
procedure TMainForm.ExecuteSelectionExecute(Sender: TObject);
begin
ChildWin.ExecSqlClick(sender, true);
end;
procedure TMainForm.ExecuteLineExecute(Sender: TObject);
begin
ChildWin.ExecSqlClick(sender, false, true);
end;
procedure TMainForm.Copy2XMLExecute(Sender: TObject);
begin
// Copy data in actual dataset as XML
if ChildWin.PageControlMain.ActivePage = ChildWin.tabData then
dataset2xml(ChildWin.GetVisualDataset(), ChildWin.SelectedTable)
else if ChildWin.PageControlMain.ActivePage = ChildWin.tabQuery then
dataset2xml(ChildWin.GetVisualDataset(), 'SQL-query');
end;
procedure TMainForm.DBNavigator1BeforeAction(Sender: TObject;
Button: TNavigateBtn);
begin
if Button = nbdelete then begin
ChildWin.Delete1Click(sender);
abort;
end;
end;
procedure TMainForm.ExportDataExecute(Sender: TObject);
var
query : TDataSet;
begin
// Save data in current dataset as CSV, HTML or XML
case ChildWin.PageControlMain.ActivePageIndex of
3 : begin query := ChildWin.GetVisualDataset(){ZQuery2}; ChildWin.SaveDialogExportData.Filename := ChildWin.SelectedTable; end;
4 : begin query := ChildWin.GetVisualDataset() {ZQuery1}; ChildWin.SaveDialogExportData.Filename := 'SQL-query'; end;
else
raise Exception.Create('Internal error: Cannot fetch query with no related active tab.');
end;
with ChildWin.SaveDialogExportData do
begin
Title := 'Export result-set from '+Filename+'...';
FieldSep := ChildWin.prefCSVSeparator;
LineSep := ChildWin.prefCSVTerminator;
FieldEncl := ChildWin.prefCSVEncloser;
ConvertHTMLSpecialChars := ChildWin.prefConvertHTMLEntities;
if Execute and (FileName <> '') then
begin
Screen.Cursor := crHourGlass;
case FilterIndex of
1 : dataset2csv(query, FieldSep, FieldEncl, LineSep, Filename);
2 : dataset2html(query, FileName, FileName, ConvertHTMLSpecialChars, APPNAME+' '+FullAppVersion);
3 : dataset2xml(query, FileName, FileName);
end;
ChildWin.prefCSVSeparator := FieldSep;
ChildWin.prefCSVTerminator := LineSep;
ChildWin.prefCSVEncloser := FieldEncl;
ChildWin.prefConvertHTMLEntities := ConvertHTMLSpecialChars;
with TRegistry.Create do
begin
openkey(REGPATH, true);
WriteBool(REGNAME_CONVERTHTMLENTITIES, ConvertHTMLSpecialChars);
WriteString(REGNAME_CSV_SEPARATOR, FieldSep);
WriteString(REGNAME_CSV_ENCLOSER, FieldEncl);
WriteString(REGNAME_CSV_TERMINATOR, LineSep);
closekey();
Free;
end;
Screen.Cursor := crDefault;
end;
end;
end;
// view HTML
procedure TMainForm.HTMLviewExecute(Sender: TObject);
var
g : TTntDBGrid;
filename,extension : String;
f : Textfile;
buffer : array[0..MAX_PATH] of char;
begin
g := ChildWin.ActiveGrid;
if g = nil then begin messagebeep(MB_ICONASTERISK); exit; end;
if g.datasource.State = dsInactive then begin
messagebeep(MB_ICONASTERISK);
exit;
end;
Screen.Cursor := crHourGlass;
showstatus('Saving contents to file...', 2);
GetTempPath(MAX_PATH, buffer);
if g.SelectedField.IsBlob and (pos('JFIF', copy(g.SelectedField.AsString, 0, 20)) <> 0) then
extension := 'jpg'
else if g.SelectedField.IsBlob and StrCmpBegin('GIF', g.SelectedField.AsString) then
extension := 'gif'
else if g.SelectedField.IsBlob and StrCmpBegin('BM', g.SelectedField.AsString) then
extension := 'bmp'
else
extension := 'html';
filename := buffer;
filename := filename+'\'+APPNAME+'-preview.'+extension;
AssignFile(f, filename);
Rewrite(f);
Write(f, g.SelectedField.AsString);
CloseFile(f);
ShowStatus( STATUS_MSG_READY, 2 );
Screen.Cursor := crDefault;
ShellExec( filename );
end;
procedure TMainForm.InsertFilesExecute(Sender: TObject);
begin
InsertFilesWindow(Self);
end;
procedure TMainForm.ExportTablesExecute(Sender: TObject);
begin
// Export SQL
ExportTablesWindow (Self);
end;
procedure TMainForm.DataSearchExecute(Sender: TObject);
begin
with ChildWin.EditDataSearch do
begin
SetFocus;
SelectAll;
end;
end;
// Drop Table(s)
procedure TMainForm.DropTablesAndViewsExecute(Sender: TObject);
var
i : Integer;
Tables, Views : TStringList;
msg, sql, activeDB : String;
begin
debug('drop table activated');
// Set default database name to to ActiveDatabase.
// Can be overwritten when someone selects a table in dbtree from different database
activeDB := Childwin.ActiveDatabase;
Tables := TStringlist.Create;
Views := TStringlist.Create;
if Childwin.PageControlMain.ActivePage = Childwin.tabDatabase then begin
// Invoked from one of the various buttons, SheetDatabase is the active page, drop highlighted table(s).
Tables := GetVTCaptions(Childwin.ListTables, True, 0, NODETYPE_BASETABLE);
Views := GetVTCaptions(Childwin.ListTables, True, 0, NODETYPE_VIEW);
end else begin
// Invoked from one of the various buttons, drop table selected in tree view.
case Childwin.GetSelectedNodeType of
NODETYPE_BASETABLE: Tables.Add(Childwin.SelectedTable);
NODETYPE_VIEW: Views.Add(Childwin.SelectedTable)
end;
end;
// Fix actions temporarily enabled for popup menu.
Childwin.ValidateControls;
// Safety stop to avoid firing DROP TABLE without tablenames
if (Tables.Count = 0) and (Views.Count = 0) then
Exit;
// Ask user for confirmation to drop selected objects
msg := 'Drop ' + IntToStr(Tables.Count+Views.Count) + ' table(s) and/or view(s) in database "'+activeDB+'"?'
+ CRLF;
if Tables.Count > 0 then msg := msg + CRLF + 'Tables: ' + ImplodeStr(', ', Tables);
if Views.Count > 0 then msg := msg + CRLF + 'Views: ' + ImplodeStr(', ', Views);
if MessageDlg(msg, mtConfirmation, [mbok,mbcancel], 0) <> mrok then
Exit;
// Compose and run DROP TABLE query
if Tables.Count > 0 then begin
sql := 'DROP TABLE ';
for i := 0 to Tables.Count - 1 do
begin
if i > 0 then
sql := sql + ', ';
sql := sql + Childwin.mask(Tables[i]);
end;
Childwin.ExecUpdateQuery( sql );
end;
FreeAndNil(Tables);
// Compose and run DROP VIEW query
if Views.Count > 0 then begin
sql := 'DROP VIEW ';
for i := 0 to Views.Count - 1 do
begin
if i > 0 then
sql := sql + ', ';
sql := sql + Childwin.mask(Views[i]);
end;
Childwin.ExecUpdateQuery( sql );
end;
FreeAndNil(Views);
// Refresh ListTables + dbtree so the dropped tables are gone:
Childwin.MenuRefreshClick(Sender)
end;
// Load SQL-file, make sure that SheetQuery is activated
procedure TMainForm.LoadSQLExecute(Sender: TObject);
begin
ChildWin.PageControlMain.ActivePage := ChildWin.tabQuery;
ChildWin.btnQueryLoadClick( sender );
end;
{**
Init main registry object and open desired key
Outsoureced from GetRegValue() to avoid redundant code
in these 3 overloaded methods.
}
procedure TMainForm.OpenRegistry(Session: String = '');
var
folder : String;
begin
if regMain = nil then
regMain := TRegistry.Create;
folder := REGPATH;
if Session <> '' then
folder := folder + REGKEY_SESSIONS + Session;
if regMain.CurrentPath <> folder then
regMain.OpenKey(folder, false);
end;
{**
Read a numeric preference value from registry
}
function TMainForm.GetRegValue( valueName: String; defaultValue: Integer; Session: String = '' ) : Integer;
begin
result := defaultValue;
OpenRegistry(Session);
if regMain.ValueExists( valueName ) then
result := regMain.ReadInteger( valueName );
end;
{***
Read a boolean preference value from registry
@param string Name of the value
@param boolean Default-value to return if valueName was not found
@param string Subkey of REGPATH where to search for the value
}
function TMainForm.GetRegValue( valueName: String; defaultValue: Boolean; Session: String = '' ) : Boolean;
begin
result := defaultValue;
OpenRegistry(Session);
if regMain.ValueExists( valueName ) then
result := regMain.ReadBool( valueName );
end;
{***
Read a text preference value from registry
}
function TMainForm.GetRegValue( valueName: String; defaultValue: String; Session: String = '' ) : String;
begin
result := defaultValue;
OpenRegistry(Session);
if regMain.ValueExists( valueName ) then
result := regMain.ReadString( valueName );
end;
{***
Return active childwin if any
@return TMDIChild
}
function TMainForm.GetChildwin: TMDIChild;
begin
result := TMDIChild( ActiveMDIChild );
end;
{**
Parse commandline for a specific name=value pair
@return Boolean True if parameter was found, False if not
}
function TMainForm.GetParamValue(const paramChar: Char; const paramName:
string; var curIdx: Byte; out paramValue: string): Boolean;
var
i, nextIdx: Integer;
param, nextParam: string;
begin
paramValue := '';
param := ParamStr(curIdx);
// Example: --user=root --session="My session name" --password
if AnsiStartsStr('--'+paramName, param) then
begin
i := Length('--'+paramName) + 1;
if param[i] = '=' then
paramValue := MidStr(param, i+1, Length(param)+1);
if (AnsiLeftStr(paramValue, 1) = '"') and (AnsiRightStr(paramValue, 1) = '"') then
paramValue := MidStr(paramValue, 2, Length(paramValue));
result := True;
end else if AnsiStartsStr('-'+paramChar, param) then
begin
if Length(param) > 2 then
begin
// Example: -uroot -s"My session name"
paramValue := MidStr(param, 3, Length(param)+1);
if (AnsiLeftStr(paramValue, 1) = '"') and (AnsiRightStr(paramValue, 1) = '"') then
paramValue := MidStr(paramValue, 2, Length(paramValue));
end else
begin
// Example: -u root -s "My session name" -p
nextIdx := curIdx + 1;
if nextIdx <= ParamCount then begin
nextParam := ParamStr(nextIdx);
if not AnsiStartsStr('-', nextParam) then
paramValue := nextParam;
end;
end;
result := True;
end else
result := False;
end;
{**
Receive connection parameters and create the mdi-window
Paremeters are either sent by connection-form or by commandline.
}
function TMainform.CreateMDIChild(parHost, parPort, parUser, parPass, parDatabase, parTimeout, parCompress, parSortDatabases, parDescription: String): Boolean;
var
cp : TOpenConnProf;
mysqlconn : TMysqlConn;
f : TMDIChild;
begin
// fill structure
ZeroMemory (@cp,SizeOf(cp));
with cp do
begin
MysqlParams.Protocol := 'mysql';
MysqlParams.Host := Trim( parHost );
MysqlParams.Port := StrToIntDef(parPort, DEFAULT_PORT);
MysqlParams.Database := '';
MysqlParams.User := parUser;
MysqlParams.Pass := parPass;
// additional
if Integer(parCompress) > 0 then
MysqlParams.PrpCompress := 'true'
else
MysqlParams.PrpCompress := 'false';
MysqlParams.PrpTimeout := parTimeout;
MysqlParams.PrpDbless := 'true';
MysqlParams.PrpClientLocalFiles := 'true';
MysqlParams.PrpClientInteractive := 'true';
DatabaseList := parDatabase;
DatabaseListSort := Boolean(StrToIntDef(parSortDatabases, 0));
Description := parDescription;
end;
mysqlconn := TMysqlConn.Create(@cp);
// attempt to establish connection
case mysqlconn.Connect of
MCR_SUCCESS:
begin
// create child window and pass it the conn params and the opened connection
f := TMDIChild.Create(Application);
f.Init(@cp,mysqlconn); // childwin responsible to free mysqlconn
Result := True;
end;
else
// attempt failed -- show error
MessageDlg ( 'Could not establish connection! Details:'+CRLF+CRLF+mysqlconn.LastError, mtError, [mbOK], 0);
Result := False;
FreeAndNil (mysqlconn);
end;
ShowStatus( STATUS_MSG_READY, 2 );
end;
procedure TMainForm.menuUpdateCheckClick(Sender: TObject);
var
frm : TfrmUpdateCheck;
begin
frm := TfrmUpdateCheck.Create(Self);
frm.ShowModal;
FreeAndNil(frm);
end;
end.