home *** CD-ROM | disk | FTP | other *** search
/ Softwarová Záchrana 3 / Softwarova-zachrana-3.bin / ArsClip / source.zip / UnitClipQueue.pas < prev    next >
Pascal/Delphi Source File  |  2004-10-11  |  46KB  |  1,637 lines

  1. unit UnitClipQueue;
  2. {
  3.     Purpose:
  4.         Encapsulate all the rules of of the queue
  5.         - number of items allowed
  6.         - deleting icons on item removal (no memory leaks)
  7.         - etc
  8.         Handle a separate queue of items removed from the ClipQueue
  9.  
  10.     Updates:
  11.         GetClipboardFormatName - check for failure
  12.         -------------------
  13.         Fix for 0 sized TPagedStringQueue
  14.         
  15.         ------------------
  16.         Better way to enumerate the clipboard formats - single API call
  17.         Workaround for WinXP when retreiving from clipboard
  18.  
  19.  
  20.         ------------------
  21.         Ability to save/load a clipboard item (TClipItem)
  22.         Full color ICons
  23.  
  24.         ------------------
  25.         Fix for DragQuery API (ClipItem) on Win9x machines.
  26.  
  27.         Revamped naming a little - removed some TClipItem wrapper functions
  28.         from the queues (that weren't really a queue function).
  29.         New Objects to simplify support for Rich Text, Unicode, and
  30.         more. TClipData & TClipItem
  31.  
  32.  
  33.         Added support for storing many data items along with text version of
  34.         and item.
  35.         Added support for loading a StringQueue (for Remove Items)
  36.  
  37.         --------------
  38.         Created a basic StringQueue that's just a FIFO to be
  39.         used for a RemovedQueue object.
  40.         MoveDuplicateTop logic moved here (where it should be)
  41.         Ignore duplicates durring Add or Instert
  42. }
  43.  
  44.  
  45. { TODO: Investigate large HTML -> RichText clips failing if large}
  46. {///////////////}
  47. {//}interface{//}
  48. {///////////////}
  49. uses classes, Windows, Graphics {TPicture};
  50.  
  51. var ClipDataDefaultIcon : HICON;
  52. {
  53.     TClipData
  54.         - data associated with a clipboard item ( aka TClipItem )
  55. }
  56. type TClipData = class(TObject)
  57.     private
  58.         size : cardinal;
  59.         s : string;
  60.         h : HICON;
  61.         timestamp : TDateTime;
  62.  
  63.     public
  64.         constructor Create(s : string; h : HICON);
  65.         destructor Destroy; override;
  66.         function GetString : string;
  67.         function GetHICON : HICON;
  68.         function GetHICONAbsolute : HICON; {used so the default icon is not saved}        
  69.         procedure SetString(s : string);
  70.         procedure SetHICON(h : HICON);
  71.         function GetCreationDate : TDateTime;
  72. end;
  73. {
  74.     TClipItem
  75.         - Get's data from the clipboard in native form,
  76.         put's data on the clipboard, Keeps a plain text version of the item,
  77.         and associates an icon with the handle (see TClipData)
  78. }
  79. const CF_FILE_RICHTEXT = CF_PRIVATEFIRST;
  80. const CF_FILE_HTML = CF_PRIVATEFIRST + 1;
  81.  
  82. type TClipItem = class(TObject)
  83.     private
  84.         CFormat : WORD; {The format & Handle of the copied clipboard item}
  85.         CHandle : THandle;  {The bare essentials for a clipboard item}
  86.  
  87.         function GetFilenamesAsText(h : THandle) : string;
  88.         procedure CleanupMemory;
  89.         //procedure PlaceOnClipboard;
  90.     public
  91.         CData : TClipdata; {Data relating to the clipboard item}
  92.                            {Used to store a text version of the file and
  93.                             save the ICON of the program that this text came from}
  94.         constructor Create;
  95.         destructor Destroy; override;
  96.  
  97.  
  98.         function GetClipboardItem(hi : HICON; OverrideFormat : WORD = 0; SizeLimit : cardinal = $FFFF) : cardinal; overload;
  99.  
  100.  
  101.         function GetAsText : string;
  102.         function HasText : boolean;
  103.         procedure OverrideTextVersionOfItem(s : string);
  104.         function GetHandle : THandle;
  105.         function GetDataSize : cardinal;
  106.         function GetFormat : cardinal;
  107.         function GetFormatName(AccessHandle : boolean = true) : string;
  108.         procedure GetDIB(pic : TPicture);
  109.         procedure GetRichText(var s : string);
  110.  
  111.         procedure SaveToFile(path : string; index : integer);
  112.         procedure LoadFromFIle(path : string; index : integer);
  113.         function GetFilename(path : string; index : integer) : string;
  114.  
  115.         procedure SaveIconToFile(path : string; index : integer; sufix : string = '');
  116.         procedure LoadIconFromFile(path : string; index : integer; sufix : string = '');
  117.         function GeticonFilename(path : string; index : integer; sufix : string = '') : string;
  118.         {util}
  119.         //function DupHandle(h : Thandle; var sizeh : cardinal; SizeLimit : boolean = false) : Thandle;
  120. end;
  121.  
  122. {
  123.     TStringQueue
  124.         - Basic FIFO for strings
  125.         - Enforces a total size
  126.         - Override for populating the list
  127.     NOTES:
  128.         TStringList container because I don't want to deal with overriding
  129.         all the many ways the list can be altered. Lazy, yes I am, but it also
  130.         saves me from myself.
  131. }
  132. type TStringQueue = class(TObject)
  133.     protected
  134.         sl : TStringList;
  135.         qSize : cardinal;
  136.     public
  137.         constructor Create;
  138.         destructor Destroy; override;
  139.         // init the rule
  140.         procedure SetQueueSize(size : longint);
  141.  
  142.         // Add - for loading history, no size check
  143.         // InsertAtStart - for FrmClipboardManagaer
  144.         procedure AddNoSizeCheck(s : string);
  145.         procedure InsertAtStart(s : string); overload;
  146.  
  147.         function GetQueueCount : cardinal;
  148.         function GetItemText(index : cardinal) : string;
  149.  
  150.  
  151.         procedure DeleteItem(index : cardinal); virtual;
  152.         procedure ClearQueue; virtual;
  153. end;
  154.  
  155. {
  156.     TClipQueue
  157.         - A string Queue that ass
  158. }
  159. type TClipQueue = class(TStringQueue)
  160.     private
  161.         MoveDuplicateTop : boolean;
  162.  
  163.  
  164.     public
  165.         constructor Create;
  166.         destructor Destroy; override;
  167.  
  168.  
  169.         {config options}
  170.         procedure SetMoveDuplicateTop(enable: boolean);
  171.  
  172.         procedure MoveToStart(index : cardinal);
  173.         procedure Move(oldIndex : cardinal; newIndex : cardinal);
  174.  
  175.         {add & retreive operations}
  176.         procedure GetQueueItems(items : TStrings);
  177.         procedure SetQueueItems(items : TStrings);
  178.  
  179.         procedure AddNoSizeCheck(s : string; ci : TClipItem = nil); virtual;
  180.         procedure InsertAtStart(ci : TClipItem); overload;
  181.         function GetClipItem(index : cardinal) : TClipItem;
  182.  
  183.  
  184.         {find & delete}
  185.         function IndexOf(s : string) : cardinal;  overload;
  186.         function IndexOf(ci : TClipItem) : cardinal; overload;
  187.         procedure DeleteItem(index : cardinal); override;
  188.         procedure ClearQueue; override;
  189. end;
  190.  
  191. {
  192.     TPagedStringQueue
  193.         Poor Engineering 101 here, but oh well. (Does not inherit
  194.         from TStringQueue)
  195.  
  196.         - Circular queue of items 0-N but the Queue "start" is the first
  197.         item in the list
  198.         - Item 0 is the oldest item, Item (GetQueueCount - 1) is the newest
  199.         item
  200. }
  201. const PAGED_EXT = '.ac';
  202. const PAGED_CLIP_EXT = '.acz';
  203. const PAGED_STATEFILE = 'start' + PAGED_EXT;
  204. const PAGED_ICON_EXT = '.bmp';
  205.  
  206. type TPagedStringQueue = class(TObject)
  207.     private
  208.         base : string;
  209.         filename : string;
  210.         cache : string;
  211.         iconcache : string;
  212.         qSize : cardinal;
  213.         qStart : cardinal;
  214.         qCount : cardinal;
  215.  
  216.         ci : TClipITem;
  217.  
  218.         procedure SaveItem(s : string; index: cardinal; ci : TClipItem = nil);
  219.  
  220.         function IsEmptyItem(index : cardinal) : boolean;
  221.         function IsEmptyItemAbsolute(absoluteIndex : cardinal) : boolean;
  222.         function IsEmptyItemClipAbsolute(absoluteIndex : cardinal) : boolean;
  223.  
  224.         procedure RemoveOldestItem;
  225.         procedure SaveQueueState;
  226.         procedure ReIndexQueue;
  227.  
  228.         function IndexTranslate(index : cardinal) : cardinal;
  229.  
  230.         function GetItemAbsolute(absoluteIndex : cardinal) : string;
  231.         function GetItemClipAbsolute(absoluteIndex : cardinal) : TClipItem;
  232.  
  233.         function GetFilename(index : cardinal) : string;
  234.         function GetFilenameAbsolute(absoluteIndex : cardinal) : string;
  235.         function GetFilenameClip(index : cardinal) : string;
  236.         function GetFilenameClipAbsolute(absoluteIndex : cardinal) : string;
  237.         function GetFilenameIcon1Absolute(absoluteIndex : cardinal) : string;
  238.         function GetFilenameIcon2Absolute(absoluteIndex : cardinal) : string;
  239.     public
  240.  
  241.         constructor Create(filename : string; folder : string = '');
  242.         destructor Destroy; override;
  243.  
  244.  
  245.         procedure InsertAtStart(s : string; ci : TClipItem = nil); overload;
  246.         function GetQueueCount : cardinal;
  247.         function GetItemText(index : cardinal) : string;
  248.         function GetItemClip(Index: cardinal): TClipItem;
  249.  
  250.         procedure ClearQueue;
  251.         procedure SetQueueSize(size : cardinal);
  252. end;
  253.  
  254.  
  255.  
  256. var ClipQueue : TClipQueue;
  257. var RemovedQueue : TPagedStringQueue;
  258.  
  259. {////////////////////}
  260. {//}implementation{//}
  261. {////////////////////}
  262.  
  263.  
  264. uses UnitFrmMainPopup, UnitFrmClipboardManager, Forms {For Application object},
  265.     Clipbrd, StrUtils, SysUtils, ShellAPI, Dialogs, UnitMisc, UnitPaste;
  266.  
  267.  
  268.  
  269.  
  270. //-------------------
  271. // (Con/De)structors
  272. //-------------------
  273. constructor TStringQueue.Create;
  274. begin
  275.     sl := TStringList.Create;
  276. end;
  277.  
  278. destructor TStringQueue.Destroy;
  279. begin
  280.     MyFree(sl);
  281.     inherited Destroy;
  282. end;
  283.  
  284. procedure TStringQueue.SetQueueSize(size : longint);
  285. begin
  286.     qSize := size;
  287.     while (sl.count > size) and (sl.count > 0) do
  288.         self.DeleteItem(sl.count - 1);
  289. end;
  290.  
  291. procedure TStringQueue.DeleteItem(index : cardinal);
  292. begin
  293.     sl.Delete(index);
  294. end;
  295.  
  296. procedure TStringQueue.InsertAtStart(s : string);
  297. begin
  298.     sl.Insert(0,s);
  299.     self.SetQueueSize(self.qSize);
  300. end;
  301.  
  302. procedure TStringQueue.AddNoSizeCheck(s: string);
  303. var i : longint;
  304. begin
  305.     i := sl.IndexOf(s);
  306.     if (i = -1) then begin
  307.         sl.Add(s);
  308.     end;
  309. end;
  310.  
  311.  
  312.  
  313.  
  314. function TStringQueue.GetQueueCount : cardinal;
  315. begin
  316.     result := sl.count;
  317. end;
  318.  
  319. function TStringQueue.GetItemText(index : cardinal) : string;
  320. begin
  321.     result := sl[index];
  322. end;
  323.  
  324. procedure TStringQueue.ClearQueue;
  325. var i : integer;
  326. begin
  327.     for i := (sl.count - 1) downto 0 do begin
  328.         self.DeleteItem(i);
  329.     end;
  330. end;
  331.  
  332.  
  333.  
  334. //////////////////////////
  335. //
  336. // TClipQueue
  337. //
  338. //////////////////////////
  339.  
  340. //-------------------
  341. // (Con/De)structors
  342. //-------------------
  343. constructor TClipQueue.Create;
  344. begin
  345.     sl := TStringList.Create;
  346. end;
  347.  
  348. destructor TClipQueue.Destroy;
  349. begin
  350.     MyFree(sl);
  351.     inherited Destroy;
  352. end;
  353.  
  354.  
  355. //-------------------
  356. // Configuration
  357. //-------------------
  358. procedure TClipQueue.SetMoveDuplicateTop(enable: boolean);
  359. begin
  360.     self.MoveDuplicateTop := enable;
  361. end;
  362.  
  363.  
  364. //-------------------
  365. // Public Interface
  366. //-------------------
  367.  
  368.  
  369. //
  370. // items added to list
  371. //
  372. procedure TClipQueue.AddNoSizeCheck(s : string; ci : TClipItem = nil);
  373. var i : longint;
  374. begin
  375.     // No dups, add move to top if configured to do so
  376.  
  377.     i := sl.IndexOf(s);
  378.     if (i = -1) then begin
  379.         sl.AddObject(s, ci);
  380.     end else if (self.MoveDuplicateTop) then begin
  381.         self.MoveToStart(i);
  382.     end;
  383. end;
  384.  
  385. procedure TClipQueue.InsertAtStart(ci : TClipItem);
  386. var i : integer;
  387.     ci2 : TClipItem;
  388. begin
  389.     i := sl.IndexOf(ci.GetAsText);
  390.     if (i = -1) then begin
  391.         sl.InsertObject(0, ci.GetAsText, ci);
  392.         self.SetQueueSize(self.qSize);
  393.     end else begin
  394.         if (self.MoveDuplicateTop) then begin
  395.             // delete an add it in its place if only differs in Case CASE case
  396.             ci2 := self.GetClipItem(i);
  397.             if CompareStr(ci2.GetAsText, ci.GetAsText) <> 0 then begin
  398.                 DeleteItem(i);
  399.                 sl.InsertObject(0, ci.GetAsText, ci);
  400.                 self.SetQueueSize(self.qSize);
  401.             end else begin
  402.                 self.MoveToStart(i);
  403.                 UnitMisc.MyDestroyIcon(ci.CData.GetHICONAbsolute);
  404.                 UnitMisc.MyFree(ci);
  405.             end;
  406.         end;
  407.     end
  408.  
  409. end;
  410.  
  411.  
  412.  
  413. procedure TClipQueue.SetQueueItems(items : TStrings);
  414. begin
  415.     //
  416.     // trim queue if needed
  417.  
  418.     self.ClearQueue;
  419.     sl.AddStrings(items);
  420.     self.SetQueueSize(self.qSize);
  421. end;
  422.  
  423.  
  424.  
  425. function TClipQueue.IndexOf(s : string) : cardinal;
  426. begin
  427.     result := sl.IndexOf(s);
  428. end;
  429.  
  430. function TClipQueue.IndexOf(ci: TClipItem): cardinal;
  431. begin
  432.     result := sl.IndexOfObject(ci); 
  433. end;
  434.  
  435.  
  436. procedure TClipQueue.GetQueueItems(items : TStrings);
  437. begin
  438.     items.Clear;
  439.     items.AddStrings( sl );
  440. end;
  441.  
  442.  
  443. function TClipQueue.GetClipItem(index: cardinal): TClipItem;
  444.     var ci : TClipItem;
  445. begin
  446.     ci := TClipItem(sl.Objects[index]);
  447.     result := ci;
  448. end;
  449.  
  450.  
  451.  
  452.  
  453. //
  454. // Move items in the list
  455. //
  456. procedure TClipQueue.MoveToStart(index : cardinal);
  457. begin
  458.     sl.Move(index, 0);
  459. end;
  460.  
  461. procedure TClipQueue.Move(oldIndex : cardinal; newIndex : cardinal);
  462. begin
  463.     sl.Move(oldIndex, newindex);
  464. end;
  465.  
  466.  
  467.  
  468. //
  469. // Remove items from queue
  470. // NOTE: Resources must be released here
  471. //
  472. procedure TClipQueue.DeleteItem(index : cardinal);
  473. var ci : TClipItem;
  474. begin
  475.     // We've got to clean up the clone icon to avoid a memory leak
  476.     ci := TClipITem(sl.Objects[index]);
  477.  
  478.     // Add the text to the removed items
  479.     // ClipItem also has global memory to free
  480.     RemovedQueue.InsertAtStart(self.GetItemText(index), ci);
  481.  
  482.     if (ci <> nil) then begin
  483.         UnitMisc.AppendLog('^Deleting Icon^');
  484.         MyDestroyIcon(ci.CData.GetHICON);
  485.     end;
  486.     MyFree(ci);
  487.  
  488.  
  489.     // this MUST be the ONLY place an item is deleted
  490.     sl.Delete(index);
  491. end;
  492.  
  493.  
  494. procedure TClipQueue.ClearQueue;
  495. var i : longint;
  496. begin
  497.     // see note in DeleteQueueItem for rules of removing items
  498.     // from queue
  499.     // I know better, this must go in reverse order
  500.     for i := (sl.count - 1) downto 0 do begin
  501.         self.DeleteItem(i);
  502.     end;
  503.  
  504. end;
  505.  
  506.  
  507.  
  508.  
  509.  
  510.  
  511.  
  512.  
  513.  
  514.  
  515.  
  516.  
  517.  
  518. //------------------------------
  519. // ClipData / ClipItem
  520. //------------------------------
  521.  
  522. { TClipData }
  523.  
  524. constructor TClipData.Create(s: string; h: HICON);
  525. begin
  526.     self.s := s;
  527.     self.h := h;
  528. end;
  529.  
  530. destructor TClipData.Destroy;
  531. var s : string;
  532. begin
  533.     s := 'du what?';
  534.   inherited;
  535. end;
  536.  
  537. function TClipData.GetCreationDate: TDateTime;
  538. begin
  539.     result := self.timestamp;
  540. end;
  541.  
  542. function TClipData.GetHICON: HICON;
  543. begin
  544.     if (self.h = 0) then begin
  545.         result := UnitClipQueue.ClipDataDefaultIcon;
  546.     end else begin
  547.         result := self.h;
  548.     end;
  549. end;
  550.  
  551. function TClipData.GetHICONAbsolute: HICON;
  552. begin
  553.     result := self.h;
  554. end;
  555.  
  556. function TClipData.GetString: string;
  557. begin
  558.     result := self.s;
  559. end;
  560.  
  561. procedure TClipData.SetHICON(h: HICON);
  562. begin
  563.     self.h := h;
  564. end;
  565.  
  566. procedure TClipData.SetString(s: string);
  567. begin
  568.     self.s := s;
  569. end;
  570.  
  571. { TClipItem }
  572.  
  573. constructor TClipItem.Create;
  574. begin
  575.     self.CData := TClipData.Create('', 0);
  576.     self.CHandle := 0;
  577.     self.CFormat := 0;
  578. end;
  579.  
  580. destructor TClipItem.Destroy;
  581. begin
  582.     self.CleanupMemory;
  583.     MyFree(CData);
  584.     inherited Destroy;
  585. end;
  586.  
  587. procedure TClipItem.CleanupMemory;
  588. begin
  589.     if (self.CHandle <> 0) then begin
  590.         Windows.GlobalFree(self.CHandle);
  591.         self.CHandle := 0;
  592.     end;
  593. end;
  594.  
  595.  
  596.  
  597.  
  598. function TClipItem.GetAsText: string;
  599. begin
  600.     result := CData.GetString;
  601. end;
  602.  
  603. //
  604. // Return the format of clipitem saved
  605. // 0 = ERROR
  606. // NOTES: GetAtText will return empty unless the clipboard has CF_TEXT or
  607. // CF_HDROP
  608. //
  609. function TClipItem.GetClipboardItem( hi: HICON; OverrideFormat : word = 0; SizeLimit : cardinal = $FFFF): cardinal;
  610. var files : string;
  611.     CVolatileHandle : THandle;
  612.     IsText : boolean;
  613.     HasText : boolean;
  614.  
  615.     Procedure ChooseAFormat;
  616.     begin
  617.         if (not Windows.OpenClipboard(Application.Handle)) then begin
  618.             UnitMisc.AppendLog('<ClipItem - can''t open clipboard> ', true);
  619.             EXIT;
  620.         end;
  621.  
  622.         // This gets the first and
  623.         // most descriptive clipboard format the clipboard contains
  624.         self.CFormat := Windows.EnumClipboardFormats(0);
  625.         Windows.CloseClipboard;
  626.  
  627.         // OVERIDE format
  628.         // CF_BITMAP kills over in DupHandle, CD_DIB does not
  629.         // A wave clip may show up as a speaker icon if CF_WAVE is not
  630.         // before CF_DIB
  631.  
  632.         HasText := Clipboard.HasFormat(CF_TEXT);
  633.         if clipboard.HasFormat(CF_WAVE) then begin
  634.             CFormat := CF_WAVE
  635.         end else if clipboard.HasFormat(CF_DIB) then begin
  636.             CFormat := CF_DIB
  637.         end else if clipboard.HasFormat(CF_HDROP) then begin
  638.             CFormat := CF_HDROP
  639.         end else if clipboard.HasFormat(frmClipboardManager.CF_RICHTEXT) then begin
  640.             CFormat := frmClipboardManager.CF_RICHTEXT;
  641.             IsText := true;
  642.         end else if clipboard.HasFormat(frmClipboardManager.CF_HTML) then begin
  643.             CFormat := frmClipboardManager.CF_HTML;
  644.             IsText := true;
  645.         end else if clipboard.HasFormat(CF_UNICODETEXT) then begin
  646.             CFormat := CF_UNICODETEXT;
  647.             IsText := true;
  648.         end;
  649.         if (CFormat = CF_OEMTEXT) then begin
  650.             CFormat := CF_TEXT;
  651.             IsText := true;
  652.         end;
  653.         UnitMisc.AppendLog('<ClipItem Overided Format = ' + self.GetFormatName(false) );
  654.     end;
  655. begin
  656.     UnitMisc.AppendLog('<ClipItem> ', true);
  657.     Windows.SetLastError(ERROR_SUCCESS);     // workaround for some OS's
  658.     CData.SetHICON(HI);  // incase retreival fails, we don't want to leak memory
  659.  
  660.  
  661.     // find the current format
  662.     // must be non-zero
  663.     self.CleanupMemory;
  664.     CFormat := 0;
  665.     CHandle := 0;
  666.     result := 0;
  667.     IsText := false;
  668.     try
  669.         if OverrideFormat = 0 then begin
  670.             ChooseAFormat;
  671.         end else begin
  672.             CFormat := OverrideFormat;
  673.         end;
  674.  
  675.     except
  676.         UnitMisc.AppendLog('<ClipItem - Find Format exception' + SysErrorMessage(GetLastError) );
  677.         Windows.CloseClipboard;
  678.         result := 0;
  679.         CFormat := 0;
  680.         CHandle := 0;
  681.         EXIT;
  682.     end;
  683.  
  684.     if (CFormat  = 0) then begin
  685.         EXIT;
  686.     end;
  687.  
  688.  
  689.     //
  690.     // Dupe the handle, exit on error
  691.     //
  692.     try
  693.  
  694.         //
  695.         // Win9X will puke if you try to dup the handle and the clipboard is
  696.         // not open. The VolatileHandle name reminds me that it is not to be
  697.         // touched anywhere but here.
  698.         //
  699.         self.CData.size := SizeLimit;
  700.         if not Windows.OpenClipboard(Application.Handle) then begin
  701.             UnitMisc.AppendLog('<ClipItem - can''t open clipboard2 > ', true);
  702.             EXIT;
  703.         end;
  704.         CVolatileHandle := Windows.GetClipboardData(CFormat);
  705.         if (CVolatileHandle = 0) then begin
  706.             UnitMisc.AppendLog('<ClipItem - can''t get handle > ', true);
  707.             Windows.CloseClipboard;
  708.             EXIT;
  709.         end;
  710.         CHandle := UnitMisc.DupHandle(CVolatileHandle, self.CData.size, (SizeLimit <> $FFFF));
  711.         if (CHandle = 0) then begin
  712.             UnitMisc.AppendLog('<ClipItem - can''t dup handle> ');
  713.             Windows.CloseClipboard;
  714.             EXIT;
  715.         end;
  716.         Windows.CloseClipboard;
  717.  
  718.         // save the extra data
  719.         // Icon handle, Text version of item
  720.  
  721.         CData.SetHICON(HI);
  722.  
  723.         if (IsText or HasText) then begin
  724.             CData.SetString(Clipboard.AsText);
  725.         end else if (CFormat = CF_HDROP) then begin;
  726.             files := self.GetFilenamesAsText(CHandle) ;
  727.             UnitMisc.AppendLog('filenames: ' + files);
  728.             CData.SetString(files);
  729.         end else begin
  730.             CData.SetString('');
  731.         end;
  732.  
  733.     except
  734.         on E: Exception do begin
  735.             UnitMisc.AppendLog('<clipItem Dup Exception - ' + E.Message + ' ', true);
  736.             Windows.CloseClipboard;
  737.             CFormat := 0;
  738.             CHandle := 0;
  739.             result := 0;
  740.             EXIT;
  741.         end;
  742.     end;
  743.  
  744.     {Future user - when the clip was created}
  745.     cdata.timestamp := now;
  746.     UnitMisc.AppendLog('<ClipItem ClipboardSave success!> size=' + IntToSTr(CData.size) );
  747.     result := CFormat;
  748. end;
  749.  
  750.  
  751. // return all filenames or return empty string
  752. function TClipItem.GetFilenamesAsText(h : THandle): string;
  753. var i, j : longint;
  754.     s : string;
  755. begin
  756.     Windows.SetLastError(ERROR_SUCCESS);
  757.     result := '';
  758.  
  759.     if (CFormat = CF_HDROP) then begin
  760.         UnitMisc.AppendLog('    Detecting Filenames...');
  761.  
  762.         try
  763.             //
  764.             // On Win9x, the handle must be on the clipboard and the clipboard
  765.             // must be open; otherwise, bad bad stuff happens
  766.             //
  767.             UnitMisc.AppendLog('GetFilenamesAsText');
  768.             s := stringofchar(#0, Windows.MAX_PATH+1);
  769.             j := ShellApi.DragQueryFile(h, Cardinal(-1){//$FFFFFFFF}, nil, 0);
  770.             UnitMisc.AppendLog('FileCount=' + IntToSTr(j));
  771.             result := '';
  772.             for i := 0 to (j - 1) do begin
  773.                 s := stringofchar(#0, Windows.MAX_PATH+1);
  774.                 ShellApi.DragQueryFile(h, i, pchar(s), Windows.MAX_PATH);
  775.                 UnitMisc.AppendLog(Trim(String(s)));
  776.                 if i = (j - 1) then begin
  777.                     result := result + Trim(PChar(s));
  778.                 end else begin
  779.                     result := result + Trim(PChar(s)) + #13 + #10;
  780.                 end;
  781.             end;
  782.  
  783.         except
  784.             on e : exception do begin
  785.                 UnitMisc.AppendLog('GetFilenamesAsText' + #13#10 +
  786.                     e.Message + ' ', true
  787.                 );
  788.             end;
  789.         end;
  790.     end;
  791. end;
  792.  
  793. function TClipItem.GetFormat: cardinal;
  794. begin
  795.     result := self.CFormat;
  796. end;
  797.  
  798. function TClipItem.GetHandle: THandle;
  799. begin
  800.     result := self.CHandle;
  801. end;
  802.  
  803. procedure TClipItem.OverrideTextVersionOfItem(s : string);
  804. begin
  805.     self.CData.SetString(s);
  806. end;
  807.  
  808. {
  809. procedure TClipItem.PlaceOnClipboard;
  810. begin
  811.     // moved all pasting logic to the appropreite location, the Paste Object
  812.     if (CHandle <> 0) then begin
  813.         Paste.SetClipboardOnlyOnce;
  814.         Paste.SendText('', self);
  815.     end else begin
  816.         UnitMisc.AppendLog('ClipItem - PlaceOnClipboard failed ' + SysErrorMessage(GetLastError));
  817.     end;
  818. end;
  819. }
  820. function TClipItem.GetFormatName(AccessHandle : boolean = true) : string;
  821. var name : array[0 .. 80] of char;
  822.     format : cardinal;
  823.     p : ^tagBITMAPINFO;
  824. begin
  825.     Windows.SetLastError(ERROR_SUCCESS);
  826.  
  827.     format := self.CFormat;
  828.  
  829.     case (format) of
  830.         CF_DIB          : begin
  831.             if (AccessHandle) then begin
  832.                 p := Windows.GlobalLock(CHandle);
  833.                 if (p <> nil) then begin
  834.                     result := 'Picture (DIB) ' + IntToStr(p^.bmiHeader.biWidth) + 'x' + IntToStr(p^.bmiHeader.biHeight);
  835.                     Windows.GlobalUnlock(self.Chandle);
  836.                 end else begin
  837.                     UnitMisc.AppendLog('GetFormatName: ' + SysErrorMessage(GetLastError));
  838.                 end;
  839.             end else begin
  840.                 result := 'Picture (DIB)';
  841.             end;
  842.         end;
  843.         CF_WAVE         : result := 'Wave Audio';
  844.         CF_BITMAP       : result := 'Picture (Bitmap)';
  845.         CF_HDROP        : result := 'File(s)';
  846.  
  847.         CF_DIF          : result := 'CF_DIF';
  848.         CF_TEXT         : result := 'CF_TEXT';
  849.         CF_SYLK         : result := 'CF_SYLK';
  850.         CF_TIFF         : result := 'CF_TIFF';
  851.         CF_RIFF         : result := 'CF_RIFF';
  852.         CF_LOCALE       : result := 'CF_LOCALE';
  853.         CF_OEMTEXT      : result := 'CF_OEMTEXT';
  854.         CF_PALETTE      : result := 'CF_PALETTE';
  855.         CF_PENDATA      : result := 'CF_PENDATA';
  856.         CF_UNICODETEXT  : result := 'Unicode';
  857.         CF_ENHMETAFILE  : result := 'CF_ENHMETAFILE';
  858.         CF_METAFILEPICT : result := 'CF_METAFILEPICT';
  859.         else begin
  860.             UnitMisc.AppendLog('Unknown format...');
  861.             if Windows.GetClipboardFormatName(format,  @name,  sizeof(name)  ) <> 0 then begin
  862.                 Result := string(name);
  863.                 if (result  = '') then begin
  864.                     result := 'Unknown';
  865.                 end;
  866.             end else begin
  867.                 UnitMisc.AppendLog('Format Name failed', true);
  868.                 result := 'Error: Unknown';
  869.             end;
  870.         end;
  871.    end;
  872.  
  873. end;
  874.  
  875. procedure TClipItem.GetDIB( pic : TPicture);
  876.     procedure DibToBitmap(hDIB:  THandle; BM : TBitmap);
  877.     var
  878.         bmfh :  TBitmapFileHeader;
  879.         bi :  PBitmapInfo;
  880.         ms :  TMemoryStream;
  881.         ColorsUsed : integer;
  882.     begin
  883.         bi := PBitmapInfo( Windows.GlobalLock(hDIB) );
  884.         try
  885.             // Caculate the number of colors used (power of 2)
  886.             ColorsUsed := bi.bmiHeader.biClrUsed;
  887.             if (ColorsUsed = 0) and (bi.bmiHeader.biBitCount <= 8) then
  888.                 ColorsUsed := 1 shl bi.bmiHeader.biBitCount;
  889.  
  890.             bmfh.bfType := $4D42;  // 'BM'
  891.             bmfh.bfReserved1 := 0;
  892.             bmfh.bfReserved2 := 0;
  893.  
  894.             // point to location of actual data
  895.             // header is variable because of the RGBQuads
  896.             bmfh.bfOffBits := SizeOf(TBitmapFileHeader)       +
  897.                        SizeOf(TBitmapInfoHeader)       +
  898.                        ColorsUsed * SizeOf(TRGBQuad);
  899.             bmfh.bfSize := bmfh.bfOffBits + bi.bmiHeader.biSizeImage;
  900.  
  901.             // Create a fake bitmap file
  902.             // and load it into a TBitmap
  903.             ms := TMemoryStream.Create;
  904.             try
  905.                 ms.Write(bmfh, SizeOf(TBitmapFileHeader));
  906.                 ms.Write(bi^, bmfh.bfSize - SizeOf(TBitmapFileHeader));
  907.                 ms.Position := 0;
  908.  
  909.                 BM.LoadFromStream(ms)
  910.             finally
  911.                 ms.Free
  912.             end;
  913.         finally
  914.             Windows.GlobalUnlock(hDIB);
  915.         end;
  916.     end;
  917.  
  918. begin
  919.     Windows.SetLastError(ERROR_SUCCESS);
  920.     if (self.CFormat = CF_DIB) then begin
  921.         DibToBitmap(self.CHandle, pic.Bitmap);
  922.     end;
  923. end;
  924.  
  925. procedure TClipItem.GetRichText(var s: string);
  926. var p : PChar;
  927.     ss : TStringStream;
  928. begin
  929.     p := Windows.GlobalLock(self.CHandle);
  930.     if (p <> nil) then begin
  931.         ss := TStringStream.Create('');
  932.         ss.Write(p^, Windows.GlobalSize(self.CHandle));
  933.         s := ss.DataString;
  934.  
  935.         Windows.GlobalUnlock(self.Chandle);
  936.  
  937.         MyFree(ss);
  938.     end;
  939. end;
  940.  
  941.  
  942. //
  943. // Static Objects
  944. //
  945.  
  946. function TClipItem.GetDataSize: cardinal;
  947. begin
  948.     result := self.CData.size;
  949. end;
  950.  
  951. function TClipItem.GetFilename(path: string; index: integer): string;
  952. begin
  953.     result := path + IntToHex(index, 8) + '.acz';
  954. end;
  955.  
  956.  
  957. procedure TClipItem.SaveToFile(path : string; index : integer);
  958. var f : file;
  959.     p : Pointer;
  960.     w : word;
  961.     clpname : string;
  962. begin
  963.     if self.CHandle = 0 then EXIT;
  964.     if self.CData.size = 0 then EXIT;
  965.  
  966.     clpname := self.GetFilename(path, index);
  967.     assign(f, clpname);
  968.     rewrite(f,1);
  969.  
  970.     if (self.CFormat = frmClipboardManager.CF_RICHTEXT) then begin
  971.         w := CF_FILE_RICHTEXT;
  972.     end else if (self.CFormat = frmClipboardManager.CF_HTML ) then begin
  973.         w := CF_FILE_HTML;
  974.     end else begin
  975.         w := self.CFormat;
  976.     end;
  977.     blockwrite(f, w, sizeof(self.CFormat));
  978.     blockwrite(f, self.CData.size, sizeof(self.CData.size));
  979.  
  980.     p := GlobalLock(self.CHandle);
  981.     blockwrite(f, p^, self.cdata.size);
  982.     GlobalUnlock(self.CHandle);
  983.  
  984.     close(f);
  985. end;
  986.  
  987. procedure TClipItem.LoadFromFile(path : string; index : integer);
  988. var f : file;
  989.     p : pointer;
  990.     clpname : string;
  991. begin
  992.     clpname := self.GetFilename(path, index); 
  993.     if not (FileExists(clpname)) then begin
  994.         EXIT;
  995.     end;
  996.  
  997.     assignfile(f, clpname);
  998.     reset(f,1);
  999.     if filesize(f) = 0 then begin
  1000.         EXIT;
  1001.     end;
  1002.     try
  1003.         blockread(f, self.CFormat, sizeof(self.CFormat));
  1004.         if (self.CFormat =  CF_FILE_RICHTEXT) then begin
  1005.             self.CFormat := frmClipboardManager.CF_RICHTEXT;
  1006.         end else if (self.CFormat = CF_FILE_HTML) then begin
  1007.             self.CFormat := frmClipboardManager.CF_HTML;
  1008.         end;
  1009.         blockread(f, self.CData.size, sizeof(self.CData.size));
  1010.     except
  1011.         on e: exception do begin
  1012.             self.CHandle := 0;
  1013.             self.CData.size := 0;
  1014.             UnitMisc.AppendLog('TClipItem.LoadFromFile: read1 error' + SysErrorMessage(GetLastError));
  1015.         end;
  1016.     end;
  1017.  
  1018.     self.CHandle := GlobalAlloc(GMEM_MOVEABLE,self.CData.size);
  1019.     p := GlobalLock(self.CHandle);
  1020.     if (p = nil) then begin
  1021.         self.CHandle := 0;
  1022.         self.CData.size := 0;
  1023.         UnitMisc.AppendLog('TClipItem.LoadFromFile: error couldn''t lock handle!');
  1024.         EXIT;
  1025.     end;
  1026.  
  1027.     try
  1028.         blockread(F, p^, self.CData.size);
  1029.         GlobalUnlock(self.CHandle);
  1030.     except
  1031.         on e: exception do begin
  1032.             self.CHandle := 0;
  1033.             self.CData.size := 0;
  1034.             UnitMisc.AppendLog('TClipItem.LoadFromFile: read2 error' + SysErrorMessage(GetLastError));
  1035.         end;
  1036.     end;
  1037.     closefile(f);
  1038. end;
  1039.  
  1040.     {
  1041.     function BitmapToIcon(Bitmap: TBitmap): HICON;
  1042.     var x, y : integer;
  1043.         info : TIconInfo;
  1044.         image, mask : TBitmap;
  1045.         i, j : Integer;
  1046.         TransparentColor : TColor;
  1047.     begin
  1048.         // this entire routine doesn't work - it erases black instead
  1049.         // of preserving tranparency
  1050.  
  1051.         X := 32;
  1052.         Y := 32;
  1053.  
  1054.         image:= TBitmap.Create;
  1055.         image.Width:= X;
  1056.         image.Height:= Y;
  1057.  
  1058.         image.Canvas.StretchDraw(Rect(0, 0, X, Y), Bitmap);
  1059.         image.TransparentColor:= Bitmap.TransparentColor;
  1060.  
  1061.         TransparentColor:= image.TransparentColor and $FFFFFF;
  1062.  
  1063.         mask:= TBitmap.Create;
  1064.         //mask.Assign(image);
  1065.         mask.Monochrome := true;
  1066.         mask.Width := image.Width;
  1067.         mask.Height := image.Height;
  1068.  
  1069.         for i := 0 to (Y - 1) do begin
  1070.             for j:= 0 to (X - 1) do begin
  1071.                 if (image.Canvas.Pixels[i, j] = TransparentColor) then begin
  1072.                     mask.Canvas.Pixels[i, j]:= clWhite;
  1073.                 end else begin
  1074.                     mask.Canvas.Pixels[i, j]:= clBlack;
  1075.                 end;
  1076.                 //image.Canvas.Pixels[i,j] := image.Canvas.Pixels[i,j];
  1077.             end;
  1078.         end;
  1079.  
  1080.         info.fIcon := True;
  1081.         info.hbmMask := mask.MaskHandle;
  1082.         info.hbmColor := image.Handle;
  1083.  
  1084.         Result := CreateIconIndirect(info);
  1085.  
  1086.         mask.Free;
  1087.         image.Free;
  1088.     end;
  1089.     }
  1090.  
  1091.  
  1092. function TClipItem.GetIconFilename(path: string; index: integer; sufix : string = ''): string;
  1093. begin
  1094.     result := path + IntToHex(index, 8) + sufix + '.bmp';
  1095. end;
  1096.  
  1097. procedure TClipItem.LoadIconFromFile(path : string; index : integer; sufix : string = '');
  1098. var f : file;
  1099.     icn : TIcon;
  1100. var bit1, bit2 : TBitmap;
  1101.     info : _ICONINFO;
  1102.     iconName : string;
  1103.     h : HICON;
  1104. begin
  1105.     // legacy support
  1106.     // load the ICO version only if the BMP
  1107.     // version does not exist
  1108.  
  1109.     iconName := self.GetIconFilename(path, index, sufix);
  1110.  
  1111.     if (FileExists( stringreplace(iconName,'.bmp','-m.bmp',[rfignorecase])))
  1112.         and (FileExists( stringreplace(iconName,'.bmp','-c.bmp',[rfignorecase]))) then begin
  1113.     {if (FileExists(IconName)) then begin}
  1114.         // preserve transparency
  1115.  
  1116.         bit1 := TBitmap.Create;
  1117.         bit1.LoadFromFile(stringreplace(iconName,'.bmp','-m.bmp',[rfignorecase]));
  1118.         bit2 := TBitmap.Create;
  1119.         bit2.LoadFromFile(stringreplace(iconName,'.bmp','-c.bmp',[rfignorecase]));
  1120.  
  1121.         info.fIcon := true;
  1122.         info.xHotspot := 0;
  1123.         info.yHotspot := 0;
  1124.         info.hbmMask := bit1.Handle;
  1125.         info.hbmColor := bit2.Handle;
  1126.  
  1127.         h := Windows.CreateIconIndirect(info);
  1128.         self.CData.SetHICON( h );
  1129.         MyFree(bit1);
  1130.         MyFree(bit2);
  1131.         {
  1132.         bit := TBitmap.Create;
  1133.         bit.LoadFromFile(IconName);
  1134.         self.CData.SetHICON( BitmapToIcon(bit) );
  1135.         MyFree(bit);
  1136.         }
  1137.     end else begin
  1138.         iconName := path + IntToStr(index) + sufix + '.ico';
  1139.  
  1140.         if (FileExists(iconName)) then begin
  1141.             assignfile(f, iconname);
  1142.             Reset(f);
  1143.             if (FileSize(f) <> 0) then begin
  1144.                 CloseFile(f);
  1145.                 icn := TIcon.Create;
  1146.                 icn.LoadFromFile(iconname);
  1147.  
  1148.                 self.CData.SetHICON(icn.Handle);
  1149.  
  1150.                 //icn.free; {Can't free this icon - it will destroy the HICON}
  1151.             end;
  1152.         end;
  1153.     end;
  1154. end;
  1155.  
  1156. procedure TClipItem.SaveIconToFile(path : string; index : integer; sufix : string = '');
  1157. var bit1, bit2 : TBitmap;
  1158.     info : _ICONINFO;
  1159.     iconName : string;
  1160. begin
  1161.     iconName := Self.GeticonFilename(path, index, sufix); 
  1162.     if (self.CData.GetHICONAbsolute <> 0) then begin
  1163.         bit1 := TBitmap.Create;
  1164.         bit2 := TBitmap.Create;
  1165.  
  1166.         Windows.GetIconInfo(self.CData.GetHICONAbsolute, info);
  1167.         bit1.Handle := info.hbmMask;
  1168.         bit1.SaveToFile( stringreplace(iconName,'.bmp','-m.bmp',[rfIgnoreCase]));
  1169.  
  1170.         bit2.Handle := info.hbmColor;
  1171.         bit2.SaveToFile( stringreplace(iconName,'.bmp','-c.bmp',[rfIgnoreCase]));
  1172.         MyFree(bit1);
  1173.         MyFree(bit2);
  1174.     end else begin
  1175.         //
  1176.         // Without this, null icons will use old cached icon from other clips
  1177.         //
  1178.         DeleteFile(stringreplace(iconName,'.bmp','-m.bmp',[rfIgnoreCase]));
  1179.         DeleteFile(stringreplace(iconName,'.bmp','-c.bmp',[rfIgnoreCase]));
  1180.     end;
  1181.     {
  1182.     //
  1183.     // This works, but I can't find a way to load it correctly.
  1184.     // Saving mask and image separately works for me
  1185.     //
  1186.     bit := TBitmap.Create;
  1187.     bit.width := 32;
  1188.     bit.height := 32;
  1189.     // 24 bit kills transparency
  1190.     bit.PixelFormat := pf8bit;
  1191.  
  1192.     DrawIcon(bit.Canvas.Handle,0,0,self.CData.GetHICON);
  1193.  
  1194.     bit.SaveToFile(iconName);
  1195.     MyFree(bit);
  1196.     }
  1197. end;
  1198.  
  1199. function TClipItem.HasText: boolean;
  1200. begin
  1201.     result := (self.GetAsText <> '') and (self.CFormat <> CF_HDROP);
  1202. end;
  1203.  
  1204.  
  1205.  
  1206. { TPagedStringQueue }
  1207.  
  1208. //--------------------
  1209. // Public Interface
  1210. //--------------------
  1211. constructor TPagedStringQueue.Create(filename: string; folder : string = '');
  1212. var tf : textfile;
  1213.     s, itemText : string;
  1214.     linecount, i, itemcount : integer;
  1215. begin
  1216.     self.ci := TClipItem.Create;
  1217.  
  1218.     self.base := IncludeTrailingPathDelimiter(ExtractFilePath(Application.ExeName));
  1219.     if folder = '' then begin
  1220.         self.cache :=  self.base + 'removedcache\';
  1221.     end else begin
  1222.         self.cache := IncludeTrailingPathDelimiter( self.base + folder );
  1223.     end;
  1224.     self.filename := self.base + filename;
  1225.     SysUtils.ForceDirectories(cache);
  1226.     self.iconcache := self.cache + 'iconcache\';
  1227.     SysUtils.ForceDirectories(self.iconcache);
  1228.  
  1229.     //
  1230.     // legacy stuff - move the old file into the new cache
  1231.     //
  1232.     if (FileExists(self.filename)) then begin
  1233.         AssignFile(tf, filename);
  1234.         Reset(tf, filename);
  1235.  
  1236.         try
  1237.             itemcount := 0;
  1238.             while not eof(tf) do begin
  1239.                 try
  1240.                     Readln(tf, s);
  1241.                     itemText := '';
  1242.                     lineCount := StrToInt(s);
  1243.  
  1244.                     for i := 0 to lineCount - 1 do begin
  1245.                         Readln(tf, s);
  1246.                         if (itemText = '') then begin
  1247.                             itemText := s;
  1248.                         end else begin
  1249.                             itemText := itemText + #13#10 + s;
  1250.                         end;
  1251.                     end;
  1252.                     self.qSize := itemCount + 1;
  1253.                     self.SaveItem(itemText, itemcount);
  1254.  
  1255.                     inc(itemcount);
  1256.                 except
  1257.                     on E: Exception do begin
  1258.                         ShowMessage('The "Load items from last session" file is corrupted - ' + filename + #13#10#13#10 +
  1259.                                     'Error Message: ' + E.Message);
  1260.                         break;
  1261.                     end;
  1262.                 end;
  1263.             end;
  1264.         finally
  1265.             CloseFile(tf);
  1266.         end;
  1267.  
  1268.         DeleteFile(self.filename);
  1269.     end;
  1270.  
  1271.  
  1272.     // fake having a zero based, largest possible queue
  1273.     // until we find the real count and size
  1274.     self.qSize := 0;
  1275.     self.qStart := 0;
  1276.     i := 0;
  1277.     self.qSize := $ffffffff;
  1278.     while FileExists(self.GetFilenameAbsolute(i)) do begin
  1279.         if (not self.IsEmptyItemAbsolute(i)) then begin
  1280.             inc(self.qCount);
  1281.         end;
  1282.         inc(i);
  1283.     end;
  1284.     self.qSize := i;
  1285.  
  1286.     // get the real "zero" item index
  1287.     if (FileExists(self.cache + PAGED_STATEFILE)) then begin
  1288.         assignfile(tf, self.cache + PAGED_STATEFILE);
  1289.         reset(tf);
  1290.         read(tf, self.qStart);
  1291.         close(tf);
  1292.     end else begin
  1293.         self.qStart := 0;
  1294.     end;
  1295.  
  1296. end;
  1297.  
  1298. destructor TPagedStringQueue.Destroy;
  1299. begin
  1300.     inherited;
  1301. end;
  1302.  
  1303. procedure TPagedStringQueue.InsertAtStart(s: string; ci : TClipItem = nil);
  1304. begin
  1305.     // ensure the queue has 1 free spot
  1306.     while (self.qCount >= self.qSize) and (self.qCount <> 0) do begin
  1307.         self.RemoveOldestItem;
  1308.     end;
  1309.  
  1310.     // save the item at the end of the queue
  1311.     self.SaveItem(s, self.qCount, ci);
  1312.     inc(self.qCount);
  1313.     self.SaveQueueState;
  1314. end;
  1315.  
  1316. function TPagedStringQueue.GetQueueCount: cardinal;
  1317. begin
  1318.     result := self.qCount;
  1319. end;
  1320.  
  1321. procedure TPagedStringQueue.ClearQueue;
  1322. begin
  1323.     {Too simple eh?}
  1324.     while (self.qCount > 0) do begin
  1325.         self.RemoveOldestItem;
  1326.     end;
  1327.     self.qStart := 0;
  1328.     self.SaveQueueState;
  1329. end;
  1330.  
  1331. procedure TPagedStringQueue.SaveQueueState;
  1332. var tf : textfile;
  1333. begin
  1334.     assignfile(tf, cache + PAGED_STATEFILE);
  1335.     rewrite(tf);
  1336.     write(tf, self.qStart);
  1337.     close(tf);
  1338. end;
  1339.  
  1340.  
  1341. procedure TPagedStringQueue.SetQueueSize(size: cardinal);
  1342. var fn : string;
  1343.     i : integer;
  1344.     tf : textfile;
  1345. begin
  1346.     // Make sure all files exist within the circular queue
  1347.     // (when the queue size is enlarged)
  1348.     // Make sure new items are blank
  1349.     for i := 0 to (size - 1) do begin
  1350.         fn := self.GetFilenameAbsolute(i);
  1351.         if not FileExists(fn) or
  1352.             (cardinal(i) >= self.qSize) then begin
  1353.             Assign(tf, fn);
  1354.             Rewrite(tf);
  1355.             Close(tf);
  1356.         end;
  1357.     end;
  1358.  
  1359.     // To Shrink the paged queue.....
  1360.     // Remove oldest items
  1361.     // Re-index items from 0-(qSize - 1)  so that virtual indexes
  1362.     // match the actuall indexes.
  1363.     // Break the contigous numbers so that only 0-(size-1) exists
  1364.     // contigously
  1365.  
  1366.     while (self.qCount > size) do begin
  1367.         self.RemoveOldestItem;
  1368.     end;
  1369.  
  1370.     self.ReIndexQueue;
  1371.  
  1372.     fn := self.GetFilenameAbsolute(size);
  1373.     if (FileExists(fn)) then begin
  1374.         DeleteFile(fn);
  1375.     end;
  1376.  
  1377.     self.qSize := size;
  1378.     self.SaveQueueState;
  1379. end;
  1380.  
  1381. function TPagedStringQueue.GetItemText(index: cardinal): string;
  1382. begin
  1383.     index := IndexTranslate(index);
  1384.     result := self.GetItemAbsolute(index);
  1385. end;
  1386. function TPagedStringQueue.GetItemClip(Index: cardinal): TClipItem;
  1387. begin
  1388.     result := self.GetItemClipAbsolute(IndexTranslate(index));
  1389.     if (result <> nil) then begin
  1390.         result.LoadIconFromFile(self.iconcache, IndexTranslate(index) );
  1391.     end;
  1392. end;
  1393.  
  1394.  
  1395. //-----------------------
  1396. // Private Implementation
  1397. //-----------------------
  1398.  
  1399. procedure TPagedStringQueue.SaveItem(s : string; index: cardinal; ci : TClipItem = nil);
  1400. var tf : textfile;
  1401. begin
  1402.     if self.qSize = 0 then EXIT;
  1403.  
  1404.     Assign(tf, self.GetFilename(index));
  1405.     Rewrite(tf);
  1406.     write(tf, s);
  1407.     close(tf);
  1408.  
  1409.     if (ci <> nil) then begin
  1410.         ci.SaveToFile(self.cache, self.IndexTranslate(index) );
  1411.         if (ci.CData.GetHICONAbsolute <> 0) then begin
  1412.             ci.SaveIconToFile(self.iconcache, self.IndexTranslate(index) );
  1413.         end;
  1414.     end;
  1415. end;
  1416.  
  1417.  
  1418. function TPagedStringQueue.IsEmptyItem(index: cardinal): boolean;
  1419. begin
  1420.     result := self.IsEmptyItemAbsolute(IndexTranslate(index));
  1421. end;
  1422. function TPagedStringQueue.IsEmptyItemAbsolute(
  1423.   absoluteIndex: cardinal): boolean;
  1424. var f : file;
  1425.     fn : string;
  1426.     fs : cardinal;
  1427. begin
  1428.     fn := self.GetFilenameAbsolute(absoluteIndex);
  1429.     Assign(f, fn);
  1430.     Reset(f, 1);
  1431.     try
  1432.         fs := FileSize(f);
  1433.         result := (fs = 0);
  1434.     finally
  1435.         Close(f);
  1436.     end;
  1437. end;
  1438.  
  1439. function TPagedStringQueue.IsEmptyItemClipAbsolute(
  1440.   absoluteIndex: cardinal): boolean;
  1441. var f : file;
  1442.     fn : string;
  1443.     fs : cardinal;
  1444. begin
  1445.     fn := self.GetFilenameClipAbsolute(absoluteIndex);
  1446.     Assign(f, fn);
  1447.     Reset(f, 1);
  1448.     try
  1449.         fs := FileSize(f);
  1450.         result := (fs = 0);
  1451.     finally
  1452.         Close(f);
  1453.     end;
  1454. end;
  1455.  
  1456.  
  1457.  
  1458.  
  1459. function TPagedStringQueue.IndexTranslate(index: cardinal): cardinal;
  1460. begin
  1461.     // since this is a circular queue, item "0" can actually
  1462.     // start anywhere from 0 to qSize - 1
  1463.  
  1464.     result := (self.qStart + index) mod self.qSize;
  1465. end;
  1466.  
  1467.  
  1468. procedure TPagedStringQueue.RemoveOldestItem;
  1469. var fn : string;
  1470. begin
  1471.     // "delete" the first/oldest item in the list
  1472.     if (self.qSize = 0) then begin
  1473.         fn := self.GetFilenameAbsolute(0);
  1474.         if (FileExists(fn)) then begin
  1475.             DeleteFile(fn);
  1476.         end;
  1477.         self.qCount := 0;
  1478.     end else begin
  1479.         self.SaveItem('', 0);
  1480.         self.qStart := (self.qStart + 1) mod qSize;
  1481.         dec(self.qCount);
  1482.     end;
  1483. end;
  1484.  
  1485. procedure TPagedStringQueue.ReIndexQueue;
  1486. var s1, s2 : string;
  1487.     i, k : integer;
  1488. begin
  1489.     if self.qStart = 0 then EXIT;
  1490.  
  1491.     // move virtual items 0-n to absolute indexes 0-n
  1492.     // replace the extension of all items so there are no name clashed
  1493.     // when re-ordering the items
  1494.  
  1495.     for i := 0 to self.qSize do begin
  1496.         s1 := self.GetFilenameAbsolute(i);
  1497.         RenameFile(s1, stringreplace(s1, PAGED_EXT, '.bak', []));
  1498.  
  1499.         s1 := self.GetFilenameClipAbsolute(i);
  1500.         RenameFile(s1, stringreplace(s1, PAGED_CLIP_EXT, '.baz', []));
  1501.  
  1502.         s1 := self.GetFilenameIcon1Absolute(i);
  1503.         RenameFile(s1, stringreplace(s1, PAGED_ICON_EXT, '.bak', []));
  1504.  
  1505.         s1 := self.GetFilenameIcon2Absolute(i);
  1506.         RenameFile(s1, stringreplace(s1, PAGED_ICON_EXT, '.bak', []));
  1507.     end;
  1508.  
  1509.  
  1510.     // move absolute item X to virtual item X
  1511.     i := self.qStart;
  1512.     k := 0;
  1513.     repeat
  1514.         s1 := stringreplace(self.GetFilenameAbsolute(i), PAGED_EXT,'.bak',[]);
  1515.         s2 := self.GetFilenameAbsolute(k);
  1516.         if FileExists(s1) then
  1517.             RenameFile(s1, s2);
  1518.  
  1519.         s1 := stringreplace(self.GetFilenameClipAbsolute(i), PAGED_CLIP_EXT,'.baz',[]);
  1520.         s2 := self.GetFilenameClipAbsolute(k);
  1521.         if FileExists(s1) then
  1522.             RenameFile(s1, s2);
  1523.  
  1524.  
  1525.         s1 := stringreplace(self.GetFilenameIcon1Absolute(i), PAGED_ICON_EXT,'.bak',[]);
  1526.         s2 := self.GetFilenameIcon1Absolute(k);
  1527.         if FileExists(s1) then
  1528.             RenameFile(s1, s2);
  1529.  
  1530.         s1 := stringreplace(self.GetFilenameIcon2Absolute(i), PAGED_ICON_EXT,'.bak',[]);
  1531.         s2 := self.GetFilenameIcon2Absolute(k);
  1532.         if FileExists(s1) then
  1533.             RenameFile(s1, s2);
  1534.  
  1535.         i := Cardinal(i + 1) mod self.qSize;
  1536.         inc(k);
  1537.     until (Cardinal(i) = self.qStart);
  1538.     self.qStart := 0;
  1539. end;
  1540.  
  1541.  
  1542.  
  1543. function TPagedStringQueue.GetFilename(index: cardinal): string;
  1544. begin
  1545.     index := self.IndexTranslate(index);
  1546.     result := self.GetFilenameAbsolute(index);
  1547. end;
  1548. function TPagedStringQueue.GetFilenameClip(index: cardinal): string;
  1549. begin
  1550.     index := self.IndexTranslate(index);
  1551.     result := self.GetFilenameClipAbsolute(index);
  1552. end;
  1553.  
  1554.  
  1555.  
  1556. function TPagedStringQueue.GetFilenameAbsolute(
  1557.   absoluteIndex: cardinal): string;
  1558. begin
  1559.     result := self.cache + IntToHex(absoluteIndex,8) + PAGED_EXT;
  1560. end;
  1561. function TPagedStringQueue.GetFilenameClipAbsolute(
  1562.   absoluteIndex: cardinal): string;
  1563. begin
  1564.     result := ci.GetFilename(self.cache, absoluteIndex);
  1565. end;
  1566. function TPagedStringQueue.GetFilenameIcon1Absolute(
  1567.   absoluteIndex: cardinal): string;
  1568. begin
  1569.     result := ci.GeticonFilename(self.iconcache, absoluteIndex, '-m');
  1570. end;
  1571. function TPagedStringQueue.GetFilenameIcon2Absolute(
  1572.   absoluteIndex: cardinal): string;
  1573. begin
  1574.     result := ci.GeticonFilename(self.iconcache, absoluteIndex, '-c');
  1575. end;
  1576.  
  1577. function TPagedStringQueue.GetItemAbsolute(
  1578.   absoluteIndex: cardinal): string;
  1579. var f : text;
  1580.     filename, s : string;
  1581. begin
  1582.     filename := self.GetFilenameAbsolute(absoluteIndex);
  1583.     result := '';
  1584.     s := '';
  1585.     if (FileExists(filename)) then begin
  1586.         try
  1587.             Assign(f, filename);
  1588.             FileMode := 0;
  1589.             Reset(f);
  1590.             while not eof(f) do begin
  1591.                 readln(f,s);
  1592.                 if (result <> '') then begin
  1593.                     result := result + #13#10  + s;
  1594.                 end else begin
  1595.                     result := s;
  1596.                 end;
  1597.             end;
  1598.             close(f);
  1599.         except
  1600.             on e : exception do begin
  1601.                 UnitMisc.AppendLog('GetItemAbsolute: ' + e.Message );
  1602.             end;
  1603.         end;
  1604.     end;
  1605. end;
  1606. function TPagedStringQueue.getItemClipAbsolute(
  1607.   absoluteIndex: cardinal): TClipItem;
  1608. begin
  1609.     result := nil;
  1610.  
  1611.     if FileExists(self.GetFilenameClipAbsolute(absoluteIndex)) then begin
  1612.         result := TClipItem.Create;
  1613.         result.LoadFromFIle(self.cache, absoluteIndex);
  1614.     end;
  1615. end;
  1616.  
  1617.  
  1618.  
  1619.  
  1620. {////////////////////}
  1621. {//}initialization{//}
  1622. {////////////////////}
  1623. begin
  1624.     RemovedQueue := TPagedStringQueue.Create('removed.txt', 'removedcache\');
  1625.  
  1626.     ClipQueue := TClipQueue.Create;
  1627.     ClipDataDefaultIcon := LoadIcon(0, IDI_APPLICATION);
  1628. end;
  1629. {//////////////////}
  1630. {//}finalization{//}
  1631. {//////////////////}
  1632. begin
  1633.     RemovedQueue.Free;
  1634.     ClipQueue.Free;
  1635. end;
  1636. end.
  1637.