Revision | 483 (tree) |
---|---|
Time | 2020-02-03 09:03:12 |
Author | ![]() |
Pasta "Editors" movida para a pasta ToolsApi. Todos os editores de componente ou de propriedade potencialmente dependem de designide, logo, este é o melhor local para esta pasta e seu conteúdo ficarem
Commit temporário. Não baixe esta revisão
@@ -1,32 +0,0 @@ | ||
1 | -unit KRK.Editors.VariableWidthColumnsEditor; | |
2 | - | |
3 | -interface | |
4 | - | |
5 | -uses Windows | |
6 | - , DesignWindows | |
7 | - , StdCtrls | |
8 | - , Buttons | |
9 | - , Controls | |
10 | - , ExtCtrls | |
11 | - , Classes | |
12 | - , CheckLst; | |
13 | - | |
14 | -type | |
15 | - TFormVariableWidthColumnsEditor = class(TDesignWindow) | |
16 | - CheckListBox_Columns: TCheckListBox; | |
17 | - Panel_Top: TPanel; | |
18 | - Panel_Bottom: TPanel; | |
19 | - Label_Top: TLabel; | |
20 | - BitBtn_Confirmar: TBitBtn; | |
21 | - BitBtn_Cancelar: TBitBtn; | |
22 | - private | |
23 | - { Private declarations } | |
24 | - public | |
25 | - { Public declarations } | |
26 | - end; | |
27 | - | |
28 | -implementation | |
29 | - | |
30 | -{$R *.dfm} | |
31 | - | |
32 | -end. |
@@ -1,558 +0,0 @@ | ||
1 | -unit KRK.Editors.SlaveComponentsEditor; | |
2 | - | |
3 | -interface | |
4 | - | |
5 | -uses Windows | |
6 | - , Menus | |
7 | - , ActnPopup | |
8 | - , Controls | |
9 | - , ComCtrls | |
10 | - , Classes | |
11 | - , Forms | |
12 | - , DesignWindows | |
13 | - , DesignIntf | |
14 | - , DesignEditors | |
15 | - , UPngImageList | |
16 | - , KRK.ToolsApi.Rtl.Common.Classes; | |
17 | - | |
18 | -type | |
19 | - TSlaveComponentClasses = array of TKRKSlaveComponentClass; | |
20 | - TOnAfterAdd = procedure (const AListItem: TListItem) of object; | |
21 | - TOnAfterRefreshListItem = TOnAfterAdd; | |
22 | - TOnAfterBuildListItem = TOnAfterAdd; | |
23 | - TOnSlaveModified = TOnAfterAdd; | |
24 | - | |
25 | - TFormSlaveComponentsEditor = class(TDesignWindow) | |
26 | - LIVI: TListView; | |
27 | - PNIL: TPngImageList; | |
28 | - TOBA: TToolBar; | |
29 | - TOBUAdd: TToolButton; | |
30 | - TOBURemove: TToolButton; | |
31 | - TOBUClear: TToolButton; | |
32 | - PPAB: TPopupActionBar; | |
33 | - procedure FormClose(Sender: TObject; var Action: TCloseAction); | |
34 | - procedure FormShow(Sender: TObject); | |
35 | - procedure LIVISelectItem(Sender: TObject; Item: TListItem; Selected: Boolean); | |
36 | - procedure FormActivate(Sender: TObject); | |
37 | - procedure LIVIChange(Sender: TObject; Item: TListItem; Change: TItemChange); | |
38 | - procedure TOBURemoveClick(Sender: TObject); | |
39 | - procedure TOBUClearClick(Sender: TObject); | |
40 | - private | |
41 | - { Private declarations } | |
42 | - FMasterComponent: TKRKMasterComponent; | |
43 | - FSlaveComponentClasses: TSlaveComponentClasses; | |
44 | - FOnAfterAdd: TOnAfterAdd; | |
45 | - FOnAfterRefreshListItem: TOnAfterRefreshListItem; | |
46 | - FOnAfterBuildListItem: TOnAfterBuildListItem; | |
47 | - FOnSlaveModified: TOnSlaveModified; | |
48 | - FDesignerClosing: Boolean; | |
49 | - procedure UpdateButtonStatus; | |
50 | - //: Configura vários aspectos da tela baseados em opções passadas no | |
51 | - //: construtor por meio do método de classe ShowMe | |
52 | - procedure Configure; | |
53 | - //: Adiciona um item na lista associado a um objeto escravo do tipo da | |
54 | - //: classe identificada AClassIndex no array privado FSlaveComponentClasses | |
55 | - procedure Add(AClassIndex: Word); | |
56 | - //: Este é o método padrão que responde a cliques no botão de adição ou nos | |
57 | - //: ítens do pop-up menu PPAB. Internamente ele é capaz de identificar se | |
58 | - //: estamos clicando em TOBUAdd diretamente ou em algum item do pop-up menu. | |
59 | - //: DoClick é atribuido ao evenvo OnClick de TOBUAdd e de cada um dos ítens | |
60 | - //: criados dinamicamente em PPAB | |
61 | - procedure DoClick(ASender: TObject); | |
62 | - //: Remove os itens selecionados em LIVI, destruindo os objetos escravos | |
63 | - //: associados a cada um deles também | |
64 | - procedure Remove; overload; | |
65 | - //: Remove o item da lista que tem o nome indicado. Este método não destrói | |
66 | - //: o objeto escravo associado, ele apenas remove o item nomeado da lista. | |
67 | - procedure Remove(ASlaveName: String); overload; | |
68 | - //: Contrói a lista de objetos escravos visual (LIVI) baseados na lista de | |
69 | - //: objetos escravos contida em FMasterComponent.SlaveComponents | |
70 | - procedure BuildList; | |
71 | - //: Limpa a lista de objetos escravos, removendo todos os elementos contidos | |
72 | - //: em LIVI e destruindo cada uma das instâncias destes objetos, as quais | |
73 | - //: estão associadas a cada um dos elementos de LIVI | |
74 | - procedure Clear; | |
75 | - public | |
76 | - { Public declarations } | |
77 | - constructor Create(ADesigner: IDesigner; AMasterComponent: TKRKMasterComponent; ASlaveComponentClasses: TSlaveComponentClasses); reintroduce; | |
78 | - destructor Destroy; override; | |
79 | - //: Para mais informações leia os comentários presentes | |
80 | - //: em DesignIntf.IDesignNotification | |
81 | - procedure ItemDeleted(const ADesigner: IDesigner; AItem: TPersistent); override; | |
82 | - //: Este método, implementado de uma Interface, funciona como um evento e é | |
83 | - //: executado sempre que algum item em qualquer form aberto na IDE for | |
84 | - //: alterado, ou seja, ele recebe notificações de vários editores de | |
85 | - //: formulário, portanto ADesigner é sempre o Designer que contem o(s) | |
86 | - //: item(ns) alterado(s). O evento OnSlaveModified é disparado sempre que há | |
87 | - //: uma modificação em algum dos objetos escravos do componente mestre atual | |
88 | - //: (FMasterComponent). Para mais informações leia os comentários presentes | |
89 | - //: em DesignIntf.IDesignNotification | |
90 | - procedure ItemsModified(const ADesigner: IDesigner); override; | |
91 | - //: Este método, implementada da interface IDesignNotification, é executado | |
92 | - //: sempre que um Designer é fechado. Para mais informações leia os | |
93 | - //: comentários presentes em DesignIntf.IDesignNotification | |
94 | - procedure DesignerClosed(const ADesigner: IDesigner; AGoingDormant: Boolean); override; | |
95 | - //: Atualiza a lista de objetos escravos lendo as propriedades de cada | |
96 | - //: objeto escravo associado e configurando um SubItem (coluna) de LIVI com | |
97 | - //: o valor de cada propriedade. Por padrão a única propriedade atualizada | |
98 | - //: automaticamente é a propriedade "Nome". A segunda coluna de LIVI | |
99 | - //: (Classe) é padrão mas nunca é atualizada porque o tipo do objeto escravo | |
100 | - //: não muda utilize o evento OnAfterRefreshListItem para atualizar colunas | |
101 | - //: adicionais de cada editor de objetos escravos derivado deste | |
102 | - procedure RefreshList; | |
103 | - //: Exibe o editor de objetos escravos | |
104 | - class function ShowMe(ADesigner: IDesigner; AMasterComponentClass: TKRKMasterComponentClass; AMasterComponentReference: TKRKMasterComponent; ASlaveComponentClasses: TSlaveComponentClasses): TFormSlaveComponentsEditor; virtual; | |
105 | - | |
106 | - property OnAfterAdd: TOnAfterAdd read FOnAfterAdd write FOnAfterAdd; | |
107 | - property OnAfterRefreshListItem: TOnAfterRefreshListItem read FOnAfterRefreshListItem write FOnAfterRefreshListItem; | |
108 | - property OnAfterBuildListItem: TOnAfterBuildListItem read FOnAfterBuildListItem write FOnAfterBuildListItem; | |
109 | - property OnSlaveModified: TOnSlaveModified read FOnSlaveModified write FOnSlaveModified; | |
110 | - end; | |
111 | - | |
112 | -{ ****************************************************************************** | |
113 | -GLOSSÁRIO/AJUDA: | |
114 | - | |
115 | -> Designer | |
116 | - É o "desenhador", ou seja, é aquilo que permite editar visualmenteum TForm, um | |
117 | - TDataModule ou um TFrame. Não confundir Designer com TForm, TDataModule ou | |
118 | - TFrame apenas porque é isso que se vê ao abrir um form, por exemplo, na IDE. O | |
119 | - que se vê na verdade é um editor de formulário (Designer) que contém um | |
120 | - formulário aberto nele. Como não é possível existir um Designer sem um TForm, | |
121 | - TDataModule ou TFrame a confusão a respeito disso é normal | |
122 | - | |
123 | -> Como implementar o procedure Register | |
124 | - 1) Registre o componente TKRKSlaveComponent usando | |
125 | - RegisterNoIcon([TKRKSlaveComponent]). Este procedure registra o componente | |
126 | - escravo pai sem colocá-lo na paleta de componentes do Delphi, ou seja, o | |
127 | - usuário não poderá selecioná-lo. Para adicioná-lo a um Designer é | |
128 | - necessário construir o componente manualmente, que é o que nosso editor de | |
129 | - componentes escravos (nesta unit) faz. Este procedure adicionalmente faz | |
130 | - com que o ícone do componente (caixinha) não apareça no Designer quando ele | |
131 | - for adicionado | |
132 | - 2) Registre cada uma das classes filhas de TKRKSlaveComponent que serão | |
133 | - manipuladas, usando RegisterClass(TEscravo). O Delphi precisa saber da | |
134 | - existência destas classes em tempo de desenvolvimento e esta é a única | |
135 | - forma de fazer isso neste caso (componentes registrados com | |
136 | - RegisterComponents já entram na lista de classes registradas) | |
137 | - 3) Registre o componente principal, herdado de TKRKMasterComponent, usando | |
138 | - RegisterComponents('Categoria na paleta', [TMestre]) de forma usual | |
139 | - 4) Registre um editor de componente para editar os escravos usando | |
140 | - RegisterComponentEditor(TMestre, TEditorDeEscravos). No método ExecuteVerb | |
141 | - do editor de componente execute o comando a seguir (exemplo): | |
142 | - | |
143 | - TKRKSlaveComponentsEditor.ShowMe(Designer,TMestre,TMestre(Component),[TEscravo1,TEscravo2]); | |
144 | - | |
145 | - É possível também usar o editor de componente padrão disponível nesta unit | |
146 | - (TKRKMasterComponentEditor), neste caso, basta registrá-lo | |
147 | - | |
148 | - 5) Exemplo completo: | |
149 | - | |
150 | - procedure Register; | |
151 | - begin | |
152 | - RegisterNoIcon([TKRKSlaveComponent]); | |
153 | - | |
154 | - RegisterClass(TEscravo1); | |
155 | - RegisterClass(TEscravo2); | |
156 | - | |
157 | - RegisterComponents('Meus Components', [TMestre]); | |
158 | - | |
159 | - RegisterComponentEditor(TMestre, TEditorDeEscravos); | |
160 | - end; | |
161 | - | |
162 | -> Como estender o editor de escravos | |
163 | - O editor de escravos contido nesta unit está no pacote runtime principal do | |
164 | - Krakatoa. Ele é um simples form que herda de TDesignWindow, um form especial | |
165 | - que implementa algumas interfaces úteis que permitem a interação do editor de | |
166 | - escravos com o Designer. Por padrão, seu .dfm não está junto de seu .pas | |
167 | - correspondente porque esta á uma regra do Krakatoa e também, este form não | |
168 | - está registrado para ser criado a partir do repositório, como acontece, por | |
169 | - exemplo, com o TKRKForm (isso poderá ser feito no futuro). Como este form não | |
170 | - está registrado para ser criado pela IDE de forma fácil, será necessário | |
171 | - realizar alguns passos manualmente para herdá-lo. | |
172 | - | |
173 | - 1) Crie um form comum e salve-o de forma usual. Você pode dar o nome final do | |
174 | - editor se quiser e nomear sua unit de forma correta | |
175 | - 2) Feche o form para que ele não seja exibido na IDE e, usando outro editor, | |
176 | - substitua a linha no seu arquivo-fonte (.pas): | |
177 | - | |
178 | - TForm1 = class(TForm) | |
179 | - | |
180 | - por: | |
181 | - | |
182 | - TForm1 = class(TKRKSlaveComponentsEditor) | |
183 | - 3) Abra o arquivo .dfm e substitua a primeira palavra object por inherited | |
184 | - | |
185 | - Após realizar estes 3 passos volte a abrir seu form na IDE. Você verá o form | |
186 | - básico herdado de TKRKSlaveComponentsEditor com seus componentes básicos e | |
187 | - você customizá-lo da forma que quiser. Para chamar seu editor customizado | |
188 | - execute a seguinte linha: | |
189 | - | |
190 | - TMeuEditorDeEscravos.ShowMe(Designer,TMestre,TMestre(Component),[TEscravo1,TEscravo2]); | |
191 | - | |
192 | - ATENÇÃO: CASO ESTE FORM ESTJA SENDO UTILIZADO, ISTO É, VISÍVEL NA TELA PORQUE | |
193 | - UM COMPONENTE MESTRE FOI DUPLAMENTE CLICADO, E O PACOTE ONDE ELE SE ENCONTRA | |
194 | - FOR RECOMPILADO, O DELPHI VAI TRAVAR E FECHAR. ACREDITO QUE ISSO SEJA "NORMAL" | |
195 | - PORQUE O COMPORTAMENTO PADRÃO AO COMPILAR UM PACOTE É FECHAR TODOS OS | |
196 | - DESIGNERS E SEUS DEPENDENTES, PORÉM QUANDO SE FAZ ISSO EM UM PACOTE QUE DEFINE | |
197 | - O FORM, APARENTEMENTE HÁ UMA RECURSIVIDADE (NÃO SEI EXPLICARO DIREITO), QUE | |
198 | - CAUSA ACCESS VIOLATION E TRAVA O DELPHI ATÉ ELE FECHAR POR COMPLETO E SEM MAIS | |
199 | - AVISOS | |
200 | -****************************************************************************** } | |
201 | - | |
202 | -implementation | |
203 | - | |
204 | -uses | |
205 | - SysUtils; | |
206 | - | |
207 | -{$R *.dfm} | |
208 | - | |
209 | -{ TSlaveComponentsEditor } | |
210 | - | |
211 | -procedure TFormSlaveComponentsEditor.Add(AClassIndex: Word); | |
212 | -var | |
213 | - LI: TListItem; | |
214 | -begin | |
215 | - LI := LIVI.Items.Add; | |
216 | - LI.Caption := Designer.UniqueName(FSlaveComponentClasses[AClassIndex].ClassName); | |
217 | - LI.Data := FSlaveComponentClasses[AClassIndex].Create(FMasterComponent.Owner); | |
218 | - TKRKSlaveComponent(LI.Data).Name := LI.Caption; | |
219 | - TKRKSlaveComponent(LI.Data).Parent := FMasterComponent; | |
220 | - LI.SubItems.Add(FSlaveComponentClasses[AClassIndex].ClassName); | |
221 | - | |
222 | - if Assigned(FOnAfterAdd) then | |
223 | - FOnAfterAdd(LI); | |
224 | -end; | |
225 | - | |
226 | -procedure TFormSlaveComponentsEditor.Clear; | |
227 | -var | |
228 | - LI: TListItem; | |
229 | -begin | |
230 | - if LIVI.Items.Count > 0 then | |
231 | - if Application.MessageBox('Quer limpar a lista de objetos? (Esta operação não pode ser desfeita)','Tem certeza?',MB_ICONQUESTION or MB_YESNO) = IDYES then | |
232 | - begin | |
233 | - // Um a um, destrói os objetos escravos associadas a cada item da lista | |
234 | - for LI in LIVI.Items do | |
235 | - begin | |
236 | - TKRKSlaveComponent(LI.Data).Free; | |
237 | - LI.Data := nil; | |
238 | - end; | |
239 | - | |
240 | - // Efetivamente remove os itens da lista | |
241 | - LIVI.Clear; | |
242 | - TOBURemove.Enabled := False; | |
243 | - TOBUClear.Enabled := False; | |
244 | - end; | |
245 | -end; | |
246 | - | |
247 | -procedure TFormSlaveComponentsEditor.Configure; | |
248 | -var | |
249 | - SCL: TKRKSlaveComponentClass; | |
250 | - MEI: TMenuItem; | |
251 | - i: Word; | |
252 | -begin | |
253 | - Caption := 'Objetos escravos de ' + FMasterComponent.Owner.Name + '.' + FMasterComponent.Name; | |
254 | - | |
255 | - TOBUAdd.OnClick := DoClick; | |
256 | - | |
257 | - if Length(FSlaveComponentClasses) = 1 then | |
258 | - begin | |
259 | - TOBUAdd.Style := tbsButton; | |
260 | - TOBUAdd.DropdownMenu := nil; | |
261 | - end | |
262 | - else | |
263 | - begin | |
264 | - TOBUAdd.Style := tbsDropDown; | |
265 | - TOBUAdd.DropdownMenu := PPAB; | |
266 | - | |
267 | - i := 0; | |
268 | - | |
269 | - for SCL in FSlaveComponentClasses do | |
270 | - begin | |
271 | - MEI := PPAB.CreateMenuItem; | |
272 | - MEI.Tag := i; | |
273 | - MEI.GroupIndex := 1; | |
274 | - MEI.AutoCheck := True; | |
275 | - MEI.OnClick := DoClick; | |
276 | - MEI.Caption := SCL.ClassName; | |
277 | - | |
278 | - if i = 0 then | |
279 | - MEI.Checked := True; | |
280 | - | |
281 | - PPAB.Items.Add(MEI); | |
282 | - Inc(i); | |
283 | - end; | |
284 | - end; | |
285 | - | |
286 | - // Força os botões a assumirem seu tamanho padrão | |
287 | - TOBA.Height := 31; | |
288 | - | |
289 | - BuildList; | |
290 | -end; | |
291 | - | |
292 | -constructor TFormSlaveComponentsEditor.Create(ADesigner: IDesigner; AMasterComponent: TKRKMasterComponent; ASlaveComponentClasses: TSlaveComponentClasses); | |
293 | -begin | |
294 | - inherited Create(Application); | |
295 | - | |
296 | - Designer := ADesigner; | |
297 | - FMasterComponent := AMasterComponent; | |
298 | - // Atribui a propriedade ComponentEditor do componente mestre, de forma que | |
299 | - // isso possa ser usado como uma forma de verificar se há um editor de | |
300 | - // componente aberto para aquele mestre específico | |
301 | - FMasterComponent.SlaveComponentsEditor := Self; | |
302 | - FSlaveComponentClasses := ASlaveComponentClasses; | |
303 | - FDesignerClosing := False; | |
304 | -end; | |
305 | - | |
306 | -procedure TFormSlaveComponentsEditor.DesignerClosed(const ADesigner: IDesigner; AGoingDormant: Boolean); | |
307 | -begin | |
308 | - if Designer = ADesigner then | |
309 | - begin | |
310 | - if not AGoingDormant then | |
311 | - Designer := nil; | |
312 | - | |
313 | - FDesignerClosing := True; | |
314 | - FMasterComponent := nil; | |
315 | - Close; | |
316 | - end; | |
317 | - | |
318 | - inherited; | |
319 | -end; | |
320 | - | |
321 | -destructor TFormSlaveComponentsEditor.Destroy; | |
322 | -begin | |
323 | - // Limpa o valor de ComponentEditor e assim deixar claro que não há mais um | |
324 | - // editor de escravos aberto. FMasterComponent será nil apenas quando o | |
325 | - // Designer for fechado, indicando que não há mais nem mesmo um | |
326 | - // FMasterComponent para se preocupar | |
327 | - if Assigned(FMasterComponent) then | |
328 | - FMasterComponent.SlaveComponentsEditor := nil; | |
329 | - | |
330 | - inherited; | |
331 | -end; | |
332 | - | |
333 | -procedure TFormSlaveComponentsEditor.DoClick(ASender: TObject); | |
334 | -begin | |
335 | - Add(TComponent(ASender).Tag); | |
336 | - | |
337 | - if ASender is TMenuItem then | |
338 | - begin | |
339 | - TOBUAdd.Tag := TMenuItem(ASender).Tag; | |
340 | - TMenuItem(ASender).Checked := True; | |
341 | - end; | |
342 | -end; | |
343 | - | |
344 | -procedure TFormSlaveComponentsEditor.FormActivate(Sender: TObject); | |
345 | -begin | |
346 | - // Por padrão, quando se ativa a tela de edição de objetos escravos, | |
347 | - // precisamos verificar se há algum item selecionado e, se houver, realizar a | |
348 | - // seleção do objeto escravo no Designer. Isso mostra as propriedades do | |
349 | - // objeto escravo no OI | |
350 | - if Assigned(LIVI.Selected) then | |
351 | - Designer.SelectComponent(TKRKSlaveComponent(LIVI.Selected.Data)); | |
352 | -end; | |
353 | - | |
354 | -procedure TFormSlaveComponentsEditor.FormClose(Sender: TObject; var Action: TCloseAction); | |
355 | -begin | |
356 | - // Ao fechar este form, tenta selecionar o seu componente mestre | |
357 | - if Assigned(FMasterComponent) then | |
358 | - Designer.SelectComponent(FMasterComponent); | |
359 | - | |
360 | - // Convenientemente, libera a memória sempre que este form for fechado. | |
361 | - // Variáveis que, porventura, contenham a referência para este form apontarão | |
362 | - // para lixo e precisarão ser configuradas como nil pelo processo chamador. | |
363 | - // Recomenda-se o uso de ShowMe sem se preocupar com seu retorno (uma | |
364 | - // instância deste form), assim nada precisará ser limpo | |
365 | - Action := caFree; | |
366 | -end; | |
367 | - | |
368 | -procedure TFormSlaveComponentsEditor.FormShow(Sender: TObject); | |
369 | -begin | |
370 | - Configure; | |
371 | -end; | |
372 | - | |
373 | -// Este método é executado o tempo todo pra qualquer coisa, não apenas exclusão, | |
374 | -// não sei porque, porém, usando o parâmetro AItem é possível saber se algum de | |
375 | -// nossos objetos escravos foi referenciado e, segundo testes, quando este | |
376 | -// método é excutado e AItem é um descendente de TSlaveItem, então foi | |
377 | -// justamente este o item excluído | |
378 | -procedure TFormSlaveComponentsEditor.ItemDeleted(const ADesigner: IDesigner; AItem: TPersistent); | |
379 | -begin | |
380 | - if FDesignerClosing then | |
381 | - Exit; | |
382 | - | |
383 | - if AItem is TKRKSlaveComponent then | |
384 | - begin | |
385 | - Remove(TKRKSlaveComponent(AItem).Name); | |
386 | - | |
387 | - end; | |
388 | - | |
389 | - inherited; | |
390 | -end; | |
391 | - | |
392 | -procedure TFormSlaveComponentsEditor.ItemsModified(const ADesigner: IDesigner); | |
393 | -var | |
394 | - DesignerSelections: IDesignerSelections; | |
395 | - i: Byte; | |
396 | - LI: TListItem; | |
397 | -begin | |
398 | - if FDesignerClosing then | |
399 | - Exit; | |
400 | - | |
401 | - inherited; | |
402 | - // Abaixo, Designer corresponde a propriedade Designer do Form Atual e que, no | |
403 | - // fim das contas representa o Form onde componente, identificado por | |
404 | - // FMasterComponent está. FMasterComponent é o componente que está sendo | |
405 | - // "editado" por este editor de componente (TSlaveComponentsEditor). Este | |
406 | - // método só é útil aqui caso estejamos notificando a respeito de algo que | |
407 | - // ocorreu no form que contém o componente mestre, por isso a condição abaixo | |
408 | - // é necessária | |
409 | - if Designer = ADesigner then | |
410 | - begin | |
411 | - DesignerSelections := CreateSelectionList; | |
412 | - Designer.GetSelections(DesignerSelections); | |
413 | - // Varre a lista de componentes selecionados buscando dentre eles | |
414 | - // descendentes de TSlaveComponent, já que apenas filhos desta classe podem | |
415 | - // ser adicionados a LIVI e sendo assim, apenas filhos desta classe | |
416 | - // precisam ser verificados. Cada TSlaveComponent encontrado em | |
417 | - // DesignerSelections foi alterado de alguma forma e potencialmente | |
418 | - // precisará ajustar sua exibição em LIVI | |
419 | - for i := 0 to Pred(DesignerSelections.Count) do | |
420 | - begin | |
421 | - if DesignerSelections[i] is TKRKSlaveComponent then | |
422 | - for LI in LIVI.Items do | |
423 | - begin | |
424 | - // Caso um item de LIVI corresponda a um dos itens de | |
425 | - // DesignerSelections (itens selecionados), significa que precisamos | |
426 | - // atualizar este item | |
427 | - if LI.Data = DesignerSelections[i] then | |
428 | - begin | |
429 | - // Por padrão atualiza a coluna "Nome" com o valor da propriedade | |
430 | - // "Nome", mesmo que ela não tenha sido modificada, isso garante a | |
431 | - // sincronia visual, pois quando a propriedade "Name" mudar isso | |
432 | - // já dará conta do recado | |
433 | - LI.Caption := TKRKSlaveComponent(LI.Data).Name; | |
434 | - | |
435 | - if Assigned(FOnSlaveModified) then | |
436 | - FOnSlaveModified(LI); | |
437 | - end; | |
438 | - end; | |
439 | - end; | |
440 | - end; | |
441 | -end; | |
442 | - | |
443 | -procedure TFormSlaveComponentsEditor.LIVIChange(Sender: TObject; Item: TListItem; Change: TItemChange); | |
444 | -begin | |
445 | - // Muda o status de habilitação dos botões de acordo com alguns critérios | |
446 | - UpdateButtonStatus; | |
447 | -end; | |
448 | - | |
449 | -procedure TFormSlaveComponentsEditor.LIVISelectItem(Sender: TObject; Item: TListItem; Selected: Boolean); | |
450 | -begin | |
451 | - // Sempre que se seleciona um item na lista, o mesmo deve ser exibido no OI e | |
452 | - // o código abaixo cuida disso | |
453 | - if Assigned(Item.Data) then | |
454 | - Designer.SelectComponent(TKRKSlaveComponent(Item.Data)); | |
455 | -end; | |
456 | - | |
457 | -procedure TFormSlaveComponentsEditor.RefreshList; | |
458 | -var | |
459 | - LI: TListItem; | |
460 | -begin | |
461 | - for LI in LIVI.Items do | |
462 | - begin | |
463 | - LI.Caption := TKRKSlaveComponent(LI.Data).Name; | |
464 | - | |
465 | - if Assigned(FOnAfterRefreshListItem) then | |
466 | - FOnAfterRefreshListItem(LI); | |
467 | - end; | |
468 | -end; | |
469 | - | |
470 | -procedure TFormSlaveComponentsEditor.BuildList; | |
471 | -var | |
472 | - SubComponent: TKRKSlaveComponent; | |
473 | - i: Word; | |
474 | - LI: TListItem; | |
475 | -begin | |
476 | - LIVI.Clear; | |
477 | - | |
478 | - // O Delphi 2006 não é capaz de usar for..in com TObjectList | |
479 | - if FMasterComponent.SlaveComponents.Count > 0 then | |
480 | - for i := 0 to Pred(FMasterComponent.SlaveComponents.Count) do | |
481 | - begin | |
482 | - SubComponent := TKRKSlaveComponent(FMasterComponent.SlaveComponents[i]); | |
483 | - | |
484 | - LI := LIVI.Items.Add; | |
485 | - LI.Caption := SubComponent.Name; | |
486 | - LI.SubItems.Add(SubComponent.ClassName); | |
487 | - LI.Data := SubComponent; | |
488 | - | |
489 | - if Assigned(FOnAfterBuildListItem) then | |
490 | - FOnAfterBuildListItem(LI); | |
491 | - end; | |
492 | -end; | |
493 | - | |
494 | -procedure TFormSlaveComponentsEditor.Remove; | |
495 | -var | |
496 | - LI: TListItem; | |
497 | -begin | |
498 | - if Assigned(LIVI.Selected) then | |
499 | - if Application.MessageBox('Quer remover os objetos selecionados? (Esta operação não pode ser desfeita)','Tem certeza?',MB_ICONQUESTION or MB_YESNO) = IDYES then | |
500 | - begin | |
501 | - for LI in LIVI.Items do | |
502 | - begin | |
503 | - if LI.Selected then | |
504 | - begin | |
505 | - TKRKSlaveComponent(LI.Data).Free; | |
506 | - LI.Data := nil; | |
507 | - end; | |
508 | - end; | |
509 | - | |
510 | - LIVI.DeleteSelected; | |
511 | - UpdateButtonStatus; | |
512 | - end; | |
513 | -end; | |
514 | - | |
515 | -procedure TFormSlaveComponentsEditor.Remove(ASlaveName: String); | |
516 | -var | |
517 | - LI: TListItem; | |
518 | -begin | |
519 | - for LI in LIVI.Items do | |
520 | - if TKRKSlaveComponent(LI.Data).Name = ASlaveName then | |
521 | - begin | |
522 | - LI.Data := nil; | |
523 | - LI.Delete; | |
524 | - UpdateButtonStatus; | |
525 | - Break; | |
526 | - end; | |
527 | -end; | |
528 | - | |
529 | -class function TFormSlaveComponentsEditor.ShowMe(ADesigner: IDesigner; AMasterComponentClass: TKRKMasterComponentClass; AMasterComponentReference: TKRKMasterComponent; ASlaveComponentClasses: TSlaveComponentClasses): TFormSlaveComponentsEditor; | |
530 | -begin | |
531 | - if Length(ASlaveComponentClasses) = 0 then | |
532 | - raise Exception.Create('É necessário informar ao menos uma classe escrava descendente de TSlaveComponentClass'); | |
533 | - | |
534 | - if Assigned(AMasterComponentReference.SlaveComponentsEditor) then | |
535 | - Result := TFormSlaveComponentsEditor(AMasterComponentReference.SlaveComponentsEditor) | |
536 | - else | |
537 | - Result := Self.Create(ADesigner,AMasterComponentReference,ASlaveComponentClasses); | |
538 | - | |
539 | - Result.Show; | |
540 | -end; | |
541 | - | |
542 | -procedure TFormSlaveComponentsEditor.TOBUClearClick(Sender: TObject); | |
543 | -begin | |
544 | - Clear; | |
545 | -end; | |
546 | - | |
547 | -procedure TFormSlaveComponentsEditor.TOBURemoveClick(Sender: TObject); | |
548 | -begin | |
549 | - Remove; | |
550 | -end; | |
551 | - | |
552 | -procedure TFormSlaveComponentsEditor.UpdateButtonStatus; | |
553 | -begin | |
554 | - TOBURemove.Enabled := Assigned(LIVI.Selected); | |
555 | - TOBUClear.Enabled := LIVI.Items.Count > 0; | |
556 | -end; | |
557 | - | |
558 | -end. |
@@ -0,0 +1,558 @@ | ||
1 | +unit KRK.ToolsApi.Editors.SlaveComponentsEditor; | |
2 | + | |
3 | +interface | |
4 | + | |
5 | +uses Windows | |
6 | + , Menus | |
7 | + , ActnPopup | |
8 | + , Controls | |
9 | + , ComCtrls | |
10 | + , Classes | |
11 | + , Forms | |
12 | + , DesignWindows | |
13 | + , DesignIntf | |
14 | + , DesignEditors | |
15 | + , UPngImageList | |
16 | + , KRK.ToolsApi.Rtl.Common.Classes; | |
17 | + | |
18 | +type | |
19 | + TSlaveComponentClasses = array of TKRKSlaveComponentClass; | |
20 | + TOnAfterAdd = procedure (const AListItem: TListItem) of object; | |
21 | + TOnAfterRefreshListItem = TOnAfterAdd; | |
22 | + TOnAfterBuildListItem = TOnAfterAdd; | |
23 | + TOnSlaveModified = TOnAfterAdd; | |
24 | + | |
25 | + TFormSlaveComponentsEditor = class(TDesignWindow) | |
26 | + LIVI: TListView; | |
27 | + PNIL: TPngImageList; | |
28 | + TOBA: TToolBar; | |
29 | + TOBUAdd: TToolButton; | |
30 | + TOBURemove: TToolButton; | |
31 | + TOBUClear: TToolButton; | |
32 | + PPAB: TPopupActionBar; | |
33 | + procedure FormClose(Sender: TObject; var Action: TCloseAction); | |
34 | + procedure FormShow(Sender: TObject); | |
35 | + procedure LIVISelectItem(Sender: TObject; Item: TListItem; Selected: Boolean); | |
36 | + procedure FormActivate(Sender: TObject); | |
37 | + procedure LIVIChange(Sender: TObject; Item: TListItem; Change: TItemChange); | |
38 | + procedure TOBURemoveClick(Sender: TObject); | |
39 | + procedure TOBUClearClick(Sender: TObject); | |
40 | + private | |
41 | + { Private declarations } | |
42 | + FMasterComponent: TKRKMasterComponent; | |
43 | + FSlaveComponentClasses: TSlaveComponentClasses; | |
44 | + FOnAfterAdd: TOnAfterAdd; | |
45 | + FOnAfterRefreshListItem: TOnAfterRefreshListItem; | |
46 | + FOnAfterBuildListItem: TOnAfterBuildListItem; | |
47 | + FOnSlaveModified: TOnSlaveModified; | |
48 | + FDesignerClosing: Boolean; | |
49 | + procedure UpdateButtonStatus; | |
50 | + //: Configura vários aspectos da tela baseados em opções passadas no | |
51 | + //: construtor por meio do método de classe ShowMe | |
52 | + procedure Configure; | |
53 | + //: Adiciona um item na lista associado a um objeto escravo do tipo da | |
54 | + //: classe identificada AClassIndex no array privado FSlaveComponentClasses | |
55 | + procedure Add(AClassIndex: Word); | |
56 | + //: Este é o método padrão que responde a cliques no botão de adição ou nos | |
57 | + //: ítens do pop-up menu PPAB. Internamente ele é capaz de identificar se | |
58 | + //: estamos clicando em TOBUAdd diretamente ou em algum item do pop-up menu. | |
59 | + //: DoClick é atribuido ao evenvo OnClick de TOBUAdd e de cada um dos ítens | |
60 | + //: criados dinamicamente em PPAB | |
61 | + procedure DoClick(ASender: TObject); | |
62 | + //: Remove os itens selecionados em LIVI, destruindo os objetos escravos | |
63 | + //: associados a cada um deles também | |
64 | + procedure Remove; overload; | |
65 | + //: Remove o item da lista que tem o nome indicado. Este método não destrói | |
66 | + //: o objeto escravo associado, ele apenas remove o item nomeado da lista. | |
67 | + procedure Remove(ASlaveName: String); overload; | |
68 | + //: Contrói a lista de objetos escravos visual (LIVI) baseados na lista de | |
69 | + //: objetos escravos contida em FMasterComponent.SlaveComponents | |
70 | + procedure BuildList; | |
71 | + //: Limpa a lista de objetos escravos, removendo todos os elementos contidos | |
72 | + //: em LIVI e destruindo cada uma das instâncias destes objetos, as quais | |
73 | + //: estão associadas a cada um dos elementos de LIVI | |
74 | + procedure Clear; | |
75 | + public | |
76 | + { Public declarations } | |
77 | + constructor Create(ADesigner: IDesigner; AMasterComponent: TKRKMasterComponent; ASlaveComponentClasses: TSlaveComponentClasses); reintroduce; | |
78 | + destructor Destroy; override; | |
79 | + //: Para mais informações leia os comentários presentes | |
80 | + //: em DesignIntf.IDesignNotification | |
81 | + procedure ItemDeleted(const ADesigner: IDesigner; AItem: TPersistent); override; | |
82 | + //: Este método, implementado de uma Interface, funciona como um evento e é | |
83 | + //: executado sempre que algum item em qualquer form aberto na IDE for | |
84 | + //: alterado, ou seja, ele recebe notificações de vários editores de | |
85 | + //: formulário, portanto ADesigner é sempre o Designer que contem o(s) | |
86 | + //: item(ns) alterado(s). O evento OnSlaveModified é disparado sempre que há | |
87 | + //: uma modificação em algum dos objetos escravos do componente mestre atual | |
88 | + //: (FMasterComponent). Para mais informações leia os comentários presentes | |
89 | + //: em DesignIntf.IDesignNotification | |
90 | + procedure ItemsModified(const ADesigner: IDesigner); override; | |
91 | + //: Este método, implementada da interface IDesignNotification, é executado | |
92 | + //: sempre que um Designer é fechado. Para mais informações leia os | |
93 | + //: comentários presentes em DesignIntf.IDesignNotification | |
94 | + procedure DesignerClosed(const ADesigner: IDesigner; AGoingDormant: Boolean); override; | |
95 | + //: Atualiza a lista de objetos escravos lendo as propriedades de cada | |
96 | + //: objeto escravo associado e configurando um SubItem (coluna) de LIVI com | |
97 | + //: o valor de cada propriedade. Por padrão a única propriedade atualizada | |
98 | + //: automaticamente é a propriedade "Nome". A segunda coluna de LIVI | |
99 | + //: (Classe) é padrão mas nunca é atualizada porque o tipo do objeto escravo | |
100 | + //: não muda utilize o evento OnAfterRefreshListItem para atualizar colunas | |
101 | + //: adicionais de cada editor de objetos escravos derivado deste | |
102 | + procedure RefreshList; | |
103 | + //: Exibe o editor de objetos escravos | |
104 | + class function ShowMe(ADesigner: IDesigner; AMasterComponentClass: TKRKMasterComponentClass; AMasterComponentReference: TKRKMasterComponent; ASlaveComponentClasses: TSlaveComponentClasses): TFormSlaveComponentsEditor; virtual; | |
105 | + | |
106 | + property OnAfterAdd: TOnAfterAdd read FOnAfterAdd write FOnAfterAdd; | |
107 | + property OnAfterRefreshListItem: TOnAfterRefreshListItem read FOnAfterRefreshListItem write FOnAfterRefreshListItem; | |
108 | + property OnAfterBuildListItem: TOnAfterBuildListItem read FOnAfterBuildListItem write FOnAfterBuildListItem; | |
109 | + property OnSlaveModified: TOnSlaveModified read FOnSlaveModified write FOnSlaveModified; | |
110 | + end; | |
111 | + | |
112 | +{ ****************************************************************************** | |
113 | +GLOSSÁRIO/AJUDA: | |
114 | + | |
115 | +> Designer | |
116 | + É o "desenhador", ou seja, é aquilo que permite editar visualmenteum TForm, um | |
117 | + TDataModule ou um TFrame. Não confundir Designer com TForm, TDataModule ou | |
118 | + TFrame apenas porque é isso que se vê ao abrir um form, por exemplo, na IDE. O | |
119 | + que se vê na verdade é um editor de formulário (Designer) que contém um | |
120 | + formulário aberto nele. Como não é possível existir um Designer sem um TForm, | |
121 | + TDataModule ou TFrame a confusão a respeito disso é normal | |
122 | + | |
123 | +> Como implementar o procedure Register | |
124 | + 1) Registre o componente TKRKSlaveComponent usando | |
125 | + RegisterNoIcon([TKRKSlaveComponent]). Este procedure registra o componente | |
126 | + escravo pai sem colocá-lo na paleta de componentes do Delphi, ou seja, o | |
127 | + usuário não poderá selecioná-lo. Para adicioná-lo a um Designer é | |
128 | + necessário construir o componente manualmente, que é o que nosso editor de | |
129 | + componentes escravos (nesta unit) faz. Este procedure adicionalmente faz | |
130 | + com que o ícone do componente (caixinha) não apareça no Designer quando ele | |
131 | + for adicionado | |
132 | + 2) Registre cada uma das classes filhas de TKRKSlaveComponent que serão | |
133 | + manipuladas, usando RegisterClass(TEscravo). O Delphi precisa saber da | |
134 | + existência destas classes em tempo de desenvolvimento e esta é a única | |
135 | + forma de fazer isso neste caso (componentes registrados com | |
136 | + RegisterComponents já entram na lista de classes registradas) | |
137 | + 3) Registre o componente principal, herdado de TKRKMasterComponent, usando | |
138 | + RegisterComponents('Categoria na paleta', [TMestre]) de forma usual | |
139 | + 4) Registre um editor de componente para editar os escravos usando | |
140 | + RegisterComponentEditor(TMestre, TEditorDeEscravos). No método ExecuteVerb | |
141 | + do editor de componente execute o comando a seguir (exemplo): | |
142 | + | |
143 | + TKRKSlaveComponentsEditor.ShowMe(Designer,TMestre,TMestre(Component),[TEscravo1,TEscravo2]); | |
144 | + | |
145 | + É possível também usar o editor de componente padrão disponível nesta unit | |
146 | + (TKRKMasterComponentEditor), neste caso, basta registrá-lo | |
147 | + | |
148 | + 5) Exemplo completo: | |
149 | + | |
150 | + procedure Register; | |
151 | + begin | |
152 | + RegisterNoIcon([TKRKSlaveComponent]); | |
153 | + | |
154 | + RegisterClass(TEscravo1); | |
155 | + RegisterClass(TEscravo2); | |
156 | + | |
157 | + RegisterComponents('Meus Components', [TMestre]); | |
158 | + | |
159 | + RegisterComponentEditor(TMestre, TEditorDeEscravos); | |
160 | + end; | |
161 | + | |
162 | +> Como estender o editor de escravos | |
163 | + O editor de escravos contido nesta unit está no pacote runtime principal do | |
164 | + Krakatoa. Ele é um simples form que herda de TDesignWindow, um form especial | |
165 | + que implementa algumas interfaces úteis que permitem a interação do editor de | |
166 | + escravos com o Designer. Por padrão, seu .dfm não está junto de seu .pas | |
167 | + correspondente porque esta á uma regra do Krakatoa e também, este form não | |
168 | + está registrado para ser criado a partir do repositório, como acontece, por | |
169 | + exemplo, com o TKRKForm (isso poderá ser feito no futuro). Como este form não | |
170 | + está registrado para ser criado pela IDE de forma fácil, será necessário | |
171 | + realizar alguns passos manualmente para herdá-lo. | |
172 | + | |
173 | + 1) Crie um form comum e salve-o de forma usual. Você pode dar o nome final do | |
174 | + editor se quiser e nomear sua unit de forma correta | |
175 | + 2) Feche o form para que ele não seja exibido na IDE e, usando outro editor, | |
176 | + substitua a linha no seu arquivo-fonte (.pas): | |
177 | + | |
178 | + TForm1 = class(TForm) | |
179 | + | |
180 | + por: | |
181 | + | |
182 | + TForm1 = class(TKRKSlaveComponentsEditor) | |
183 | + 3) Abra o arquivo .dfm e substitua a primeira palavra object por inherited | |
184 | + | |
185 | + Após realizar estes 3 passos volte a abrir seu form na IDE. Você verá o form | |
186 | + básico herdado de TKRKSlaveComponentsEditor com seus componentes básicos e | |
187 | + você customizá-lo da forma que quiser. Para chamar seu editor customizado | |
188 | + execute a seguinte linha: | |
189 | + | |
190 | + TMeuEditorDeEscravos.ShowMe(Designer,TMestre,TMestre(Component),[TEscravo1,TEscravo2]); | |
191 | + | |
192 | + ATENÇÃO: CASO ESTE FORM ESTJA SENDO UTILIZADO, ISTO É, VISÍVEL NA TELA PORQUE | |
193 | + UM COMPONENTE MESTRE FOI DUPLAMENTE CLICADO, E O PACOTE ONDE ELE SE ENCONTRA | |
194 | + FOR RECOMPILADO, O DELPHI VAI TRAVAR E FECHAR. ACREDITO QUE ISSO SEJA "NORMAL" | |
195 | + PORQUE O COMPORTAMENTO PADRÃO AO COMPILAR UM PACOTE É FECHAR TODOS OS | |
196 | + DESIGNERS E SEUS DEPENDENTES, PORÉM QUANDO SE FAZ ISSO EM UM PACOTE QUE DEFINE | |
197 | + O FORM, APARENTEMENTE HÁ UMA RECURSIVIDADE (NÃO SEI EXPLICARO DIREITO), QUE | |
198 | + CAUSA ACCESS VIOLATION E TRAVA O DELPHI ATÉ ELE FECHAR POR COMPLETO E SEM MAIS | |
199 | + AVISOS | |
200 | +****************************************************************************** } | |
201 | + | |
202 | +implementation | |
203 | + | |
204 | +uses | |
205 | + SysUtils; | |
206 | + | |
207 | +{$R *.dfm} | |
208 | + | |
209 | +{ TSlaveComponentsEditor } | |
210 | + | |
211 | +procedure TFormSlaveComponentsEditor.Add(AClassIndex: Word); | |
212 | +var | |
213 | + LI: TListItem; | |
214 | +begin | |
215 | + LI := LIVI.Items.Add; | |
216 | + LI.Caption := Designer.UniqueName(FSlaveComponentClasses[AClassIndex].ClassName); | |
217 | + LI.Data := FSlaveComponentClasses[AClassIndex].Create(FMasterComponent.Owner); | |
218 | + TKRKSlaveComponent(LI.Data).Name := LI.Caption; | |
219 | + TKRKSlaveComponent(LI.Data).Parent := FMasterComponent; | |
220 | + LI.SubItems.Add(FSlaveComponentClasses[AClassIndex].ClassName); | |
221 | + | |
222 | + if Assigned(FOnAfterAdd) then | |
223 | + FOnAfterAdd(LI); | |
224 | +end; | |
225 | + | |
226 | +procedure TFormSlaveComponentsEditor.Clear; | |
227 | +var | |
228 | + LI: TListItem; | |
229 | +begin | |
230 | + if LIVI.Items.Count > 0 then | |
231 | + if Application.MessageBox('Quer limpar a lista de objetos? (Esta operação não pode ser desfeita)','Tem certeza?',MB_ICONQUESTION or MB_YESNO) = IDYES then | |
232 | + begin | |
233 | + // Um a um, destrói os objetos escravos associadas a cada item da lista | |
234 | + for LI in LIVI.Items do | |
235 | + begin | |
236 | + TKRKSlaveComponent(LI.Data).Free; | |
237 | + LI.Data := nil; | |
238 | + end; | |
239 | + | |
240 | + // Efetivamente remove os itens da lista | |
241 | + LIVI.Clear; | |
242 | + TOBURemove.Enabled := False; | |
243 | + TOBUClear.Enabled := False; | |
244 | + end; | |
245 | +end; | |
246 | + | |
247 | +procedure TFormSlaveComponentsEditor.Configure; | |
248 | +var | |
249 | + SCL: TKRKSlaveComponentClass; | |
250 | + MEI: TMenuItem; | |
251 | + i: Word; | |
252 | +begin | |
253 | + Caption := 'Objetos escravos de ' + FMasterComponent.Owner.Name + '.' + FMasterComponent.Name; | |
254 | + | |
255 | + TOBUAdd.OnClick := DoClick; | |
256 | + | |
257 | + if Length(FSlaveComponentClasses) = 1 then | |
258 | + begin | |
259 | + TOBUAdd.Style := tbsButton; | |
260 | + TOBUAdd.DropdownMenu := nil; | |
261 | + end | |
262 | + else | |
263 | + begin | |
264 | + TOBUAdd.Style := tbsDropDown; | |
265 | + TOBUAdd.DropdownMenu := PPAB; | |
266 | + | |
267 | + i := 0; | |
268 | + | |
269 | + for SCL in FSlaveComponentClasses do | |
270 | + begin | |
271 | + MEI := PPAB.CreateMenuItem; | |
272 | + MEI.Tag := i; | |
273 | + MEI.GroupIndex := 1; | |
274 | + MEI.AutoCheck := True; | |
275 | + MEI.OnClick := DoClick; | |
276 | + MEI.Caption := SCL.ClassName; | |
277 | + | |
278 | + if i = 0 then | |
279 | + MEI.Checked := True; | |
280 | + | |
281 | + PPAB.Items.Add(MEI); | |
282 | + Inc(i); | |
283 | + end; | |
284 | + end; | |
285 | + | |
286 | + // Força os botões a assumirem seu tamanho padrão | |
287 | + TOBA.Height := 31; | |
288 | + | |
289 | + BuildList; | |
290 | +end; | |
291 | + | |
292 | +constructor TFormSlaveComponentsEditor.Create(ADesigner: IDesigner; AMasterComponent: TKRKMasterComponent; ASlaveComponentClasses: TSlaveComponentClasses); | |
293 | +begin | |
294 | + inherited Create(Application); | |
295 | + | |
296 | + Designer := ADesigner; | |
297 | + FMasterComponent := AMasterComponent; | |
298 | + // Atribui a propriedade ComponentEditor do componente mestre, de forma que | |
299 | + // isso possa ser usado como uma forma de verificar se há um editor de | |
300 | + // componente aberto para aquele mestre específico | |
301 | + FMasterComponent.SlaveComponentsEditor := Self; | |
302 | + FSlaveComponentClasses := ASlaveComponentClasses; | |
303 | + FDesignerClosing := False; | |
304 | +end; | |
305 | + | |
306 | +procedure TFormSlaveComponentsEditor.DesignerClosed(const ADesigner: IDesigner; AGoingDormant: Boolean); | |
307 | +begin | |
308 | + if Designer = ADesigner then | |
309 | + begin | |
310 | + if not AGoingDormant then | |
311 | + Designer := nil; | |
312 | + | |
313 | + FDesignerClosing := True; | |
314 | + FMasterComponent := nil; | |
315 | + Close; | |
316 | + end; | |
317 | + | |
318 | + inherited; | |
319 | +end; | |
320 | + | |
321 | +destructor TFormSlaveComponentsEditor.Destroy; | |
322 | +begin | |
323 | + // Limpa o valor de ComponentEditor e assim deixar claro que não há mais um | |
324 | + // editor de escravos aberto. FMasterComponent será nil apenas quando o | |
325 | + // Designer for fechado, indicando que não há mais nem mesmo um | |
326 | + // FMasterComponent para se preocupar | |
327 | + if Assigned(FMasterComponent) then | |
328 | + FMasterComponent.SlaveComponentsEditor := nil; | |
329 | + | |
330 | + inherited; | |
331 | +end; | |
332 | + | |
333 | +procedure TFormSlaveComponentsEditor.DoClick(ASender: TObject); | |
334 | +begin | |
335 | + Add(TComponent(ASender).Tag); | |
336 | + | |
337 | + if ASender is TMenuItem then | |
338 | + begin | |
339 | + TOBUAdd.Tag := TMenuItem(ASender).Tag; | |
340 | + TMenuItem(ASender).Checked := True; | |
341 | + end; | |
342 | +end; | |
343 | + | |
344 | +procedure TFormSlaveComponentsEditor.FormActivate(Sender: TObject); | |
345 | +begin | |
346 | + // Por padrão, quando se ativa a tela de edição de objetos escravos, | |
347 | + // precisamos verificar se há algum item selecionado e, se houver, realizar a | |
348 | + // seleção do objeto escravo no Designer. Isso mostra as propriedades do | |
349 | + // objeto escravo no OI | |
350 | + if Assigned(LIVI.Selected) then | |
351 | + Designer.SelectComponent(TKRKSlaveComponent(LIVI.Selected.Data)); | |
352 | +end; | |
353 | + | |
354 | +procedure TFormSlaveComponentsEditor.FormClose(Sender: TObject; var Action: TCloseAction); | |
355 | +begin | |
356 | + // Ao fechar este form, tenta selecionar o seu componente mestre | |
357 | + if Assigned(FMasterComponent) then | |
358 | + Designer.SelectComponent(FMasterComponent); | |
359 | + | |
360 | + // Convenientemente, libera a memória sempre que este form for fechado. | |
361 | + // Variáveis que, porventura, contenham a referência para este form apontarão | |
362 | + // para lixo e precisarão ser configuradas como nil pelo processo chamador. | |
363 | + // Recomenda-se o uso de ShowMe sem se preocupar com seu retorno (uma | |
364 | + // instância deste form), assim nada precisará ser limpo | |
365 | + Action := caFree; | |
366 | +end; | |
367 | + | |
368 | +procedure TFormSlaveComponentsEditor.FormShow(Sender: TObject); | |
369 | +begin | |
370 | + Configure; | |
371 | +end; | |
372 | + | |
373 | +// Este método é executado o tempo todo pra qualquer coisa, não apenas exclusão, | |
374 | +// não sei porque, porém, usando o parâmetro AItem é possível saber se algum de | |
375 | +// nossos objetos escravos foi referenciado e, segundo testes, quando este | |
376 | +// método é excutado e AItem é um descendente de TSlaveItem, então foi | |
377 | +// justamente este o item excluído | |
378 | +procedure TFormSlaveComponentsEditor.ItemDeleted(const ADesigner: IDesigner; AItem: TPersistent); | |
379 | +begin | |
380 | + if FDesignerClosing then | |
381 | + Exit; | |
382 | + | |
383 | + if AItem is TKRKSlaveComponent then | |
384 | + begin | |
385 | + Remove(TKRKSlaveComponent(AItem).Name); | |
386 | + | |
387 | + end; | |
388 | + | |
389 | + inherited; | |
390 | +end; | |
391 | + | |
392 | +procedure TFormSlaveComponentsEditor.ItemsModified(const ADesigner: IDesigner); | |
393 | +var | |
394 | + DesignerSelections: IDesignerSelections; | |
395 | + i: Byte; | |
396 | + LI: TListItem; | |
397 | +begin | |
398 | + if FDesignerClosing then | |
399 | + Exit; | |
400 | + | |
401 | + inherited; | |
402 | + // Abaixo, Designer corresponde a propriedade Designer do Form Atual e que, no | |
403 | + // fim das contas representa o Form onde componente, identificado por | |
404 | + // FMasterComponent está. FMasterComponent é o componente que está sendo | |
405 | + // "editado" por este editor de componente (TSlaveComponentsEditor). Este | |
406 | + // método só é útil aqui caso estejamos notificando a respeito de algo que | |
407 | + // ocorreu no form que contém o componente mestre, por isso a condição abaixo | |
408 | + // é necessária | |
409 | + if Designer = ADesigner then | |
410 | + begin | |
411 | + DesignerSelections := CreateSelectionList; | |
412 | + Designer.GetSelections(DesignerSelections); | |
413 | + // Varre a lista de componentes selecionados buscando dentre eles | |
414 | + // descendentes de TSlaveComponent, já que apenas filhos desta classe podem | |
415 | + // ser adicionados a LIVI e sendo assim, apenas filhos desta classe | |
416 | + // precisam ser verificados. Cada TSlaveComponent encontrado em | |
417 | + // DesignerSelections foi alterado de alguma forma e potencialmente | |
418 | + // precisará ajustar sua exibição em LIVI | |
419 | + for i := 0 to Pred(DesignerSelections.Count) do | |
420 | + begin | |
421 | + if DesignerSelections[i] is TKRKSlaveComponent then | |
422 | + for LI in LIVI.Items do | |
423 | + begin | |
424 | + // Caso um item de LIVI corresponda a um dos itens de | |
425 | + // DesignerSelections (itens selecionados), significa que precisamos | |
426 | + // atualizar este item | |
427 | + if LI.Data = DesignerSelections[i] then | |
428 | + begin | |
429 | + // Por padrão atualiza a coluna "Nome" com o valor da propriedade | |
430 | + // "Nome", mesmo que ela não tenha sido modificada, isso garante a | |
431 | + // sincronia visual, pois quando a propriedade "Name" mudar isso | |
432 | + // já dará conta do recado | |
433 | + LI.Caption := TKRKSlaveComponent(LI.Data).Name; | |
434 | + | |
435 | + if Assigned(FOnSlaveModified) then | |
436 | + FOnSlaveModified(LI); | |
437 | + end; | |
438 | + end; | |
439 | + end; | |
440 | + end; | |
441 | +end; | |
442 | + | |
443 | +procedure TFormSlaveComponentsEditor.LIVIChange(Sender: TObject; Item: TListItem; Change: TItemChange); | |
444 | +begin | |
445 | + // Muda o status de habilitação dos botões de acordo com alguns critérios | |
446 | + UpdateButtonStatus; | |
447 | +end; | |
448 | + | |
449 | +procedure TFormSlaveComponentsEditor.LIVISelectItem(Sender: TObject; Item: TListItem; Selected: Boolean); | |
450 | +begin | |
451 | + // Sempre que se seleciona um item na lista, o mesmo deve ser exibido no OI e | |
452 | + // o código abaixo cuida disso | |
453 | + if Assigned(Item.Data) then | |
454 | + Designer.SelectComponent(TKRKSlaveComponent(Item.Data)); | |
455 | +end; | |
456 | + | |
457 | +procedure TFormSlaveComponentsEditor.RefreshList; | |
458 | +var | |
459 | + LI: TListItem; | |
460 | +begin | |
461 | + for LI in LIVI.Items do | |
462 | + begin | |
463 | + LI.Caption := TKRKSlaveComponent(LI.Data).Name; | |
464 | + | |
465 | + if Assigned(FOnAfterRefreshListItem) then | |
466 | + FOnAfterRefreshListItem(LI); | |
467 | + end; | |
468 | +end; | |
469 | + | |
470 | +procedure TFormSlaveComponentsEditor.BuildList; | |
471 | +var | |
472 | + SubComponent: TKRKSlaveComponent; | |
473 | + i: Word; | |
474 | + LI: TListItem; | |
475 | +begin | |
476 | + LIVI.Clear; | |
477 | + | |
478 | + // O Delphi 2006 não é capaz de usar for..in com TObjectList | |
479 | + if FMasterComponent.SlaveComponents.Count > 0 then | |
480 | + for i := 0 to Pred(FMasterComponent.SlaveComponents.Count) do | |
481 | + begin | |
482 | + SubComponent := TKRKSlaveComponent(FMasterComponent.SlaveComponents[i]); | |
483 | + | |
484 | + LI := LIVI.Items.Add; | |
485 | + LI.Caption := SubComponent.Name; | |
486 | + LI.SubItems.Add(SubComponent.ClassName); | |
487 | + LI.Data := SubComponent; | |
488 | + | |
489 | + if Assigned(FOnAfterBuildListItem) then | |
490 | + FOnAfterBuildListItem(LI); | |
491 | + end; | |
492 | +end; | |
493 | + | |
494 | +procedure TFormSlaveComponentsEditor.Remove; | |
495 | +var | |
496 | + LI: TListItem; | |
497 | +begin | |
498 | + if Assigned(LIVI.Selected) then | |
499 | + if Application.MessageBox('Quer remover os objetos selecionados? (Esta operação não pode ser desfeita)','Tem certeza?',MB_ICONQUESTION or MB_YESNO) = IDYES then | |
500 | + begin | |
501 | + for LI in LIVI.Items do | |
502 | + begin | |
503 | + if LI.Selected then | |
504 | + begin | |
505 | + TKRKSlaveComponent(LI.Data).Free; | |
506 | + LI.Data := nil; | |
507 | + end; | |
508 | + end; | |
509 | + | |
510 | + LIVI.DeleteSelected; | |
511 | + UpdateButtonStatus; | |
512 | + end; | |
513 | +end; | |
514 | + | |
515 | +procedure TFormSlaveComponentsEditor.Remove(ASlaveName: String); | |
516 | +var | |
517 | + LI: TListItem; | |
518 | +begin | |
519 | + for LI in LIVI.Items do | |
520 | + if TKRKSlaveComponent(LI.Data).Name = ASlaveName then | |
521 | + begin | |
522 | + LI.Data := nil; | |
523 | + LI.Delete; | |
524 | + UpdateButtonStatus; | |
525 | + Break; | |
526 | + end; | |
527 | +end; | |
528 | + | |
529 | +class function TFormSlaveComponentsEditor.ShowMe(ADesigner: IDesigner; AMasterComponentClass: TKRKMasterComponentClass; AMasterComponentReference: TKRKMasterComponent; ASlaveComponentClasses: TSlaveComponentClasses): TFormSlaveComponentsEditor; | |
530 | +begin | |
531 | + if Length(ASlaveComponentClasses) = 0 then | |
532 | + raise Exception.Create('É necessário informar ao menos uma classe escrava descendente de TSlaveComponentClass'); | |
533 | + | |
534 | + if Assigned(AMasterComponentReference.SlaveComponentsEditor) then | |
535 | + Result := TFormSlaveComponentsEditor(AMasterComponentReference.SlaveComponentsEditor) | |
536 | + else | |
537 | + Result := Self.Create(ADesigner,AMasterComponentReference,ASlaveComponentClasses); | |
538 | + | |
539 | + Result.Show; | |
540 | +end; | |
541 | + | |
542 | +procedure TFormSlaveComponentsEditor.TOBUClearClick(Sender: TObject); | |
543 | +begin | |
544 | + Clear; | |
545 | +end; | |
546 | + | |
547 | +procedure TFormSlaveComponentsEditor.TOBURemoveClick(Sender: TObject); | |
548 | +begin | |
549 | + Remove; | |
550 | +end; | |
551 | + | |
552 | +procedure TFormSlaveComponentsEditor.UpdateButtonStatus; | |
553 | +begin | |
554 | + TOBURemove.Enabled := Assigned(LIVI.Selected); | |
555 | + TOBUClear.Enabled := LIVI.Items.Count > 0; | |
556 | +end; | |
557 | + | |
558 | +end. |
@@ -0,0 +1,32 @@ | ||
1 | +unit KRK.ToolsApi.Editors.VariableWidthColumnsEditor; | |
2 | + | |
3 | +interface | |
4 | + | |
5 | +uses Windows | |
6 | + , DesignWindows | |
7 | + , StdCtrls | |
8 | + , Buttons | |
9 | + , Controls | |
10 | + , ExtCtrls | |
11 | + , Classes | |
12 | + , CheckLst; | |
13 | + | |
14 | +type | |
15 | + TFormVariableWidthColumnsEditor = class(TDesignWindow) | |
16 | + CheckListBox_Columns: TCheckListBox; | |
17 | + Panel_Top: TPanel; | |
18 | + Panel_Bottom: TPanel; | |
19 | + Label_Top: TLabel; | |
20 | + BitBtn_Confirmar: TBitBtn; | |
21 | + BitBtn_Cancelar: TBitBtn; | |
22 | + private | |
23 | + { Private declarations } | |
24 | + public | |
25 | + { Public declarations } | |
26 | + end; | |
27 | + | |
28 | +implementation | |
29 | + | |
30 | +{$R *.dfm} | |
31 | + | |
32 | +end. |
@@ -4,7 +4,8 @@ | ||
4 | 4 | |
5 | 5 | uses Classes |
6 | 6 | , Contnrs |
7 | - , Graphics; | |
7 | + , Graphics | |
8 | + , ToolsApi; | |
8 | 9 | |
9 | 10 | type |
10 | 11 | //: Componente mestre (aparece no Object Inspector como uma caixinha) usado |