Browse Source

Added right click menu options for groups to add, delete and properties

git-svn-id: svn://svn.code.sf.net/p/ditto-cp/code/trunk@740 595ec19a-5cb4-439b-94a8-42fb3063c22c
sabrogden 10 years ago
parent
commit
ff33e71584
7 changed files with 292 additions and 73 deletions
  1. 10 0
      CP_Main.rc
  2. 89 2
      GroupTree.cpp
  3. 7 0
      GroupTree.h
  4. 3 0
      QListCtrl.h
  5. 168 68
      QPasteWnd.cpp
  6. 8 1
      QPasteWnd.h
  7. 7 2
      Resource.h

+ 10 - 0
CP_Main.rc

@@ -351,6 +351,16 @@ BEGIN
     END
 END
 
+IDR_MENU_GROUPS MENU
+BEGIN
+    POPUP "Menu"
+    BEGIN
+        MENUITEM "New Sub Group",               ID_MENU_NEWGROUP32896
+        MENUITEM "Delete Group",                ID_MENU_DELETEGROUP
+        MENUITEM "Properties",                  ID_MENU_PROPERTIES32898
+    END
+END
+
 
 /////////////////////////////////////////////////////////////////////////////
 //

+ 89 - 2
GroupTree.cpp

@@ -30,11 +30,16 @@ BEGIN_MESSAGE_MAP(CGroupTree, CTreeCtrl)
 	//{{AFX_MSG_MAP(CGroupTree)
 	ON_WM_CREATE()
 	ON_NOTIFY_REFLECT(TVN_SELCHANGED, OnSelchanged)
+	ON_NOTIFY_REFLECT(NM_RCLICK, OnRclickQuickPaste)
 	ON_WM_KILLFOCUS()
 	ON_WM_ACTIVATE()
 	ON_NOTIFY_REFLECT(NM_DBLCLK, OnDblclk)
 	ON_NOTIFY_REFLECT(TVN_KEYDOWN, OnKeydown)
+	ON_WM_RBUTTONDOWN()
 	//}}AFX_MSG_MAP
+	ON_COMMAND(ID_MENU_NEWGROUP32896, &CGroupTree::OnMenuNewgroup32896)
+	ON_COMMAND(ID_MENU_DELETEGROUP, &CGroupTree::OnMenuDeletegroup)
+	ON_COMMAND(ID_MENU_PROPERTIES32898, &CGroupTree::OnMenuProperties32898)
 END_MESSAGE_MAP()
 
 
@@ -129,7 +134,7 @@ void CGroupTree::OnSelchanged(NMHDR* pNMHDR, LRESULT* pResult)
 //		::SendMessage(m_NotificationWnd, NM_GROUP_TREE_MESSAGE, GetItemData(pNMTreeView->itemNew.hItem), 0);
 //	}
 	
-	*pResult = 0;
+	//*pResult = 0;
 }
 
 void CGroupTree::OnKillFocus(CWnd* pNewWnd) 
@@ -216,4 +221,86 @@ bool CGroupTree::AddNode(CString csText, int id)
 	SetItemData(hItem, id);
 
 	return true;
-}
+}
+
+void CGroupTree::OnRButtonDown(UINT nFlags, CPoint point)
+{
+	UINT nHitFlags = 0;
+	HTREEITEM hClickedItem = HitTest(point, &nHitFlags);
+
+	if (nHitFlags&TVHT_ONITEM)
+		if (GetSelectedCount() < 2)
+			SelectItem(hClickedItem);
+
+	CTreeCtrl::OnRButtonDown(nFlags, point);
+}
+
+UINT CGroupTree::GetSelectedCount() const
+{
+	// Only visible items should be selected!
+	UINT uCount = 0;
+	for (HTREEITEM hItem = GetRootItem(); hItem != NULL; hItem = GetNextVisibleItem(hItem))
+		if (GetItemState(hItem, TVIS_SELECTED) & TVIS_SELECTED)
+			uCount++;
+
+	return uCount;
+}
+
+void CGroupTree::OnRclickQuickPaste(NMHDR *pNMHDR, LRESULT *pResult)
+{
+	POINT pp;
+	CMenu cmPopUp;
+	CMenu *cmSubMenu = NULL;
+
+	GetCursorPos(&pp);
+	if (cmPopUp.LoadMenu(IDR_MENU_GROUPS) != 0)
+	{
+		cmSubMenu = cmPopUp.GetSubMenu(0);
+		if (!cmSubMenu)
+		{
+			return;
+		}
+		
+		cmSubMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON, pp.x, pp.y, this, NULL);
+	}
+
+	*pResult = 0;
+}
+
+void CGroupTree::OnMenuNewgroup32896()
+{
+	HTREEITEM hItem = GetSelectedItem();
+	if (hItem)
+	{
+		int id = (int) GetItemData(hItem);
+		::PostMessage(m_NotificationWnd, NM_NEW_GROUP, id, 0);		
+	}
+}
+
+
+void CGroupTree::OnMenuDeletegroup()
+{
+	HTREEITEM hItem = GetSelectedItem();
+	if (hItem)
+	{
+		int id = (int) GetItemData(hItem);
+		if (id >= 0)
+		{
+			::PostMessage(m_NotificationWnd, NM_DELETE_ID, id, 0);
+		}
+	}
+}
+
+
+void CGroupTree::OnMenuProperties32898()
+{
+	HTREEITEM hItem = GetSelectedItem();
+	if (hItem)
+	{
+		int id = (int) GetItemData(hItem);
+		if (id >= 0)
+		{
+			::PostMessage(m_NotificationWnd, NM_SHOW_PROPERTIES, id, 0);
+		}
+	}	
+}

+ 7 - 0
GroupTree.h

@@ -31,6 +31,7 @@ public:
 protected:
 	void FillTree(int parentId, HTREEITEM hParent);
 	void SendToParent(int parentId);
+	UINT GetSelectedCount() const;
 
 	HWND m_NotificationWnd;
 	CBitmap m_bmOpenFolder;
@@ -57,6 +58,8 @@ protected:
 	afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized);
 	afx_msg void OnDblclk(NMHDR* pNMHDR, LRESULT* pResult);
 	afx_msg void OnKeydown(NMHDR* pNMHDR, LRESULT* pResult);
+	afx_msg void OnRclickQuickPaste(NMHDR *pNMHDR, LRESULT *pResult);
+	afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
 	//}}AFX_MSG
 
 	DECLARE_MESSAGE_MAP()
@@ -64,6 +67,10 @@ protected:
 	//{{AFX_DISPATCH(CGroupTree)
 		// NOTE - the ClassWizard will add and remove member functions here.
 	//}}AFX_DISPATCH
+public:
+	afx_msg void OnMenuNewgroup32896();
+	afx_msg void OnMenuDeletegroup();
+	afx_msg void OnMenuProperties32898();
 };
 
 /////////////////////////////////////////////////////////////////////////////

+ 3 - 0
QListCtrl.h

@@ -38,6 +38,9 @@
 #define NM_SHOW_HIDE_SCROLLBARS		WM_USER+0x122
 #define NM_CANCEL_SEARCH			WM_USER+0x123
 #define NM_POST_OPTIONS_WINDOW		WM_USER+0x124
+#define NM_SHOW_PROPERTIES			WM_USER+0x125
+#define NM_NEW_GROUP				WM_USER+0x126
+#define NM_DELETE_ID				WM_USER+0x127
 
 
 #define COPY_BUFFER_HOT_KEY_1_ID	-100

+ 168 - 68
QPasteWnd.cpp

@@ -217,6 +217,9 @@ ON_COMMAND(ID_COMPARE_COMPARE, &CQPasteWnd::OnCompareCompare)
 ON_COMMAND(ID_COMPARE_SELECTLEFTCOMPARE, &CQPasteWnd::OnCompareSelectleftcompare)
 ON_COMMAND(ID_COMPARE_COMPAREAGAINST, &CQPasteWnd::OnCompareCompareagainst)
 ON_UPDATE_COMMAND_UI(ID_COMPARE_COMPARE, &CQPasteWnd::OnUpdateCompareCompare)
+ON_MESSAGE(NM_SHOW_PROPERTIES, OnShowProperties)
+ON_MESSAGE(NM_NEW_GROUP, OnNewGroup)
+ON_MESSAGE(NM_DELETE_ID, OnDeleteId)
 END_MESSAGE_MAP()
 
 
@@ -329,6 +332,9 @@ int CQPasteWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
 	m_actions.AddAccel(ActionEnums::SELECTIONDOWN, VK_DOWN);
 	m_actions.AddAccel(ActionEnums::MOVEFIRST, VK_HOME);
 	m_actions.AddAccel(ActionEnums::MOVELAST, VK_END);
+	m_actions.AddAccel(ActionEnums::BACKGRROUP, VK_BACK);
+	m_actions.AddAccel(ActionEnums::PASTE_SELECTED, VK_RETURN);
+	m_actions.AddAccel(ActionEnums::DELETE_SELECTED, VK_DELETE);
 
 	m_actions.AddAccel(ActionEnums::SHOWDESCRIPTION, VK_F3);
 	m_actions.AddAccel(ActionEnums::NEXTDESCRIPTION, 'N');
@@ -345,10 +351,9 @@ int CQPasteWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
 	m_actions.AddAccel(ActionEnums::EDITCLIP, ACCEL_MAKEKEY('E', HOTKEYF_CONTROL));	
 	m_actions.AddAccel(ActionEnums::CANCELFILTER, ACCEL_MAKEKEY('C', HOTKEYF_ALT));
 	m_actions.AddAccel(ActionEnums::HOMELIST, VK_HOME);
-	m_actions.AddAccel(ActionEnums::BACKGRROUP, VK_BACK);
+	
 	m_actions.AddAccel(ActionEnums::TOGGLESHOWPERSISTANT, ACCEL_MAKEKEY(VK_SPACE, HOTKEYF_CONTROL));
-	m_actions.AddAccel(ActionEnums::PASTE_SELECTED, VK_RETURN);
-	m_actions.AddAccel(ActionEnums::DELETE_SELECTED, VK_DELETE);
+	
 	m_actions.AddAccel(ActionEnums::CLIP_PROPERTIES, ACCEL_MAKEKEY(VK_RETURN, HOTKEYF_ALT));
 	m_actions.AddAccel(ActionEnums::PASTE_SELECTED_PLAIN_TEXT, ACCEL_MAKEKEY(VK_RETURN, HOTKEYF_SHIFT));
 	m_actions.AddAccel(ActionEnums::COMPARE_SELECTED_CLIPS, ACCEL_MAKEKEY(VK_F2, HOTKEYF_CONTROL));
@@ -746,12 +751,8 @@ BOOL CQPasteWnd::OpenIndex(int item)
     return OpenID(m_lstHeader.GetItemData(item), pasteOptions);
 }
 
-BOOL CQPasteWnd::NewGroup(bool bGroupSelection)
+BOOL CQPasteWnd::NewGroup(bool bGroupSelection, int parentId)
 {
-    //Get the selected ids
-    CClipIDs IDs;
-    m_lstHeader.GetSelectionItemData(IDs);
-
     CGroupName Name;
     CString csName("");
 
@@ -773,7 +774,7 @@ BOOL CQPasteWnd::NewGroup(bool bGroupSelection)
         }
     }
 
-    int id = NewGroupID(theApp.GetValidGroupID(), csName);
+    int id = NewGroupID(parentId, csName);
 
     if(id <= 0)
     {
@@ -787,6 +788,8 @@ BOOL CQPasteWnd::NewGroup(bool bGroupSelection)
         return TRUE;
     }
 
+	CClipIDs IDs;
+	m_lstHeader.GetSelectionItemData(IDs);
     IDs.MoveTo(id);
     theApp.EnterGroupID(id);
     return TRUE;
@@ -2440,55 +2443,63 @@ void CQPasteWnd::DeleteSelectedRows()
     {
         return ;
     }
-
-    POSITION pos = m_lstHeader.GetFirstSelectedItemPosition();
-    int nFirstSel = m_lstHeader.GetNextSelectedItem(pos);
+	   
 
     m_lstHeader.GetSelectionItemData(IDs);
     m_lstHeader.GetSelectionIndexes(Indexs);
 
-    IDs.DeleteIDs(true, theApp.m_db);
+	DeleteClips(IDs, Indexs);   
+}
+
+bool CQPasteWnd::DeleteClips(CClipIDs &IDs, ARRAY &Indexs)
+{
+	POSITION pos = m_lstHeader.GetFirstSelectedItemPosition();
+	int nFirstSel = m_lstHeader.GetNextSelectedItem(pos);
+
+	IDs.DeleteIDs(true, theApp.m_db);
 
-    Indexs.SortDescending();
-    INT_PTR count = Indexs.GetSize();
+	Indexs.SortDescending();
+	INT_PTR count = Indexs.GetSize();
 
-    int erasedCount = 0;
+	int erasedCount = 0;
 
-    {
+	{
 		ATL::CCritSecLock csLock(m_CritSection.m_sect);
 
-        for(int i = 0; i < count; i++)
-        {
-			if(Indexs[i] < (int)m_listItems.size())
+		for (int i = 0; i < count; i++)
+		{
+			if (Indexs[i] < (int) m_listItems.size())
 			{
-				m_listItems.erase(m_listItems.begin( ) + Indexs[i]);
-                erasedCount++;
+				m_listItems.erase(m_listItems.begin() + Indexs[i]);
+				erasedCount++;
 
 				CF_DibTypeMap::iterator iterDib = m_cf_dibCache.find(m_lstHeader.GetItemData(Indexs[i]));
-				if(iterDib != m_cf_dibCache.end())
+				if (iterDib != m_cf_dibCache.end())
 				{
 					m_cf_dibCache.erase(iterDib);
 				}
 
 				CF_DibTypeMap::iterator iterRtf = m_cf_rtfCache.find(m_lstHeader.GetItemData(Indexs[i]));
-				if(iterRtf != m_cf_rtfCache.end())
+				if (iterRtf != m_cf_rtfCache.end())
 				{
 					m_cf_rtfCache.erase(iterRtf);
 				}
-            }
-        }
-    }
+			}
+		}
+	}
 
-    m_lstHeader.SetItemCountEx(m_lstHeader.GetItemCount() - erasedCount);
-	
-    // if there are no items after the one we deleted, then select the last one.
-    if(nFirstSel >= m_lstHeader.GetItemCount())
-    {
-        nFirstSel = m_lstHeader.GetItemCount() - 1;
-    }
+	m_lstHeader.SetItemCountEx(m_lstHeader.GetItemCount() - erasedCount);
+
+	// if there are no items after the one we deleted, then select the last one.
+	if (nFirstSel >= m_lstHeader.GetItemCount())
+	{
+		nFirstSel = m_lstHeader.GetItemCount() - 1;
+	}
 
-    m_lstHeader.SetListPos(nFirstSel);
+	m_lstHeader.SetListPos(nFirstSel);
 	UpdateStatus();
+
+	return true;
 }
 
 CString CQPasteWnd::LoadDescription(int nItem)
@@ -2810,14 +2821,14 @@ bool CQPasteWnd::DoActionShowMenu()
 
 bool CQPasteWnd::DoActionNewGroup()
 {
-	NewGroup(false);
+	NewGroup(false, theApp.GetValidGroupID());
 
 	return true;
 }
 
 bool CQPasteWnd::DoActionNewGroupSelection()
 {
-	NewGroup(true);
+	NewGroup(true, theApp.GetValidGroupID());
 
 	return true;
 }
@@ -3077,6 +3088,8 @@ bool CQPasteWnd::DoActionDeleteSelected()
 
 bool CQPasteWnd::DoActionClipProperties()
 {
+	bool ret = false;
+
 	if(::GetFocus() == m_lstHeader.GetSafeHwnd())
 	{
 		m_bHideWnd = false;
@@ -3088,7 +3101,7 @@ bool CQPasteWnd::DoActionClipProperties()
 		INT_PTR size = IDs.GetSize();
 		if(size < 1)
 		{
-			return false;
+			return ret;
 		}
 
 		int id = IDs[0];
@@ -3096,61 +3109,92 @@ bool CQPasteWnd::DoActionClipProperties()
 
 		if(id < 0)
 		{
-			return false;
+			return ret;
 		}
 
 		m_lstHeader.RemoveAllSelection();
 		m_lstHeader.SetSelection(row);
 
-		CCopyProperties props(id, this);
-		INT_PTR doModalRet = props.DoModal();
+		ret = ShowProperties(id, row);
+
+		m_lstHeader.SetListPos(row);
+	}
 
-		if(doModalRet == IDOK)
+	return false;
+}
+
+bool CQPasteWnd::ShowProperties(int id, int row)
+{
+	m_bHideWnd = false;	
+
+	if (id < 0)
+	{
+		return false;
+	}
+
+	CCopyProperties props(id, this);
+	INT_PTR doModalRet = props.DoModal();
+
+	if (doModalRet == IDOK)
+	{
 		{
-			{
-				ATL::CCritSecLock csLock(m_CritSection.m_sect);
+			ATL::CCritSecLock csLock(m_CritSection.m_sect);
 
-				if(row < (int)m_listItems.size())
+			if(row < 0)
+			{
+				bool selectedItem = false;
+				int index = 0;
+				std::vector<CMainTable>::iterator iter = m_listItems.begin();
+				while (iter != m_listItems.end())
 				{
-					CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT * FROM Main WHERE lID = %d"), id);
-					if(!q.eof())
+					if (iter->m_lID == id)
 					{
-						FillMainTable(m_listItems[row], q);
+						row = index;
+						break;
 					}
+					iter++;
+					index++;
 				}
+			}
 
-				CF_DibTypeMap::iterator iterDib = m_cf_dibCache.find(id);
-				if(iterDib != m_cf_dibCache.end())
-				{
-					m_cf_dibCache.erase(iterDib);
-				}
-
-				CF_DibTypeMap::iterator iterRtf = m_cf_rtfCache.find(id);
-				if(iterRtf != m_cf_rtfCache.end())
+			if (row >= 0 &&
+				row < (int) m_listItems.size())
+			{
+				CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT * FROM Main WHERE lID = %d"), id);
+				if (!q.eof())
 				{
-					m_cf_rtfCache.erase(iterRtf);
+					FillMainTable(m_listItems[row], q);
 				}
 			}
 
-			m_thread.FireLoadAccelerators();
-
-			m_lstHeader.RefreshVisibleRows();
+			CF_DibTypeMap::iterator iterDib = m_cf_dibCache.find(id);
+			if (iterDib != m_cf_dibCache.end())
+			{
+				m_cf_dibCache.erase(iterDib);
+			}
 
-			if(props.m_lGroupChangedTo >= 0)
+			CF_DibTypeMap::iterator iterRtf = m_cf_rtfCache.find(id);
+			if (iterRtf != m_cf_rtfCache.end())
 			{
-				CSpecialPasteOptions pasteOptions;
-				OpenID(props.m_lGroupChangedTo, pasteOptions);
+				m_cf_rtfCache.erase(iterRtf);
 			}
+		}
+
+		m_thread.FireLoadAccelerators();
+
+		m_lstHeader.RefreshVisibleRows();
 
-			m_lstHeader.SetFocus();
-			m_lstHeader.SetListPos(row);
+		if (props.m_lGroupChangedTo >= 0)
+		{
+			CSpecialPasteOptions pasteOptions;
+			OpenID(props.m_lGroupChangedTo, pasteOptions);
 		}
 
-		m_bHideWnd = true;
-		return true;
+		m_lstHeader.SetFocus();		
 	}
 
-	return false;
+	m_bHideWnd = true;
+	return true;
 }
 
 bool CQPasteWnd::DoActionPasteSelectedPlainText()
@@ -4519,3 +4563,59 @@ void CQPasteWnd::UpdateMenuShortCut(CCmdUI *pCmdUI, DWORD action)
 		pCmdUI->SetText(cs);
 	}
 }
+
+LRESULT CQPasteWnd::OnShowProperties(WPARAM wParam, LPARAM lParam)
+{
+	return ShowProperties((int) wParam, -1);
+}
+
+LRESULT CQPasteWnd::OnNewGroup(WPARAM wParam, LPARAM lParam)
+{
+	NewGroup(false, (int) wParam);
+
+	return TRUE;
+}
+
+LRESULT CQPasteWnd::OnDeleteId(WPARAM wParam, LPARAM lParam)
+{
+	if (g_Opt.GetPromptWhenDeletingClips())
+	{
+		bool bStartValue = m_bHideWnd;
+		m_bHideWnd = false;
+
+		int nRet = MessageBox(theApp.m_Language.GetString("Delete_Clip_Groups", "Delete Group?"), _T("Ditto"), MB_YESNO);
+
+		m_bHideWnd = bStartValue;
+
+		if (nRet == IDNO)
+		{
+			return FALSE;
+		}
+	}
+
+	CClipIDs IDs;
+	ARRAY Indexs;
+
+	IDs.Add((int) wParam);
+
+	bool selectedItem = false;
+	int index = 0;
+	{
+		ATL::CCritSecLock csLock(m_CritSection.m_sect);
+		std::vector<CMainTable>::iterator iter = m_listItems.begin();
+		while (iter != m_listItems.end())
+		{
+			if (iter->m_lID == wParam)
+			{
+				Indexs.Add(index);
+				break;
+			}
+			iter++;
+			index++;
+		}
+	}
+
+	DeleteClips(IDs, Indexs);
+
+	return TRUE;
+}

+ 8 - 1
QPasteWnd.h

@@ -16,6 +16,7 @@
 #include "editwithbutton.h"
 #include "GdipButton.h"
 #include "SpecialPasteOptions.h"
+#include "ClipIds.h"
 
 class CMainTable
 {
@@ -173,7 +174,7 @@ public:
 	BOOL OpenID(int id, CSpecialPasteOptions pasteOptions);
 	BOOL OpenSelection(CSpecialPasteOptions pasteOptions);
     BOOL OpenIndex(int item);
-    BOOL NewGroup(bool bGroupSelection = true);
+	BOOL NewGroup(bool bGroupSelection = true, int parentId = -1);
 
     CString LoadDescription(int nItem);
     bool SaveDescription(int nItem, CString text);
@@ -246,6 +247,9 @@ public:
 
 	void UpdateMenuShortCut(CCmdUI *pCmdUI, DWORD action);
 
+	bool ShowProperties(int id, int row);
+	bool DeleteClips(CClipIDs &IDs, ARRAY &Indexs);
+
     // Generated message map functions
 protected:
     //{{AFX_MSG(CQPasteWnd)
@@ -392,4 +396,7 @@ public:
 	afx_msg void OnCompareSelectleftcompare();
 	afx_msg void OnCompareCompareagainst();
 	afx_msg void OnUpdateCompareCompare(CCmdUI *pCmdUI);
+	afx_msg LRESULT OnShowProperties(WPARAM wParam, LPARAM lParam);
+	afx_msg LRESULT OnNewGroup(WPARAM wParam, LPARAM lParam);
+	afx_msg LRESULT OnDeleteId(WPARAM wParam, LPARAM lParam);
 };

+ 7 - 2
Resource.h

@@ -105,6 +105,8 @@
 #define IDB_PNG8                        237
 #define IDB_STICKY_16_16                237
 #define IDR_DESC_OPTIONS_MENU           238
+#define IDR_MENU1                       239
+#define IDR_MENU_GROUPS                 239
 #define IDC_PATH                        1000
 #define IDC_GET_PATH                    1001
 #define IDC_SELECT_SOUND                1002
@@ -485,14 +487,17 @@
 #define ID_COMPARE_SELECTLEFTCOMPARE    32893
 #define ID_COMPARE_COMPAREAGAINST       32894
 #define ID_COMPARE_COMPARE              32895
+#define ID_MENU_NEWGROUP32896           32896
+#define ID_MENU_DELETEGROUP             32897
+#define ID_MENU_PROPERTIES32898         32898
 
 // Next default values for new objects
 // 
 #ifdef APSTUDIO_INVOKED
 #ifndef APSTUDIO_READONLY_SYMBOLS
 #define _APS_3D_CONTROLS                     1
-#define _APS_NEXT_RESOURCE_VALUE        239
-#define _APS_NEXT_COMMAND_VALUE         32896
+#define _APS_NEXT_RESOURCE_VALUE        240
+#define _APS_NEXT_COMMAND_VALUE         32899
 #define _APS_NEXT_CONTROL_VALUE         2125
 #define _APS_NEXT_SYMED_VALUE           101
 #endif