/
CnWizIdeUtils.pas
2868 lines (2543 loc) · 80.3 KB
/
CnWizIdeUtils.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
{******************************************************************************}
{ CnPack For Delphi/C++Builder }
{ 中国人自己的开放源码第三方开发包 }
{ (C)Copyright 2001-2017 CnPack 开发组 }
{ ------------------------------------ }
{ }
{ 本开发包是开源的自由软件,您可以遵照 CnPack 的发布协议来修 }
{ 改和重新发布这一程序。 }
{ }
{ 发布这一开发包的目的是希望它有用,但没有任何担保。甚至没有 }
{ 适合特定目的而隐含的担保。更详细的情况请参阅 CnPack 发布协议。 }
{ }
{ 您应该已经和开发包一起收到一份 CnPack 发布协议的副本。如果 }
{ 还没有,可访问我们的网站: }
{ }
{ 网站地址:http://www.cnpack.org }
{ 电子邮件:master@cnpack.org }
{ }
{******************************************************************************}
{******************************************************************************}
{ Unit Note: }
{ This file is partly derived from GExperts 1.2 }
{ }
{ Original author: }
{ GExperts, Inc http://www.gexperts.org/ }
{ Erik Berry <eberry@gexperts.org> or <eb@techie.com> }
{******************************************************************************}
unit CnWizIdeUtils;
{* |<PRE>
================================================================================
* 软件名称:CnPack IDE 专家包
* 单元名称:IDE 相关公共单元
* 单元作者:周劲羽 (zjy@cnpack.org)
* LiuXiao(刘啸)liuxiao@cnpack.org
* 备 注:该单元部分内容移植自 GExperts 1.12 Src
* 其原始内容受 GExperts License 的保护
* 开发平台:PWin2000Pro + Delphi 5.01
* 兼容测试:PWin9X/2000/XP + Delphi 5/6/7 + C++Builder 5/6
* 本 地 化:该窗体中的字符串均符合本地化处理方式
* 单元标识:$Id$
* 修改记录:2016.04.04 by liuxiao
* 增加 2010 以上版本的新风格控件板的支持
* 2012.09.19 by shenloqi
* 移植到Delphi XE3
* 2005.05.06 V1.3
* hubdog 增加 获取版本信息的函数
* 2004.03.19 V1.2
* LiuXiao 增加 CnPaletteWrapper,封装控件面板的各个属性
* 2003.03.06 V1.1
* GetLibraryPath 扩展了路径搜索范围,支持工程搜索路径
* 2002.12.05 V1.0
* 创建单元,实现功能
================================================================================
|</PRE>}
interface
{$I CnWizards.inc}
uses
Windows, Messages, Classes, Controls, SysUtils, Graphics, Forms, Tabs,
Menus, Buttons, ComCtrls, StdCtrls, ExtCtrls, TypInfo, ToolsAPI, ImgList,
{$IFDEF COMPILER6_UP}
DesignIntf, DesignEditors, ComponentDesigner,
{$ELSE}
DsgnIntf, LibIntf,
{$ENDIF}
CnPasCodeParser, CnWidePasParser,
CnWizUtils, CnWizEditFiler, CnCommon, CnWizOptions, CnWizCompilerConst;
//==============================================================================
// IDE 中的常量定义
//==============================================================================
const
// IDE Action 名称
SEditSelectAllCommand = 'EditSelectAllCommand';
// Editor 窗口右键菜单名称
SMenuClosePageName = 'ecClosePage';
SMenuClosePageIIName = 'ecClosePageII';
SMenuEditPasteItemName = 'EditPasteItem';
SMenuOpenFileAtCursorName = 'ecOpenFileAtCursor';
// Editor 窗口相关类名
EditorFormClassName = 'TEditWindow';
EditControlName = 'Editor';
EditControlClassName = 'TEditControl';
DesignControlClassName = 'TEditorFormDesigner';
WelcomePageClassName = 'TWelcomePageFrame';
DisassemblyViewClassName = 'TDisassemblyView';
EditorStatusBarName = 'StatusBar';
{$IFDEF BDS}
{$IFDEF BDS4_UP} // BDS 2006 RAD Studio 2007 的标签页类名
XTabControlClassName = 'TIDEGradientTabSet'; // TWinControl 子类
{$ELSE} // BDS 2005 的标签页类名
XTabControlClassName = 'TCodeEditorTabControl'; // TTabSet 子类
{$ENDIF}
{$ELSE} // Delphi BCB 的标签页类名
XTabControlClassName = 'TXTabControl';
{$ENDIF BDS}
XTabControlName = 'TabControl';
TabControlPanelName = 'TabControlPanel';
CodePanelName = 'CodePanel';
TabPanelName = 'TabPanel';
// 对象查看器
PropertyInspectorClassName = 'TPropertyInspector';
PropertyInspectorName = 'PropertyInspector';
// 编辑器设置对话框
{$IFDEF BDS}
SEditorOptionDlgClassName = 'TDefaultEnvironmentDialog';
SEditorOptionDlgName = 'DefaultEnvironmentDialog';
{$ELSE} {$IFDEF BCB}
SEditorOptionDlgClassName = 'TCppEditorPropertyDialog';
SEditorOptionDlgName = 'CppEditorPropertyDialog';
{$ELSE}
SEditorOptionDlgClassName = 'TPasEditorPropertyDialog';
SEditorOptionDlgName = 'PasEditorPropertyDialog';
{$ENDIF} {$ENDIF}
// 控件板相关类名和属性名
SCnPaletteTabControlClassName = 'TComponentPaletteTabControl';
SCnPalettePropSelectedIndex = 'SelectedIndex';
SCnPalettePropSelectedToolName = 'SelectedToolName';
SCnPalettePropSelector = 'Selector';
SCnPalettePropPalToolCount = 'PalToolCount';
// D2010 或以上版本的新控件板,一个 TComponentToolbarFrame 里包着 TGradientTabSet
SCnNewPaletteFrameClassName = 'TComponentToolbarFrame';
SCnNewPaletteFrameName = 'ComponentToolbarFrame';
SCnNewPaletteTabClassName = 'TGradientTabSet';
SCnNewPaletteTabName = 'TabControl';
SCnNewPaletteTabItemsPropName = 'Items';
SCnNewPaletteTabIndexPropName = 'TabIndex';
SCnNewPalettePanelContainerName = 'PanelButtons';
SCnNewPaletteButtonClassName = 'TPalItemSpeedButton';
// 消息窗口
SCnMessageViewFormClassName = 'TMessageViewForm';
SCnMessageViewTabSetName = 'MessageGroups';
SCnMvEditSourceItemName = 'mvEditSourceItem';
{$IFDEF BDS}
SCnTreeMessageViewClassName = 'TBetterHintWindowVirtualDrawTree';
{$ELSE}
SCnTreeMessageViewClassName = 'TTreeMessageView';
{$ENDIF}
// 引用单元功能的 Action 名称
{$IFDEF DELPHI}
SCnUseUnitActionName = 'FileUseUnitCommand';
{$ELSE}
SCnUseUnitActionName = 'FileIncludeUnitHdrCommand';
{$ENDIF}
SCnColor16Table: array[0..15] of TColor =
( clBlack, clMaroon, clGreen, clOlive,
clNavy, clPurple, clTeal, clLtGray, clDkGray, clRed, clLime,
clYellow, clBlue, clFuchsia, clAqua, clWhite);
type
{$IFDEF BDS}
{$IFDEF BDS2006_UP}
TXTabControl = TWinControl;
{$ELSE}
TXTabControl = TTabSet;
{$ENDIF}
{$ELSE}
TXTabControl = TTabControl;
{$ENDIF BDS}
{$IFDEF BDS}
TXTreeView = TCustomControl;
{$ELSE}
TXTreeView = TTreeView;
{$ENDIF BDS}
//==============================================================================
// IDE 代码编辑器功能函数
//==============================================================================
function IdeGetEditorSelectedLines(Lines: TStringList): Boolean;
{* 取得当前代码编辑器选择行的代码,使用整行模式。如果选择块为空,则返回当前行代码。}
function IdeGetEditorSelectedText(Lines: TStringList): Boolean;
{* 取得当前代码编辑器选择块的代码。}
function IdeGetEditorSourceLines(Lines: TStringList): Boolean;
{* 取得当前代码编辑器全部源代码。}
function IdeSetEditorSelectedLines(Lines: TStringList): Boolean;
{* 替换当前代码编辑器选择行的代码,使用整行模式。如果选择块为空,则替换当前行代码。}
function IdeSetEditorSelectedText(Lines: TStringList): Boolean;
{* 替换当前代码编辑器选择块的代码。}
function IdeSetEditorSourceLines(Lines: TStringList): Boolean;
{* 替换当前代码编辑器全部源代码。}
function IdeInsertTextIntoEditor(const Text: string): Boolean;
{* 插入文本到当前编辑器,支持多行文本。}
function IdeEditorGetEditPos(var Col, Line: Integer): Boolean;
{* 返回当前光标位置,如果 EditView 为空使用当前值。 }
function IdeEditorGotoEditPos(Col, Line: Integer; Middle: Boolean): Boolean;
{* 移动光标到指定位置,Middle 表示是否移动视图到中心。}
function IdeGetBlockIndent: Integer;
{* 获得当前编辑器块缩进宽度 }
function IdeGetSourceByFileName(const FileName: string): AnsiString;
{* 根据文件名取得内容。如果文件在 IDE 中打开,返回编辑器中的内容,否则返回文件内容。}
function IdeSetSourceByFileName(const FileName: string; Source: TStrings;
OpenInIde: Boolean): Boolean;
{* 根据文件名写入内容。如果文件在 IDE 中打开,写入内容到编辑器中,否则如果
OpenInIde 为真打开文件写入到编辑器,OpenInIde 为假直接写入文件。}
function IsCurrentToken(AView: Pointer; AControl: TControl; Token: TCnPasToken): Boolean;
{* 判断标识符是否在光标下,频繁调用,因此此处 View 用指针来避免引用计数从而优化速度,各版本均可使用 }
function IsCurrentTokenW(AView: Pointer; AControl: TControl; Token: TCnWidePasToken): Boolean;
{* 判断标识符是否在光标下,同上,但使用 WideToken,可供 Unicode/Utf8 环境下调用}
//==============================================================================
// IDE 窗体编辑器功能函数
//==============================================================================
function IdeGetFormDesigner(FormEditor: IOTAFormEditor = nil): IDesigner;
{* 取得窗体编辑器的设计器,FormEditor 为 nil 表示取当前窗体 }
function IdeGetDesignedForm(Designer: IDesigner = nil): TCustomForm;
{* 取得当前设计的窗体 }
function IdeGetFormSelection(Selections: TList; Designer: IDesigner = nil;
ExcludeForm: Boolean = True): Boolean;
{* 取得当前设计窗体上已选择的组件 }
function IdeGetIsEmbeddedDesigner: Boolean;
{* 取得当前是否是嵌入式设计窗体模式}
var
IdeIsEmbeddedDesigner: Boolean = False;
{* 标记当前是否是嵌入式设计窗体模式,initiliazation 时被初始化,请勿手工修改其值。
使用此全局变量可以避免频繁调用 IdeGetIsEmbeddedDesigner 函数}
//==============================================================================
// 修改自 GExperts Src 1.12 的 IDE 相关函数
//==============================================================================
function GetIdeMainForm: TCustomForm;
{* 返回 IDE 主窗体 (TAppBuilder) }
function GetIdeEdition: string;
{* 返回 IDE 版本}
function GetComponentPaletteTabControl: TTabControl;
{* 返回组件面板对象,可能为空,只支持 2010 以下版本}
function GetNewComponentPaletteTabControl: TWinControl;
{* 返回 2010 或以上的新组件面板上半部分 Tab 对象,可能为空}
function GetNewComponentPaletteComponentPanel: TWinControl;
{* 返回 2010 或以上的新组件面板下半部分容纳组件列表的容器对象,可能为空}
function GetEditWindowStatusBar(EditWindow: TCustomForm = nil): TStatusBar;
{* 返回编辑器窗口下方的状态栏,只支持 D567,可能为空}
function GetObjectInspectorForm: TCustomForm;
{* 返回对象检查器窗体,可能为空}
function GetComponentPalettePopupMenu: TPopupMenu;
{* 返回组件面板右键菜单,可能为空}
function GetComponentPaletteControlBar: TControlBar;
{* 返回组件面板所在的ControlBar,可能为空}
function GetMainMenuItemHeight: Integer;
{* 返回主菜单项高度 }
function IsIdeEditorForm(AForm: TCustomForm): Boolean;
{* 判断指定窗体是否编辑器窗体}
function IsIdeDesignForm(AForm: TCustomForm): Boolean;
{* 判断指定窗体是否是设计期窗体}
procedure BringIdeEditorFormToFront;
{* 将源码编辑器设为活跃}
function IDEIsCurrentWindow: Boolean;
{* 判断 IDE 是否是当前的活动窗口 }
//==============================================================================
// 其它的 IDE 相关函数
//==============================================================================
function GetInstallDir: string;
{* 取编译器安装目录}
{$IFDEF BDS}
function GetBDSUserDataDir: string;
{* 取得 BDS (Delphi8以后版本) 的用户数据目录 }
{$ENDIF}
procedure GetProjectLibPath(Paths: TStrings);
{* 取当前工程组的相关 Path 内容}
function GetFileNameFromModuleName(AName: string; AProject: IOTAProject = nil): string;
{* 根据模块名获得完整文件名}
function CnOtaGetVersionInfoKeys(Project: IOTAProject = nil): TStrings;
{* 获取当前项目中的版本信息键值}
procedure GetLibraryPath(Paths: TStrings; IncludeProjectPath: Boolean = True);
{* 取环境设置中的 LibraryPath 内容}
function GetComponentUnitName(const ComponentName: string): string;
{* 取组件定义所在的单元名}
procedure GetInstalledComponents(Packages, Components: TStrings);
{* 取已安装的包和组件,参数允许为 nil(忽略)}
function GetIDERegistryFont(const RegItem: string; AFont: TFont): Boolean;
{* 从某项注册表中载入某项字体并赋值给 AFont
RegItem 可以是 '', 'Assembler', 'Comment', 'Preprocessor',
'Identifier', 'Reserved word', 'Number', 'Whitespace', 'String', 'Symbol'
等注册表里头已经定义了的键值}
function GetIDEBigImageList: TImageList;
{* 获取一个大尺寸的 IDE 的 ImageList 引用,从 IDE 的 ImageList 拉扯而来}
procedure ClearIDEBigImageList;
{* 清空大尺寸的 IDE 的 ImageList,供通知重建而使用}
type
TEnumEditControlProc = procedure (EditWindow: TCustomForm; EditControl:
TControl; Context: Pointer) of object;
function IsEditControl(AControl: TComponent): Boolean;
{* 判断指定控件是否代码编辑器控件 }
function IsXTabControl(AControl: TComponent): Boolean;
{* 判断指定控件是否编辑器窗口的 TabControl 控件 }
function GetEditControlFromEditorForm(AForm: TCustomForm): TControl;
{* 返回编辑器窗口的编辑器控件 }
function GetCurrentEditControl: TControl;
{* 返回当前的代码编辑器控件 }
function GetTabControlFromEditorForm(AForm: TCustomForm): TXTabControl;
{* 返回编辑器窗口的 TabControl 控件 }
function GetEditorTabTabs(ATab: TXTabControl): TStrings;
{* 返回编辑器 TabControl 控件的 Tabs 属性}
function GetEditorTabTabIndex(ATab: TXTabControl): Integer;
{* 返回编辑器 TabControl 控件的 Index 属性}
function GetStatusBarFromEditor(EditControl: TControl): TStatusBar;
{* 从编辑器控件获得其所属的编辑器窗口的状态栏}
function EnumEditControl(Proc: TEnumEditControlProc; Context: Pointer;
EditorMustExists: Boolean = True): Integer;
{* 枚举 IDE 中的代码编辑器窗口和 EditControl 控件,调用回调函数,返回总数 }
function GetCurrentSyncButton: TControl;
{* 获取当前最前端编辑器的语法编辑按钮,注意语法编辑按钮存在不等于可见}
function GetCurrentSyncButtonVisible: Boolean;
{* 获取当前最前端编辑器的语法编辑按钮是否可见,无按钮或不可见均返回 False}
function GetCodeTemplateListBox: TControl;
{* 返回编辑器中的代码模板自动输入框}
function GetCodeTemplateListBoxVisible: Boolean;
{* 返回编辑器中的代码模板自动输入框是否可见,无或不可见均返回 False}
type
TCnSrcEditorPage = (epCode, epDesign, epCPU, epWelcome, epOthers);
function GetCurrentTopEditorPage(AControl: TWinControl): TCnSrcEditorPage;
{* 取当前编辑窗口顶层页面类型,传入编辑器父控件 }
procedure BeginBatchOpenClose;
{* 开始批量打开或关闭文件 }
procedure EndBatchOpenClose;
{* 结束批量打开或关闭文件 }
function ConvertIDETreeNodeToTreeNode(Node: TObject): TTreeNode;
{* 将 IDE 内部使用的 TTreeControl的 Items 属性值的 TreeNode 强行转换成公用的 TreeNode}
function ConvertIDETreeNodesToTreeNodes(Nodes: TObject): TTreeNodes;
{* 将 IDE 内部使用的 TTreeControl的 Items 属性值的 TreeNodes 强行转换成公用的 TreeNodes}
//==============================================================================
// 扩展控件
//==============================================================================
type
TCnToolBarComboBox = class(TComboBox)
private
procedure CNKeyDown(var Message: TWMKeyDown); message CN_KEYDOWN;
end;
//==============================================================================
// 组件面板封装类
//==============================================================================
type
{ TCnPaletteWrapper }
TCnPaletteWrapper = class(TObject)
{* 封装了控件板各个属性的类,大部分只支持低版本控件板
高版本控件板由上下两个 Panel 组成,上面 Panel 容纳 TGradientTab 与 ToolbarSearch
下面 Panel 容纳滚动按钮以及多个 TPalItemSpeedButton 的控件图标按钮
}
private
FPalTab: TWinControl; // 低版本指大的 TabControl 容器,高版本指上半部分的 TGradientTabSet
FPalette: TWinControl; // 低版本指大的 TabControl 内的组件容器,高版本指下半部分的组件容器
{$IFNDEF IDE_HAS_NEW_COMPONENT_PALETTE}
FPageScroller: TWinControl;
{$ENDIF}
FUpdateCount: Integer;
{$IFDEF COMPILER6_UP}
{$IFNDEF IDE_HAS_NEW_COMPONENT_PALETTE}
FOldRootClass: TClass;
{$ENDIF}
{$ENDIF}
{$IFDEF IDE_HAS_NEW_COMPONENT_PALETTE}
function ParseCompNameFromHint(const Hint: string): string;
function ParseUnitNameFromHint(const Hint: string): string;
{$ENDIF}
function GetSelectedIndex: Integer;
function GetSelectedToolName: string;
function GetSelectedUnitName: string;
function GetSelector: TSpeedButton;
function GetPalToolCount: Integer;
function GetActiveTab: string;
function GetTabCount: Integer;
function GetIsMultiLine: Boolean;
procedure SetSelectedIndex(const Value: Integer);
function GetTabIndex: Integer;
procedure SetTabIndex(const Value: Integer);
function GetVisible: Boolean;
procedure SetVisible(const Value: Boolean);
function GetEnabled: Boolean;
procedure SetEnabled(const Value: Boolean);
function GetTabs(Index: Integer): string;
{$IFDEF SUPPORTS_PALETTE_ENHANCE}
{$IFDEF IDE_HAS_NEW_COMPONENT_PALETTE}
procedure GetComponentImageFromNewPalette(Bmp: TBitmap; const AComponentClassName: string);
{$ELSE}
procedure GetComponentImageFromOldPalette(Bmp: TBitmap; const AComponentClassName: string);
{$ENDIF}
{$ENDIF}
public
constructor Create;
procedure BeginUpdate;
{* 开始更新,禁止刷新页面 }
procedure EndUpdate;
{* 停止更新,恢复刷新页面 }
function SelectComponent(const AComponent: string; const ATab: string): Boolean;
{* 根据类名选中控件板中的某控件,返回是否成功 }
function FindTab(const ATab: string): Integer;
{* 查找某页面的索引 }
procedure GetComponentImage(Bmp: TBitmap; const AComponentClassName: string);
{* 将控件板上指定的组件名的图标绘制到 Bmp 中,Bmp 推荐尺寸为 26 * 26}
property SelectedIndex: Integer read GetSelectedIndex write SetSelectedIndex;
{* 按下的控件在本页的序号,0 开头,支持高版本的新控件板 }
property SelectedToolName: string read GetSelectedToolName;
{* 按下的控件的类名,未按下则为空,支持高版本的新控件板 }
property SelectedUnitName: string read GetSelectedUnitName;
{* 按下的控件的单元名,未按下为空,支持高版本的新控件版,可解析 Hint 而来}
property Selector: TSpeedButton read GetSelector;
{* 获得用来切换到鼠标光标的 SpeedButton,低版本在组件区内,高版本在 Tab 头中 }
property PalToolCount: Integer read GetPalToolCount;
{* 当前页控件个数,支持高版本的新控件板 }
property ActiveTab: string read GetActiveTab;
{* 当前页标题,支持高版本的新控件板 }
property TabIndex: Integer read GetTabIndex write SetTabIndex;
{* 当前页索引,支持高版本的新控件板 }
property Tabs[Index: Integer]: string read GetTabs;
{* 根据索引得到页名称,支持高版本的新控件板 }
property TabCount: Integer read GetTabCount;
{* 控件板总页数,支持高版本的新控件板 }
property IsMultiLine: Boolean read GetIsMultiLine;
{* 控件板是否多行,支持高版本的新控件板但高版本新控件板不支持多行 }
property Visible: Boolean read GetVisible write SetVisible;
{* 控件板是否可见,支持高版本的新控件板 }
property Enabled: Boolean read GetEnabled write SetEnabled;
{* 控件板是否使能,支持高版本的新控件板 }
end;
{TCnMessageViewWrapper}
TCnMessageViewWrapper = class(TObject)
{* 封装了消息显示窗口的各个属性的类 }
private
FMessageViewForm: TCustomForm;
FTreeView: TXTreeView;
FTabSet: TTabSet;
FEditMenuItem: TMenuItem;
{$IFNDEF BDS}
function GetMessageCount: Integer;
function GetSelectedIndex: Integer;
procedure SetSelectedIndex(const Value: Integer);
function GetCurrentMessage: string;
{$ENDIF}
function GetTabCaption: string;
function GetTabCount: Integer;
function GetTabIndex: Integer;
procedure SetTabIndex(const Value: Integer);
function GetTabSetVisible: Boolean;
public
constructor Create;
procedure UpdateAllItems;
procedure EditMessageSource;
{* 双击信息窗口}
property MessageViewForm: TCustomForm read FMessageViewForm;
{* 信息窗口}
property TreeView: TXTreeView read FTreeView;
{* 信息树组件实例,BDS 下非 TreeView,因此只能返回 CustomControl }
{$IFNDEF BDS}
property SelectedIndex: Integer read GetSelectedIndex write SetSelectedIndex;
{* 信息中选中的序号}
property MessageCount: Integer read GetMessageCount;
{* 现有的信息数}
property CurrentMessage: string read GetCurrentMessage;
{* 当前选中的信息,但似乎老是返回空}
{$ENDIF}
property TabSet: TTabSet read FTabSet;
{* 返回分页组件的实例}
property TabSetVisible: Boolean read GetTabSetVisible;
{* 返回分页组件是否可见,D5 下默认不可见}
property TabIndex: Integer read GetTabIndex write SetTabIndex;
{* 返回/设置当前页序号}
property TabCount: Integer read GetTabCount;
{* 返回总页数}
property TabCaption: string read GetTabCaption;
{* 返回当前页的字符串}
property EditMenuItem: TMenuItem read FEditMenuItem;
{* '编辑'菜单项}
end;
function CnPaletteWrapper: TCnPaletteWrapper;
function CnMessageViewWrapper: TCnMessageViewWrapper;
implementation
uses
{$IFDEF DEBUG}
CnDebug,
{$ENDIF}
Registry, CnGraphUtils;
const
SSyncButtonName = 'SyncButton';
SCodeTemplateListBoxName = 'CodeTemplateListBox';
{$IFDEF BDS4_UP}
const
SBeginBatchOpenCloseName = '@Editorform@BeginBatchOpenClose$qqrv';
SEndBatchOpenCloseName = '@Editorform@EndBatchOpenClose$qqrv';
var
BeginBatchOpenCloseProc: TProcedure = nil;
EndBatchOpenCloseProc: TProcedure = nil;
{$ENDIF}
var
FIDEBigImageList: TImageList = nil;
type
TCustomControlHack = class(TCustomControl);
//==============================================================================
// IDE功能函数
//==============================================================================
type
TGetCodeMode = (smLine, smSelText, smSource);
function DoGetEditorSrcInfo(Mode: TGetCodeMode; View: IOTAEditView;
var StartPos, EndPos, NewRow, NewCol, BlockStartLine, BlockEndLine: Integer): Boolean;
var
Block: IOTAEditBlock;
Row, Col: Integer;
Stream: TMemoryStream;
begin
Result := False;
if View <> nil then
begin
Block := View.Block;
StartPos := 0;
EndPos := 0;
BlockStartLine := 0;
BlockEndLine := 0;
NewRow := 0;
NewCol := 0;
if Mode = smLine then
begin
if (Block <> nil) and Block.IsValid then
begin // 选择文本扩大到整行
BlockStartLine := Block.StartingRow;
StartPos := CnOtaEditPosToLinePos(OTAEditPos(1, BlockStartLine), View);
BlockEndLine := Block.EndingRow;
// 光标不在行首时,处理到下一行行首
if Block.EndingColumn > 1 then
begin
if BlockEndLine < View.Buffer.GetLinesInBuffer then
begin
Inc(BlockEndLine);
EndPos := CnOtaEditPosToLinePos(OTAEditPos(1, BlockEndLine), View);
end
else
EndPos := CnOtaEditPosToLinePos(OTAEditPos(255, BlockEndLine), View);
end
else
EndPos := CnOtaEditPosToLinePos(OTAEditPos(1, BlockEndLine), View);
end
else
begin // 未选择表示转换整行。
if CnOtaGetCurSourcePos(Col, Row) then
begin
StartPos := CnOtaEditPosToLinePos(OTAEditPos(1, Row), View);
if Row < View.Buffer.GetLinesInBuffer then
begin
EndPos := CnOtaEditPosToLinePos(OTAEditPos(1, Row + 1), View);
NewRow := Row + 1;
NewCol := Col;
end
else
EndPos := CnOtaEditPosToLinePos(OTAEditPos(255, Row), View);
end
else
begin
Exit;
end;
end;
end
else if Mode = smSelText then
begin
if (Block <> nil) and (Block.IsValid) then
begin // 仅处理选择的文本
StartPos := CnOtaEditPosToLinePos(OTAEditPos(Block.StartingColumn,
Block.StartingRow), View);
EndPos := CnOtaEditPosToLinePos(OTAEditPos(Block.EndingColumn,
Block.EndingRow), View);
end;
end
else
begin
StartPos := 0;
Stream := TMemoryStream.Create;
try
CnOtaSaveCurrentEditorToStream(Stream, False);
EndPos := Stream.Size; // 用笨办法得到编辑的长度
finally
Stream.Free;
end;
end;
Result := True;
end;
end;
function DoGetEditorLines(Mode: TGetCodeMode; Lines: TStringList): Boolean;
const
SCnOtaBatchSize = $7FFF;
var
View: IOTAEditView;
Text: AnsiString;
Res: string;
Buf: PAnsiChar;
BlockStartLine, BlockEndLine: Integer;
StartPos, EndPos, Len, ReadStart, ASize: Integer;
Reader: IOTAEditReader;
NewRow, NewCol: Integer;
begin
Result := False;
View := CnOtaGetTopMostEditView;
if View <> nil then
begin
if not DoGetEditorSrcInfo(Mode, View, StartPos, EndPos, NewRow, NewCol,
BlockStartLine, BlockEndLine) then
Exit;
Len := EndPos - StartPos;
Assert(Len >= 0);
SetLength(Text, Len);
Buf := Pointer(Text);
ReadStart := StartPos;
Reader := View.Buffer.CreateReader;
try
while Len > SCnOtaBatchSize do // 逐次读取
begin
ASize := Reader.GetText(ReadStart, Buf, SCnOtaBatchSize);
Inc(Buf, ASize);
Inc(ReadStart, ASize);
Dec(Len, ASize);
end;
if Len > 0 then // 读最后剩余的
Reader.GetText(ReadStart, Buf, Len);
finally
Reader := nil;
end;
{$IFDEF UNICODE}
Res := ConvertEditorTextToTextW(Text); // Unicode 下不经过 Ansi 转换以避免丢字符
{$ELSE}
Res := ConvertEditorTextToText(Text);
{$ENDIF}
Lines.Text := Res;
Result := Text <> '';
end;
end;
function DoSetEditorLines(Mode: TGetCodeMode; Lines: TStringList): Boolean;
const
SCnOtaBatchSize = $7FFF;
var
View: IOTAEditView;
Text: AnsiString;
BlockStartLine, BlockEndLine: Integer;
StartPos, EndPos: Integer;
Writer: IOTAEditWriter;
NewRow, NewCol: Integer;
begin
Result := False;
View := CnOtaGetTopMostEditView;
if View <> nil then
begin
if not DoGetEditorSrcInfo(Mode, View, StartPos, EndPos, NewRow, NewCol,
BlockStartLine, BlockEndLine) then
Exit;
{$IFDEF UNICODE_STRING}
Text := AnsiString(StringReplace(Lines.Text, #0, ' ', [rfReplaceAll]));
{$ELSE}
Text := StringReplace(Lines.Text, #0, ' ', [rfReplaceAll]);
{$ENDIF}
Writer := View.Buffer.CreateUndoableWriter;
try
Writer.CopyTo(StartPos);
Writer.Insert(PAnsiChar(ConvertTextToEditorText(Text)));
Writer.DeleteTo(EndPos);
finally
Writer := nil;
end;
if (NewRow > 0) and (NewCol > 0) then
begin
View.CursorPos := OTAEditPos(NewCol, NewRow);
end
else if (BlockStartLine > 0) and (BlockEndLine > 0) then
begin
CnOtaSelectBlock(View.Buffer, OTACharPos(0, BlockStartLine),
OTACharPos(0, BlockEndLine));
end;
Result := True;
end;
end;
function IdeGetEditorSelectedLines(Lines: TStringList): Boolean;
begin
Result := DoGetEditorLines(smLine, Lines);
end;
function IdeGetEditorSelectedText(Lines: TStringList): Boolean;
begin
Result := DoGetEditorLines(smSelText, Lines);
end;
function IdeGetEditorSourceLines(Lines: TStringList): Boolean;
begin
Result := DoGetEditorLines(smSource, Lines);
end;
function IdeSetEditorSelectedLines(Lines: TStringList): Boolean;
begin
Result := DoSetEditorLines(smLine, Lines);
end;
function IdeSetEditorSelectedText(Lines: TStringList): Boolean;
begin
Result := DoSetEditorLines(smSelText, Lines);
end;
function IdeSetEditorSourceLines(Lines: TStringList): Boolean;
begin
Result := DoSetEditorLines(smSource, Lines);
end;
function IdeInsertTextIntoEditor(const Text: string): Boolean;
begin
if CnOtaGetTopMostEditView <> nil then
begin
CnOtaInsertTextIntoEditor(Text);
Result := True;
end
else
Result := False;
end;
function IdeEditorGetEditPos(var Col, Line: Integer): Boolean;
var
EditPos: TOTAEditPos;
begin
if CnOtaGetTopMostEditView <> nil then
begin
EditPos := CnOtaGetEditPos(CnOtaGetTopMostEditView);
Col := EditPos.Col;
Line := EditPos.Line;
Result := True;
end
else
Result := False;
end;
function IdeEditorGotoEditPos(Col, Line: Integer; Middle: Boolean): Boolean;
begin
if CnOtaGetTopMostEditView <> nil then
begin
CnOtaGotoEditPos(OTAEditPos(Col, Line), CnOtaGetTopMostEditView, Middle);
Result := True;
end
else
Result := False;
end;
function IdeGetBlockIndent: Integer;
begin
Result := CnOtaGetBlockIndent;
end;
function IdeGetSourceByFileName(const FileName: string): AnsiString;
var
Strm: TMemoryStream;
begin
Strm := TMemoryStream.Create;
try
EditFilerSaveFileToStream(FileName, Strm, True);
Result := PAnsiChar(Strm.Memory);
finally
Strm.Free;
end;
end;
function IdeSetSourceByFileName(const FileName: string; Source: TStrings;
OpenInIde: Boolean): Boolean;
var
Strm: TMemoryStream;
begin
Result := False;
if OpenInIde and not CnOtaOpenFile(FileName) then
Exit;
if CnOtaIsFileOpen(FileName) then
begin
Strm := TMemoryStream.Create;
try
Source.SaveToStream(Strm);
Strm.Position := 0;
with TCnEditFiler.Create(FileName) do
try
ReadFromStream(Strm);
finally
Free;
end;
finally
Strm.Free;
end;
end
else
Source.SaveToFile(FileName);
Result := True;
end;
// 判断标识符是否在光标下,各版本均可使用
function IsCurrentToken(AView: Pointer; AControl: TControl; Token: TCnPasToken): Boolean;
var
{$IFDEF BDS}
Text: AnsiString;
{$ENDIF}
LineNo, Col: Integer;
View: IOTAEditView;
begin
if not Assigned(AView) then
begin
Result := False;
Exit;
end;
View := IOTAEditView(AView);
LineNo := View.CursorPos.Line;
Col := View.CursorPos.Col;
if Token.EditLine <> LineNo then // 行号不等时直接退出
begin
Result := False;
Exit;
end;
// 行相等才需要读出行内容进行比较,其中 Col 是直观的 Ansi 概念,双字节字符占 2 列
{$IFDEF BDS}
Text := AnsiString(GetStrProp(AControl, 'LineText')); // D2009 以上 Unicode 也得转换成 Ansi
if Text <> '' then
begin
// TODO: 用 TextWidth 获得光标位置精确对应的源码字符位置,但实现较难。
// 当存在占据单字符位置的双字节字符时,以下算法会有偏差。
{$IFNDEF UNICODE}
// D2005~2007 获得的是 Utf8 字符串,需要转换为 Ansi 才能进行直观列比较
Col := Length(CnUtf8ToAnsi(Copy(Text, 1, Col)));
{$ENDIF}
end;
{$ENDIF}
Result := (Col >= Token.EditCol) and (Col <= Token.EditCol + Length(Token.Token));
end;
{* 判断标识符是否在光标下,使用 WideToken,可供 Unicode/Utf8 环境下调用}
function IsCurrentTokenW(AView: Pointer; AControl: TControl; Token: TCnWidePasToken): Boolean;
var
LineNo, Col: Integer;
View: IOTAEditView;
begin
if not Assigned(AView) then
begin
Result := False;
Exit;
end;
View := IOTAEditView(AView);
LineNo := View.CursorPos.Line;
Col := View.CursorPos.Col;
if Token.EditLine <> LineNo then // 行号不等时直接退出
begin
Result := False;
Exit;
end;
// 行相等才需要比较列,并且由于 CursorPos 是 ANSI 的光标位置,
// 所以得把 Utf16 转成 Ansi 来比较
Result := (Col >= Token.EditCol) and (Col <= Token.EditCol +
CalcAnsiLengthFromWideString(Token.Token));
end;
//==============================================================================
// IDE 窗体编辑器功能函数
//==============================================================================
// 取得窗体编辑器的设计器,FormEditor 为 nil 表示取当前窗体
function IdeGetFormDesigner(FormEditor: IOTAFormEditor = nil): IDesigner;
begin
Result := CnOtaGetFormDesigner(FormEditor);
end;
// 取得当前设计的窗体
function IdeGetDesignedForm(Designer: IDesigner = nil): TCustomForm;
begin
Result := nil;
try
if Designer = nil then
Designer := IdeGetFormDesigner;
if Designer = nil then Exit;
{$IFDEF COMPILER6_UP}
if Designer.Root is TCustomForm then
Result := TCustomForm(Designer.Root);
{$ELSE}
Result := Designer.Form;
{$ENDIF}
except
;
end;
end;
// 取得当前设计窗体上已选择的组件
function IdeGetFormSelection(Selections: TList; Designer: IDesigner = nil;
ExcludeForm: Boolean = True): Boolean;
var
i: Integer;