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
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
|
<sect1 id="developers-plugins">
<title
>&chalk;-Module entwickeln</title>
<sect2 id="developers-plugins-introduction">
<title
>Einleitung</title>
<para
>&chalk; kann durch Module erweitert werden. Werkzeuge, Filter, große Teile der Bedienoberfläche und sogar Farbräume sind Module. &chalk; erkennt diese sechs Typen von Modulen: </para>
<itemizedlist>
<listitem
><para
>Farbräume — diese definieren die Kanäle, die ein einzelnes Pixel bilden</para
></listitem>
<listitem
><para
>Werkzeuge — alles, was mit der Maus oder einem Tablett gemacht wird</para
></listitem>
<listitem
><para
>Malverfahren — modulare Maleffekte für Werkzeuge</para
></listitem>
<listitem
><para
>Bildfilter — ändern alle oder die gewählten Pixel einer Ebene</para
></listitem>
<listitem
><para
>Ansichtenmodule — erweitern Chalks Benutzeroberfläche um neue Dialoge, Paletten oder Operationen.</para
></listitem>
<listitem
><para
>Import- und Exportfilter — lesen oder schreiben verschiedener Bildformate</para
></listitem>
</itemizedlist>
<para
>&chalk; besteht aus drei Bibliotheksebenen und einem Verzeichnis mit einigen Klassen zur Unterstützung: chalkcolor, chalkimage und chalkui. In &chalk; können Objekte als <classname
>KisID</classname
>-Objekt dargestellt werden, was eine Kombination aus einer eindeutigen, nicht übersetzbaren Zeichenkette (wird beim Speichern verwendet) und einer übersetzbaren Zeichenkette für die Benutzeroberfläche ist. </para
><para
>Ein Wort zur Kompatibilität: &chalk; befindet sich noch in der Entwicklung. Von &chalk; 1.5 auf 1.6 werden keine großen Änderungen an der API erwartet, aber einige könnte es dennoch geben. Von &chalk; 1.6 auf 2.0 werden wir von &Qt;3 zu &Qt;4, von &kde;3 zu &kde;4 und von <command
>automake</command
> zu <command
>cmake</command
> wechseln: es sind viele Änderungen zu erwarten. Sollten Sie ein Modul in &chalk;s Subversion-Repository entwickeln möchten, stehen die Chancen gut, dass wir Sie bei der Portierung unterstützten. Diese Änderungen gefährden auch die Aktualität dieses Dokuments. Lesen Sie daher immer die API-Dokumentation oder die Header-Dateien, die auf Ihrem System installiert wurden. </para>
<sect3 id="developers-plugins-introduction-chalkcolor">
<title
>ChalkColor</title>
<para
>Die erste Bibliothek ist chalkcolor. Diese Bibliothek lädt die Farbraummodule. </para
><para
>Ein Farbraum-Modul sollte die abstrakte Klasse <classname
>KisColorSpace</classname
> implementieren oder, wenn die grundlegenden Fähigkeiten von <command
>lcms</command
> (<ulink url="http://www.littlecms.com/"
></ulink
>) implementiert werden, <classname
>KisAbstractColorSpace</classname
> erweitern. Die Bibliothek chalkcolor kann von anderen Anwendungen verwendet werden und hängt nicht von &koffice; ab. </para>
</sect3>
<sect3 id="developers-plugins-introduction-chalkimage">
<title
>ChalkImage</title>
<para
>Die Bibliothek libchalkimage lädt die Module Filter und Malverfahren und ist für die Arbeit mit Bilddaten verantwortlich: Pixel ändern, mischen und malen. libchalkimage lädt zudem Pinsel, Paletten und Muster.Wir haben uns das Ziel gesetzt, libchalkimage von &koffice; unabhängig zu machen, teilen aber derzeit den Code zum Laden eines Verlaufs mit &koffice;. </para
><para
>Im Moment ist es nicht einfach, neue Arten von Hilfsmitteln wie Pinsel, Paletten, Verläufe oder Muster zu &chalk; hinzuzufügen. (Das Hinzufügen von neuen Pinseln, Paletten, Verläufen und Mustern ist natürlich einfach.) &chalk; folgt in diesem Punkt den Richtlinien des Create-Projekts (<ulink url="http://create.freedesktop.org/"
></ulink
>). Um Unterstützung für das Dateiformat für Photoshop-Pinsel hinzuzufügen, bedarf es noch einiger Arbeit an libchalkimage, Gimp-Pinsel hinzuzufügen nicht. </para
><para
><classname
>ChalkImage</classname
> lädt die folgenden Modultypen: </para>
<itemizedlist>
<listitem
><para
>Filter in &chalk; müssen die abstrakten Klassen <classname
>KisFilter</classname
>, <classname
>KisFilterConfiguration</classname
> und möglicherweise <classname
>KisFilterConfigurationWidget</classname
> erweitern und implementieren. Ein Filter ist z. B. "Unscharf maskieren".</para
></listitem>
<listitem
><para
>Malverfahren oder "paintops" sind eine Reihe von Verfahren, die mit Malwerkzeugen, wie z. B. Freihand oder Kreis verfügbar sind. Beispiele von paintops sind Stift, Airbrush und Radierer. Paintops sollten die Basisklasse <classname
>KisPaintop</classname
> erweitern. Beispiele für neue paintops könnten ein Kreidepinsel, ein Ölfarbenpinsel oder ein komplexer, programmierbarer Pinsel sein.</para
></listitem>
</itemizedlist>
</sect3>
<sect3 id="developers-plugins-introduction-chalkui">
<title
>ChalkUI</title>
<para
>libchalkui lädt die Werkzeug- und Ansichtenmodule. Diese Bibliothek ist eine &koffice;-Part-Anwendung, enthält aber zusätzlich einige nützliche Elemente für Graphikanwendungen. Möglicherweise wird diese Bibliothek für die Version 2.0 in chalkpart und chalkui aufgeteilt. Derzeit haben Skriptautoren keinen Zugriff auf diese Bibliothek, und Modulautoren nur unter der Voraussetzung, dass sie Werkzeug- oder Ansichtenmodule schreiben. <classname
>ChalkUI</classname
> lädt die folgenden Modultypen: </para>
<itemizedlist>
<listitem
><para
>Werkzeuge werden abgeleitet von <classname
>KisTool</classname
> oder einer der spezialisierten Werzeug-Basis-Klassen, wie <classname
>KisToolPaint</classname
>, <classname
>KisToolNonPaint</classname
> oder <classname
>KisToolFreehand</classname
>. Ein neues Werkzeug könnte z. B. ein Auswahlwerkzeug für Vordergrundobjekte sein. Malwerkzeuge (das beinhaltet auch Werkzeuge zum Malen einer Auswahl) können jedes Malverfahren benutzen um zu bestimmen, wie die Pixel geändert werden.</para
></listitem>
<listitem
><para
>Ansichtenmodule sind normale KPart-Anwendungen, die <command
>kxmlgui</command
> verwenden, um sich selbst &chalk;s Benutzerschnittstelle zu unterstellen. Menüpunkte, Dialoge, Werkzeugleisten — jede Art von Erweiterung der Benutzeroberfläche kann ein Ansichtenmodul sein. Tatsächlich sind wichtige Teile von &chalk;, wie die Unterstützung für Skripte als Ansichtenmodul geschrieben.</para
></listitem>
</itemizedlist>
</sect3>
<sect3 id="developers-plugins-introduction-importexport">
<title
>Import/Export-Filter</title>
<para
>Import/Export-Filter sind &koffice;-Filter; Unterklassen von <classname
>KoFilter</classname
>. Filter lesen und schreiben Bildinformationen in jedes der vielen existierenden Bildformate. Ein Beispiel für einen neuen Import/Export-Filter für &chalk; könnte ein PDF-Filter sein. Filter werden von den &koffice;-Bibliotheken geladen. </para>
</sect3>
</sect2>
<sect2 id="developers-plugins-creating">
<title
>Module erstellen</title>
<para
>Module werden in C++ geschrieben und können die APIs von &kde;, &Qt; und &chalk; verwenden. Nur Ansichtenmodule sollten die &koffice;-API verwenden. Die API von &chalk; ist ziemlich sauber und ausführlich dokumentiert (für Freie Software) und einen ersten Filter zu schreiben, ist einfach. </para
><para
>Wenn Sie nicht C++ benutzen möchten, können Sie Skripte in Python oder Ruby schreiben. Dies ist jedoch etwas ganz anderes, und Sie können derzeit keine Skripte für Werkzeuge, Farbräume, Malverfahren und Import/Export-Filter schreiben. </para
><para
>Module in &chalk; verwenden den Parts-Mechanismus von &kde;, daher ist die Dokumentation unter <ulink url="http://developer.kde.org"
></ulink
> auch hier wichtig. </para
><para
>Ihre Distribution sollte entweder die benötigten Header-Dateien mit &chalk; mitinstallieren, oder sie in eigene &koffice;-dev- oder &chalk;-dev-Pakete ausgelagert haben. Sie finden die API-Dokumentation für &chalk; unter <ulink url="http://koffice.org/developer/apidocs/chalk/html/"
></ulink
>. </para>
<sect3 id="developers-plugins-creating-automake">
<title
>Automake (und CMake)</title>
<para
>&kde; 3.x und demnach auch &koffice; 1.5 und 1.6 verwenden <command
>automake</command
>; &kde; 4.0 und &koffice; 2.0 benutzen <command
>cmake</command
>. Diese Anleitung beschreibt die Erstellung von Modulen über <command
>automake</command
>. </para
><para
>Module sind &kde;-Module und sollten im <filename
>Makefile.am</filename
> entsprechend als solche markiert werden. Filter, Werkzeuge, Malverfahren, Farbräume und Import/Export-Filter benötigen <literal role="extension"
>.desktop</literal
>-Dateien; Ansichtenmodule zusätzlich eine <application
>KXMLGui</application
>-<filename
>pluginname.rc</filename
>-Datei. Zu Beginn ist es einfacher, das chalk-plugin-Projekt aus dem Subversion-Repository von &koffice; auszuchecken und es als Basis für das eigene Projekt zu verwenden. Wir beabsichtigen, ein Vorlagen-Paket für &chalk;-Module in KDevelop zu integrieren, wozu jedoch bisher die Zeit fehlte. </para>
<sect4 id="d-p-c-a-makefile">
<title
><filename
>Makefile.am</filename
></title>
<para
>Werfen wir einen Blick auf den Rohbau eines Moduls. Zuerst die Datei <filename
>Makefile.am</filename
>. Diese verwendet &kde;, um das Makefile zu erstellen, das Ihr Modul baut: <programlisting>
kde_services_DATA = chalkLIBRARYNAME.desktop
INCLUDES = $(all_includes)
chalkLIBRARYNAME_la_SOURCES = sourcefile1.cpp sourcefile2.cpp
kde_module_LTLIBRARIES = chalkLIBRARYNAME.la
noinst_HEADERS = header1.h header2.h
chalkLIBRARYNAME_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
chalkLIBRARY_la_LIBADD = -lchalkcommon
chalkextensioncolorsfilters_la_METASOURCES = AUTO
</programlisting
> Dies ist das Makefile für ein Filtermodul. Ersetzen Sie <replaceable
>LIBRARYNAME</replaceable
> durch den Namen Ihrer Arbeit. </para
><para
>Ist Ihr Modul ein Ansichtenmodul, möchten Sie wahrscheinlich auch eine <literal role="extension"
>.rc</literal
>-Datei mit den Einträgen für Menü- und Werkzeugleisten installieren. Ebenso Mauszeiger und Symbole. All das erledigen die normalen &kde;-<filename
>Makefile.am</filename
>-Zauberformeln: <programlisting
>chalkrcdir = $(kde_datadir)/chalk/chalkplugins
chalkrc_DATA = LIBRARYNAME.rc
EXTRA_DIST = $(chalkrc_DATA)
chalkpics_DATA = \
bla.png \
bla_cursor.png
chalkpicsdir = $(kde_datadir)/chalk/pics
</programlisting>
</para>
</sect4>
<sect4 id="d-p-c-a-desktop">
<title
>Desktop-Dateien</title>
<para
>Die <literal role="extension"
>.desktop</literal
>-Datei zeigt die Art des Moduls an: <programlisting
>[Desktop Entry]
Encoding=UTF-8
Icon=
Name=User-visible Name
ServiceTypes=Chalk/Filter
Type=Service
X-TDE-Library=chalkLIBRARYNAME
X-TDE-Version=2
</programlisting>
</para
><para
>Mögliche Service-Arten sind: </para>
<itemizedlist>
<listitem
><para
>Chalk/Filter</para
></listitem>
<listitem
><para
>Chalk/Paintop</para
></listitem>
<listitem
><para
>Chalk/ViewPlugin</para
></listitem>
<listitem
><para
>Chalk/Tool</para
></listitem>
<listitem
><para
>Chalk/ColorSpace</para
></listitem>
</itemizedlist>
<para
>Import- und Exportfilter für Dateien verwenden das &koffice;-Filter-Framework und müssen gesondert behandelt werden. </para>
</sect4>
<sect4 id="d-p-c-a-boilerplate">
<title
>Vorlage</title>
<para
>Sie benötigen eine kleine Vorlage, die vom &kde;-Part-Framework aufgerufen wird, um das Modul zu instanziieren — eine Header-Datei und eine Anwendungsdatei. </para
><para
>Eine Headerdatei: <programlisting
>#ifndef TOOL_STAR_H_
#define TOOL_STAR_H_
#include <tdeparts/plugin.h>
/**
* A module that provides a star tool.
*/
class ToolStar : public KParts::Plugin
{
TQ_OBJECT
public:
ToolStar(TQObject *parent, const char *name, const QStringList &);
virtual ~ToolStar();
};
#endif // TOOL_STAR_H_
</programlisting>
</para>
<para
>Und eine Anwendungsdatei: <programlisting
>#include <kinstance.h>
#include <kgenericfactory.h>
#include <kis_tool_registry.h>
#include "tool_star.h"
#include "kis_tool_star.h"
typedef KGenericFactory<ToolStar> ToolStarFactory;
K_EXPORT_COMPONENT_FACTORY( chalktoolstar, ToolStarFactory( "chalk" ) )
ToolStar::ToolStar(TQObject *parent, const char *name, const QStringList &)
: KParts::Plugin(parent, name)
{
setInstance(ToolStarFactory::instance());
if ( parent->inherits("KisToolRegistry") )
{
KisToolRegistry * r = dynamic_cast<KisToolRegistry*>( parent );
r -> add(new KisToolStarFactory());
}
}
ToolStar::~ToolStar()
{
}
#include "tool_star.moc"
</programlisting>
</para>
</sect4>
<sect4 id="d-p-c-a-registries">
<title
>Registrierungen</title>
<para
>Werzeuge werden von der Werkzeugregistrierung geladen und registrieren sich selbst mit der Werkzeugregistrierung. Module wie Werkzeuge, Filter und Malverfahren werden nur einmal geladen: Ansichtenmodule werden für jede neu erstellte Ansicht erneut geladen. Beachten Sie, dass grob gesagt "Fabriken" registriert werden. Z. B. wird bei Werzeugen für jeden Zeiger (Maus, Stift, Radierer) eine neue Instanz des Werkzeuges erzeugt. Ein neues Malverfahren wird erzeugt, wenn ein Werkzeug ein "Mouse-Down"-Ereignis bekommt. </para>
<para
>Filter rufen die Filterregistrierung auf: <programlisting
>if (parent->inherits("KisFilterRegistry")) {
KisFilterRegistry * manager = dynamic_cast<KisFilterRegistry *>(parent);
manager->add(new KisFilterInvert());
}
</programlisting>
</para
><para
>Paintops die paintop-Registrierung <programlisting
>if ( parent->inherits("KisPaintOpRegistry") ) {
KisPaintOpRegistry * r = dynamic_cast<KisPaintOpRegistry*>(parent);
r -> add ( new KisSmearyOpFactory );
}
</programlisting>
</para
><para
>Farbräume die Farbraum-Registrierung (mit einigen Komplikationen): <programlisting
>if ( parent->inherits("KisColorSpaceFactoryRegistry") ) {
KisColorSpaceFactoryRegistry * f = dynamic_cast<isColorSpaceFactoryRegistry*>(parent);
KisProfile *defProfile = new KisProfile(cmsCreate_sRGBProfile());
f->addProfile(defProfile);
KisColorSpaceFactory * csFactory = new KisRgbColorSpaceFactory();
f->add(csFactory);
KisColorSpace * colorSpaceRGBA = new KisRgbColorSpace(f, 0);
KisHistogramProducerFactoryRegistry::instance() -> add(
new KisBasicHistogramProducerFactory<KisBasicU8HistogramProducer>
(KisID("RGB8HISTO", i18n("RGB8 Histogram")), colorSpaceRGBA) );
}
</programlisting>
</para
><para
>Ansichtenmodule müssen sich nicht registrieren, und sie erhalten Zugriff auf ein <classname
>KisView</classname
>-Objekt: <programlisting
>if ( parent->inherits("KisView") )
{
setInstance(ShearImageFactory::instance());
setXMLFile(locate("data","chalkplugins/shearimage.rc"), true);
(void) new TDEAction(i18n("&Shear Image..."), 0, 0, this, SLOT(slotShearImage()), actionCollection(), "shearimage");
(void) new TDEAction(i18n("&Shear Layer..."), 0, 0, this, SLOT(slotShearLayer()), actionCollection(), "shearlayer");
m_view = (KisView*) parent;
}
</programlisting>
</para
><para
>Merken Sie sich, dass für jede Ansicht, die der Benutzer öffnet, ein Ansichtenmodul erstellt wird: bei der Aufteilung der Ansicht werden alle Module erneut geladen. </para>
</sect4>
<sect4 id="d-p-c-a-versioning">
<title
>Modulversionen</title>
<para
>&chalk; 1.5 lädt Module mit der Einstellung <literal
>X-TDE-Version=2</literal
> in der <literal role="extension"
>.desktop</literal
>-Datei. &chalk;-1.6-Module sind inkompatibel zu 1.5-Modulen und benötigen die Versionsnummer 3. &chalk;-2.0-Module werden die Versionnummer 3 benötigen. </para>
</sect4>
</sect3>
</sect2>
<sect2 id="developers-plugins-colorspaces">
<title
>Farbräume</title>
<para
>Farbräume implementieren die virtuelle Klasse <classname
>KisColorSpace</classname
>. Es gibt zwei Arten von Farbräumen: die einen können <command
>lcms</command
> zum konvertieren zwischen Farbräumen verwenden, die anderen sind zu bizarr, dass <command
>lcms</command
> sie beherrschen könnte. Beispiel der ersten Art sind cmyk, rgb, yuf. Ein Beispiel der zweiten Art ist Wasserfarben oder "Nass & Klebrig". Farbräume, die <command
>lcms</command
> verwenden, können von <classname
>KisAbstractColorSpace</classname
>, oder einer der Basisklassen abgeleitet werden, die auf eine bestimmte Anzahl Bits pro Kanal spezialisiert sind. </para
><para
>Einen Farbraum zu implementieren ist recht einfach. Das allgemeine Prinzip ist, dass Farbräume mit einem einfachen Array aus Bytes arbeiten. Die Interpretation dieser Bytes ist dem Farbraum überlassen. Z. B. besteht ein Pixel in 16-bit-GrayA aus vier Bytes: zwei Bytes für den Grauwert und zwei Bytes für den Alphawert. Sie können auch ein "struct" verwenden, um mit dem Speicherabbild eines Pixels zu arbeiten; diese Information wird aber nicht exportiert. Der einzige Weg, wie der Rest von &chalk; erfährt, aus welchen Kanäle und Arten von Kanälen die Pixel Ihres Farbraumes bestehen, ist über die Klasse <classname
>KisChannelInfo</classname
>. </para
><para
>Filter und Malverfahren verwenden die große Anzahl Methoden, die von <classname
>KisColorSpace</classname
> zur Verfügung gestellt werden. In den meisten Fällen reicht die Standardimplementierung aus <classname
>KisAbstractColorSpace</classname
>. Diese ist jedoch langsamer als eine eigene Implementierung, da <classname
>KisAbtractColorSpace</classname
> alle Pixel in 16-bit-L*a*b* unz zurück konvertiert. </para>
<sect3 id="developers-plugins-colorspaces-kischannelinfo">
<title
><classname
>KisChannelInfo</classname
></title>
<programlisting
>(http://websvn.kde.org/trunk/koffice/chalk/chalkcolor/kis_channelinfo.h)
</programlisting>
<para
>Diese Klasse definiert die Kanäle, aus denen in einem bestimmten Farbraum ein Pixel besteht. Ein Kanal besitzt die folgenden, wichtigen Nenndaten: </para>
<itemizedlist>
<listitem
><para
>einen Namen, der in der Benutzeroberfläche angezeigt wird</para
></listitem>
<listitem
><para
>eine Position: das Byte in dem Pixel, an dem die Bytes beginnen, die den Kanal darstellen.</para
></listitem>
<listitem
><para
>einen Typ: Farbe, Alpha, Substanz oder Träger. Farbe ist reine Farbe, Alpha ist die Durchsichtigkeit, Substanz enthält Informationen wie die Menge der Pigmente, Träger enthält die Art der Zeichenfläche. </para
></listitem>
<listitem
><para
>einen Wertetyp: byte, short, integer, float — oder andere.</para
></listitem>
<listitem
><para
>Größe: die Anzahl der Bytes, die dieser Kanal benötigt</para
></listitem>
<listitem
><para
>Farbe: eine <classname
>TQColor</classname
>-Darstellung dieses Kanals für die grafische Benutzeroberfläche, z. B. für das Histogramm.</para
></listitem>
<listitem
><para
>eine Abkürzung zur Benutzung in der grafischen Benutzeroberfläche, falls nicht genügend Platz zur Verfügung steht</para
></listitem>
</itemizedlist>
</sect3>
<sect3 id="developers-plugins-colorspaces-kiscompositeop">
<title
><classname
>KisCompositeOp</classname
></title>
<para
>Es gibt mehrere Wege, Pixel zu einer neuen Farbe zu kombinieren. Die Klasse <classname
>KisCompositeOp</classname
> definiert die meisten davon: diese Liste ist nicht einfach erweiterbar, außer durch Veränderung der Bibliothek chalkcolor. </para
><para
>Ein Farbraummodul kann jede Untergruppe dieser möglichen Verfahren unterstützen, es muss aber immer "OVER" (das gleiche wie "NORMAL") und "COPY" enthalten. Alle anderen sind mehr oder weniger optional, wobei natürlich mehr besser ist. </para>
</sect3>
<sect3 id="developers-plugins-colorspaces-kiscolorspace">
<title
><classname
>KisColorSpace</classname
></title>
<para
>Die Methoden in der virtuellen Klasse <classname
>KisColorSpace</classname
> können in Gruppen unterteilt werden: Konvertierung, Erkennung und Manipulation. </para
><para
>Alle Klassen müssen in der Lage sein, ein Pixel aus und in 8-bit-RGB zu konvertieren (d. h., ein <classname
>TQColor</classname
>), und möglichst auch in und aus 16-bit-L*a*b*. Zusätzlich gibt es eine Methode, um vom aktuellen Farbraum in jeden anderen Farbraum zu konvertieren. </para
><para
>Farbräume werden vom <classname
>KisChannelInfo</classname
>-Vektor beschrieben. Anzahl der Kanäle, Anzahl Bytes pro Pixel, ob High Dynamic Range unterstützt wird, und mehr. </para
><para
>Manipulationen sind z. B. das Kombinieren von zwei Pixeln in ein neues Pixel: bitBlt, Abdunkeln oder Falten von Pixeln. </para
><para
>Bitte lesen Sie die API-Dokumentation für eine vollständige Liste der Methoden, die Sie in einem Farbraum implementieren müssen. </para
><para
><classname
>KisAbstractColorSpace</classname
> implementiert viele der virtuellen Methoden von <classname
>KisColorSpace</classname
>, wobei Funktionen aus der <command
>lcms</command
>-Bibliothek verwendet werden. Oberhalb von <classname
>KisAbstractColorSpace</classname
> gibt es Basis-Farbraum-Klassen für Farbräume in 8- und 16-bit-Integer und 16- und 32-bit-Float, die allgemeine Verfahren zum Verschieben zwischen Farbtiefen definieren. </para>
</sect3>
</sect2>
<sect2 id="developers-plugins-filters">
<title
>Filter</title>
<para
>Filter sind Module, die Pixel einer Ebene untersuchen und diese dann verändern. Obwohl &chalk; ein effizientes Speicher-Backend zum Speichern der Pixel verwenden, brauchen sich Filterautoren darum nicht zu kümmern. Wenn Sie ein Filtermodul für die &Java;-Graphik-API, Photoshop oder Gimp schreiben, müssen Sie sich über die Position der Pixel im Speicher Gedanken machen und die die einzelnen Teile <quote
>zusammenflicken</quote
>: &chalk; versteckt die Details der Implementierung. </para>
<note
><para
>Beachten Sie, dass es theoretisch einfach ist, das aktuelle Speicher-Backend durch eine neues zu ersetzen, sie aber derzeit aus Performancegründen nicht modular gestaltet sind.</para
></note>
<para
>&chalk; benutztz Iteratoren zum Lesen und Schreiben von Pixelwerten. Alternativ können Sie einen Pixelblock in den Speicher laden, mit diesem Block arbeiten und dann als Block wieder zurückschreiben. Dieses Vorgehen ist aber nicht grunsdätzlich effizienter und kann sogar langsamer als die Benutzung eines Iterators sein; es könnte allerdings angenehmer sein. Ziehen Sie hierfür die API-Dokumentation zu Rate. </para
><para
>Bilder in &chalk; sind auf Ebenen zusammen gesetzt, von denen es derzeit vier Arten gibt: Farbebenen, Gruppenebenen, Justierungsebenen (die einen Filter enthalten, der dynamisch auf Ebenen unter der Ebene angewandt werden) und Teilebenen. Filter arbeiten immer auf Farbebenen. Farbebenen enthalten Malwerzeuge der Klasse <classname
>KisPaintDevice</classname
>. Ein Malwerkzeug gewährt Zugriff auf die eigentlichen Pixel. </para
><para
><classname
>PaintDevice</classname
>s werden über "Shared Pointers" verwendet. Ein "Shared Pointer" verwaltet Informationen darüber, an wie vielen Stellen das Zeichengerät verwendet wird und löscht es, wenn es nirgends mehr verwendet wird. Sie erkennen einen "Shared Pointer" an der Endung <literal
>SP</literal
>. Denken Sie daran, dass Sie sich nicht um das Löschen eines <classname
>KisPaintDeviceSP</classname
> kümmern müssen. </para
><para
>Lassen Sie uns einen sehr einfachen Filter untersuchen, einen, der jedes Pixel invertiert. Der Quelltext dieses Filters liegt im verzeichnis <filename class="directory"
>koffice/chalk/plugins/filters/example</filename
>. Die Hauptmethode ist <programlisting>
KisFilterInvert::process(KisPaintDeviceSP src, KisPaintDeviceSP dst,
KisFilterConfiguration* /*config*/, const QRect& rect).
</programlisting
> Die Funktion bekommt zwei Maleinheiten übergeben, ein Konfigurationsobjekt (das in diesem einfachen Filter nicht verwendet wird) und ein Rechteck (<varname
>rect</varname
>). Das <varname
>rect</varname
> beschreibt die Fläche auf der der Filter arbeiten soll. Diese Fläche wird durch Integer beschrieben, was keine Subpixelgenauigkeit zulässt. </para
><para
>Die <varname
>src</varname
>-Maleinheit arbeitet lesend, die <varname
>dst</varname
>-Maleinheit arbeitet schreibend. Diese Parameter können auf die gleiche Maleinheit oder auf zwei verschiedene Maleinheiten zeigen. (Beachten Sie, dass sich dies in Zukunft ändern kann.) </para
><para
>Lassen Sie uns nun den Quelltext Zeile für Zeile durchgehen: </para>
<programlisting
>void KisFilterInvert::process(KisPaintDeviceSP src, KisPaintDeviceSP dst,
KisFilterConfiguration* /*config*/, const QRect& rect)
{
Q_ASSERT(src != 0);
Q_ASSERT(dst != 0);
KisRectIteratorPixel srcIt = src->createRectIterator(rect.x(), rect.y(), rect.width(), rect.height(), false); <co id="invert1" />
KisRectIteratorPixel dstIt = dst->createRectIterator(rect.x(), rect.y(), rect.width(), rect.height(), true ); <co id="invert2" />
int pixelsProcessed = 0;
setProgressTotalSteps(rect.width() * rect.height());
KisColorSpace * cs = src->colorSpace();
Q_INT32 psize = cs->pixelSize();
while( ! srcIt.isDone() )
{
if(srcIt.isSelected()) <co id="invert3" />
{
memcpy(dstIt.rawData(), srcIt.oldRawData(), psize); <co id="invert4" />
cs->invertColor( dstIt.rawData(), 1); <co id="invert5" />
}
setProgress(++pixelsProcessed);
++srcIt;
++dstIt;
}
setProgressDone(); // Must be called even if you don't really support progression
}
</programlisting>
<calloutlist>
<callout arearefs="invert1">
<para
>Diese erzeugt einen Iterator, um vorhandene Pixel zu lesen. Chalk hat drei Arten von Iteratoren: horizontale, vertokale und rechteckige. Der rechteckige Iterator nimmt den effizientesten Weg durch die Bilddaten, garantiert aber keine Informationen über die Position des nächten Pixels, den er zurück gibt. Das bedeutet, dass Sie nicht sicher sein können, dass das nächste Pixel, den Sie erhalten, an das aktuelle Pixel angrenzt. Der horizontale und der vertikale Iterator garantieren die Position des nächsten Pixels, den sie zurück geben. </para
></callout>
<callout arearefs="invert2"
><para
>(2) Wir erstellen den Ziel-Iterator, indem wir <literal
>write</literal
> auf <literal
>true</literal
> setzen. Das bedeutet, dass, wenn die Ziel-Maleinheit kleiner ist als das Rechteck, das geschrieben wird, erstere automatisch vergrößert wird, damit alle Pixel iteriert werden können. Beachten Sie, dass hier Potential für Fehler besteht: sind <varname
>dst</varname
> und <varname
>src</varname
> nicht die gleiche Einheit, ist es sehr warscheinlich, dass die zurückgegebenen Pixel nicht zusammen passen. Der <varname
>src</varname
>-Iterator könnte z. B. an der Position 165,200 stehen, während der <varname
>dst</varname
>-Iterator bei 20,8 steht — die ausgeführte Aktion verschiebt also das Bild. </para
></callout>
<callout arearefs="invert3"
><para
>Wollen Sie wissen, ob ein Pixel ausgewählt ist? Das ist einfach — benutzen Sie dazu die <methodname
>isSelected</methodname
>-Methode. Allerdings sind Auswahlen keine binäre Eigenschaft eines Pixels; er kann halb gewählt, kaum gewählt oder fast vollständig gewählt sein. Diesen Wert bekommen Sie vom Iterator. Auswahlen sind eigentlich maskierte Maleinheiten mit einem Bereich von 0 bis 255, wobei 0 keine Auswahl und 255 eine vollständige Auswahl bedeuten. Der Iterator hat zwei Methoden: <methodname
>isSelected()</methodname
> und <methodname
>selectedNess()</methodname
>. Die erste gibt "true" zurück, wenn das Pixel in irgendeiner Weise ausgewählt ist (d. h. der Wert größer 1 ist), die andere gibt den Wert zurück. </para
></callout>
<callout arearefs="invert4"
><para
>Wie weiter oben erwähnt, ist <literal
>memcpy</literal
> ein großer, schlimmer Fehler... <methodname
>rawData()</methodname
> gibt ein Array aus Bytes zurück, das den aktuellen Zustand des Pixels enthält; <methodname
>oldRawData()</methodname
> gibt ein Array aud Bytes mit dem Zustand zurück, den das Pixel hatte, bevor der Iterator erstellt wurde. An dieser Stelle könnten wir das falsche Pixel kopieren. In der Praxis passiert das nicht sehr oft, solange nicht <varname
>dst</varname
> schon existiert und mit <varname
>src</varname
> verbunden ist. </para
></callout>
<callout arearefs="invert5"
><para
>Eines ist aber richtig: anstatt herauszufinden, welches Byte welchen Kanal darstellt, benutzen wir eine Funktion, die von allen Farbräumen bereitgestellt wird, um das aktuelle Pixel zu invertieren. Farbräume bieten viele Operationen, die Sie benutzen können. </para
></callout>
</calloutlist>
<para
>Das war noch nicht alles zum Erstellen von Filtern. Filter haben zwei weitere wichtige Komponenten: ein Konfigurationsobjekt und ein Konfigurationswidget. Diese beiden arbeiten eng zusammen. Das Konfigurationswidget erstellt ein Konfigurationsobjekt, kann aber auch von einem, schon existierenden Konfigurationsobjekt gefüllt werden. Konfigurationsobjekte können als XML dargestellt werden und aus XML erstellt werden. Diese Eigenschaft macht Justierungsebenen möglich. </para>
<sect3 id="developers-plugins-filters-iterators">
<title
>Iteratoren</title>
<para
>Es gibt drei Arten von Iteratoren: </para>
<itemizedlist>
<listitem
><para
>Waagerechte Linien</para
></listitem>
<listitem
><para
>Senkrechte Linien</para
></listitem>
<listitem
><para
>Rechteckige Iteratoren</para
></listitem>
</itemizedlist>
<para
>Die horizontalen und vertikalen Zeileniteratoren haben eine Methode, um den Iterator in die nächste Zeile oder Spalte zu bewegen: <methodname
>nextRow()</methodname
> und <methodname
>nextCol()</methodname
>. Diese zu verwenden ist viel schneller als einen neuen Iterator für jede Zeile oder Spalte zu erstellen. </para
><para
>Iteratoren in &chalk; sind thread-sicher. Es ist als möglich, die Arbeit auf mehrere Threads aufzuteilen. In zukünftigen Versionen wird &chalk; die Methode <methodname
>supportsThreading()</methodname
>, um zu bestimmen, ob Ihr Filter auf Teile des Bildes angewandt werden kann (d. h. alle Pixel unabhängig voneinander geändert werden, anstatt Pixel abhängig von anderen Pixeln zu verändern), und diesen dann automatisch in Threads auszuführen. </para>
</sect3>
<sect3 id="developers-plugins-filters-kisfilterconfiguration">
<title
><classname
>KisFilterConfiguration</classname
></title>
<para
><classname
>KisFilterConfiguration</classname
> ist eine Struktur zum Speichern der Filtereinstellungen auf den Datenträger (z. B. für Justierungsebenen). Das Skriptmodul benutzt die Eigenschaftenkarte, die hinter <classname
>KisFilterConfiguration</classname
> steht, damit Filter in Skripten verwendet werden können. Filter können ein eigenes Widget besitzen, das &chalk; in der Filtergalerie, dem Filtervorschau-Dialog und der Werkzeug-Karteikarte des "Mit Filtern malen"-Werkzeugs anzeigt. </para>
<para
>Ein Beipiel vom Ölgemäldefilter: </para>
<programlisting
>class KisOilPaintFilterConfiguration : public KisFilterConfiguration
{
public:
KisOilPaintFilterConfiguration(Q_UINT32 brushSize, Q_UINT32 smooth)
: KisFilterConfiguration( "oilpaint", 1 )
{
setProperty("brushSize", brushSize);
setProperty("smooth", smooth);
};
public:
inline Q_UINT32 brushSize() { return getInt("brushSize"); };
inline Q_UINT32 smooth() {return getInt("smooth"); };
};
</programlisting>
</sect3>
<sect3 id="developers-plugins-filters-kisfilterconfigurationwidget">
<title
><classname
>KisFilterConfigurationWidget</classname
></title>
<para
>Die meisten Filter können vom Benutzer justiert werden. Sie können eine Konfigurationsvorrichtung erstellen, die Chalk verwenden wird, wenn Ihr Filter verwendet wird. Ein Beispiel: </para>
<para>
<screenshot>
<screeninfo
>Der Dialog <guilabel
>Ölgemälde</guilabel
></screeninfo>
<mediaobject>
<imageobject>
<imagedata fileref="dialogs-oilpaint.png" format="PNG"/>
</imageobject>
<textobject>
<phrase
>Der Dialog <guilabel
>Ölgemälde</guilabel
></phrase>
</textobject>
<caption
><para
>Der Dialog <guilabel
>Ölgemälde</guilabel
></para
></caption>
</mediaobject>
</screenshot>
</para>
<para
>Beachten Sie, dass Sie sich nur um den linken Teil kümmern müssen: &chalk; erledigt den Rest. Sie können eine Einstellungen-Vorrichtung auf drei Arten erstellen: </para>
<itemizedlist>
<listitem
><para
>Benutzen Sie den &Qt;-Designer um eine Basisvorrichtung zu bekommen, und erstellen Sie sich daraus eine Unterklasse für Ihren Filter</para
></listitem>
<listitem
><para
>Benutzen Sie eines der einfachen Widgets mit Schiebereglern für Integer, Doubles oder Bool'sche Werte. Diese sind nützlich, wenn Ihr Filter , wie in dem obigen Bildschirmphoto, über eine Reihe Zahlen aus Integern, Doubles oder Bool'schen Werten konfiguriert werden kann. Lesen Sie die API-dox zu <classname
>KisMultiIntegerFilterWidget</classname
>, <classname
>KisMultiDoubleFilterWidget</classname
> und <classname
>KisMultiBoolFilterWidget</classname
>.</para
></listitem>
<listitem
><para
>Ein Widget von Hand schreiben. Diese Vorgehensweise wird nicht empfohlen; sollten Sie dies tun und Ihren Filter gerne als Teil der offiziellen &chalk;-Version sehen, werde ich Sie bitten, das handgeschriebene Widget durch ein Qt-Designer-Widget zu erstetzen.</para
></listitem>
</itemizedlist>
<para
>Der Ölgemälde-Filter verwendet die Multi-Integer-Vorrichtung: </para>
<programlisting
>KisFilterConfigWidget * KisOilPaintFilter::createConfigurationWidget(TQWidget* parent, KisPaintDeviceSP /*dev*/)
{
vKisIntegerWidgetParam param;
param.push_back( KisIntegerWidgetParam( 1, 5, 1, i18n("Brush size"), "brushSize" ) );
param.push_back( KisIntegerWidgetParam( 10, 255, 30, i18n("Smooth"), "smooth" ) );
return new KisMultiIntegerFilterWidget(parent, id().id().ascii(), id().id().ascii(), param );
}
KisFilterConfiguration* KisOilPaintFilter::configuration(TQWidget* nwidget)
{
KisMultiIntegerFilterWidget* widget = (KisMultiIntegerFilterWidget*) nwidget;
if( widget == 0 )
{
return new KisOilPaintFilterConfiguration( 1, 30);
} else {
return new KisOilPaintFilterConfiguration( widget->valueAt( 0 ), widget->valueAt( 1 ) );
}
}
std::list<KisFilterConfiguration*> KisOilPaintFilter::listOfExamplesConfiguration(KisPaintDeviceSP )
{
std::list<KisFilterConfiguration*> list;
list.insert(list.begin(), new KisOilPaintFilterConfiguration( 1, 30));
return list;
}
</programlisting>
<para
>Sehen Sie es in Aktion: füllen Sie einen "vector" mit Ihren Integer-Parametern und erstellen Sie das Widget. Die Methode <methodname
>configuration()</methodname
> untersucht das Widget und erstellt das richtige Konfigurationsobjekt für den Filter, in diesem Fall <classname
>KisOilPaintFilterConfiguration</classname
>. Die Methode <methodname
>listOfExamplesConfiguration</methodname
> (die in richtiges Englisch umbenannt werden sollte...) gibt eine Liste mit Beispielkonfigurationsobjekten für die Filtergalerie zurück. </para>
</sect3>
<sect3 id="developers-plugins-filters-conclusion">
<title
>Zusammenfassung zu Filtern</title>
<para
>Es gehört natürlich mehr dazu, interessante Filter zu schreiben, aber mit dieser Beschreibung, der API-Dokumentation und Zugriff auf unseren Quelltext, sollten Sie in der Lage sein, den Einstieg zu schaffen. Zögern Sie nicht, die &chalk;-Entwickler im IRC oder über die Mailingliste zu kontaktieren. </para>
</sect3>
</sect2>
<sect2 id="developers-plugins-tools">
<title
>Werkzeuge</title>
<para
>Werkzeuge erscheinen in &chalk;s Werkzeugkasten. Das bedeutet, dass der Platz für neue Werkzeuge beschränkt ist — denken Sie darüber nach, ob ein Malverfahren möglicherweise für Ihren Verwendungszweck ausrecht. Werkzeuge können Maus/Tablett und Tastatur auf viele Weise nutzen, was Malverfahren nicht können. Das ist der Grund dafür, dass "Duplizieren" ein Werkzeug ist und "Airbrush" ein Malverfahren. </para
><para
>Seien Sie vorsichtig mit statischen Daten in Ihrem Werkzeug: für jedes Eingabegerät wird eine neue Instanz erzeugt: Maus, Schreibstift, Radierer, Airbrush — was auch immer. Werkzeuge werden in logische Gruppen unterteilt: </para>
<itemizedlist>
<listitem
><para
>Werkzeuge zum Malen von Formen (Kreis, Rechteck)</para
></listitem>
<listitem
><para
>Freihand-Malwerkzeuge (Pinsel)</para
></listitem>
<listitem
><para
>Werkzeuge zum Umformen, die die Geometrie Ihrer Ebene verändern</para
></listitem>
<listitem
><para
>Füllwerkzeuge (wie Zusammenhängend füllen oder Verlauf)</para
></listitem>
<listitem
><para
>Ansichtswerkzeuge (die keine Pixel verändern, aber die Art verändern, wie Sie die Zeichenfläche betrachten, wie z. B. Zoom)</para
></listitem>
<listitem
><para
>Auswahlwerkzeuge (die die Auswahlmaske verändern)</para
></listitem>
</itemizedlist>
<para
>Das Interface für Werkzeuge wird in der API-Dokumentation zu <classname
>KisTool</classname
> beschrieben. Es gibt drei Unterklassen: <classname
>KisToolPaint</classname
>, <classname
>KisToolNonPaint</classname
> und <classname
>KisToolShape</classname
> (was eigentlich eine Unterklasse von <classname
>KisToolPaint</classname
> ist), die <classname
>KisTool</classname
> für Malaufgaben (d. h. zum Ändern von Pixeln), Aufgaben, die nicht malen, und welchen, die Formen malen, spezialisieren. </para
><para
>Ein Werkzeug hat ein Einstellungs-Widget, ebenso wie Filter. Derzeit werden diese Widgets auf einer Karteikarte in einem angedockten Fenster angezeigt. Möglicherweise ändern wir dies für &chalk; 2.0 in einen Streifen unter dem Hauptmenü (der dann die Werkzeugleiste ersetzt), entwerfen Sie Ihr Widget aber vorerst so, dass es auf die Karteikarte passt.Wie immer ist es am besten, dazu den &Qt; Designer zu verwenden. </para
><para
>Ein gutes Beispiel für ein Werkzeug ist das Stern-Werkzeug: </para>
<screen
>kis_tool_star.cpp Makefile.am tool_star_cursor.png wdg_tool_star.ui
kis_tool_star.h Makefile.in tool_star.h
chalktoolstar.desktop tool_star.cpp tool_star.png
</screen>
<para
>Wie Sie sehen, benötigen Sie zwei Symbole. Eines für den Mauszeiger und eines für den Werkzeugkasten. <filename
>tool_star.cpp</filename
> ist nur der Aufrufer für das Modul, ähnlich dem oben gesehenen. Den wichtigen Code enthält die Implementierung: </para>
<programlisting
>KisToolStar::KisToolStar()
: KisToolShape(i18n("Star")),
m_dragging (false),
m_currentImage (0)
{
setName("tool_star");
setCursor(KisCursor::load("tool_star_cursor.png", 6, 6));
m_innerOuterRatio=40;
m_vertices=5;
}
</programlisting>
<para
>Der Konstruktor setzt den internen Namen — der nicht übersetzt wird — und der Aufruf der Überklasse setzt den sichtbaren Namen. Wir laden auch den Mauszeiger und setzten einige Variablen. </para>
<programlisting
>void KisToolStar::update (KisCanvasSubject *subject)
{
KisToolShape::update (subject);
if (m_subject)
m_currentImage = m_subject->currentImg();
}
</programlisting>
<para
>Die Methode <methodname
>update()</methodname
> wird aufgerufen, wenn ein Werkzeug ausgewählt wird. Dies ist keine <classname
>KisTool</classname
>-Methode, sondern eine <classname
>KisCanvasObserver</classname
>-Methode. Canvas Observer (Zeichenflächenüberwachung) werden benachrichtigt, sogald sich etwas in der Ansicht ändert, was nützlich für Werkzeuge sein kann. </para
><para
>Die folgenden Methoden (<methodname
>buttonPress</methodname
>, <methodname
>move</methodname
> und <methodname
>buttonRelease</methodname
>) werden von &chalk; aufgerufen, wenn das Eingebegerät (Maus, Stift, Radierer &etc;) gedrückt, bewegt, oder losgelassen wird.Beachten Sie, dass Sie auch "move"-Ereignisse bekommen, wenn die Maustaste nicht gedrückt ist. Die Ereignisse sind keine normalen &Qt;-Events, sondern künstliche &chalk;-Events, da wir ein paar Low-Level-Tricks verwenden um genügend Ereignisse für eine glatte Linie zu bekommen.Toolkits wie &Qt; (und GTK) lassen Ereignisse fallen, wenn sie zu beschäftigt sein, und wir brauchen alle. </para>
<programlisting
>void KisToolStar::buttonPress(KisButtonPressEvent *event)
{
if (m_currentImage && event->button() == LeftButton) {
m_dragging = true;
m_dragStart = event->pos();
m_dragEnd = event->pos();
m_vertices = m_optWidget->verticesSpinBox->value();
m_innerOuterRatio = m_optWidget->ratioSpinBox->value();
}
}
void KisToolStar::move(KisMoveEvent *event)
{
if (m_dragging) {
// erase old lines on canvas
draw(m_dragStart, m_dragEnd);
// move (alt) or resize star
if (event->state() & Qt::AltButton) {
KisPoint trans = event->pos() - m_dragEnd;
m_dragStart += trans;
m_dragEnd += trans;
} else {
m_dragEnd = event->pos();
}
// draw new lines on canvas
draw(m_dragStart, m_dragEnd);
}
}
void KisToolStar::buttonRelease(KisButtonReleaseEvent *event)
{
if (!m_subject || !m_currentImage)
return;
if (m_dragging && event->button() == LeftButton) {
// erase old lines on canvas
draw(m_dragStart, m_dragEnd);
m_dragging = false;
if (m_dragStart == m_dragEnd)
return;
if (!m_currentImage)
return;
if (!m_currentImage->activeDevice())
return;
KisPaintDeviceSP device = m_currentImage->activeDevice ();;
KisPainter painter (device);
if (m_currentImage->undo()) painter.beginTransaction (i18n("Star"));
painter.setPaintColor(m_subject->fgColor());
painter.setBackgroundColor(m_subject->bgColor());
painter.setFillStyle(fillStyle());
painter.setBrush(m_subject->currentBrush());
painter.setPattern(m_subject->currentPattern());
painter.setOpacity(m_opacity);
painter.setCompositeOp(m_compositeOp);
KisPaintOp * op =
KisPaintOpRegistry::instance()->paintOp(m_subject->currentPaintop(), m_subject->currentPaintopSettings(), &painter);
painter.setPaintOp(op); // Painter takes ownership
vKisPoint coord = starCoordinates(m_vertices, m_dragStart.x(), m_dragStart.y(), m_dragEnd.x(), m_dragEnd.y());
painter.paintPolygon(coord);
device->setDirty( painter.dirtyRect() );
notifyModified();
if (m_currentImage->undo()) {
m_currentImage->undoAdapter()->addCommand(painter.endTransaction());
}
}
}
</programlisting>
<para
>Die Methode <methodname
>draw()</methodname
> ist eine interne Methode von <classname
>KisToolStar</classname
> und malt den Umriss eines Sterns. Wir rufen dieses aud <methodname
>move()</methodname
> auf um dem Benutzer Rückmeldungen über die Größe und Form des Sterns zu geben. Beachten Sie, dass wir die Rasteroperation <varname
>Qt::NotROP</varname
> verwenden, was bedeutet, dass ein zweiter Aufruf von <methodname
>draw()</methodname
> mit dem gleichen Start- und Endpunkt den vorherigen Stern löscht. </para>
<programlisting
>void KisToolStar::draw(const KisPoint& start, const KisPoint& end )
{
if (!m_subject || !m_currentImage)
return;
KisCanvasController *controller = m_subject->canvasController();
KisCanvas *canvas = controller->kiscanvas();
KisCanvasPainter p (canvas);
QPen pen(Qt::SolidLine);
KisPoint startPos;
KisPoint endPos;
startPos = controller->windowToView(start);
endPos = controller->windowToView(end);
p.setRasterOp(Qt::NotROP);
vKisPoint points = starCoordinates(m_vertices, startPos.x(), startPos.y(), endPos.x(), endPos.y());
for (uint i = 0; i < points.count() - 1; i++) {
p.drawLine(points[i].floorQPoint(), points[i + 1].floorQPoint());
}
p.drawLine(points[points.count() - 1].floorQPoint(), points[0].floorQPoint());
p.end ();
}
</programlisting>
<para
>Die Methode <methodname
>setup()</methodname
> ist notwendig: hier erstellen wir die Aktion, die in den Werkzeugkasten integriert wird, damit Benutzer das Werkzeug auswählen können. Zudem weisen wir ein Tastenkürzel zu. Beachten Sie, dass an diesem Bereich gerade gearbeitet wird: es könnten Dinge geändert werden. </para>
<programlisting
>void KisToolStar::setup(TDEActionCollection *collection)
{
m_action = static_cast<TDERadioAction *>(collection->action(name()));
if (m_action == 0) {
TDEShortcut shortcut(Qt::Key_Plus);
shortcut.append(TDEShortcut(Qt::Key_F9));
m_action = new TDERadioAction(i18n("&Star"),
"tool_star",
shortcut,
this,
SLOT(activate()),
collection,
name());
TQ_CHECK_PTR(m_action);
m_action->setToolTip(i18n("Draw a star"));
m_action->setExclusiveGroup("tools");
m_ownAction = true;
}
}
</programlisting>
<para
>Die <methodname
>starCoordinates()</methodname
>-Methode enthält etwas erschreckende Mathematik — sie ist aber nicht so interessant in der Auseinandersetzung mit dem Erstellen eines Werkzeugmoduls. </para>
<programlisting
>KisPoint KisToolStar::starCoordinates(int N, double mx, double my, double x, double y)
{
double R=0, r=0;
Q_INT32 n=0;
double angle;
vKisPoint starCoordinatesArray(2*N);
// the radius of the outer edges
R=sqrt((x-mx)*(x-mx)+(y-my)*(y-my));
// the radius of the inner edges
r=R*m_innerOuterRatio/100.0;
// the angle
angle=-atan2((x-mx),(y-my));
//set outer edges
for(n=0;n<N;n++){
starCoordinatesArray[2*n] = KisPoint(mx+R*cos(n * 2.0 * M_PI / N + angle),my+R*sin(n *2.0 * M_PI / N+angle));
}
//set inner edges
for(n=0;n<N;n++){
starCoordinatesArray[2*n+1] = KisPoint(mx+r*cos((n + 0.5) * 2.0 * M_PI / N + angle),my+r*sin((n +0.5) * 2.0 * M_PI / N + angle));
}
return starCoordinatesArray;
}
</programlisting>
<para
>Die Methode <methodname
>createOptionWidget()</methodname
> wird aufgerufen, um ein Einstellungs-Widget zu erstellen, das &chalk; auf der Karteikarte anzeigt. Da es ein Werkzeug pro Eingabegeräte pro Ansicht ist, kann der Zustand eines Werkzeugs im Werkzeug gehalten werden. Diese Methode wird nur einmal aufgerufen: das Einstellungs-Widget wird gespeichert und beim nächsten Aufruf wieder hergestellt. </para>
<programlisting
>TQWidget* KisToolStar::createOptionWidget(TQWidget* parent)
{
TQWidget *widget = KisToolShape::createOptionWidget(parent);
m_optWidget = new WdgToolStar(widget);
TQ_CHECK_PTR(m_optWidget);
m_optWidget->ratioSpinBox->setValue(m_innerOuterRatio);
QGridLayout *optionLayout = new QGridLayout(widget, 1, 1);
super::addOptionWidgetLayout(optionLayout);
optionLayout->addWidget(m_optWidget, 0, 0);
return widget;
}
</programlisting>
<sect3 id="developers-plugins-tools-conclusions">
<title
>Zusammenfassung über Werkzeuge</title>
<para
>Werkzeuge sind nicht schwer zu erstellen. Sie müssen die Schnittstellen von <classname
>KisTool</classname
> und <classname
>KisCanvasObserver</classname
> kombinieren, um ein Werkzeug zu erstellen. </para>
</sect3>
</sect2>
<sect2 id="developers-plugins-paintoperations">
<title
>Malverfahren</title>
<para
>Malverfahren gehören zu den innovativeren Arten von Modulen in Chalk (zusammen mit modularen Farbräumen). Ein Malverfahren definiert, wie Werkzeuge die Pixel verändern, die sie berühren. Airbrush, Treppeneffekt-Pinsel, geglätteter Pixelpinsel: all das sind Malverfahren. Sie können auch — mit viel Arbeit — ein Malverfahren erstellen, das Pinseldefinitionen aus Corel-Painter-XML liest und diese verwendet um zu bestimmen, wie gemalt wird. </para
><para
>Malverfahren werden instanziiert, wenn ein Malwerkzeug ein <literal
>mouseDown</literal
>-Ereignis empfängt, und werden bei einem mouseUp-Ereignis gelöscht. Dazwischen kann das Malverfahren vorherige Positionen und andere Daten, wie Druckstufen beobachten, wenn der Benutzer ein Tablett benutzt. </para
><para
>Die grundlegende Funktion eines Malverfahrens ist, die Pixel an der Mauszeigerposition zu verändern. Dies kann einmal gemacht werden, oder das Malverfahren kann verlangen, in regelmäßigen Abständen zu laufen. Das erste Verfahren ist z. B. für einen Stift nützlich, das zweite z. B. für ein Airbrush-Malverfahren. </para
><para
>Malverfahren können ein kleines Einstellungs-Widget haben, das in einer Werkzeugleiste plaziert wird. Daher sollte es horizontal aufgebaut sein und nicht höher als ein Werkzeugleistenknopf sein. </para
><para
>Lassen Sie uns ein einfaches Malverfahrenmodul betrachten, eines das etwas programmierte Intelligenz zeigt. Zuerst wird in der Header-Datei eine Fabrik definiert. Diesee Fabrik erstellt ein Malverfahren, wenn das aktive Werkzeug eines benötigt: </para>
<programlisting
>public:
KisSmearyOpFactory() {}
virtual ~KisSmearyOpFactory() {}
virtual KisPaintOp * createOp(const KisPaintOpSettings *settings, KisPainter * painter);
virtual KisID id() { return KisID("paintSmeary", i18n("Smeary Brush")); }
virtual bool userVisible(KisColorSpace * ) { return false; }
virtual TQString pixmap() { return ""; }
};
</programlisting>
<para
>Die Fabrik enthält auch die <classname
>KisID</classname
> mit dem privaten und öffentlichen Namen für das Malverfahren — stellen Sie sicher, dass der private Name Ihres Malverfahrens nicht mit anderen Malverfahren kollidiert! — und kann wahlweise eine Pixmap zurück geben. &chalk; kann diese Pixmap zusammen mit dem Namen anzeigen, um den Wiedererkennungswert zu erhöhen. </para
><para
>Die Anwendung eines Malverfahrens ist überschaubar: </para>
<programlisting
>KisSmearyOp::KisSmearyOp(KisPainter * painter)
: KisPaintOp(painter)
{
}
KisSmearyOp::~KisSmearyOp()
{
}
void KisSmearyOp::paintAt(const KisPoint &pos, const KisPaintInformation& info)
{
</programlisting>
<para
>Die Methode <methodname
>paintAt()</methodname
> ist der Punkt, an dem alles passiert, was mit Malverfahren zu tun hat. Sie übernimmt zwei Parameter: die aktuelle Position (in Floats, nicht in ganzen Pixeln) und ein Objekt der Klasse <classname
>KisPaintInformation</classname
>, das die Druckstufe, X- und Y-Winkel und die Bewegungsrichtung enthält. In Zukunft könnten diese Informationen erweitert werden. </para>
<programlisting
>if (!m_painter->device()) return;
KisBrush *brush = m_painter->brush();
</programlisting>
<para
>Ein <classname
>KisBrush</classname
>-Objekt ist die Vertretung für eine Gimp-Pinsel-Datei: es ist eine Maske oder eine Reihe von Masken. Eigentlich verwenden wir hier den Pinsel nicht, außer um den <quote
>hotspot</quote
> unter dem Mauszeiger zu bestimmen. </para>
<programlisting
>Q_ASSERT(brush);
if (!brush) return;
if (! brush->canPaintFor(info) )
return;
KisPaintDeviceSP device = m_painter->device();
KisColorSpace * colorSpace = device->colorSpace();
KisColor kc = m_painter->paintColor();
kc.convertTo(colorSpace);
KisPoint hotSpot = brush->hotSpot(info);
KisPoint pt = pos - hotSpot;
// Split the coordinates into integer plus fractional parts. The integer
// is where the dab will be positioned and the fractional part determines
// the sub-pixel positioning.
Q_INT32 x, y;
double xFraction, yFraction;
splitCoordinate(pt.x(), &x, &xFraction);
splitCoordinate(pt.y(), &y, &yFraction);
KisPaintDeviceSP dab = new KisPaintDevice(colorSpace, "smeary dab");
TQ_CHECK_PTR(dab);
</programlisting>
<para
>Wir ändern die Pixel einer Maleinheit nicht direkt: stattdessen erstellen wir eine kleine Maleinheit, einen Tupfer, und fügen das der aktuellen Maleinheit hinzu. </para>
<programlisting
>m_painter->setPressure(info.pressure);
</programlisting>
<para
>Wie den Kommentaren zu entnehmen ist, wird hier der eigentliche Tupfer erstellt. In diesem Fall malen wir ein paar Linien. Wenn das Malverfahren fertig ist, werden die Länge, die Position und die Dicke der Linien von der Druckstufe und der Farbmenge äbhängig sein, und wir müssen eine harte, schmierige Ölfarbenpinsel erstellen. Dazu fehlte bisher aber die Zeit. </para>
<programlisting
>// Compute the position of the tufts. The tufts are arranged in a line
// perpendicular to the motion of the brush, i.e, the straight line between
// the current position and the previous position.
// The tufts are spread out through the pressure
KisPoint previousPoint = info.movement.toKisPoint();
KisVector2D brushVector(-previousPoint.y(), previousPoint.x());
KisVector2D currentPointVector = KisVector2D(pos);
brushVector.normalize();
KisVector2D vl, vr;
for (int i = 0; i < (NUMBER_OF_TUFTS / 2); ++i) {
// Compute the positions on the new vector.
vl = currentPointVector + i * brushVector;
KisPoint pl = vl.toKisPoint();
dab->setPixel(pl.roundX(), pl.roundY(), kc);
vr = currentPointVector - i * brushVector;
KisPoint pr = vr.toKisPoint();
dab->setPixel(pr.roundX(), pr.roundY(), kc);
}
vr = vr - vl;
vr.normalize();
</programlisting>
<para
>Schleißlich kopieren wir den Tupfer in die eigentliche Maleinheit und sagen dem Painter, dass wir ein kleines Rechteck der Maleinheit beschmutzt haben. </para>
<programlisting
>if (m_source->hasSelection()) {
m_painter->bltSelection(x - 32, y - 32, m_painter->compositeOp(), dab.data(),
m_source->selection(), m_painter->opacity(), x - 32, y -32, 64, 64);
}
else {
m_painter->bitBlt(x - 32, y - 32, m_painter->compositeOp(), dab.data(), m_painter->opacity(), x - 32, y -32, 64, 64);
}
m_painter->addDirtyRect(QRect(x -32, y -32, 64, 64));
}
KisPaintOp * KisSmearyOpFactory::createOp(const KisPaintOpSettings */*settings*/, KisPainter * painter)
{
KisPaintOp * op = new KisSmearyOp(painter);
TQ_CHECK_PTR(op);
return op;
}
</programlisting>
<para
>Das ist alles: Malverfahren sind einfach und machen Spaß. </para>
</sect2>
<sect2 id="developers-plugins-viewplugins">
<title
>Ansichtenmodule</title>
<para
>Ansichtenmodule sind die bizarrsten im Bunde: ein Ansichtenmodul ist eine normale KPart-Anwendung, die Teile einer Benutzeroberfläche und etwas Funktionalität bereitstellt. Z. B. sind die Karteikarte für das Histogramm und der "Drehen"-Dialog Ansichtenmodule. </para>
</sect2>
<sect2 id="developers-plugins-importexport">
<title
>Import/Export-Filter</title>
<para
>&chalk; arbeitet mit der normalen &koffice;-Dateifilter-Architektur. Es gibt eine Anleitung, etwas alt, aber dennoch nützlich, unter <ulink url="http://koffice.org/developer/filters/oldfaq.php"
></ulink
>. Es ist warscheinlich das beste, beim Erstellen von Dateifiltern mit dem &chalk;-Team zusammen zu arbeiten, und im &koffice;-Filter zu entwickeln. Beachten Sie, dass Sie Ihren Filter testen können ohne &chalk; zu starten. Benutzen Sie dazu das Programm <command
>koconverter</command
>. </para
><para
>Filter haben zwei Seiten: importieren und exportieren. Dies sind normalerweise zwei verschiedene Module, die sich etwas Code teilen können. </para
><para
>Die wichtigen Einträge in <filename
>Makefile.am</filename
> sind: </para>
<programlisting
>service_DATA = chalk_XXX_import.desktop chalk_XXX_export.desktop
servicedir = $(kde_servicesdir)
kdelnk_DATA = chalk_XXX.desktop
kdelnkdir = $(kde_appsdir)/Office
libchalkXXXimport_la_SOURCES = XXXimport.cpp
libchalkXXXexport_la_SOURCES = XXXexport.cpp
METASOURCES = AUTO
</programlisting>
<para
>Ob Sie Import- oder Exportfilter rstellen, es läuft immer auf die Implementierung der folgenden Funktion hinaus: </para>
<programlisting
>virtual KoFilter::ConversionStatus convert(const QCString& from, const QCString& to);
</programlisting>
<para
>Die Einstellungen in der <literal role="extension"
>.desktop</literal
>-Datei bestimmen, in welcher Richtung der Filter konvertiert: </para
><para
>Importieren: </para>
<programlisting
>X-TDE-Export=application/x-chalk
X-TDE-Import=image/x-xcf-gimp
X-TDE-Weight=1
X-TDE-Library=libchalkXXXimport
ServiceTypes=KOfficeFilter
</programlisting>
<para
>Exportieren: </para>
<programlisting
>X-TDE-Export=image/x-xcf-gimp
X-TDE-Import=application/x-chalk
ServiceTypes=KOfficeFilter
Type=Service
X-TDE-Weight=1
X-TDE-Library=libchalkXXXexport
</programlisting>
<para
>Der Mimetype, der für das Beispiel gewählt wurde, ist ein Hinweis. Würden Sie bitte einen xcf-Filter implementieren? </para>
<sect3 id="plugins-developers-importexport-import">
<title
>Importieren</title>
<para
>Das große Problem mit Imprtfiltern ist, die Daten von dem Datenträger zu lesen. Die Vorlage, diesen Code aufzurufen, ist recht einfach: </para>
<note
><para
>Notitz: Wir sollten einen Weg finden, &chalk; zu ermöglichen, eine Datei offen zu halten, und Daten nur zu lesen, wenn es nötig ist, anstatt den gesamten Inhalt in die interne Vertretung der Maleinheit zu kopieren. Das ist jedoch derzeit nicht umgesetzt.</para
></note>
<programlisting
>KoFilter::ConversionStatus XXXImport::convert(const QCString&, const QCString& to)
{
if (to != "application/x-chalk") <co id="import1" />
return KoFilter::BadMimeType;
KisDoc * doc = dynamic_cast<KisDoc*>(m_chain -> outputDocument()); <co id="import2" />
KisView * view = static_cast<KisView*>(doc -> views().getFirst()); <co id="import3" />
TQString filename = m_chain -> inputFile(); <co id="import4" />
if (!doc)
return KoFilter::CreationError;
doc -> prepareForImport(); <co id="import5" />
if (!filename.isEmpty()) {
KURL url(filename);
if (url.isEmpty())
return KoFilter::FileNotFound;
KisImageXXXConverter ib(doc, doc -> undoAdapter()); <co id="import6" />
if (view != 0)
view -> canvasSubject() -> progressDisplay() -> setSubject(&ib, false, true);
switch (ib.buildImage(url)) <co id="import7" /> {
case KisImageBuilder_RESULT_UNSUPPORTED:
return KoFilter::NotImplemented;
break;
case KisImageBuilder_RESULT_INVALID_ARG:
return KoFilter::BadMimeType;
break;
case KisImageBuilder_RESULT_NO_URI:
case KisImageBuilder_RESULT_NOT_LOCAL:
return KoFilter::FileNotFound;
break;
case KisImageBuilder_RESULT_BAD_FETCH:
case KisImageBuilder_RESULT_EMPTY:
return KoFilter::ParsingError;
break;
case KisImageBuilder_RESULT_FAILURE:
return KoFilter::InternalError;
break;
case KisImageBuilder_RESULT_OK:
doc -> setCurrentImage( ib.image()); <co id="import8" />
return KoFilter::OK;
default:
break;
}
}
return KoFilter::StorageCreationError;
}
</programlisting>
<calloutlist>
<callout arearefs="import1"
><para
>Das soll ein Importfilter sein. Wenn er nicht zum Konvertieren eines Bildes in &chalk; aufgerufen wird, ist irgendetwas falsch.</para
></callout>
<callout arearefs="import2"
><para
>Die Filterkette hat für uns ein Ausgabedokument erstellt. Wir müssen es in ein <classname
>KisDocM</classname
> casten, da &chalk;-Dokumente besonderer Behandlung bedürfen. Sie sollten überprüfen, dass das Ergebnis des Casts nicht 0 ist, da, wenn er 0 ist, der Import fehlschlägt.</para
></callout>
<callout arearefs="import3"
><para
>Wenn wir diesen Filter aus der GUI aufrufen, versuchen wir, die Ansicht zu bekommen. Gibt es eine Ansicht, kann der Konvertierungscode versuchen, die Fortschrittsanzeige zu aktualisieren.</para
></callout>
<callout arearefs="import4"
><para
>Der Filter hat für uns den Dateinamen der Eingabedatei.</para
></callout>
<callout arearefs="import5"
><para
><classname
>KisDoc</classname
> muss für den Import verbereitet werden. Bestimmte Einstellungen werden initialisiert und die "Rückgäning"-Funktion wird deaktiviert. Sonst könnten Sie Teilschritte des Filters rückgängig machen, was seltsames Verhalten wäre.</para
></callout>
<callout arearefs="import6"
><para
>Ich habe beschlossen, den eigentlichen Import-Code in eine separate Klasse auszulagern, die ich hier instanziire. Sie können Ihren ganzen Code hier hinein schreiben, was allerdings etwas chaotisch wäre.</para
></callout>
<callout arearefs="import7"
><para
>Mein Importeur gibt einen Statuscode zurück, den ich verwenden kann, um den Status des Importfilters zu setzen. &koffice; kümmert sich um die Anzeige von Fehlermeldungen.</para
></callout>
<callout arearefs="import8"
><para
>Wenn die Erstellunge von <classname
>KisImage</classname
> erfolgreich war, setzen wir das aktuelle Bild des Dokuments auf das neu erstellte Bild. Wir sind nun fertig: <literal
>return KoFilter::OK</literal
>.</para
></callout>
</calloutlist>
</sect3>
</sect2>
</sect1>
|