Просмотр исходного кода

Custom Accelerators - CAccel and CAccels

git-svn-id: svn://svn.code.sf.net/p/ditto-cp/code/trunk@28 595ec19a-5cb4-439b-94a8-42fb3063c22c
ingenuus 22 лет назад
Родитель
Сommit
8619cfcffb
7 измененных файлов с 346 добавлено и 66 удалено
  1. 7 2
      Changes.txt
  2. 94 27
      MainTable.cpp
  3. 16 1
      MainTable.h
  4. 130 0
      Misc.cpp
  5. 48 0
      Misc.h
  6. 47 33
      QListCtrl.cpp
  7. 4 3
      QListCtrl.h

+ 7 - 2
Changes.txt

@@ -9,9 +9,14 @@
 
 + Ensure Connected to Clipboard Viewer Chain using dummy WM_DRAWCLIPBOARD
   CClipboardViewer:
-  *	OnCreate: SetTimer(TIMER_ENSURE_VIEWER_IN_CHAIN, ONE_MINUTE, 0);
+  * OnCreate: SetTimer(TIMER_ENSURE_VIEWER_IN_CHAIN, ONE_MINUTE, 0);
   * WM_CV_RECONNECT - force reconnect if not in chain.
-	* WM_CV_IS_CONNECTED - performs a ping of chain.
+  * WM_CV_IS_CONNECTED - performs a ping of chain.
+
++ Custom Accelerators
+  + Misc.h/.cpp: CAccel and CAccels
+  ! fixes bug "can't use accelerators with clip IDs > 65535 (USHRT_MAX)"
+    . this was due to win32 ACCEL only capable of handling a WORD cmd
 
 
 03 Sept 5

+ 94 - 27
MainTable.cpp

@@ -29,9 +29,10 @@ CMainTable::CMainTable(CDaoDatabase* pdb)
 	m_lDontAutoDelete = 0;
 	m_lTotalCopySize = 0;
 
-	m_nFields = 7;
+	m_nFieldCount = m_nFields = 7;
 	//}}AFX_FIELD_INIT
 	m_nDefaultType = dbOpenDynaset;
+	m_bBindFields = true;
 }
 
 CString CMainTable::GetDefaultDBName()
@@ -46,6 +47,9 @@ CString CMainTable::GetDefaultSQL()
 
 void CMainTable::DoFieldExchange(CDaoFieldExchange* pFX)
 {
+	// make sure this isn't called when we aren't using bound fields
+	VERIFY( m_bBindFields == true );
+
 	//{{AFX_FIELD_MAP(CMainTable)
 	pFX->SetFieldType(CDaoFieldExchange::outputColumn);
 	DFX_Long(pFX, _T("[lID]"), m_lID);
@@ -58,20 +62,38 @@ void CMainTable::DoFieldExchange(CDaoFieldExchange* pFX)
 	//}}AFX_FIELD_MAP
 }
 
-/////////////////////////////////////////////////////////////////////////////
-// CMainTable diagnostics
+void CMainTable::Open(int nOpenType , LPCTSTR lpszSql , int nOptions)
+{
+	OnQuery();
+	CDaoRecordset::Open(nOpenType, lpszSql, nOptions);
+}
 
-#ifdef _DEBUG
-void CMainTable::AssertValid() const
+void CMainTable::Requery()
 {
-	CDaoRecordset::AssertValid();
+	OnQuery();
+	CDaoRecordset::Requery();
 }
 
-void CMainTable::Dump(CDumpContext& dc) const
+
+/////////////////////////////////////////////////////////////////////////////
+// CMainTable member functions
+
+void CMainTable::OnQuery()
+{}
+
+bool CMainTable::SetBindFields(bool bVal)
 {
-	CDaoRecordset::Dump(dc);
+bool bOld = m_bBindFields;
+
+	m_bBindFields = bVal;
+
+	if(m_bBindFields)
+		m_nFields = m_nFieldCount;
+	else
+		m_nFields = 0;
+
+	return bOld;
 }
-#endif //_DEBUG
 
 // only deletes from Main
 BOOL CMainTable::DeleteAll()
@@ -92,7 +114,7 @@ BOOL CMainTable::DeleteAll()
 	return bRet;
 }
 
-HACCEL CMainTable::LoadAcceleratorKeys()
+void CMainTable::LoadAcceleratorKeys( CAccels& accels )
 {
 	CMainTable recset;
 
@@ -100,35 +122,65 @@ HACCEL CMainTable::LoadAcceleratorKeys()
 	{
 		recset.Open("SELECT * FROM Main WHERE lShortCut > 0");
 		
-		CArray<ACCEL, ACCEL> keys;
+		accels.StartBuildingTable();
 
+		CAccel a;
 		while(!recset.IsEOF())
 		{
-			ACCEL me;
-			me.cmd = (USHORT)recset.m_lID;
-			me.fVirt = 0;
-			if( HIBYTE(recset.m_lShortCut) & HOTKEYF_SHIFT )   me.fVirt |= FSHIFT;
-			if( HIBYTE(recset.m_lShortCut) & HOTKEYF_CONTROL ) me.fVirt |= FCONTROL;
-			if( HIBYTE(recset.m_lShortCut) & HOTKEYF_ALT )     me.fVirt |= FALT;	
-			me.fVirt |= FVIRTKEY;
-			me.key = LOBYTE(recset.m_lShortCut);
-
-			keys.Add(me);
+			a.Cmd = recset.m_lID;
+			a.Key = recset.m_lShortCut;
+			accels.AddAccel( a );
 
 			recset.MoveNext();
 		}
 
-		if(keys.GetSize() > 0)
-			return CreateAcceleratorTable(keys.GetData(), keys.GetSize());
+		accels.FinishBuildingTable();
 	}
 	catch(CDaoException* e)
 	{
 		e->Delete();
 	}
-
-	return NULL;
 }
 
+/*
+//HACCEL CMainTable::LoadAcceleratorKeys()
+//{
+//	CMainTable recset;
+//
+//	try
+//	{
+//		recset.Open("SELECT * FROM Main WHERE lShortCut > 0");
+//		
+//		CArray<ACCEL, ACCEL> keys;
+//
+//		while(!recset.IsEOF())
+//		{
+//			ACCEL me;
+//			me.cmd = (USHORT)recset.m_lID;
+//			me.fVirt = 0;
+//			if( HIBYTE(recset.m_lShortCut) & HOTKEYF_SHIFT )   me.fVirt |= FSHIFT;
+//			if( HIBYTE(recset.m_lShortCut) & HOTKEYF_CONTROL ) me.fVirt |= FCONTROL;
+//			if( HIBYTE(recset.m_lShortCut) & HOTKEYF_ALT )     me.fVirt |= FALT;	
+//			me.fVirt |= FVIRTKEY;
+//			me.key = LOBYTE(recset.m_lShortCut);
+//
+//			keys.Add(me);
+//
+//			recset.MoveNext();
+//		}
+//
+//		if(keys.GetSize() > 0)
+//			return CreateAcceleratorTable(keys.GetData(), keys.GetSize());
+//	}
+//	catch(CDaoException* e)
+//	{
+//		e->Delete();
+//	}
+//
+//	return NULL;
+//}
+*/
+
 void CMainTable::Open(LPCTSTR lpszFormat,...) 
 {
 	m_pDatabase = theApp.EnsureOpenDB();
@@ -141,5 +193,20 @@ void CMainTable::Open(LPCTSTR lpszFormat,...)
 	csText.FormatV(lpszFormat,vlist);
 	va_end(vlist);
 	
-	CDaoRecordset::Open(AFX_DAO_USE_DEFAULT_TYPE, csText, 0);
-}
+	Open(AFX_DAO_USE_DEFAULT_TYPE, csText, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CMainTable diagnostics
+
+#ifdef _DEBUG
+void CMainTable::AssertValid() const
+{
+	CDaoRecordset::AssertValid();
+}
+
+void CMainTable::Dump(CDumpContext& dc) const
+{
+	CDaoRecordset::Dump(dc);
+}
+#endif //_DEBUG

+ 16 - 1
MainTable.h

@@ -5,6 +5,7 @@
 #pragma once
 #endif // _MSC_VER > 1000
 #include "ArrayEx.h"
+#include "Misc.h"
 
 class CMainTable : public CDaoRecordset
 {
@@ -31,13 +32,27 @@ public:
 	virtual CString GetDefaultDBName();		// Default database name
 	virtual CString GetDefaultSQL();		// Default SQL for Recordset
 	virtual void DoFieldExchange(CDaoFieldExchange* pFX);  // RFX support
+	virtual void Open(int nOpenType = AFX_DAO_USE_DEFAULT_TYPE, LPCTSTR lpszSql = NULL, int nOptions = 0);
+	virtual void Requery();
 	//}}AFX_VIRTUAL
 
 public:
+	void OnQuery();
+
+	int		m_nFieldCount;
+	bool	m_bBindFields;
+	bool SetBindFields(bool bVal);
+
+//	long GetID();
+
+	int		m_nCurPos;
+	long	m_lCurID; // used to validate m_nCurPos
+
 	// only deletes from Main
 	static BOOL DeleteAll();
 	
-	static HACCEL LoadAcceleratorKeys();
+	static void LoadAcceleratorKeys( CAccels& accels );
+//	static HACCEL LoadAcceleratorKeys(); //!!!!!
 	void Open(LPCTSTR lpszFormat,...);
 
 // Implementation

+ 130 - 0
Misc.cpp

@@ -1353,3 +1353,133 @@ void GetMonitorRect(int iMonitor, LPRECT lpDestRect)
 	}
 }
 
+
+/*------------------------------------------------------------------*\
+	CAccel - an Accelerator (in-app hotkey)
+
+    - the win32 CreateAcceleratorTable using ACCEL was insufficient
+    because it only allowed a WORD for the cmd associated with it.
+\*------------------------------------------------------------------*/
+ 
+/*------------------------------------------------------------------*\
+	CAccels - Manages a set of CAccel
+\*------------------------------------------------------------------*/
+
+int CompareAccel( const void* pLeft, const void* pRight )
+{
+WORD w;
+int l,r;
+	// swap bytes: place the VirtualKey in the MSB and the modifier in the LSB
+	//  so that Accels based upon the same vkey are grouped together.
+	// this is required by our use of m_Index
+	// alternatively, we could store them this way in CAccel.
+	w = (WORD) ((CAccel*)pLeft)->Key;
+	l = (ACCEL_VKEY(w) << 8) | ACCEL_MOD(w);
+	w = (WORD) ((CAccel*)pRight)->Key;
+	r = (ACCEL_VKEY(w) << 8) | ACCEL_MOD(w);
+	return l - r;
+}
+
+CAccels::CAccels()
+{}
+
+void CAccels::AddAccel( CAccel& a )
+{
+	Add( a );
+}
+
+void CAccels::StartBuildingTable( bool bBigAndFast, int size )
+{
+	SetSize(0);
+
+	// m_Index is used as a fast hash table based upon the 1-byte vkey
+	if( bBigAndFast )
+		m_Index.SetSize( 256 );
+	else
+		m_Index.SetSize( 0 );
+
+int count = m_Index.GetCount();
+	for( int i=0; i < count; i++ )
+		m_Index[i] = NULL;
+}
+
+void CAccels::FinishBuildingTable()
+{
+int count = GetCount();
+	if( count <= 0 )
+		return;
+	// sort by key
+	qsort( GetData(), count, sizeof(CAccel), CompareAccel );
+
+CAccel* pAccel;
+int index;
+int idxCount = m_Index.GetCount();
+	if( idxCount != 256 )
+		return;
+	// setup m_Index hash table with each CAccel
+	for( int i=0; i < count; i++ )
+	{
+		pAccel = &GetAt(i);
+		index = ACCEL_VKEY( pAccel->Key );
+		// place the first accel for this vkey in the index
+		if( m_Index[index] == NULL )
+			m_Index[index] = pAccel;
+	}
+}
+
+CAccel* CAccels::OnMsg( MSG* pMsg )
+{
+	// bit 30 (0x40000000) is 1 if this is NOT the first msg of the key
+	//  i.e. auto-repeat may cause multiple msgs of the same key
+	if( (pMsg->lParam & 0x40000000) ||
+	    (pMsg->message != WM_KEYDOWN &&
+	     pMsg->message != WM_SYSKEYDOWN) )
+	{	return NULL; }
+
+int count = GetCount();
+	if( !pMsg || count <= 0 )
+		return NULL;
+
+BYTE vkey = LOBYTE(pMsg->wParam);
+BYTE mod  = GetKeyStateModifiers();
+DWORD key = ACCEL_MAKEKEY( vkey, mod );
+CAccel* pAccel;
+
+	// if we don't have an appropriately sized Index, do a binary search
+int idxCount = m_Index.GetCount();
+	if( idxCount != 256 )
+	{
+	CAccel a(key,0);
+		return (CAccel*) bsearch( &a, GetData(), count, sizeof(CAccel), CompareAccel );
+	}
+	// else we should have a valid m_Index hash table to use
+
+	pAccel = m_Index[ vkey ];
+
+	if( pAccel == NULL )
+		return NULL;
+
+CAccel* pLast = &GetAt(count-1);
+	// for each CAccel that matches vkey
+    while( vkey == ACCEL_VKEY(pAccel->Key) && pAccel <= pLast )
+	{
+		// if modifiers are also the same, then this is the key we are looking for
+		if( mod == ACCEL_MOD(pAccel->Key) )
+			return pAccel;
+		pAccel++;
+	}
+
+	return NULL;
+}
+
+BYTE GetKeyStateModifiers()
+{
+BYTE m=0;
+	if( GetKeyState(VK_SHIFT) & 0x8000 )
+		m |= HOTKEYF_SHIFT;
+	if( GetKeyState(VK_CONTROL) & 0x8000 )
+		m |= HOTKEYF_CONTROL;
+	if( GetKeyState(VK_MENU) & 0x8000 )
+		m |= HOTKEYF_ALT;
+	return m;
+}

+ 48 - 0
Misc.h

@@ -293,4 +293,52 @@ public:
 
 extern CHotKeys g_HotKeys;
 
+/*------------------------------------------------------------------*\
+	CAccel - an Accelerator (in-app hotkey)
+
+    - the win32 CreateAcceleratorTable using ACCEL was insufficient
+    because it only allowed a WORD for the cmd associated with it.
+\*------------------------------------------------------------------*/
+
+#define ACCEL_VKEY(key)			LOBYTE(key)
+#define ACCEL_MOD(key)			HIBYTE(key)
+#define ACCEL_MAKEKEY(vkey,mod) ((mod << 8) | vkey)
+
+class CAccel
+{
+public:
+	DWORD	Key; // directly uses the CHotKeyCtrl format
+	DWORD	Cmd;
+	CAccel( DWORD key=0, DWORD cmd=0 ) { Key = key;  Cmd = cmd; } 
+};
+
+/*------------------------------------------------------------------*\
+	CAccels - Manages a set of CAccel
+\*------------------------------------------------------------------*/
+class CAccels : public CArray<CAccel>
+{
+public:
+	CArray<CAccel*>	m_Index;
+
+	CAccels();
+
+	void Clear() { SetSize(0); }
+	void AddAccel( CAccel& a );
+
+	// "bBigAndFast" means:
+	// - big: about 1024 bytes larger
+	// - fast: approx. constant speed lookup versus binary search
+	void StartBuildingTable( bool bBigAndFast = true, int size = -1 );
+	void FinishBuildingTable();
+
+	// handles a key's first WM_KEYDOWN or WM_SYSKEYDOWN message.
+	// it uses GetKeyState to test for modifiers.
+	// returns a pointer to the internal CAccel if it matches the given key or NULL
+	CAccel* OnMsg( MSG* pMsg );
+};
+
+// returns a BYTE representing the current GetKeyState modifiers:
+//  HOTKEYF_SHIFT, HOTKEYF_CONTROL, HOTKEYF_ALT
+BYTE GetKeyStateModifiers();
+
 #endif // !defined(AFX_CP_GUI_GLOBALS__FBCDED09_A6F2_47EB_873F_50A746EBC86B__INCLUDED_)

+ 47 - 33
QListCtrl.cpp

@@ -45,7 +45,7 @@ CQListCtrl::CQListCtrl()
 	m_SmallFont = CreateFontIndirect(&lf);
 
 	m_bShowTextForFirstTenHotKeys = true;
-	m_Accelerator = NULL;
+//	m_Accelerator = NULL; //!!!!!
 }
 
 CQListCtrl::~CQListCtrl()
@@ -516,16 +516,22 @@ int CQListCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
 
 BOOL CQListCtrl::PreTranslateMessage(MSG* pMsg) 
 {
-	if(m_Accelerator)
-	{
-		m_CheckingAccelerator = true;
-		if(TranslateAccelerator(m_hWnd, m_Accelerator, pMsg) != 0)
-		{
-			m_CheckingAccelerator = false;
-			return TRUE;
-		}
-		m_CheckingAccelerator = false;
-	}
+	/* !!!!!
+	//if(m_Accelerator)
+	//{
+	//	m_CheckingAccelerator = true;
+	//	if(TranslateAccelerator(m_hWnd, m_Accelerator, pMsg) != 0)
+	//	{
+	//		m_CheckingAccelerator = false;
+	//		return TRUE;
+	//	}
+	//	m_CheckingAccelerator = false;
+	//}
+	*/
+
+	CAccel* pAccel = m_Accels.OnMsg( pMsg );
+	if( pAccel )
+		GetParent()->SendMessage(NM_SELECT_DB_ID, pAccel->Cmd, 0);
 
 	switch(pMsg->message) 
 	{
@@ -632,27 +638,35 @@ void CQListCtrl::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
 
 void CQListCtrl::DestroyAndCreateAccelerator(BOOL bCreate)
 {
-	if(m_Accelerator)
-	{
-		DestroyAcceleratorTable(m_Accelerator);
-		m_Accelerator = NULL;
-	}
-	
-	if(bCreate)
-		m_Accelerator = CMainTable::LoadAcceleratorKeys();
+// !!!!!!
+	//if(m_Accelerator)
+	//{
+	//	DestroyAcceleratorTable(m_Accelerator);
+	//	m_Accelerator = NULL;
+	//}
+	//
+	//if(bCreate)
+	//	m_Accelerator = CMainTable::LoadAcceleratorKeys();
+
+	m_Accels.Clear();
+
+	if( bCreate )
+		CMainTable::LoadAcceleratorKeys( m_Accels );
 }
 
-BOOL CQListCtrl::OnCommand(WPARAM wParam, LPARAM lParam) 
-{
-	//return 1 if from accelerator
-	if((HIWORD(wParam) == 1) && (m_CheckingAccelerator))
-	{
-		USHORT usPasteID = LOWORD(wParam);
-
-		GetParent()->SendMessage(NM_SELECT_DB_ID, usPasteID, 0);
-
-		return TRUE;
-	}
-	
-	return CListCtrl::OnCommand(wParam, lParam);
-}
+/* !!!!!
+//BOOL CQListCtrl::OnCommand(WPARAM wParam, LPARAM lParam) 
+//{
+//	//return 1 if from accelerator
+//	if((HIWORD(wParam) == 1) && (m_CheckingAccelerator))
+//	{
+//		USHORT usPasteID = LOWORD(wParam);
+//
+//		GetParent()->SendMessage(NM_SELECT_DB_ID, usPasteID, 0);
+//
+//		return TRUE;
+//	}
+//	
+//	return CListCtrl::OnCommand(wParam, lParam);
+//}
+*/

+ 4 - 3
QListCtrl.h

@@ -50,7 +50,7 @@ public:
 	public:
 	virtual int OnToolHitTest(CPoint point, TOOLINFO * pTI) const;
 	virtual BOOL PreTranslateMessage(MSG* pMsg);
-	virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
+//	virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam); //!!!!!!
 	//}}AFX_VIRTUAL
 
 // Implementation
@@ -85,8 +85,9 @@ protected:
 	BOOL  m_bShowTextForFirstTenHotKeys;
 
 	//Accelerator
-	HACCEL	m_Accelerator;
-	bool	m_CheckingAccelerator;
+	CAccels	m_Accels;
+//	HACCEL	m_Accelerator; // !!!!!
+//	bool	m_CheckingAccelerator;
 	
 	// Generated message map functions
 protected: