ProcessPaste.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839
  1. #include "stdafx.h"
  2. #include "CP_Main.h"
  3. #include "ProcessPaste.h"
  4. #ifdef _DEBUG
  5. #undef THIS_FILE
  6. static char THIS_FILE[]=__FILE__;
  7. #define new DEBUG_NEW
  8. #endif
  9. // returns the increment necessary to fit "count" elements between (dStart,dEnd)
  10. // if this returns 0, then "count" elements cannot fit between (dStart,dEnd).
  11. double GetFitIncrement( int count, double dStart, double dEnd )
  12. {
  13. VERIFY( count > 0 && dStart <= dEnd );
  14. double dIncrement = (dEnd - dStart) / ((double) (count+1));
  15. if( dIncrement == 0 )
  16. return 0;
  17. // verify that each element is unique
  18. // I'm not sure if this is necessary, but I'm doing it just to be on the safe side
  19. // I think the resolution of floating points are variable.
  20. // i.e. we cannot depend upon an increment always effecting a change.
  21. int i = 0;
  22. double dPrev = dStart;
  23. double dOrder = dStart + dIncrement;
  24. while( dOrder < dEnd )
  25. {
  26. i++;
  27. if( dOrder <= dPrev )
  28. return 0;
  29. dPrev = dOrder;
  30. dOrder = dOrder + dIncrement;
  31. }
  32. // verify count (and that we're not too close to dEnd)
  33. if( i < count )
  34. return 0;
  35. return dIncrement;
  36. }
  37. /*------------------------------------------------------------------*\
  38. ID based Globals
  39. \*------------------------------------------------------------------*/
  40. BOOL MarkClipAsPasted(long lID)
  41. {
  42. CGetSetOptions::SetTripPasteCount(-1);
  43. CGetSetOptions::SetTotalPasteCount(-1);
  44. if( !g_Opt.m_bUpdateTimeOnPaste )
  45. return FALSE;
  46. try
  47. {
  48. //Update the time it was copied so that it appears at the top of the
  49. //paste list. Items are sorted by this time.
  50. CMainTable ctMain;
  51. ctMain.Open("SELECT * FROM Main WHERE lID = %d", lID);
  52. ctMain.Edit();
  53. CTime now = CTime::GetCurrentTime();
  54. ctMain.m_lDate = (long)now.GetTime();
  55. ctMain.Update();
  56. ctMain.Close();
  57. return TRUE;
  58. }
  59. catch(CDaoException *e)
  60. {
  61. ASSERT(FALSE);
  62. e->Delete();
  63. }
  64. return FALSE;
  65. }
  66. long NewGroupID( long lParentID, CString text )
  67. {
  68. long lID=0;
  69. CTime time;
  70. time = CTime::GetCurrentTime();
  71. try
  72. {
  73. CMainTable recset;
  74. recset.Open(dbOpenTable, "Main");
  75. recset.AddNew(); // overridden to set m_lID to the new autoincr number
  76. lID = recset.m_lID;
  77. recset.m_lDate = (long) time.GetTime();
  78. recset.m_lDontAutoDelete = (long) time.GetTime();
  79. if( text != "" )
  80. recset.m_strText = text;
  81. else
  82. recset.m_strText = time.Format("NewGroup %y/%m/%d %H:%M:%S");
  83. recset.m_bIsGroup = TRUE;
  84. recset.m_lParentID = lParentID;
  85. recset.Update();
  86. // recset.SetBookmark( recset.GetLastModifiedBookmark() );
  87. // lID = recset.m_lID;
  88. recset.Close();
  89. }
  90. catch(CDaoException* e)
  91. {
  92. e->ReportError();
  93. ASSERT(FALSE);
  94. e->Delete();
  95. return 0;
  96. }
  97. return lID;
  98. }
  99. // !!!!!! UNTESTED SQL
  100. // creates copies of all lSrc Data and returns the copy's lDataID (or 0 on fail)
  101. long NewCopyDataID( long lSrc )
  102. {
  103. long lDataID = 0;
  104. CString sql;
  105. // create the copies
  106. sql.Format(
  107. "INSERT INTO Data (strClipBoardFormat, ooData) "
  108. "SELECT strClipBoardFormat, ooData FROM Data "
  109. "WHERE lDataID = %d", lSrc );
  110. // each lID should be assigned a unique ID (autoinc)
  111. // lDataID should be assigned to 0 (default value) or NULL
  112. ExecuteSQL( sql );
  113. // assign lDataID to the first record's lID
  114. try
  115. {
  116. CDataTable recs;
  117. recs.Open("SELECT * FROM Data WHERE lDataID = 0 OR lDataID IS NULL");
  118. if( !recs.IsEOF() )
  119. lDataID = recs.m_lID;
  120. recs.Close();
  121. }
  122. CATCHDAO
  123. // assign the copies to lDest
  124. sql.Format(
  125. "UPDATE Data "
  126. "SET lDataID = %d "
  127. "WHERE lDataID = 0 OR lDataID IS NULL", lDataID );
  128. ExecuteSQL( sql );
  129. return lDataID;
  130. }
  131. // deletes the given item
  132. BOOL DeleteID( long lID, bool bDisband )
  133. {
  134. BOOL bIsGroup;
  135. long lDataID = 0;
  136. int i = 0;
  137. COleVariant varKey( (long) 0 ); // VT_I4
  138. try
  139. {
  140. CMainTable recs;
  141. recs.Open( dbOpenTable, "Main" );
  142. recs.SetCurrentIndex("lID"); // set "Seek" to use this index.
  143. varKey.lVal = lID;
  144. // Goto the record whose [lID] field == varKey
  145. if( !recs.Seek( _T("="), &varKey ) )
  146. {
  147. ASSERT(0);
  148. return FALSE;
  149. }
  150. lDataID = recs.m_lDataID;
  151. bIsGroup = recs.m_bIsGroup;
  152. // must delete this record first so that DeleteDataID can properly
  153. // determine if any other Clip is using the same Data.
  154. recs.Delete();
  155. if( bIsGroup )
  156. DeleteGroupID( lID, bDisband );
  157. else
  158. DeleteDataID( lDataID );
  159. recs.Close();
  160. }
  161. CATCHDAO
  162. return TRUE;
  163. }
  164. // deletes all items in the group
  165. BOOL DeleteGroupID( long lGroupID, bool bDisband )
  166. {
  167. if( bDisband )
  168. {
  169. return ExecuteSQL(
  170. StrF("UPDATE Main SET lParentID = 0 WHERE lParentID = %d", lGroupID) );
  171. }
  172. try
  173. {
  174. CMainTable recs;
  175. recs.Open( "SELECT * FROM Main WHERE lParentID = %d", lGroupID );
  176. while( !recs.IsEOF() )
  177. {
  178. DeleteID( recs.m_lID );
  179. recs.MoveNext();
  180. }
  181. recs.Close();
  182. }
  183. CATCHDAO
  184. return TRUE;
  185. }
  186. // deletes all data for the given ID
  187. // NOTE!: this checks to see if there are any records in Main which reference
  188. // the given lDataID, and if so, does NOT delete the Data.
  189. // THEREFORE, Main records should be deleted BEFORE calling this function.
  190. BOOL DeleteDataID( long lDataID )
  191. {
  192. CMainTable recs;
  193. BOOL bEmpty;
  194. // check to see if the data is referenced by any Clips
  195. recs.Open( "SELECT * FROM Main WHERE lDataID = %d", lDataID );
  196. bEmpty = recs.IsEOF();
  197. recs.Close();
  198. // if the data is no longer referenced, delete the data
  199. if( bEmpty )
  200. return ExecuteSQL( StrF("DELETE FROM Data WHERE lDataID = %d", lDataID) );
  201. // else, there are more clips which use the data
  202. return TRUE;
  203. }
  204. BOOL DeleteAllIDs()
  205. {
  206. CMainTable MainTable;
  207. CDataTable DataTable;
  208. MainTable.DeleteAll();
  209. DataTable.DeleteAll();
  210. if( CompactDatabase() )
  211. {
  212. RepairDatabase();
  213. }
  214. return TRUE;
  215. }
  216. // all "formatIDs" (Data.lID) must be elements of the "lDataID" (Data.lDataID) set
  217. BOOL DeleteFormats( long lDataID, ARRAY& formatIDs )
  218. {
  219. long lNewTotalSize = 0;
  220. bool bIsHeadDeleted = false;
  221. long lNewDataID = 0;
  222. if( formatIDs.GetSize() <= 0 )
  223. return TRUE;
  224. formatIDs.SortAscending();
  225. try
  226. {
  227. CDataTable recs;
  228. recs.Open("SELECT * FROM Data WHERE lDataID = %d", lDataID);
  229. //Go through the data table and find the deleted items
  230. recs.MoveFirst();
  231. while(!recs.IsEOF())
  232. {
  233. if(formatIDs.Find(recs.m_lID))
  234. {
  235. // if we are deleting the head, then we need a new head
  236. // actually, this might not be absolutely necessary if
  237. // lID autoincr field never reuses IDs.
  238. if( lDataID == recs.m_lID )
  239. bIsHeadDeleted = true;
  240. recs.Delete();
  241. }
  242. else
  243. lNewTotalSize += recs.m_ooData.m_dwDataLength;
  244. recs.MoveNext();
  245. }
  246. if( bIsHeadDeleted )
  247. {
  248. recs.MoveFirst();
  249. if( !recs.IsEOF() )
  250. lNewDataID = recs.m_lID;
  251. recs.Close();
  252. // update the Main Table with lNewTotalSize and lNewDataID
  253. ExecuteSQL( StrF(
  254. "UPDATE Main SET lTotalCopySize = %d, lDataID = %d WHERE lDataID = %d",
  255. lNewTotalSize, lNewDataID, lDataID) );
  256. // update the Data Table with lNewDataID
  257. ExecuteSQL( StrF(
  258. "UPDATE Data SET lDataID = %d WHERE lDataID = %d",
  259. lNewDataID, lDataID) );
  260. }
  261. else // still update the total copy size
  262. {
  263. recs.Close();
  264. ExecuteSQL( StrF(
  265. "UPDATE Main SET lTotalCopySize = %d WHERE lDataID = %d",
  266. lNewTotalSize, lDataID) );
  267. }
  268. }
  269. CATCHDAO
  270. return TRUE;
  271. }
  272. /*------------------------------------------------------------------*\
  273. CClipIDs
  274. \*------------------------------------------------------------------*/
  275. //-------------------
  276. // PASTING FUNCTIONS
  277. //-------------------
  278. // allocate an HGLOBAL of the given Format Type representing these Clip IDs.
  279. HGLOBAL CClipIDs::Render( UINT cfType )
  280. {
  281. int count = GetSize();
  282. if( count <= 0 )
  283. return 0;
  284. if( count == 1 )
  285. return CClip::LoadFormat( ElementAt(0), cfType );
  286. CString text = AggregateText( CF_TEXT, "\r\n" );
  287. return NewGlobalP( (void*)(LPCSTR) text, text.GetLength()+1 );
  288. }
  289. void CClipIDs::GetTypes( CClipTypes& types )
  290. {
  291. int count = GetSize();
  292. types.RemoveAll();
  293. if( count > 1 )
  294. types.Add( CF_TEXT );
  295. else if( count == 1 )
  296. CClip::LoadTypes( ElementAt(0), types );
  297. }
  298. // Aggregates the cfType Format Data of the Clip IDs in this array, assuming
  299. // each Format is NULL terminated and placing pSeparator between them.
  300. // This assumes that the given cfType is a null terminated text type.
  301. CString CClipIDs::AggregateText( UINT cfType, char* pSeparator )
  302. {
  303. CString csSQL;
  304. CDataTable recset;
  305. CString text;
  306. char* pData = NULL;
  307. DWORD len;
  308. DWORD maxLen;
  309. // maybe we should sum up the "recset.m_ooData.m_dwDataLength" of all IDs first
  310. // in order to determine the max space required?? Or would that be wastefull?
  311. // allocate a large initial buffer to minimize realloc for concatenations
  312. text.GetBuffer(1000);
  313. text.ReleaseBuffer(0);
  314. int numIDs = GetSize();
  315. int* pIDs = GetData();
  316. csSQL.Format(
  317. "SELECT Data.* FROM Data "
  318. "INNER JOIN Main ON Main.lDataID = Data.lDataID "
  319. "WHERE Data.strClipBoardFormat = \'%s\' "
  320. "AND Main.lID = %%d",
  321. GetFormatName(cfType));
  322. try
  323. {
  324. for( int i=0; i < numIDs; i++ )
  325. {
  326. recset.Open( csSQL, pIDs[i] );
  327. if( !recset.IsBOF() && !recset.IsEOF() )
  328. {
  329. maxLen = recset.m_ooData.m_dwDataLength;
  330. if( maxLen == 0 )
  331. continue;
  332. pData = (char*) GlobalLock(recset.m_ooData.m_hData);
  333. ASSERT( pData );
  334. // verify that pData is null terminated
  335. // do a quick check to see if the last character is null
  336. if( pData[maxLen-1] != '\0' )
  337. {
  338. for( len=0; len < maxLen && pData[len] != '\0'; len++ ) {}
  339. // if it is not null terminated, skip this item
  340. if( len >= maxLen )
  341. continue;
  342. }
  343. text += pData;
  344. GlobalUnlock(recset.m_ooData.m_hData);
  345. if( pSeparator )
  346. text += pSeparator;
  347. }
  348. recset.Close();
  349. }
  350. }
  351. CATCHDAO
  352. return text;
  353. }
  354. //----------------------------------------------
  355. // ELEMENT (Clip or Group) MANAGEMENT FUNCTIONS
  356. //----------------------------------------------
  357. // returns the address of the given id in this array or NULL.
  358. long* CClipIDs::FindID( long lID )
  359. {
  360. int count = GetSize();
  361. long* pID = (long*) GetData();
  362. for( int i=0; i < count; i++ )
  363. {
  364. if( *pID == lID )
  365. return pID;
  366. pID++;
  367. }
  368. return NULL;
  369. }
  370. // Blindly Moves IDs into the lParentID Group sequentially with the given order
  371. // (i.e. this does not check to see if the IDs' order conflict)
  372. // if( dIncrement < 0 ), this does not change the order
  373. BOOL CClipIDs::MoveTo( long lParentID, double dFirst, double dIncrement )
  374. {
  375. int count = GetSize();
  376. int i = 0;
  377. COleVariant varKey( (long) 0 ); // VT_I4
  378. double dOrder = dFirst;
  379. BOOL bChangeOrder = (dIncrement >= 0);
  380. try
  381. {
  382. CMainTable recs;
  383. recs.Open( dbOpenTable, "Main" );
  384. recs.SetCurrentIndex("lID"); // set "Seek" to use this index.
  385. // for each id, assign lParentID
  386. while( i < count )
  387. {
  388. varKey.lVal = ElementAt(i);
  389. // Goto the record whose [lID] field == varKey
  390. if( !recs.Seek( _T("="), &varKey ) )
  391. {
  392. ASSERT(0);
  393. break;
  394. }
  395. // don't allow an item to become its own parent
  396. // NOTE!: deeper recursion is not checked, so it is theoretically
  397. // possible for: A -> B -> A
  398. if( recs.m_lID != lParentID )
  399. {
  400. recs.Edit();
  401. recs.m_lParentID = lParentID;
  402. recs.m_lDontAutoDelete = (long)CTime::GetCurrentTime().GetTime();
  403. if( bChangeOrder )
  404. recs.m_dOrder = dOrder;
  405. recs.Update();
  406. dOrder = dOrder + dIncrement;
  407. }
  408. i++;
  409. }
  410. recs.Close();
  411. }
  412. CATCHDAO
  413. return (i == count);
  414. }
  415. // reorders the "lParentID" Group, inserting before the given id.
  416. // if the id cannot be found, this appends the IDs.
  417. BOOL CClipIDs::ReorderGroupInsert( long lParentID, long lInsertBeforeID )
  418. {
  419. int count = GetSize();
  420. int i = 1; // start the enumeration
  421. int insert = 0;
  422. BOOL bResult;
  423. try
  424. {
  425. MoveTo(-1); // move all elements outside any group
  426. CMainTable recs;
  427. recs.m_strSort = "dOrder ASC";
  428. recs.Open( "SELECT * FROM Main WHERE lParentID = %d", lParentID );
  429. while( !recs.IsEOF() )
  430. {
  431. if( recs.m_lID == lInsertBeforeID )
  432. {
  433. insert = i;
  434. i = insert + count;
  435. }
  436. recs.Edit();
  437. recs.m_dOrder = i;
  438. recs.Update();
  439. i++;
  440. recs.MoveNext();
  441. }
  442. recs.Close();
  443. if( insert == 0 )
  444. insert = i;
  445. // move the elements into their proper position in the group
  446. bResult = MoveTo( lParentID, (double) insert, (double) 1 );
  447. }
  448. CATCHDAO
  449. return bResult;
  450. }
  451. // Empties this array and fills it with the elements of the given group ID
  452. BOOL CClipIDs::LoadElementsOf( long lGroupID )
  453. {
  454. int fldID; // index of the lID field
  455. COleVariant varID; // value of the lID field
  456. int count = 0;
  457. SetSize(0);
  458. try
  459. {
  460. CMainTable recs;
  461. recs.SetBindFields(false);
  462. recs.Open("SELECT lID FROM Main WHERE lParentID = %d", lGroupID);
  463. fldID = GetFieldPos(recs,"lID");
  464. VERIFY( fldID == 0 ); // should be 0 since it is the only field
  465. while( !recs.IsEOF() )
  466. {
  467. recs.GetFieldValue( fldID, varID );
  468. Add( varID.lVal );
  469. recs.MoveNext();
  470. }
  471. recs.Close();
  472. }
  473. CATCHDAO
  474. return GetSize();
  475. }
  476. // Creates copies (duplicates) of all items in this array and assigns the
  477. // lParentID of the copies to the given "lParentID" group.
  478. // - if lParentID <= 0, then the copies have the same parent as the source
  479. // - pCopies is filled with the corresponding duplicate IDs.
  480. // - pAddNewTable and pSeekTable are used for more efficient recursion.
  481. // - the primary overhead for recursion is one ID array per level deep.
  482. // an alternative design would be to have one CMainTable per level deep,
  483. // but I thought that might be too costly, so I implemented it this way.
  484. BOOL CClipIDs::CopyTo( long lParentID, CClipIDs* pCopies,
  485. CMainTable* pAddNewTable, CMainTable* pSeekTable )
  486. {
  487. int count = GetSize();
  488. if( pCopies )
  489. {
  490. pCopies->SetSize( count );
  491. // initialize all IDs to 0
  492. for( int i=0; i < count; i++ )
  493. pCopies->ElementAt(i) = 0;
  494. }
  495. if( count == 0 )
  496. return TRUE;
  497. // for Seeking
  498. BOOL bSeekFailed = FALSE;
  499. COleVariant varID( (long) 0, VT_I4 );
  500. // for recursing into groups
  501. CMainTable* pAddTable = pAddNewTable;
  502. CMainTable* pTable = pSeekTable;
  503. CClipIDs groupIDs;
  504. long lCopyID;
  505. try
  506. {
  507. if( pTable == NULL )
  508. {
  509. pTable = new CMainTable;
  510. pTable->Open(dbOpenTable,"Main");
  511. pTable->SetCurrentIndex("lID");
  512. }
  513. if( pAddTable == NULL )
  514. {
  515. pAddTable = new CMainTable;
  516. pAddTable->Open(dbOpenTable,"Main");
  517. // pAddTable->SetCurrentIndex("lID");
  518. }
  519. for( int i=0; i < count; i++ )
  520. {
  521. varID.lVal = ElementAt(i);
  522. // Find first record whose [lID] field == lID
  523. if( pTable->Seek(_T("="), &varID) )
  524. {
  525. // copy the record
  526. pAddTable->AddNew(); // overridden to fetch autoincr lID
  527. lCopyID = pAddTable->m_lID;
  528. pAddTable->CopyRec( *pTable ); // copy the fields
  529. pAddTable->m_lDontAutoDelete = (long)CTime::GetCurrentTime().GetTime();
  530. if( lParentID > 0 ) // if valid, assign the given parent
  531. pAddTable->m_lParentID = lParentID;
  532. pAddTable->Update();
  533. // if it's a group, copy its elements
  534. if( pTable->m_bIsGroup )
  535. {
  536. groupIDs.LoadElementsOf( pTable->m_lID );
  537. // RECURSION
  538. groupIDs.CopyTo( lCopyID, NULL, pAddTable, pTable );
  539. }
  540. }
  541. else
  542. {
  543. ASSERT(0);
  544. bSeekFailed = TRUE;
  545. break;
  546. }
  547. if( pCopies )
  548. pCopies->ElementAt(i) = lCopyID;
  549. }
  550. // if we were not given the table, then we created it, so we must delete it
  551. if( pAddTable && pAddNewTable == NULL )
  552. {
  553. pAddTable->Close();
  554. delete pAddTable;
  555. }
  556. if( pTable && pSeekTable == NULL )
  557. {
  558. pTable->Close();
  559. delete pTable;
  560. }
  561. }
  562. CATCHDAO
  563. return !bSeekFailed;
  564. }
  565. BOOL CClipIDs::DeleteIDs( bool bDisband )
  566. {
  567. CPopup status(0,0,::GetForegroundWindow());
  568. bool bAllowShow;
  569. bAllowShow = IsAppWnd(::GetForegroundWindow());
  570. BOOL bRet = TRUE;
  571. int count = GetSize();
  572. if(count <= 0)
  573. return FALSE;
  574. for( int i=0; i < count; i++ )
  575. {
  576. if( bAllowShow )
  577. status.Show( StrF("Deleting %d out of %d...",i+1,count) );
  578. bRet = bRet && DeleteID( ElementAt(i), bDisband );
  579. }
  580. return bRet;
  581. }
  582. /*------------------------------------------------------------------*\
  583. COleClipSource
  584. \*------------------------------------------------------------------*/
  585. //IMPLEMENT_DYNAMIC(COleClipSource, COleDataSource)
  586. COleClipSource::COleClipSource()
  587. {
  588. }
  589. COleClipSource::~COleClipSource()
  590. {
  591. }
  592. BOOL COleClipSource::DoDelayRender()
  593. {
  594. CClipTypes types;
  595. m_ClipIDs.GetTypes( types );
  596. int count = types.GetSize();
  597. for( int i=0; i < count; i++ )
  598. DelayRenderData( types[i] );
  599. return count;
  600. }
  601. BOOL COleClipSource::DoImmediateRender()
  602. {
  603. int count = m_ClipIDs.GetSize();
  604. if( count <= 0 )
  605. return 0;
  606. if( count == 1 )
  607. {
  608. CClipFormats formats;
  609. CClipFormat* pCF;
  610. CClip::LoadFormats( m_ClipIDs[0], formats );
  611. count = formats.GetSize(); // reusing "count"
  612. for( int i=0; i < count; i++ )
  613. {
  614. pCF = &formats[i];
  615. CacheGlobalData( pCF->m_cfType, pCF->m_hgData );
  616. pCF->m_hgData = 0; // OLE owns it now
  617. }
  618. formats.RemoveAll();
  619. return count;
  620. }
  621. HGLOBAL hGlobal;
  622. CString text = m_ClipIDs.AggregateText( CF_TEXT, "\r\n" );
  623. hGlobal = NewGlobalP( (void*)(LPCSTR) text, text.GetLength()+1 );
  624. CacheGlobalData( CF_TEXT, hGlobal );
  625. return hGlobal != 0;
  626. }
  627. BEGIN_MESSAGE_MAP(COleClipSource, COleDataSource)
  628. END_MESSAGE_MAP()
  629. // COleClipSource message handlers
  630. BOOL COleClipSource::OnRenderGlobalData(LPFORMATETC lpFormatEtc, HGLOBAL* phGlobal)
  631. {
  632. HGLOBAL hData = m_ClipIDs.Render( lpFormatEtc->cfFormat );
  633. if( !hData )
  634. return FALSE;
  635. // if phGlobal is null, we can just give the allocated mem
  636. // else, our data must fit within the GlobalSize(*phGlobal)
  637. if( *phGlobal == 0 )
  638. *phGlobal = hData;
  639. else
  640. {
  641. UINT len = min( ::GlobalSize(*phGlobal), ::GlobalSize(hData) );
  642. if( len )
  643. CopyToGlobalHH( *phGlobal, hData, len );
  644. ::GlobalFree( hData );
  645. }
  646. return TRUE;
  647. }
  648. /*------------------------------------------------------------------*\
  649. CProcessPaste
  650. \*------------------------------------------------------------------*/
  651. CProcessPaste::CProcessPaste()
  652. {
  653. m_pOle = new COleClipSource;
  654. m_bSendPaste = true;
  655. }
  656. CProcessPaste::~CProcessPaste()
  657. {
  658. DELETE_PTR(m_pOle);
  659. }
  660. BOOL CProcessPaste::DoPaste()
  661. {
  662. if( m_pOle->DoImmediateRender() )
  663. {
  664. // MarkAsPasted() must be done first since it makes use of
  665. // m_pOle->m_ClipIDs and m_pOle is inaccessible after
  666. // SetClipboard is called.
  667. MarkAsPasted();
  668. // Ignore the clipboard change that we will cause IF:
  669. // 1) we are pasting a single element, since the element is already
  670. // in the db and its lDate was updated by MarkAsPasted().
  671. // OR
  672. // 2) we are pasting multiple, but g_Opt.m_bSaveMultiPaste is false
  673. if( GetClipIDs().GetSize() == 1 || !g_Opt.m_bSaveMultiPaste )
  674. m_pOle->CacheGlobalData(theApp.m_cfIgnoreClipboard, NewGlobalP("Ignore", sizeof("Ignore")));
  675. m_pOle->SetClipboard(); // m_pOle is now managed by the OLE clipboard
  676. m_pOle = NULL; // m_pOle should not be accessed past this point
  677. if(m_bSendPaste)
  678. theApp.SendPaste();
  679. return TRUE;
  680. }
  681. return FALSE;
  682. }
  683. BOOL CProcessPaste::DoDrag()
  684. {
  685. m_pOle->DoDelayRender();
  686. DROPEFFECT de = m_pOle->DoDragDrop( DROPEFFECT_COPY );
  687. if( de != DROPEFFECT_NONE )
  688. {
  689. MarkAsPasted();
  690. return TRUE;
  691. }
  692. return FALSE;
  693. }
  694. void CProcessPaste::MarkAsPasted()
  695. {
  696. CClipIDs& clips = GetClipIDs();
  697. if( clips.GetSize() == 1 )
  698. MarkClipAsPasted( clips.ElementAt(0) );
  699. }