diff host/tools/pkgadmin/win32/PkgAdminDlg.cpp @ 76:435cced73e2f ecos-v1_3_1-release

eCos v1.3.1 merged from eCos master repository on 2000-03-27-23:22:51-BST
author jlarmour
date Tue, 28 Mar 2000 14:10:45 +0000
parents
children d397fc472bcf
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/host/tools/pkgadmin/win32/PkgAdminDlg.cpp
@@ -0,0 +1,604 @@
+//####COPYRIGHTBEGIN####
+//                                                                          
+// ----------------------------------------------------------------------------
+// Copyright (C) 1998, 1999, 2000 Red Hat, Inc.
+//
+// This program is part of the eCos host tools.
+//
+// This program 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 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.  See the GNU General Public License for 
+// more details.
+// 
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 
+// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//
+// ----------------------------------------------------------------------------
+//                                                                          
+//####COPYRIGHTEND####
+// PkgAdminDlg.cpp : implementation file
+//
+
+#include "stdafx.h"
+
+#include "PkgAdmin.h"
+#include "PkgAdminDlg.h"
+#include "PkgAdminLicenseDlg.h"
+#include "PkgAdminTclWaitDlg.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CPkgadminAboutDlg dialog used for App About
+
+class CPkgadminAboutDlg : public CDialog
+{
+public:
+	CPkgadminAboutDlg();
+
+// Dialog Data
+	//{{AFX_DATA(CPkgadminAboutDlg)
+	enum { IDD = IDD_PKGADMIN_ABOUTBOX };
+	//}}AFX_DATA
+
+	// ClassWizard generated virtual function overrides
+	//{{AFX_VIRTUAL(CPkgadminAboutDlg)
+	protected:
+	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
+	//}}AFX_VIRTUAL
+
+// Implementation
+protected:
+	//{{AFX_MSG(CPkgadminAboutDlg)
+	//}}AFX_MSG
+	DECLARE_MESSAGE_MAP()
+};
+
+CPkgadminAboutDlg::CPkgadminAboutDlg() : CDialog(CPkgadminAboutDlg::IDD)
+{
+	//{{AFX_DATA_INIT(CPkgadminAboutDlg)
+	//}}AFX_DATA_INIT
+}
+
+void CPkgadminAboutDlg::DoDataExchange(CDataExchange* pDX)
+{
+	CDialog::DoDataExchange(pDX);
+	//{{AFX_DATA_MAP(CPkgadminAboutDlg)
+	//}}AFX_DATA_MAP
+}
+
+BEGIN_MESSAGE_MAP(CPkgadminAboutDlg, CDialog)
+	//{{AFX_MSG_MAP(CPkgadminAboutDlg)
+		// No message handlers
+	//}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CPkgAdminDlg dialog
+
+CPkgAdminDlg::CPkgAdminDlg(LPCTSTR pszRepository,LPCTSTR pszUserTools)
+	: CeCosDialog(CPkgAdminDlg::IDD, NULL)
+{
+	//{{AFX_DATA_INIT(CPkgAdminDlg)
+		// NOTE: the ClassWizard will add member initialization here
+	//}}AFX_DATA_INIT
+	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
+  if(pszRepository){
+	  m_strRepository=pszRepository;
+  }
+  if(pszUserTools){
+	  m_strUserTools=pszUserTools;
+  }
+	m_CdlPkgData = NULL;
+}
+
+void CPkgAdminDlg::DoDataExchange(CDataExchange* pDX)
+{
+	CeCosDialog::DoDataExchange(pDX);
+	//{{AFX_DATA_MAP(CPkgAdminDlg)
+	DDX_Control(pDX, IDC_PKGADMIN_REMOVE, m_btnRemove);
+	DDX_Control(pDX, IDC_PKGADMIN_TREE, m_ctrlPackageTree);
+	//}}AFX_DATA_MAP
+}
+
+BEGIN_MESSAGE_MAP(CPkgAdminDlg, CeCosDialog)
+	//{{AFX_MSG_MAP(CPkgAdminDlg)
+	ON_WM_SYSCOMMAND()
+	ON_WM_PAINT()
+	ON_WM_QUERYDRAGICON()
+	ON_BN_CLICKED(IDC_PKGADMIN_REMOVE, OnPkgadminRemove)
+	ON_WM_DESTROY()
+	ON_BN_CLICKED(IDC_PKGADMIN_ADD, OnPkgadminAdd)
+	ON_BN_CLICKED(IDCLOSE, OnClose)
+	ON_BN_CLICKED(IDC_PKGADMIN_REPOSITORY, OnPkgadminRepository)
+	ON_NOTIFY(TVN_SELCHANGED, IDC_PKGADMIN_TREE, OnSelchangedPkgadminTree)
+	//}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CPkgAdminDlg message handlers
+
+BOOL CPkgAdminDlg::OnInitDialog()
+{
+	CeCosDialog::OnInitDialog();
+
+  if(this==AfxGetApp()->m_pMainWnd){ // only if the dialog is the application
+	  // Add "About..." menu item to system menu.
+
+	  // IDM_ABOUTBOX must be in the system command range.
+	  ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
+	  ASSERT(IDM_ABOUTBOX < 0xF000);
+
+	  CMenu* pSysMenu = GetSystemMenu(FALSE);
+	  if (pSysMenu != NULL)
+	  {
+		  CString strAboutMenu;
+		  strAboutMenu.LoadString(IDS_ABOUTBOX);
+		  if (!strAboutMenu.IsEmpty())
+		  {
+			  pSysMenu->AppendMenu(MF_SEPARATOR);
+			  pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
+		  }
+	  }
+
+	// Set the icon for this dialog.  The framework does this automatically
+	//  when the application's main window is not a dialog
+	  SetIcon(m_hIcon, TRUE);			// Set big icon
+	
+  // The following AppWizard-generated call was causing the 32x32 icon to
+	//  be resized for use as a 16x16 icon. Removing the call causes the
+	//  correct 16x16 icon to be displayed.
+//	SetIcon(m_hIcon, FALSE);		// Set small icon
+	
+	// TODO: Add extra initialization here
+
+	// setup the repsoitory location
+
+	  if (m_strRepository.IsEmpty()) // if the repository cannot be located
+	  {
+		  OnPkgadminRepository (); // prompt the user for the repository location
+		  if (m_strRepository.IsEmpty ())
+		  {
+			  PostMessage (WM_COMMAND,IDCANCEL);
+			  return TRUE;
+		  }
+	  }
+  } else {
+    GetDlgItem(IDC_PKGADMIN_REPOSITORY)->ShowWindow(SW_HIDE);
+  }
+
+	// setup the path to the user tools (tar and gunzip)
+
+	if ((! m_strUserTools.IsEmpty()) || FindUserToolsPath ()) // if the user tools can be located
+	{
+		// add the user tools to the PATH environment variable
+		const DWORD nLength = GetEnvironmentVariable (_T("PATH"), NULL, 0) + 1;
+		TCHAR * pszOldPath  = new TCHAR [nLength];
+		GetEnvironmentVariable (_T("PATH"), pszOldPath, nLength);
+		SetEnvironmentVariable (_T("PATH"), CString (pszOldPath) + _T(";") + m_strUserTools);
+		delete [] pszOldPath;
+	}
+
+	// setup the package tree image list
+
+	m_ilTreeIcons.Create (IDB_PKGADMIN_TREEICONS, 16, 1, RGB (0,128,128));
+	m_ctrlPackageTree.SetImageList (&m_ilTreeIcons, TVSIL_NORMAL);
+
+	// populate the package tree
+
+	while (! PopulatePackageTree (m_strRepository))
+	{
+		m_strRepository = _T("");
+		OnPkgadminRepository (); // prompt the user for the repository location
+		if (m_strRepository.IsEmpty ()) // if dialog was cancelled
+		{
+			PostQuitMessage (1);
+			return TRUE;
+		}
+	}
+	
+	return TRUE;  // return TRUE  unless you set the focus to a control
+}
+
+void CPkgAdminDlg::OnSysCommand(UINT nID, LPARAM lParam)
+{
+	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
+	{
+		CPkgadminAboutDlg dlgAbout;
+		dlgAbout.DoModal();
+	}
+	else
+	{
+		CeCosDialog::OnSysCommand(nID, lParam);
+	}
+}
+
+// If you add a minimize button to your dialog, you will need the code below
+//  to draw the icon.  For MFC applications using the document/view model,
+//  this is automatically done for you by the framework.
+
+void CPkgAdminDlg::OnPaint() 
+{
+	if (IsIconic())
+	{
+		CPaintDC dc(this); // device context for painting
+
+		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
+
+		// Center icon in client rectangle
+		int cxIcon = GetSystemMetrics(SM_CXICON);
+		int cyIcon = GetSystemMetrics(SM_CYICON);
+		CRect rect;
+		GetClientRect(&rect);
+		int x = (rect.Width() - cxIcon + 1) / 2;
+		int y = (rect.Height() - cyIcon + 1) / 2;
+
+		// Draw the icon
+		dc.DrawIcon(x, y, m_hIcon);
+	}
+	else
+	{
+		CeCosDialog::OnPaint();
+	}
+}
+
+// The system calls this to obtain the cursor to display while the user drags
+//  the minimized window.
+HCURSOR CPkgAdminDlg::OnQueryDragIcon()
+{
+	return (HCURSOR) m_hIcon;
+}
+
+bool CPkgAdminDlg::PopulatePackageTree(LPCTSTR pszPackagesPath)
+{
+	// delete any existing CDL database
+
+	if (m_CdlPkgData)
+	{
+		delete m_CdlPkgData;
+		m_CdlPkgData = NULL;
+	}
+
+	// load the package database
+
+	try
+	{
+        m_CdlPkgData = CdlPackagesDatabaseBody::make (UnicodeToStdStr (pszPackagesPath));
+	}
+	catch (CdlStringException exception)
+	{
+		CString strMessage;
+		strMessage.Format (_T("Error loading database:\n\n%s"), CString (exception.get_message ().c_str ()));
+		AfxMessageBox (strMessage);
+		return false;
+	}
+	catch (...)
+	{
+		AfxMessageBox (_T("Error loading database"));
+		return false;
+	}
+
+	// clear the old package tree
+
+	ClearPackageTree ();
+
+	// populate the new package tree
+
+	const std::vector<std::string>& packages = m_CdlPkgData->get_packages ();
+	for (std::vector<std::string>::const_iterator package = packages.begin (); package != packages.end (); package++)
+	{
+		// add a package node
+
+		TRACE (_T("Adding package %s:"), CString (package->c_str ()));
+		HTREEITEM hPackage = m_ctrlPackageTree.InsertItem (CString (m_CdlPkgData->get_package_aliases (*package) [0].c_str ()));
+		m_ctrlPackageTree.SetItemData (hPackage, (DWORD) new CString (package->c_str ()));
+		m_ctrlPackageTree.SetItemImage (hPackage, 0, 0);
+
+		const std::vector<std::string>& versions = m_CdlPkgData->get_package_versions (* package);
+		for (std::vector<std::string>::const_iterator version = versions.begin (); version != versions.end (); version++)
+		{
+			// add a version node
+
+			TRACE (_T(" %s"), CString (version->c_str ()));
+			const HTREEITEM hVersion = m_ctrlPackageTree.InsertItem (CString (version->c_str ()), hPackage);
+			m_ctrlPackageTree.SetItemImage (hVersion, 1, 1);
+		}
+		TRACE (_T("\n"));
+		m_ctrlPackageTree.SortChildren (hPackage); // sort the version nodes
+	}
+
+	m_ctrlPackageTree.SortChildren (NULL); // sort the package nodes
+
+  if(this==AfxGetApp()->m_pMainWnd){ // if the dialog is the application
+	  // update the caption bar
+	  CString strCaption (m_strRepository);
+	  strCaption.Replace (_TCHAR('/'), _TCHAR('\\'));
+	  strCaption += _T(" - eCos Package Administration Tool");
+	  SetWindowText (strCaption);
+  }
+
+	return true;
+}
+
+void CPkgAdminDlg::OnPkgadminRemove() 
+{
+	const HTREEITEM hTreeItem = m_ctrlPackageTree.GetSelectedItem ();
+	if (! hTreeItem)
+		return;
+
+	if (IDYES != CWnd::MessageBox (_T("The selected package will be deleted from the repository. Core eCos packages may be restored only by reinstalling eCos.\n\nDo you wish to continue?"),
+		_T("Remove Package"), MB_YESNO | MB_ICONEXCLAMATION))
+		return;
+
+	const CString * pstrPackage = (const CString *) m_ctrlPackageTree.GetItemData (hTreeItem);
+	if (pstrPackage) // if a package node is selected
+	{
+		// remove all package version nodes
+
+		bool bStatus = true;
+		HTREEITEM hChildItem = m_ctrlPackageTree.GetChildItem (hTreeItem);
+		while (hChildItem && bStatus)
+		{
+			const HTREEITEM hNextChildItem = m_ctrlPackageTree.GetNextSiblingItem (hChildItem);			
+			bStatus = RemovePackageVersion (hChildItem);
+			hChildItem = hNextChildItem;
+		}
+
+		// remove the package node
+
+		if (bStatus)
+		{
+			delete pstrPackage;
+			m_ctrlPackageTree.DeleteItem (hTreeItem);
+		}
+	}
+	else // a version node is selected
+	{
+		// remove the version node
+
+		const HTREEITEM hParentItem = m_ctrlPackageTree.GetParentItem (hTreeItem);
+		ASSERT (hParentItem);
+		if (RemovePackageVersion (hTreeItem) && ! m_ctrlPackageTree.ItemHasChildren (hParentItem)) // if the only version was deleted
+		{
+			// remove the package node
+
+			delete pstrPackage;
+			m_ctrlPackageTree.DeleteItem (hParentItem); 
+		}
+	}
+}
+
+void CPkgAdminDlg::ClearPackageTree()
+{
+	HTREEITEM hPackage = m_ctrlPackageTree.GetRootItem ();
+	if (! hPackage) // if no packages in the tree...
+		return;     // ...nothing to do
+
+	while (hPackage)
+	{
+		const HTREEITEM hNextPackage = m_ctrlPackageTree.GetNextSiblingItem (hPackage);
+		TRACE (_T("Deleting package %s\n"), * ((CString *) m_ctrlPackageTree.GetItemData (hPackage)));
+		delete (CString *) m_ctrlPackageTree.GetItemData (hPackage);
+		m_ctrlPackageTree.DeleteItem (hPackage);
+		hPackage = hNextPackage;
+	}
+}
+
+void CPkgAdminDlg::OnDestroy() 
+{
+	CeCosDialog::OnDestroy();
+	
+	// free memory allocated to the tree item data CStrings
+
+	ClearPackageTree ();
+
+	// free memory allocated to the CDL database
+
+	if (m_CdlPkgData)
+		delete m_CdlPkgData;
+
+}
+
+void CPkgAdminDlg::OnPkgadminAdd() 
+{
+	CFileDialog dlg (TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT, _T("eCos Package Files (*.epk)|*.epk||"), GetParent ());
+	TCHAR szBuffer [MAX_PATH * 16] = _T("");
+	dlg.m_ofn.lpstrFile = szBuffer;
+	dlg.m_ofn.nMaxFile = MAX_PATH * 16;
+	dlg.m_ofn.lpstrTitle = _T("Open eCos Package Files");
+	if (IDOK == dlg.DoModal ())
+	{
+		bool bRepositoryChanged = false;
+		POSITION posPathName = dlg.GetStartPosition ();
+		while (posPathName)
+		{
+			// get an eCos package distribution file
+
+			CString strPathName = dlg.GetNextPathName (posPathName);
+
+			// extract the licence file
+
+			CString strCommand;
+			strCommand.Format (_T("add %s --extract_license"), strPathName);
+			strCommand.Replace (_TCHAR('\\'), _TCHAR('/')); // backslashes -> forward slashes for Tcl_EvalFile
+			EvalTclFile (3, strCommand);
+			CString strLicenseFile = m_strRepository + _T("/pkgadd.txt");
+			strLicenseFile.Replace (_TCHAR('/'), _TCHAR('\\')); // forward slashes -> backslashes for Win32
+
+			// read the license file
+
+			CFile fileLicenseFile;
+			if (fileLicenseFile.Open (strLicenseFile, CFile::modeRead))
+			{
+				TRACE (_T("License file found at %s\n"), strLicenseFile);
+				const DWORD dwLicenseLength = fileLicenseFile.GetLength ();
+				char * pszBuffer = new char [dwLicenseLength + 1]; // allocate a buffer
+				fileLicenseFile.Read (pszBuffer, dwLicenseLength);
+				fileLicenseFile.Close ();
+				CFile::Remove (strLicenseFile); // delete the license file when read
+				pszBuffer [dwLicenseLength] = NULL; // terminate the string in the buffer
+				CString strLicenseText (pszBuffer); // copy into a CString to convert to Unicode
+				delete [] pszBuffer;
+				if (-1 == strLicenseText.Find (_T("\r\n"))) // if the file has LF line endings...
+					strLicenseText.Replace (_T("\n"), _T("\r\n")); // ... replace with CRLF line endings
+
+				// display the license text
+
+				CPkgAdminLicenseDlg dlgLicense (this);
+				dlgLicense.m_strLicense = strLicenseText;
+				dlgLicense.SetCaption (strPathName + _T(" - Add Packages"));
+				if (IDOK != dlgLicense.DoModal ()) // if license not accepted by user
+					continue; // try the next file
+			}
+
+			// add the contents of the package distribution file
+
+			strCommand.Format (_T("add %s --accept_license"), strPathName);
+			strCommand.Replace (_TCHAR('\\'), _TCHAR('/')); // backslashes -> forward slashes for Tcl_EvalFile
+			if (! EvalTclFile (3, strCommand))  // if not successful
+				continue; // try the next file
+
+			bRepositoryChanged = true;
+		}
+
+		// refresh the package tree only if necessary
+
+		if (bRepositoryChanged && ! PopulatePackageTree (m_strRepository))
+			DestroyWindow ();
+	}
+}
+
+bool CPkgAdminDlg::EvalTclFile(int nArgc, LPCTSTR pszArgv)
+{
+	CPkgAdminTclWaitDlg dlgWait;
+
+	TRACE (_T("Evaluating ecosadmin.tcl %s\n"), pszArgv);
+
+	// set up the data structure which is passed to the Tcl thread
+
+	CString strArgc;
+	strArgc.Format (_T("%d"), nArgc);
+	std::string argv0 = UnicodeToStdStr (m_strRepository) + "/ecosadmin.tcl";
+	std::string argv = UnicodeToStdStr (pszArgv);
+	std::string argc = UnicodeToStdStr (strArgc);
+	dlgWait.etsInfo.argv0 = (char *) argv0.c_str ();
+	dlgWait.etsInfo.argv = (char *) argv.c_str ();
+	dlgWait.etsInfo.argc = (char *) argc.c_str ();
+
+	// display the 'please wait' dialog
+	// the Tcl command is invoked from CPkgAdminTclWaitDlg::OnCreate()
+
+	CWaitCursor curWait;
+	dlgWait.DoModal ();
+	curWait.Restore ();
+
+	// retrieve status information from the data structure
+
+	int nStatus = dlgWait.etsInfo.status;
+	CString strErrorMessage (dlgWait.etsInfo.result);
+
+	// report any error
+
+	if (! strErrorMessage.IsEmpty ())
+	{
+		AfxMessageBox (_T("Command execution error:\n\n") + strErrorMessage);
+		return false;
+	}
+	else if (TCL_OK != nStatus)
+	{
+		AfxMessageBox (_T("Command execution error"));
+		return false;
+	}
+
+	return true;
+}
+
+bool CPkgAdminDlg::RemovePackageVersion(HTREEITEM hTreeItem)
+{
+	const HTREEITEM hParentItem = m_ctrlPackageTree.GetParentItem (hTreeItem);
+	ASSERT (hParentItem);
+	CString * pstrPackage = (CString *) m_ctrlPackageTree.GetItemData (hParentItem);
+	ASSERT (pstrPackage);
+	CString strCommand;
+	strCommand.Format (_T("remove %s --version %s"), * pstrPackage, m_ctrlPackageTree.GetItemText (hTreeItem));
+	if (! EvalTclFile (3, strCommand)) // if not successful
+		return false;
+	
+	m_ctrlPackageTree.DeleteItem (hTreeItem); // remove the selected item from the tree
+	return true;
+}
+
+void CPkgAdminDlg::OnPkgadminRepository() 
+{
+	CFileDialog dlg (TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, _T("eCos Package Database Files (ecos.db)|ecos.db||"), GetParent ());
+	dlg.m_ofn.lpstrTitle = _T("Open eCos Package Database File");
+
+	int nStatus;
+	do
+	{
+		nStatus = dlg.DoModal ();
+		if (IDOK == nStatus)
+		{
+			const CString strPathName = dlg.GetPathName ();
+			const int nPathNameIndex = strPathName.ReverseFind (_TCHAR('\\'));
+			ASSERT (nPathNameIndex != -1);
+			m_strRepository = strPathName.Mid (0, nPathNameIndex);
+			m_strRepository.Replace (_TCHAR('\\'), _TCHAR('/'));
+		}
+	}
+	while ((IDOK == nStatus) && ! PopulatePackageTree (m_strRepository));
+}
+
+void CPkgAdminDlg::OnSelchangedPkgadminTree(NMHDR*, LRESULT* pResult) 
+{
+	//NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
+
+	// enable the remove button only if a node is selected
+	m_btnRemove.EnableWindow (NULL != m_ctrlPackageTree.GetSelectedItem ());
+
+	*pResult = 0;
+}
+
+std::string CPkgAdminDlg::UnicodeToStdStr(LPCTSTR str)
+{
+	int nLength = 1 + _tcslen (str);
+	char * pszString = new char [nLength];
+
+	#ifdef _UNICODE
+	WideCharToMultiByte (CP_ACP, 0, str, -1, pszString, nLength, NULL, NULL);
+	#else
+	strcpy (pszString, str);
+	#endif
+
+	std::string stdstr = std::string (pszString);
+	delete [] pszString;
+	return stdstr;
+}
+
+bool CPkgAdminDlg::FindUserToolsPath()
+{
+	HKEY hKey;
+	if (ERROR_SUCCESS != RegOpenKeyEx (HKEY_CURRENT_USER, _T("Software\\Red Hat\\eCos\\Configuration Tool\\User Tools"), 0, KEY_READ, &hKey))
+		return false;
+	
+	TCHAR szBuffer [MAX_PATH + 1];
+	DWORD dwBufferLength = MAX_PATH + 1;
+	LONG lStatus = RegQueryValueEx (hKey, _T("Folder"), NULL, NULL, (LPBYTE) szBuffer, &dwBufferLength);
+	RegCloseKey (hKey);
+	if (ERROR_SUCCESS != lStatus)
+		return false;
+
+	m_strUserTools = szBuffer;
+	TRACE (_T("User tools found at %s\n"), m_strUserTools);
+	return ! m_strUserTools.IsEmpty ();
+}