浏览代码

HistoryStartTop and Popups

git-svn-id: svn://svn.code.sf.net/p/ditto-cp/code/trunk@29 595ec19a-5cb4-439b-94a8-42fb3063c22c
ingenuus 22 年之前
父节点
当前提交
90a62e85fd
共有 14 个文件被更改,包括 560 次插入91 次删除
  1. 16 13
      CP_Main.rc
  2. 40 11
      Changes.txt
  3. 41 34
      CopyProperties.cpp
  4. 1 0
      CopyProperties.h
  5. 199 0
      Misc.cpp
  6. 69 0
      Misc.h
  7. 4 1
      OptionsQuickPaste.cpp
  8. 3 0
      OptionsQuickPaste.h
  9. 8 5
      ProcessCopy.cpp
  10. 91 16
      QListCtrl.cpp
  11. 16 1
      QListCtrl.h
  12. 65 9
      QPasteWnd.cpp
  13. 4 0
      QPasteWnd.h
  14. 3 1
      Resource.h

+ 16 - 13
CP_Main.rc

@@ -277,28 +277,31 @@ CAPTION "Quick Paste"
 FONT 8, "MS Sans Serif", 0, 0, 0x0
 BEGIN
     CONTROL         "Enable Quick Paste Transparency",IDC_TRANSPARENCY,
-                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,18,30,122,10
-    EDITTEXT        IDC_TRANS_PERC,140,29,19,12,ES_AUTOHSCROLL
-    LTEXT           "%",IDC_STATIC,162,32,8,8
-    LTEXT           "Text Lines per Row",IDC_STATIC,18,45,62,8
-    EDITTEXT        IDC_LINES_ROW,82,43,19,12,ES_AUTOHSCROLL
-    GROUPBOX        "Positioning",IDC_STATIC,11,58,196,41
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,18,41,122,10
+    EDITTEXT        IDC_TRANS_PERC,140,40,19,12,ES_AUTOHSCROLL
+    LTEXT           "%",IDC_STATIC,162,43,8,8
+    LTEXT           "Text Lines per Row",IDC_STATIC,18,56,62,8
+    EDITTEXT        IDC_LINES_ROW,82,54,19,12,ES_AUTOHSCROLL
+    GROUPBOX        "Positioning",IDC_STATIC,11,69,196,41
     CONTROL         "At Caret",IDC_AT_CARET,"Button",BS_AUTORADIOBUTTON,22,
-                    67,41,10
+                    78,41,10
     CONTROL         "At Cursor",IDC_AT_CURSOR,"Button",BS_AUTORADIOBUTTON,22,
-                    77,45,10
+                    88,45,10
     CONTROL         "At Previous Position",IDC_AT_PREVIOUS,"Button",
-                    BS_AUTORADIOBUTTON,22,87,79,10
+                    BS_AUTORADIOBUTTON,22,98,79,10
     CONTROL         "Use Ctrl - Num for first ten copy hot keys",
                     IDC_CTRL_CLICK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,18,
-                    8,142,10
+                    19,142,10
     CONTROL         "Show text for first ten copy hot keys",
                     IDC_SHOW_TEXT_FOR_FIRST_TEN_HOT_KEYS,"Button",
-                    BS_AUTOCHECKBOX | WS_TABSTOP,18,18,128,10
+                    BS_AUTOCHECKBOX | WS_TABSTOP,18,29,128,10
     LTEXT           "** before text  = text has a hot key assigned to it.",
-                    IDC_STATIC,18,106,153,8
+                    IDC_STATIC,18,117,153,8
     LTEXT           "* before text = text will never be auto deleted.",
-                    IDC_STATIC,18,117,143,8
+                    IDC_STATIC,18,128,143,8
+    CONTROL         "History Starts at the Top of the list (vs. Bottom)",
+                    IDC_HISTORY_START_TOP,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,18,8,161,10
 END
 
 IDD_OPTIONS_KEYSTROKES DIALOG  0, 0, 186, 90

+ 40 - 11
Changes.txt

@@ -1,7 +1,34 @@
+03 Sept 10
+----------
++ HistoryStartTop - History can be shown top-down or bottom-up
+  + CGetSetOptions::m_bHistoryStartTop, SetHistoryStartTop, GetHistoryStartTop
+  + BOOL CQPasteWnd::m_bAscending - sort ascending (true) or descending (false)
+  * OptionsQuickPaste - added checkbox
+  * CQListCtrl::
+    + BOOL m_bStartTop - start at the top (true) or the bottom (false)
+    + BOOL SetListPos( int index ) - moves cursor to a single index position
+    + int GetFirstTenNum( int index ) - convert index to number
+    + int GetFirstTenIndex( int num ) - convert number to index
+    * OnCustomdrawList() - draws FirstTen block top or bottom
+    * PreTranslateMessage() - handles FirstTen block (1-9,0) accelerators
+    
++ CPopup - Manually display tooltips (Misc.h/.cpp)
+
++ View Full Description by pressing F3 using CPopup
+  CQListCtrl::
+    + CPopup m_Popup
+    + OnKillFocus - removes Popup
+    * PreTranslateMessage - handles F3 key
+
+* CopyProperties
+  + CTokenizer (ASCII only) (Misc.h/.cpp)
+  * Multiple delimiter characters are used rather than a single separator string.
+  * Defaults to Focus on Description when not a NamedCopy
+  * Uses CPopup for status
+
+
 03 Sept 8
 ---------
-* Moved SendPaste to CCP_Main to implicitly use Target
-
 + static bool CDataTable::DeleteParent( long lParentID )
   . Deletes all records in Data Table with the given "lParentID"
   
@@ -18,6 +45,8 @@
   ! 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
 
+* Moved SendPaste to CCP_Main to implicitly use Target
+
 
 03 Sept 5
 ---------
@@ -55,6 +84,15 @@
 
 03 Sept 2
 ---------
++ ShowPersistent: always-on-top "persistent show"
+  . Toggled by double clicking the titlebar or <Ctrl>-<Space>
+  + theApp.ShowPersistent( bool bVal )
+  * required modification of Target window tracking system.
+
++ Misc.h/.cpp
+  + Utility functions (HGLOBAL funcs, IsAppWnd, StrF)
+  + Debug functions (Log, SetThreadName)
+
 * ProcessCopy - redesigned to fix the fast copy bugs.
   . The same source object is used for the description as is stored in the db.
   . Actual copying from the clipboard is (usually) not interrupted.
@@ -80,11 +118,6 @@
   + void OnPasteCompleted();
   + void SetStatus(char*) // for displaying status in the titlebar.
 
-+ ShowPersistent: always-on-top "persistent show"
-  . Toggled by double clicking the titlebar or <Ctrl>-<Space>
-  + theApp.ShowPersistent( bool bVal )
-  * required modification of Target window tracking system.
-
 * Targeting the previous focus window
   + CCP_MainApp::
     + HWND m_hTargetWnd;
@@ -136,10 +169,6 @@
     . Handle in CMainFrame::OnHotKey()
     . Control user input in OptionsKeyBoard.
 
-+ Misc.h/.cpp
-  + Utility functions (HGLOBAL funcs, IsAppWnd, StrF)
-  + Debug functions (Log, SetThreadName)
-
 
 03 Aug 8
 --------

+ 41 - 34
CopyProperties.cpp

@@ -45,6 +45,7 @@ void CCopyProperties::DoDataExchange(CDataExchange* pDX)
 	//}}AFX_DATA_MAP
 	DDX_Control(pDX, IDC_PARSE_EDIT, m_ParseEdit);
 	DDX_Control(pDX, IDC_PARSE_BUTTON, m_ParseButton);
+	DDX_Control(pDX, IDC_EDIT_DISPLAY_TEXT, m_DisplayTextEdit);
 }
 
 
@@ -105,11 +106,14 @@ BOOL CCopyProperties::OnInitDialog()
 
 	UpdateData(FALSE);
 
-
 	SetWindowPos(&CWnd::wndTopMost, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
 
-	m_HotKey.SetFocus();
-	
+	// if this is a result of a NamedCopy, focus on the hotkey
+	if( theApp.m_bShowCopyProperties )
+		m_HotKey.SetFocus();
+	else
+		m_DisplayTextEdit.SetFocus();
+
 	return FALSE;
 }
 
@@ -264,17 +268,26 @@ void CCopyProperties::OnCancel()
 
 void CCopyProperties::OnBnClickedParseButton()
 {
-CString sep;
-	m_ParseEdit.GetWindowText(sep);
-	sep = RemoveEscapes( sep );
-
-int nSep = sep.GetLength();
-	if( nSep <= 0 )
+//RECT rcScreen;
+//	SystemParametersInfo (SPI_GETWORKAREA, 0, &rcScreen, 0);
+CPoint pos(0,0);
+	ClientToScreen(&pos);
+CPopup status( pos );
+	status.Show("Parsing...");
+
+CString delims;
+	m_ParseEdit.GetWindowText(delims);
+	delims = RemoveEscapes( delims );
+
+	// validate delimiters
+int nDelims = delims.GetLength();
+	if( nDelims <= 0 )
 	{
-		::MessageBox( m_hWnd, "Token Separator is not valid.", "Parse Failure", MB_OK|MB_ICONINFORMATION );
+		::MessageBox( m_hWnd, "Token Delimiters are not valid.", "Parse Failure", MB_OK|MB_ICONINFORMATION );
 		return;
 	}
 
+	// load the text
 HGLOBAL hGlobal = CClip::LoadFormat( m_lCopyID, CF_TEXT );
 	if( !hGlobal )
 	{
@@ -282,10 +295,11 @@ HGLOBAL hGlobal = CClip::LoadFormat( m_lCopyID, CF_TEXT );
 		return;
 	}
 
+	// copy the text from the global into a string
 int nDataSize = ::GlobalSize( hGlobal );
 char* pData = (char*) ::GlobalLock( hGlobal );
+
 char* pDataEnd = pData + nDataSize;
-int nDataLen = 0;
 char* p = pData;
 	ASSERT( pData != NULL && nDataSize > 0 );
 	// Find the terminating NULL
@@ -296,38 +310,32 @@ char* p = pData;
 		ASSERT(0);
 		return;
 	}
-	// set pDataEnd to the terminating NULL
-	pDataEnd = p;
-	
+	// copy the data into a string
+CString text = pData;
+	// free the global
+	::GlobalUnlock( hGlobal );
+	::GlobalFree(hGlobal);
+
+	// tokenize the text
+CString token;
 CStringArray tokens;
-char* pToken = pData;
-	p = pData;
-	while( p = strstr( pToken, sep ) )
+CTokenizer tokenizer(text,delims);
+	while( tokenizer.Next( token ) )
 	{
-		// null out the separator string
-		for( int i=0; i < nSep; i++, p++ ) { *p = '\0'; }
-		// add the substring token
-		tokens.Add( pToken );
-		pToken = p; // start the next token after the separator
+		tokens.Add( token );
 	}
 
-	// if we found at least one occurrence of sep, then get the last token
-	if( tokens.GetCount() > 0 )
-		tokens.Add( pToken );
-
-	::GlobalUnlock( hGlobal );
-	::GlobalFree(hGlobal);
-
+	// save the tokens to the db
 CClip clip;
 int len;
 long lDate = (long) CTime::GetCurrentTime().GetTime();
 int count = tokens.GetCount();
-	// add in reverse order so that right to left corresponds to top to bottom
-	//  in the Ditto window (ditto sorts by time).
-	for( int i = count - 1; i >= 0; i-- )
+
+	for( int i = 0; i < count; i++ )
 	{
-		theApp.SetStatus( StrF("%d",i+1) );
+		status.Show( StrF("Saving Token %d out of %d", i+1, count) );
 		len = tokens[i].GetLength();
+		// ignore 0 length tokens
 		if( len <= 0 )
 			continue;
 		clip.AddFormat( CF_TEXT, (void*) (LPCTSTR) tokens[i], len+1 );
@@ -336,7 +344,6 @@ int count = tokens.GetCount();
 		clip.Clear();
 		lDate++; // make sure they are sequential
 	}
-	theApp.SetStatus();
 
 	if( count <= 0 )
 		::MessageBox( m_hWnd, "No new tokens found by parsing", "Parse Failed", MB_OK|MB_ICONINFORMATION );

+ 1 - 0
CopyProperties.h

@@ -65,6 +65,7 @@ public:
 	CEdit m_ParseEdit;
 	CButton m_ParseButton;
 	afx_msg void OnBnClickedParseButton();
+	CEdit m_DisplayTextEdit;
 };
 
 //{{AFX_INSERT_LOCATION}}

+ 199 - 0
Misc.cpp

@@ -438,6 +438,7 @@ BOOL CGetSetOptions::m_bAllowDuplicates;
 BOOL CGetSetOptions::m_bUpdateTimeOnPaste;
 BOOL CGetSetOptions::m_bSaveMultiPaste;
 BOOL CGetSetOptions::m_bShowPersistent;
+BOOL CGetSetOptions::m_bHistoryStartTop;
 
 CGetSetOptions g_Opt;
 
@@ -448,6 +449,7 @@ CGetSetOptions::CGetSetOptions()
 	m_bUpdateTimeOnPaste = GetUpdateTimeOnPaste();
 	m_bSaveMultiPaste = GetSaveMultiPaste();
 	m_bShowPersistent = GetShowPersistent();
+	m_bHistoryStartTop = GetHistoryStartTop();
 }
 
 CGetSetOptions::~CGetSetOptions()
@@ -885,6 +887,9 @@ BOOL CGetSetOptions::GetSaveMultiPaste()			{	return GetProfileLong("SaveMultiPas
 void CGetSetOptions::SetShowPersistent(BOOL bVal)	{	SetProfileLong("ShowPersistent", bVal); m_bShowPersistent = bVal; }
 BOOL CGetSetOptions::GetShowPersistent()			{	return GetProfileLong("ShowPersistent", 0); }
 
+void CGetSetOptions::SetHistoryStartTop(BOOL bVal)	{	SetProfileLong("HistoryStartTop", bVal); m_bHistoryStartTop = bVal; }
+BOOL CGetSetOptions::GetHistoryStartTop()			{	return GetProfileLong("HistoryStartTop", 0); }
+
 void CGetSetOptions::SetShowTextForFirstTenHotKeys(BOOL bVal)	{	SetProfileLong("ShowTextForFirstTenHotKeys", bVal);			}
 BOOL CGetSetOptions::GetShowTextForFirstTenHotKeys()			{	return GetProfileLong("ShowTextForFirstTenHotKeys", TRUE);	}
 
@@ -1483,3 +1488,197 @@ BYTE m=0;
 		m |= HOTKEYF_ALT;
 	return m;
 }
+
+/*------------------------------------------------------------------*\
+	CTokenizer - Tokenizes a string using given delimiters
+\*------------------------------------------------------------------*/
+
+CTokenizer::CTokenizer(const CString& cs, const CString& csDelim):
+	m_cs(cs),
+	m_nCurPos(0)
+{
+	SetDelimiters(csDelim);
+}
+
+void CTokenizer::SetDelimiters(const CString& csDelim)
+{
+	for(int i = 0; i < csDelim.GetLength(); ++i)
+		m_delim.set(static_cast<BYTE>(csDelim[i]));
+}
+
+bool CTokenizer::Next(CString& cs)
+{
+int len = m_cs.GetLength();
+
+	cs.Empty();
+
+	while(m_nCurPos < len && m_delim[static_cast<BYTE>(m_cs[m_nCurPos])])
+		++m_nCurPos;
+
+	if(m_nCurPos >= len)
+		return false;
+
+	int nStartPos = m_nCurPos;
+	while(m_nCurPos < len && !m_delim[static_cast<BYTE>(m_cs[m_nCurPos])])
+		++m_nCurPos;
+	
+	cs = m_cs.Mid(nStartPos, m_nCurPos - nStartPos);
+
+	return true;
+}
+
+CString	CTokenizer::Tail() const
+{
+int len = m_cs.GetLength();
+int nCurPos = m_nCurPos;
+
+	while(nCurPos < len && m_delim[static_cast<BYTE>(m_cs[nCurPos])])
+		++nCurPos;
+
+CString csResult;
+	if(nCurPos < len)
+		csResult = m_cs.Mid(nCurPos);
+
+	return csResult;
+}
+
+
+/*------------------------------------------------------------------*\
+	Global ToolTip Manual Control Functions
+\*------------------------------------------------------------------*/
+
+void InitToolInfo( TOOLINFO& ti )
+{
+	// INITIALIZE MEMBERS OF THE TOOLINFO STRUCTURE
+	ti.cbSize = sizeof(TOOLINFO);
+	ti.uFlags = TTF_TRACK;
+	ti.hwnd = NULL;
+	ti.hinst = NULL;
+	ti.uId = 0; // CPopup only uses uid 0
+	ti.lpszText = NULL;
+    // ToolTip control will cover the whole window
+	ti.rect.left = 0;
+	ti.rect.top = 0;
+	ti.rect.right = 0;
+	ti.rect.bottom = 0;
+}
+
+/*------------------------------------------------------------------*\
+	CPopup - a tooltip that pops up manually (when Show is called).
+	- technique learned from codeproject "ToolTipZen" by "Zarembo Maxim"
+\*------------------------------------------------------------------*/
+
+// when using this constructor, you must call Init( hWnd ) before using the CPopup.
+CPopup::CPopup()
+{
+	m_bOwnTT = false;
+	m_hTTWnd = NULL;
+	m_bIsShowing = false;
+}
+
+CPopup::CPopup( CPoint& pos, HWND hTTWnd )
+{
+	m_Pos = pos;
+	Init(hTTWnd);
+}
+
+CPopup::~CPopup()
+{
+	if( m_bOwnTT && ::IsWindow(m_hTTWnd) )
+		::DestroyWindow( m_hTTWnd );
+}
+
+void CPopup::Init( HWND hTTWnd, TOOLINFO* pTI )
+{
+	if( pTI )
+		m_TI = *pTI;
+	else
+		InitToolInfo( m_TI );
+
+	m_hTTWnd = hTTWnd;
+	if( hTTWnd )
+	{
+		m_bOwnTT = false;
+		// if our uid tooltip already exists, get the data, else add it.
+		if( ! ::SendMessage(m_hTTWnd, TTM_GETTOOLINFO, 0, (LPARAM)(LPTOOLINFO) &m_TI) )
+			::SendMessage(m_hTTWnd, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &m_TI);
+	}
+	else
+	{
+		m_bOwnTT = true;
+		CreateToolTip();
+	}
+	m_bIsShowing = false;
+}
+
+void CPopup::CreateToolTip()
+{
+	if( m_hTTWnd != NULL )
+		return;
+
+	// CREATE A TOOLTIP WINDOW
+	m_hTTWnd = CreateWindowEx(
+		WS_EX_TOPMOST,
+		TOOLTIPS_CLASS,
+		NULL,
+		TTS_NOPREFIX | TTS_ALWAYSTIP,		
+		CW_USEDEFAULT,
+		CW_USEDEFAULT,
+		CW_USEDEFAULT,
+		CW_USEDEFAULT,
+		NULL,
+		NULL,
+		NULL,
+		NULL
+		);
+	m_bOwnTT = true;
+
+	// SEND AN ADDTOOL MESSAGE TO THE TOOLTIP CONTROL WINDOW
+	::SendMessage(m_hTTWnd, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &m_TI);
+}
+
+void CPopup::SetTimeout( int timeout )
+{
+	if( m_hTTWnd == NULL )
+		return;
+	::SendMessage(m_hTTWnd, TTM_SETDELAYTIME, TTDT_AUTOMATIC, timeout);
+}
+
+void CPopup::Show( CString text, CPoint& pos )
+{
+	if( m_hTTWnd == NULL )
+		return;
+
+	// deactivate if it is currently activated
+	::SendMessage(m_hTTWnd, TTM_TRACKACTIVATE, false, (LPARAM)(LPTOOLINFO) &m_TI);
+
+	//Replace the tabs with spaces, the tooltip didn't like the \t s
+	text.Replace("\t", "  ");
+
+	m_TI.lpszText = (LPSTR) (LPCTSTR) text;
+
+	// make sure the tooltip will be on top.
+	::SetWindowPos( m_hTTWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE );
+	// this allows \n and \r to be interpreted correctly
+	::SendMessage(m_hTTWnd, TTM_SETMAXTIPWIDTH, 0, 500);
+	// set the text
+	::SendMessage(m_hTTWnd, TTM_SETTOOLINFO, 0, (LPARAM) (LPTOOLINFO) &m_TI);
+	// set the position
+	::SendMessage(m_hTTWnd, TTM_TRACKPOSITION, 0, (LPARAM)(DWORD) MAKELONG(pos.x, pos.y));
+	// show the tooltip
+	::SendMessage(m_hTTWnd, TTM_TRACKACTIVATE, true, (LPARAM)(LPTOOLINFO) &m_TI);
+
+	m_bIsShowing = true;
+}
+
+void CPopup::Show( CString text )
+{ Show( text, m_Pos ); }
+
+void CPopup::Hide()
+{
+	if( m_hTTWnd == NULL )
+		return;
+	// deactivate if it is currently activated
+	::SendMessage(m_hTTWnd, TTM_TRACKACTIVATE, false, (LPARAM)(LPTOOLINFO) &m_TI);
+	m_bIsShowing = false;
+}

+ 69 - 0
Misc.h

@@ -203,6 +203,10 @@ public:
 	static void		SetShowPersistent(BOOL bVal);
 	static BOOL		GetShowPersistent();
 
+	static BOOL	m_bHistoryStartTop;
+	static void		SetHistoryStartTop(BOOL bVal);
+	static BOOL		GetHistoryStartTop();
+
 	static void		SetShowTextForFirstTenHotKeys(BOOL bVal);
 	static BOOL		GetShowTextForFirstTenHotKeys();
 
@@ -341,4 +345,69 @@ public:
 //  HOTKEYF_SHIFT, HOTKEYF_CONTROL, HOTKEYF_ALT
 BYTE GetKeyStateModifiers();
 
+/*------------------------------------------------------------------*\
+	CTokenizer - Tokenizes a string using given delimiters
+\*------------------------------------------------------------------*/
+// Based upon:
+// Date:        Monday, October 22, 2001
+// Autor:       Eduardo Velasquez
+// Description: Tokenizer class for CStrings. Works like strtok.
+///////////////
+
+#if !defined(_BITSET_)
+#	include <bitset>
+#endif // !defined(_BITSET_)
+
+class CTokenizer
+{
+public:
+	CString m_cs;
+	std::bitset<256> m_delim;
+	int m_nCurPos;
+
+	CTokenizer(const CString& cs, const CString& csDelim);
+	void SetDelimiters(const CString& csDelim);
+
+	bool Next(CString& cs);
+	CString	Tail() const;
+};
+
+
+/*------------------------------------------------------------------*\
+	Global ToolTip Manual Control Functions
+\*------------------------------------------------------------------*/
+
+void InitToolInfo( TOOLINFO& ti ); // initializes toolinfo with uid 0
+
+/*------------------------------------------------------------------*\
+	CPopup - a tooltip that pops up manually (when Show is called).
+	- technique learned from codeproject "ToolTipZen" by "Zarembo Maxim"
+\*------------------------------------------------------------------*/
+
+class CPopup
+{
+public:
+	bool m_bOwnTT;
+
+	HWND m_hTTWnd; // handle to the ToolTip control
+	TOOLINFO m_TI; // struct specifying info about tool in ToolTip control
+
+	bool m_bIsShowing;
+	CPoint m_Pos;
+
+	CPopup();
+	CPopup( CPoint& pos, HWND hTTWnd = NULL );
+	~CPopup();
+
+	void Init( HWND hTTWnd = NULL, TOOLINFO* pTI = NULL );
+	void CreateToolTip();
+
+	void SetTimeout( int timeout );
+
+	void Show( CString text, CPoint& pos );
+	void Show( CString text );
+	void Hide();
+};
+
+
 #endif // !defined(AFX_CP_GUI_GLOBALS__FBCDED09_A6F2_47EB_873F_50A746EBC86B__INCLUDED_)

+ 4 - 1
OptionsQuickPaste.cpp

@@ -36,6 +36,7 @@ void COptionsQuickPaste::DoDataExchange(CDataExchange* pDX)
 	DDX_Control(pDX, IDC_TRANS_PERC, m_eTransparencyPercent);
 	DDX_Control(pDX, IDC_TRANSPARENCY, m_btEnableTransparency);
 	DDX_Control(pDX, IDC_CTRL_CLICK, m_btUseCtrlNum);
+	DDX_Control(pDX, IDC_HISTORY_START_TOP, m_btHistoryStartTop);
 	//}}AFX_DATA_MAP
 }
 
@@ -65,6 +66,7 @@ BOOL COptionsQuickPaste::OnInitDialog()
 	else if(CGetSetOptions::GetQuickPastePosition() == POS_AT_PREVIOUS)
 		CheckDlgButton(IDC_AT_PREVIOUS, BST_CHECKED);
 
+	m_btHistoryStartTop.SetCheck(g_Opt.m_bHistoryStartTop);
 	m_btUseCtrlNum.SetCheck(CGetSetOptions::GetUseCtrlNumForFirstTenHotKeys());
 
 	m_btShowText.SetCheck(CGetSetOptions::GetShowTextForFirstTenHotKeys());
@@ -84,7 +86,8 @@ BOOL COptionsQuickPaste::OnApply()
 		CGetSetOptions::SetQuickPastePosition(POS_AT_CURSOR);
 	else if(IsDlgButtonChecked(IDC_AT_PREVIOUS))
 		CGetSetOptions::SetQuickPastePosition(POS_AT_PREVIOUS);
-	
+
+	g_Opt.SetHistoryStartTop( m_btHistoryStartTop.GetCheck() );
 	CGetSetOptions::SetUseCtrlNumForFirstTenHotKeys(m_btUseCtrlNum.GetCheck());
 	CGetSetOptions::SetShowTextForFirstTenHotKeys(m_btShowText.GetCheck());
 	

+ 3 - 0
OptionsQuickPaste.h

@@ -7,6 +7,7 @@
 // OptionsQuickPaste.h : header file
 #include "NumberEdit.h"
 #include "OptionsSheet.h"
+#include "afxwin.h"
 //
 
 /////////////////////////////////////////////////////////////////////////////
@@ -29,6 +30,7 @@ public:
 	CNumberEdit	m_eTransparencyPercent;
 	CButton	m_btEnableTransparency;
 	CButton	m_btUseCtrlNum;
+	CButton m_btHistoryStartTop;
 	//}}AFX_DATA
 
 
@@ -52,6 +54,7 @@ protected:
 	//}}AFX_MSG
 	DECLARE_MESSAGE_MAP()
 
+public:
 };
 
 //{{AFX_INSERT_LOCATION}}

+ 8 - 5
ProcessCopy.cpp

@@ -379,7 +379,7 @@ bool CClip::FindDuplicate( CMainTable& recset, BOOL bCheckLastOnly )
 int CClip::CompareFormatDataTo( long lID )
 {
 int nRet = 0;
-int nRecs, nFormats;
+int nRecs=0, nFormats=0;
 CClipFormat* pFormat = NULL;
 	try
 	{
@@ -387,12 +387,15 @@ CClipFormat* pFormat = NULL;
 
 		recset.Open("SELECT * FROM Data WHERE lParentID = %d", lID);
 
-		// Verify the same number of saved types
-		recset.MoveLast();
-		nRecs = recset.GetRecordCount();
+		if( !recset.IsBOF() && !recset.IsEOF() )
+		{
+			// Verify the same number of saved types
+			recset.MoveLast();
+			nRecs = recset.GetRecordCount();
+		}
 		nFormats = m_Formats.GetCount();
 		nRet = nFormats - nRecs;
-		if( nRet != 0 )
+		if( nRet != 0 || nRecs == 0 )
 		{	recset.Close();	return nRet; }
 
 		// For each format type in the db

+ 91 - 16
QListCtrl.cpp

@@ -1,4 +1,4 @@
- // QListCtrl.cpp : implementation file
+// QListCtrl.cpp : implementation file
 //
 
 #include "stdafx.h"
@@ -45,6 +45,7 @@ CQListCtrl::CQListCtrl()
 	m_SmallFont = CreateFontIndirect(&lf);
 
 	m_bShowTextForFirstTenHotKeys = true;
+	m_bStartTop = true;
 //	m_Accelerator = NULL; //!!!!!
 }
 
@@ -59,6 +60,42 @@ CQListCtrl::~CQListCtrl()
 	DestroyAndCreateAccelerator(FALSE);
 }
 
+// returns the position 1-10 if the index is in the FirstTen block else -1
+int CQListCtrl::GetFirstTenNum( int index )
+{
+// set firstTenNum to the first ten number (1-10) corresponding to the given index
+int firstTenNum = -1; // -1 means that nItem is not in the FirstTen block.
+int count = GetItemCount();
+
+	if( m_bStartTop )
+	{
+		if( 0 <= index && index <= 9 )
+			firstTenNum = index + 1;
+	}
+	else // we are starting at the bottom and going up
+	{
+	int idxStartFirstTen = count-10; // start of the FirstTen block
+		// if index is within the FirstTen block
+		if( idxStartFirstTen <= index && index < count )
+			firstTenNum = count - index;
+	}
+	return firstTenNum;
+}
+
+// returns the list index corresponding to the given FirstTen position number.
+// (ret < 0) means that "num" is not in the FirstTen block
+int CQListCtrl::GetFirstTenIndex( int num )
+{
+	if( num <= 0 || num > 10 )
+		return -1;
+
+	if( m_bStartTop )
+		return num-1;
+	// else we are starting at the bottom and going up
+int count = GetItemCount();
+	return count - num;
+}
+
 
 BEGIN_MESSAGE_MAP(CQListCtrl, CListCtrl)
 	//{{AFX_MSG_MAP(CQListCtrl)
@@ -73,6 +110,7 @@ BEGIN_MESSAGE_MAP(CQListCtrl, CListCtrl)
 	//}}AFX_MSG_MAP
 	ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
 	ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
+	ON_WM_KILLFOCUS()
 END_MESSAGE_MAP()
 
 /////////////////////////////////////////////////////////////////////////////
@@ -214,7 +252,20 @@ BOOL CQListCtrl::SetCaret(int nRow, BOOL bFocus)
 
 long CQListCtrl::GetCaret()
 {
-	return GetNextItem(0, LVNI_FOCUSED);
+	return GetNextItem(-1, LVNI_FOCUSED);
+}
+
+// moves the caret to the given index, selects it, and ensures it is visible.
+BOOL CQListCtrl::SetListPos( int index )
+{
+	if( index < 0 || index >= GetItemCount() )
+		return FALSE;
+
+	RemoveAllSelection();
+	SetCaret(index);
+	SetSelection(index);
+	EnsureVisible(index,FALSE);
+	return TRUE;
 }
 
 BOOL CQListCtrl::SetFormattedText(int nRow, int nCol, LPCTSTR lpszFormat,...)
@@ -329,11 +380,15 @@ void CQListCtrl::OnCustomdrawList(NMHDR* pNMHDR, LRESULT* pResult)
 		
         // Draw the text.
         CString csText = GetItemText(nItem, 0);
-	
-		if(m_bShowTextForFirstTenHotKeys)
+
+		// set firstTenNum to the first ten number (1-10) corresponding to
+		//  the current nItem.
+		// -1 means that nItem is not in the FirstTen block.
+		int firstTenNum = GetFirstTenNum(nItem);
+
+		if( m_bShowTextForFirstTenHotKeys && firstTenNum > 0 )
 		{
-			if(nItem < 10)
-				rcText.left += 12;
+			rcText.left += 12;
 		}
 
 		pDC->DrawText(csText, rcText, DT_VCENTER | DT_EXPANDTABS);
@@ -342,13 +397,13 @@ void CQListCtrl::OnCustomdrawList(NMHDR* pNMHDR, LRESULT* pResult)
         if(bListHasFocus && (rItem.state & LVIS_FOCUSED))
 			pDC->DrawFocusRect(rcItem);
 
-		if((nItem < 10) && (m_bShowTextForFirstTenHotKeys))
+		if( m_bShowTextForFirstTenHotKeys && firstTenNum > 0 )
 		{
 			CString cs;
-			if(nItem == 9)
-				cs.Format("%d", 0);
+			if( firstTenNum == 10 )
+				cs = "0";
 			else
-				cs.Format("%d", nItem+1);
+				cs.Format("%d", firstTenNum);
 
 			CRect crClient;
 			
@@ -424,8 +479,8 @@ BOOL CQListCtrl::OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult )
 	// Like use its file size, etc.
 	strTipText = GetToolTipText(nID-1);
 
-	//Replace the tabs with 5 spaces, the tooltip didn't like the \t s
-	strTipText.Replace("\t", "     ");
+	//Replace the tabs with spaces, the tooltip didn't like the \t s
+	strTipText.Replace("\t", "  ");
 
 #ifndef _UNICODE
 	if (pNMHDR->code == TTN_NEEDTEXTA)
@@ -511,6 +566,10 @@ int CQListCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
 	
 	EnableToolTips();
 
+	m_Popup.Init();
+//	m_Popup.Init( GetToolTips()->m_hWnd );
+//	m_Popup.m_TI.hwnd = m_hWnd;
+    
 	return 0;
 }
 
@@ -530,12 +589,14 @@ BOOL CQListCtrl::PreTranslateMessage(MSG* pMsg)
 	*/
 
 	CAccel* pAccel = m_Accels.OnMsg( pMsg );
-	if( pAccel )
-		GetParent()->SendMessage(NM_SELECT_DB_ID, pAccel->Cmd, 0);
+	if( pAccel && GetParent()->SendMessage(NM_SELECT_DB_ID, pAccel->Cmd, 0) )
+		return TRUE;
 
 	switch(pMsg->message) 
 	{
 	case WM_KEYDOWN:
+		m_Popup.Hide(); // hide the manual tooltip on any keypress
+
 		WPARAM vk = pMsg->wParam;
 		// if a number key was pressed
 		if( '0' <= vk && vk <= '9' )
@@ -548,7 +609,8 @@ BOOL CQListCtrl::PreTranslateMessage(MSG* pMsg)
 			// '0' is actually 10 in the ditto window
 			if( index == 0 )
 				index = 10;
-			index--; // 0 based index
+			// translate num 1-10 into the actual index (based upon m_bStartTop)
+			index = GetFirstTenIndex( index );
 			GetParent()->SendMessage(NM_SELECT_INDEX, index, 0);
 			return TRUE;
 		}
@@ -566,6 +628,13 @@ BOOL CQListCtrl::PreTranslateMessage(MSG* pMsg)
 				return TRUE;
 			}
 			break;
+
+		case VK_F3:
+			int nItem = GetCaret();
+				GetItemPosition( nItem, &m_Popup.m_Pos );
+				ClientToScreen( &m_Popup.m_Pos );
+//				GetClientRect( &m_Popup.m_TI.rect ); // put tooltip to the left of the item
+				m_Popup.Show( GetToolTipText(nItem) );
 		} // end switch(vk)
 
 		break; // end case WM_KEYDOWN
@@ -669,4 +738,10 @@ void CQListCtrl::DestroyAndCreateAccelerator(BOOL bCreate)
 //	
 //	return CListCtrl::OnCommand(wParam, lParam);
 //}
-*/
+*/
+
+void CQListCtrl::OnKillFocus(CWnd* pNewWnd)
+{
+	CListCtrl::OnKillFocus(pNewWnd);
+	m_Popup.Hide();
+}

+ 16 - 1
QListCtrl.h

@@ -57,6 +57,17 @@ public:
 public:
 	virtual ~CQListCtrl();
 
+	CPopup	m_Popup;
+
+	// The "FirstTen" block is either at the top or the bottom
+	//  of the list based upon m_bStartTop.
+	BOOL	m_bShowTextForFirstTenHotKeys;
+	BOOL	m_bStartTop;
+	// returns the position 1-10 if the index is in the FirstTen block else -1
+	int GetFirstTenNum( int index );
+	// returns the list index corresponding to the given FirstTen position number.
+	int GetFirstTenIndex( int num );
+
 	void SetNumberOfLinesPerRow(int nLines);
 	void GetSelectionIndexes(ARRAY &arr);
 	void GetSelectionItemData(ARRAY &arr);
@@ -67,6 +78,9 @@ public:
 	BOOL SetFormattedText(int nRow, int nCol, LPCTSTR lpszFormat,...);
 	BOOL SetCaret(int nRow, BOOL bFocus = TRUE);
 	long GetCaret();
+	// moves the caret to the given index, selects it, and ensures it is visible.
+	BOOL SetListPos( int index );
+
 	DWORD GetItemData(int nItem);
 	CString GetToolTipText(int nItem);
 
@@ -82,7 +96,6 @@ protected:
 	TCHAR *m_pchTip;
 
 	HFONT m_SmallFont;
-	BOOL  m_bShowTextForFirstTenHotKeys;
 
 	//Accelerator
 	CAccels	m_Accels;
@@ -103,6 +116,8 @@ protected:
 	//}}AFX_MSG
 	afx_msg BOOL OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult );
 	DECLARE_MESSAGE_MAP()
+public:
+	afx_msg void OnKillFocus(CWnd* pNewWnd);
 };
 
 /////////////////////////////////////////////////////////////////////////////

+ 65 - 9
QPasteWnd.cpp

@@ -31,7 +31,7 @@ CQPasteWnd::CQPasteWnd()
 {	
 	m_Title = QPASTE_TITLE;
 	m_bHideWnd = true;
-	m_Recset.m_strSort = "lDate DESC";
+	m_bAscending = false;
 }
 
 CQPasteWnd::~CQPasteWnd()
@@ -424,6 +424,15 @@ BOOL CQPasteWnd::FillList(CString csSQLSearch/*=""*/)
 //	if(m_Recset.IsOpen())
 //		m_Recset.Close();
 
+	// currently, we only have a History Group, so assign it directly.
+	m_lstHeader.m_bStartTop = g_Opt.m_bHistoryStartTop;
+	m_bAscending = !g_Opt.m_bHistoryStartTop;
+
+	if( m_bAscending )
+		m_Recset.m_strSort = "lDate ASC";
+	else
+		m_Recset.m_strSort = "lDate DESC";
+
 	m_lstHeader.DeleteAllItems();
 
 	CRect crRect;
@@ -468,8 +477,18 @@ BOOL CQPasteWnd::FillList(CString csSQLSearch/*=""*/)
 		e->Delete();
 	}
 
-	m_lstHeader.SetSelection(0);
-	m_lstHeader.SetCaret(0);
+	// set the caret based upon which end we're starting from
+	if( m_lstHeader.m_bStartTop )
+	{
+		m_lstHeader.SetListPos( 0 );
+	}
+	else
+	{
+	int idx = m_lstHeader.GetItemCount() - 1;
+		// if there are elements
+		if( idx >= 0 )
+			m_lstHeader.SetListPos( idx );
+	}
 
 //	m_lstHeader.Invalidate();
 	RedrawWindow(0,0,RDW_INVALIDATE);
@@ -737,7 +756,7 @@ void CQPasteWnd::OnMenuProperties()
 	if(nDo == IDOK || nDo == IDCANCEL)
 	{
 		m_lstHeader.SetFocus();
-		m_lstHeader.SetCaret(nRow);
+		m_lstHeader.SetListPos(nRow);
 	}
 }
 
@@ -814,15 +833,44 @@ void CQPasteWnd::DeleteSelectedRows()
 		if( nFirstSel >= lCount )
 			nFirstSel = lCount - 1;
 
-		m_lstHeader.SetSelection(nFirstSel);
-		m_lstHeader.SetCaret(nFirstSel);
-		m_lstHeader.EnsureVisible(nFirstSel,FALSE);
+		m_lstHeader.SetListPos(nFirstSel);
 	}
 
 	m_lstHeader.RefreshVisibleRows();
 }
 
+CString CQPasteWnd::LoadDescription( int nItem )
+{
+	if( nItem < 0 || nItem >= m_lstHeader.GetItemCount() )
+		return "";
 
+CString cs;
+	try
+	{
+		m_Recset.SetAbsolutePosition( nItem );
+		cs = m_Recset.m_strText;
+	}
+	CATCHDAO
+
+	return cs;
+}
+
+bool CQPasteWnd::SaveDescription( int nItem, CString text )
+{
+	if( nItem < 0 || nItem >= m_lstHeader.GetItemCount() )
+		return false;
+
+	try
+	{
+		m_Recset.SetAbsolutePosition( nItem );
+		m_Recset.Edit();
+		m_Recset.m_strText = text;
+		m_Recset.Update();
+	}
+	CATCHDAO
+
+	return true;
+}
 
 BOOL CQPasteWnd::PreTranslateMessage(MSG* pMsg) 
 {
@@ -861,6 +909,7 @@ BOOL CQPasteWnd::PreTranslateMessage(MSG* pMsg)
 			}
 			return TRUE;
 		}
+
 		case 'A': // Ctrl-A = Select All
 			if(GetKeyState(VK_CONTROL) & 0x8000)
 			{
@@ -872,6 +921,7 @@ BOOL CQPasteWnd::PreTranslateMessage(MSG* pMsg)
 				return TRUE;
 			}
 			break;
+
 		} // end switch( pMsg->wParam )
 
 		break; // end case WM_KEYDOWN 
@@ -994,13 +1044,19 @@ void CQPasteWnd::GetDispInfo(NMHDR* pNMHDR, LRESULT* pResult)
 void CQPasteWnd::OnGetToolTipText(NMHDR* pNMHDR, LRESULT* pResult)
 {
 	CQListToolTipText* pInfo = (CQListToolTipText*)pNMHDR;
-	if(!pInfo)
+	if( !pInfo )
+		return;
+
+	if( pInfo->lItem < 0 )
+	{
+		strcpy(pInfo->cText, "no item selected");
 		return;
+	}
 
 	try
 	{
 		CString cs;
-		
+	
 		m_Recset.SetAbsolutePosition(pInfo->lItem);
 
 		cs = m_Recset.m_strText;

+ 4 - 0
QPasteWnd.h

@@ -58,6 +58,7 @@ public:
 	CButton			m_btCancel;
 	bool			m_bHideWnd;
 	CMainTable		m_Recset;
+	bool			m_bAscending;
 
 	CString			m_Title;
 
@@ -70,6 +71,9 @@ public:
 
 	void DeleteSelectedRows();
 
+	CString LoadDescription( int nItem );
+	bool SaveDescription( int nItem, CString text );
+
 	//Menu Items
 	void SetLinesPerRow(long lLines);
 	void SetTransparency(long lPercent);

+ 3 - 1
Resource.h

@@ -56,6 +56,8 @@
 #define IDC_BUTTON1                     1040
 #define IDC_PARSE_BUTTON                1040
 #define IDC_PARSE_EDIT                  1041
+#define IDC_CHECK1                      1042
+#define IDC_HISTORY_START_TOP           1042
 #define IDD_OPTIONS_KEYSTROKES          2001
 #define IDC_HOTKEY                      2002
 #define IDC_DISPLAY_IN_SYSTEMTRAY       2003
@@ -112,7 +114,7 @@
 #define _APS_3D_CONTROLS                     1
 #define _APS_NEXT_RESOURCE_VALUE        138
 #define _APS_NEXT_COMMAND_VALUE         32780
-#define _APS_NEXT_CONTROL_VALUE         1042
+#define _APS_NEXT_CONTROL_VALUE         1043
 #define _APS_NEXT_SYMED_VALUE           101
 #endif
 #endif