ProcessPaste.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893
  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", g_Opt.m_bMultiPasteReverse && g_Opt.m_bHistoryStartTop);
  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, BOOL bReverse)
  302. {
  303. CString csSQL;
  304. CDataTable recset;
  305. CString text;
  306. char* pData = NULL;
  307. DWORD len;
  308. DWORD maxLen;
  309. CLIPFORMAT cfRTF = GetFormatID(CF_RTF);
  310. // maybe we should sum up the "recset.m_ooData.m_dwDataLength" of all IDs first
  311. // in order to determine the max space required?? Or would that be wastefull?
  312. // allocate a large initial buffer to minimize realloc for concatenations
  313. text.GetBuffer(1000);
  314. text.ReleaseBuffer(0);
  315. int numIDs = GetSize();
  316. int* pIDs = GetData();
  317. csSQL.Format(
  318. "SELECT Data.* FROM Data "
  319. "INNER JOIN Main ON Main.lDataID = Data.lDataID "
  320. "WHERE Data.strClipBoardFormat = \'%s\' "
  321. "AND Main.lID = %%d",
  322. GetFormatName(cfType));
  323. try
  324. {
  325. int nIndex;
  326. for( int i=0; i < numIDs; i++ )
  327. {
  328. nIndex = i;
  329. if(bReverse)
  330. {
  331. nIndex = numIDs - i - 1;
  332. }
  333. recset.Open( csSQL, pIDs[nIndex] );
  334. if( !recset.IsBOF() && !recset.IsEOF() )
  335. {
  336. maxLen = recset.m_ooData.m_dwDataLength;
  337. if( maxLen == 0 )
  338. continue;
  339. pData = (char*) GlobalLock(recset.m_ooData.m_hData);
  340. ASSERT( pData );
  341. // verify that pData is null terminated
  342. // do a quick check to see if the last character is null
  343. if( pData[maxLen-1] != '\0' )
  344. {
  345. for( len=0; len < maxLen && pData[len] != '\0'; len++ ) {}
  346. // if it is not null terminated, skip this item
  347. if( len >= maxLen )
  348. continue;
  349. }
  350. // if(i == 0 && cfType == cfRTF)
  351. // {
  352. // pData[maxLen-2] = '\0';
  353. // }
  354. text += pData;
  355. GlobalUnlock(recset.m_ooData.m_hData);
  356. if( pSeparator )
  357. text += pSeparator;
  358. // if((i == numIDs-1) && cfType == cfRTF)
  359. // {
  360. // text += "}";
  361. // }
  362. }
  363. recset.Close();
  364. }
  365. }
  366. CATCHDAO
  367. return text;
  368. }
  369. //----------------------------------------------
  370. // ELEMENT (Clip or Group) MANAGEMENT FUNCTIONS
  371. //----------------------------------------------
  372. // returns the address of the given id in this array or NULL.
  373. long* CClipIDs::FindID( long lID )
  374. {
  375. int count = GetSize();
  376. long* pID = (long*) GetData();
  377. for( int i=0; i < count; i++ )
  378. {
  379. if( *pID == lID )
  380. return pID;
  381. pID++;
  382. }
  383. return NULL;
  384. }
  385. // Blindly Moves IDs into the lParentID Group sequentially with the given order
  386. // (i.e. this does not check to see if the IDs' order conflict)
  387. // if( dIncrement < 0 ), this does not change the order
  388. BOOL CClipIDs::MoveTo( long lParentID, double dFirst, double dIncrement )
  389. {
  390. int count = GetSize();
  391. int i = 0;
  392. COleVariant varKey( (long) 0 ); // VT_I4
  393. double dOrder = dFirst;
  394. BOOL bChangeOrder = (dIncrement >= 0);
  395. try
  396. {
  397. CMainTable recs;
  398. recs.Open( dbOpenTable, "Main" );
  399. recs.SetCurrentIndex("lID"); // set "Seek" to use this index.
  400. // for each id, assign lParentID
  401. while( i < count )
  402. {
  403. varKey.lVal = ElementAt(i);
  404. // Goto the record whose [lID] field == varKey
  405. if( !recs.Seek( _T("="), &varKey ) )
  406. {
  407. ASSERT(0);
  408. break;
  409. }
  410. // don't allow an item to become its own parent
  411. // NOTE!: deeper recursion is not checked, so it is theoretically
  412. // possible for: A -> B -> A
  413. if( recs.m_lID != lParentID )
  414. {
  415. recs.Edit();
  416. recs.m_lParentID = lParentID;
  417. recs.m_lDontAutoDelete = (long)CTime::GetCurrentTime().GetTime();
  418. if( bChangeOrder )
  419. recs.m_dOrder = dOrder;
  420. recs.Update();
  421. dOrder = dOrder + dIncrement;
  422. }
  423. i++;
  424. }
  425. recs.Close();
  426. }
  427. CATCHDAO
  428. return (i == count);
  429. }
  430. // reorders the "lParentID" Group, inserting before the given id.
  431. // if the id cannot be found, this appends the IDs.
  432. BOOL CClipIDs::ReorderGroupInsert( long lParentID, long lInsertBeforeID )
  433. {
  434. int count = GetSize();
  435. int i = 1; // start the enumeration
  436. int insert = 0;
  437. BOOL bResult;
  438. try
  439. {
  440. MoveTo(-1); // move all elements outside any group
  441. CMainTable recs;
  442. recs.m_strSort = "dOrder ASC";
  443. recs.Open( "SELECT * FROM Main WHERE lParentID = %d", lParentID );
  444. while( !recs.IsEOF() )
  445. {
  446. if( recs.m_lID == lInsertBeforeID )
  447. {
  448. insert = i;
  449. i = insert + count;
  450. }
  451. recs.Edit();
  452. recs.m_dOrder = i;
  453. recs.Update();
  454. i++;
  455. recs.MoveNext();
  456. }
  457. recs.Close();
  458. if( insert == 0 )
  459. insert = i;
  460. // move the elements into their proper position in the group
  461. bResult = MoveTo( lParentID, (double) insert, (double) 1 );
  462. }
  463. CATCHDAO
  464. return bResult;
  465. }
  466. // Empties this array and fills it with the elements of the given group ID
  467. BOOL CClipIDs::LoadElementsOf( long lGroupID )
  468. {
  469. int fldID; // index of the lID field
  470. COleVariant varID; // value of the lID field
  471. int count = 0;
  472. SetSize(0);
  473. try
  474. {
  475. CMainTable recs;
  476. recs.SetBindFields(false);
  477. recs.Open("SELECT lID FROM Main WHERE lParentID = %d", lGroupID);
  478. fldID = GetFieldPos(recs,"lID");
  479. VERIFY( fldID == 0 ); // should be 0 since it is the only field
  480. while( !recs.IsEOF() )
  481. {
  482. recs.GetFieldValue( fldID, varID );
  483. Add( varID.lVal );
  484. recs.MoveNext();
  485. }
  486. recs.Close();
  487. }
  488. CATCHDAO
  489. return GetSize();
  490. }
  491. // Creates copies (duplicates) of all items in this array and assigns the
  492. // lParentID of the copies to the given "lParentID" group.
  493. // - if lParentID <= 0, then the copies have the same parent as the source
  494. // - pCopies is filled with the corresponding duplicate IDs.
  495. // - pAddNewTable and pSeekTable are used for more efficient recursion.
  496. // - the primary overhead for recursion is one ID array per level deep.
  497. // an alternative design would be to have one CMainTable per level deep,
  498. // but I thought that might be too costly, so I implemented it this way.
  499. BOOL CClipIDs::CopyTo( long lParentID, CClipIDs* pCopies,
  500. CMainTable* pAddNewTable, CMainTable* pSeekTable )
  501. {
  502. int count = GetSize();
  503. if( pCopies )
  504. {
  505. pCopies->SetSize( count );
  506. // initialize all IDs to 0
  507. for( int i=0; i < count; i++ )
  508. pCopies->ElementAt(i) = 0;
  509. }
  510. if( count == 0 )
  511. return TRUE;
  512. // for Seeking
  513. BOOL bSeekFailed = FALSE;
  514. COleVariant varID( (long) 0, VT_I4 );
  515. // for recursing into groups
  516. CMainTable* pAddTable = pAddNewTable;
  517. CMainTable* pTable = pSeekTable;
  518. CClipIDs groupIDs;
  519. long lCopyID;
  520. try
  521. {
  522. if( pTable == NULL )
  523. {
  524. pTable = new CMainTable;
  525. pTable->Open(dbOpenTable,"Main");
  526. pTable->SetCurrentIndex("lID");
  527. }
  528. if( pAddTable == NULL )
  529. {
  530. pAddTable = new CMainTable;
  531. pAddTable->Open(dbOpenTable,"Main");
  532. // pAddTable->SetCurrentIndex("lID");
  533. }
  534. for( int i=0; i < count; i++ )
  535. {
  536. varID.lVal = ElementAt(i);
  537. // Find first record whose [lID] field == lID
  538. if( pTable->Seek(_T("="), &varID) )
  539. {
  540. // copy the record
  541. pAddTable->AddNew(); // overridden to fetch autoincr lID
  542. lCopyID = pAddTable->m_lID;
  543. pAddTable->CopyRec( *pTable ); // copy the fields
  544. pAddTable->m_lDontAutoDelete = (long)CTime::GetCurrentTime().GetTime();
  545. if( lParentID > 0 ) // if valid, assign the given parent
  546. pAddTable->m_lParentID = lParentID;
  547. pAddTable->Update();
  548. // if it's a group, copy its elements
  549. if( pTable->m_bIsGroup )
  550. {
  551. groupIDs.LoadElementsOf( pTable->m_lID );
  552. // RECURSION
  553. groupIDs.CopyTo( lCopyID, NULL, pAddTable, pTable );
  554. }
  555. }
  556. else
  557. {
  558. ASSERT(0);
  559. bSeekFailed = TRUE;
  560. break;
  561. }
  562. if( pCopies )
  563. pCopies->ElementAt(i) = lCopyID;
  564. }
  565. // if we were not given the table, then we created it, so we must delete it
  566. if( pAddTable && pAddNewTable == NULL )
  567. {
  568. pAddTable->Close();
  569. delete pAddTable;
  570. }
  571. if( pTable && pSeekTable == NULL )
  572. {
  573. pTable->Close();
  574. delete pTable;
  575. }
  576. }
  577. CATCHDAO
  578. return !bSeekFailed;
  579. }
  580. BOOL CClipIDs::DeleteIDs( bool bDisband )
  581. {
  582. CPopup status(0,0,::GetForegroundWindow());
  583. bool bAllowShow;
  584. bAllowShow = IsAppWnd(::GetForegroundWindow());
  585. BOOL bRet = TRUE;
  586. int count = GetSize();
  587. if(count <= 0)
  588. return FALSE;
  589. for( int i=0; i < count; i++ )
  590. {
  591. if( bAllowShow )
  592. status.Show( StrF("Deleting %d out of %d...",i+1,count) );
  593. bRet = bRet && DeleteID( ElementAt(i), bDisband );
  594. }
  595. return bRet;
  596. }
  597. /*------------------------------------------------------------------*\
  598. COleClipSource
  599. \*------------------------------------------------------------------*/
  600. //IMPLEMENT_DYNAMIC(COleClipSource, COleDataSource)
  601. COleClipSource::COleClipSource()
  602. {
  603. m_bLoadedFormats = false;
  604. m_bOnlyPaste_CF_TEXT = false;
  605. }
  606. COleClipSource::~COleClipSource()
  607. {
  608. }
  609. BOOL COleClipSource::DoDelayRender()
  610. {
  611. CClipTypes types;
  612. m_ClipIDs.GetTypes( types );
  613. int count = types.GetSize();
  614. for( int i=0; i < count; i++ )
  615. DelayRenderData( types[i] );
  616. return count;
  617. }
  618. BOOL COleClipSource::DoImmediateRender()
  619. {
  620. if(m_bLoadedFormats)
  621. return TRUE;
  622. m_bLoadedFormats = true;
  623. int count = m_ClipIDs.GetSize();
  624. if(count <= 0)
  625. return 0;
  626. if(count == 1)
  627. {
  628. CClipFormats formats;
  629. CClip::LoadFormats(m_ClipIDs[0], formats, m_bOnlyPaste_CF_TEXT);
  630. return LoadFormats(&formats);
  631. }
  632. HGLOBAL hGlobal;
  633. CString text = m_ClipIDs.AggregateText(CF_TEXT, "\r\n", g_Opt.m_bMultiPasteReverse && g_Opt.m_bHistoryStartTop);
  634. hGlobal = NewGlobalP((void*)(LPCSTR) text, text.GetLength()+1);
  635. CacheGlobalData(CF_TEXT, hGlobal);
  636. // text = "{\rtf1";
  637. // text += m_ClipIDs.AggregateText(GetFormatID(CF_RTF), "\r\n", true);
  638. // text += "}";
  639. //
  640. // hGlobal = NewGlobalP((void*)(LPCSTR) text, text.GetLength()+1);
  641. // CacheGlobalData(GetFormatID(CF_RTF), hGlobal);
  642. return hGlobal != 0;
  643. }
  644. long COleClipSource::LoadFormats(CClipFormats *pFormats)
  645. {
  646. CClipFormat* pCF;
  647. int count = pFormats->GetSize(); // reusing "count"
  648. for(int i = 0; i < count; i++)
  649. {
  650. pCF = &pFormats->ElementAt(i);
  651. CacheGlobalData( pCF->m_cfType, pCF->m_hgData );
  652. pCF->m_hgData = 0; // OLE owns it now
  653. }
  654. pFormats->RemoveAll();
  655. m_bLoadedFormats = true;
  656. return count;
  657. }
  658. BEGIN_MESSAGE_MAP(COleClipSource, COleDataSource)
  659. END_MESSAGE_MAP()
  660. // COleClipSource message handlers
  661. BOOL COleClipSource::OnRenderGlobalData(LPFORMATETC lpFormatEtc, HGLOBAL* phGlobal)
  662. {
  663. HGLOBAL hData = m_ClipIDs.Render( lpFormatEtc->cfFormat );
  664. if( !hData )
  665. return FALSE;
  666. // if phGlobal is null, we can just give the allocated mem
  667. // else, our data must fit within the GlobalSize(*phGlobal)
  668. if( *phGlobal == 0 )
  669. *phGlobal = hData;
  670. else
  671. {
  672. UINT len = min( ::GlobalSize(*phGlobal), ::GlobalSize(hData) );
  673. if( len )
  674. CopyToGlobalHH( *phGlobal, hData, len );
  675. ::GlobalFree( hData );
  676. }
  677. return TRUE;
  678. }
  679. /*------------------------------------------------------------------*\
  680. CProcessPaste
  681. \*------------------------------------------------------------------*/
  682. CProcessPaste::CProcessPaste()
  683. {
  684. m_pOle = new COleClipSource;
  685. m_bSendPaste = true;
  686. m_bActivateTarget = true;
  687. m_bOnlyPaste_CF_TEXT = false;
  688. }
  689. CProcessPaste::~CProcessPaste()
  690. {
  691. DELETE_PTR(m_pOle);
  692. }
  693. BOOL CProcessPaste::DoPaste()
  694. {
  695. m_pOle->m_bOnlyPaste_CF_TEXT = m_bOnlyPaste_CF_TEXT;
  696. if( m_pOle->DoImmediateRender() )
  697. {
  698. // MarkAsPasted() must be done first since it makes use of
  699. // m_pOle->m_ClipIDs and m_pOle is inaccessible after
  700. // SetClipboard is called.
  701. MarkAsPasted();
  702. // Ignore the clipboard change that we will cause IF:
  703. // 1) we are pasting a single element, since the element is already
  704. // in the db and its lDate was updated by MarkAsPasted().
  705. // OR
  706. // 2) we are pasting multiple, but g_Opt.m_bSaveMultiPaste is false
  707. if( GetClipIDs().GetSize() == 1 || !g_Opt.m_bSaveMultiPaste )
  708. m_pOle->CacheGlobalData(theApp.m_cfIgnoreClipboard, NewGlobalP("Ignore", sizeof("Ignore")));
  709. m_pOle->SetClipboard(); // m_pOle is now managed by the OLE clipboard
  710. m_pOle = NULL; // m_pOle should not be accessed past this point
  711. //#ifndef _DEBUG
  712. if(m_bSendPaste)
  713. theApp.SendPaste(m_bActivateTarget);
  714. //#endif
  715. return TRUE;
  716. }
  717. return FALSE;
  718. }
  719. BOOL CProcessPaste::DoDrag()
  720. {
  721. m_pOle->DoDelayRender();
  722. DROPEFFECT de = m_pOle->DoDragDrop( DROPEFFECT_COPY );
  723. if( de != DROPEFFECT_NONE )
  724. {
  725. MarkAsPasted();
  726. return TRUE;
  727. }
  728. return FALSE;
  729. }
  730. void CProcessPaste::MarkAsPasted()
  731. {
  732. CClipIDs& clips = GetClipIDs();
  733. if( clips.GetSize() == 1 )
  734. MarkClipAsPasted( clips.ElementAt(0) );
  735. }