ProcessPaste.cpp 18 KB

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